리눅스 환경에서의 프로그램의 배포
시작하며
리눅스에서 프로그램을 배포하는 방법은 다양하게 존재합니다. 데비안 계열의 경우 deb, 레드햇 계열의 경우 rpm 등과 같은 바이너리 패키지를 이용해서 실행 파일 뿐만 아니라 관련 라이브러리, 설정 파일 등을 묶어서 제공할 수 있습니다. 하지만 배포판 종류에 따라 바이너리 패키지 파일 형식이 다르기 때문에 다양한 배포판에서도 설치 및 실행이 가능하려면 지원하려는 배포판의 종류에 맞게 바이너리 패키지를 구성해야하는 단점이 있습니다. 특정 배포판이 아닌 다양한 리눅스 환경에서도 설치 및 실행이 가능하도록 하려면 조금 더 원론적인 방법으로 배포할 수 있는 방법을 선택해야 합니다. VMware 의 인스톨러 프로그램이나 nVidia 의 드라이버 설치 프로그램, Firefox 의 인스톨러 등이 그러한 예 입니다. 이들은 압축 파일 형식으로 제공되서 사용자가 직접 압축을 풀고 실행하거나 또는 하나의 실행 파일로 제공되서 실행시키면 자동으로 설치를 진행하거나 합니다. 어찌되었든 중요한 것은 대부분의 리눅스 환경에서 설치 및 실행이 가능하며 사용자가 쉽게 설치할 수 있다는 점입니다. 문서에서는 리눅스 환경에서 프로그램을 배포할 때 생길 수 있는 문제와 그에 대한 경험적인 해결 방법을 정리합니다.
전제 조건
프로그램의 배포에 있어 전제 조건은 다음과 같습니다.
- 일반적인 리눅스 시스템에서 설치 및 실행이 될 것
- 루트 권한일 경우와 그렇지 않을 경우 구분해서 설치할 것
리눅스의 실행 파일은 다른 운영체제의 실행 파일 형식과 다를지는 몰라도 정적 라이브러리와 동적 라이브러리를 사용한다는 점에서는 대동소이합니다. 컴파일 시 링크 옵션에 따라 정적으로 연결되어 라이브러리가 바이너리에 포함되거나 동적으로 연결되어 라이브러리가 바이너리에 포함되지 않기도 합니다. 다양한 리눅스 환경에서 동작 가능한 실행 파일을 배포할 때 가장 문제가 되는 부분은 실행 파일 안에 포함되지 않는 동적 라이브러리입니다. 동적 라이브러리는 시스템의 라이브러리 경로에 존재하며 실행하는 시점에 찾아서 연결하기 때문에 실행하는 시스템에 해당 동적 라이브러리가 없을 경우 제대로 실행을 시킬 수 없습니다. 그러므로 실행 파일 뿐만 아니라 실행 파일이 필요로 하는 동적 라이브러리도 같이 포함해야지만 프로그램이 제대로 동작하는 것을 보장할 수 있습니다.
리눅스에서는 사용자 계정의 권한이 존재하며 시스템 전반의 모든 항목에 대해 권한을 갖는 루트 사용자와 그렇지 않은 일반 사용자로 나뉩니다. 시스템 디렉터리에 파일을 추가하거나 변경할 수 있는 루트 사용자와는 달리 일반 사용자는 보통 자신의 홈디렉터리 하부의 파일에 대해서만 갱신이 가능합니다. 이렇게 사용자 계정의 권한에 따라 시스템 디렉터리에 프로그램을 설치 가능 여부가 달라지기 때문에 루트 권한인 경우는 시스템 디렉터리에 일반 사용자의 경우 사용자가 접근할 수 있는 디렉터리에 설치할 수 있는 기능이 필요합니다.
설치
tar 볼 압축 파일
배포할 프로그램을 tar 볼 압축 파일 형태로 제공하는 것은 가장 일반적이며 널리 사용하는 방식입니다.
사용자는 압축 파일을 다운로드 받은 후 압축 해제를 한 후 해당 디렉터리 하부에서 바로
실행을 하거나 또는 install.sh 등의 쉘 스크립트를 실행해서 시스템에 설치합니다.
보통 README 또는 INSTALL 등의 텍스트 파일을 두어서 사용자의 설치 과정을 도와줍니다.
가장 일반적인 형태로 압축을 푼 후의 디렉터리 구조와 install.sh 만 잘 만들어 놓으면
되기 때문에 가장 간편하다는 것이 장점이지만 사용자가 직접 압축을 풀고
특정 쉘 스크립트를 실행해야 하기 때문에 번거로울 수 있다는 점이 단점입니다.
shar 쉘 아카이브
우분투의 경우 sharutils 라는 패키지가 있는데 이 패키지를 설치하면
쉘 아카이브를 만들어주는 shar 유틸리티를 사용할 수 있습니다.
shar 를 이용해서 만든 쉘 아카이브는 앞 부분이 평범한 쉘 스크립트이기 때문에
사용자는 sh prog.shar 와 같은 식으로 실행하면 압축을 풀 수 있습니다.
단지 실행만 시키면 되기 때문에 압축 파일을 해제할 줄 모르는 사용자에게는
tar 볼 형태로 제공하는 것에 비해 설치하기 쉬워 보일 수 있을지 모르나
shar 로 묶은 파일 그 자체는 tar 볼 압축 파일과 다를 바가 없으므로
압축 해제 후 다시 install.sh 등의 쉘 스크립트를 실행해서 설치를 진행해야 합니다.
또한 tar 프로그램이 대부분의 시스템에 존재하는 것과는 달리 shar 로 묶은
쉘 아카이브를 풀려면 sharutils 패키지 안에 있는 uudecode 프로그램이
있어야 하기 때문에 자칫 사용자가 압축을 해제하지 못할 수도 있습니다.
또한 tar 볼 압축에 피해 압축 파일의 용량이 크거나 압축 해제시 시간이
상대적으로 더 걸리는 것도 단점입니다.
tar 볼 쉘 아카이브
tar 볼 압축 파일과 shar 쉘 아카이브의 장점을 묶은 방법도 있습니다.
tar 볼 압축 파일의 장점은 대부분의 리눅스 시스템에서 해제를 할 수 있으며
속도 및 압축률이 뛰어나다는 것입니다.
shar 쉘 아카이브는 사용자가 tar 라는 프로그램을 사용하지 않고
파일 그 자체를 실행하기 때문에 상대적으로 더 쉽게 느낄 수 있으며
그 자체가 쉘 스크립트이므로 압축 해제 전 후에 필요한
사전 또는 사후 작업을 유연하게 처리할 수 있다는 것이 장점입니다.
쉘 아카이브는 다음처럼 만들 수 있습니다:
#!/bin/sh
PROG_NAME="dvr-cms"
INSTALL_DIR="/usr/lib/$PROG_NAME"
TEMP_DIR="/tmp/$PROG_NAME"
WORK_DIR=$PWD
#
# before decompressing
#
cp -f $0 $TEMP_DIR
cd $TEMP_DIR
SKIP=`awk '/^__END_OF_HEADER__/ { print NR + 1; exit 0; }' $0`
tail +$SKIP `basename $0` | tar xz
cd $WORK_DIR
cp -r $TEMP_DIR $INSTALL_DIR
#
# after decompressing
#
__END_OF_HEADER__
앞의 헤더 쉘 스크립트 파일 명을 dvr-cms.template 이라고 하고
배포할 프로그램의 디렉터리 구조를 압축한 파일이 dvr-cms.tar.gz 라고하면
다음 명령을 통해서 tar 볼 쉘 아카이브를 제작할 수 있습니다:
cat dvr-cms.template dvr-cms.tar.gz > dvr-cms.bin
이렇게 제작한 dvr-cms.bin 프로그램은 다음 명령을 이용해서 설치할 수 있습니다:
sh dvr-cms.bin
위의 쉘 아카이브 헤더는 단순히 압축을 해제해서 복사하는 동작만 수행하지만 압축을 해제하는 코드 전후로 원하는 작업을 추가할 수 있습니다.
사용자 별 설치 위치 변경
루트 사용자나 일반 사용자에 따라 설치 위치를 달리 하려면 압축을 해제하기 전에 설치용 쉘 아카이브를 실행시킨 사용자를 확인할 필요가 있습니다. 코드는 다음과 같습니다:
#!/bin/sh
PROG_NAME="dvr-cms"
INSTALL_DIR="/usr/lib/$PROG_NAME"
TEMP_DIR="/tmp/$PROG_NAME"
WORK_DIR=$PWD
#
# before decompressing
#
if [ "x$ID" != "xroot" ]; then
# normal user
if [ "x$1" != "x" ]; then
INSTALL_DIR="$1"
else
INSTALL_DIR="dvr-cms"
fi
fi
동적 라이브러리
구성 요소 확인
프로그램을 배포할 때 주의해야할 것 중 하나는 실행 파일이 사용하는 동적 라이브러리입니다.
동적 라이브러리는 실행 파일에 포함되어 있지 않기 때문에 실행하는 시스템의 라이브러리 경로에
해당 동적 라이브러리가 반드시 존재해야지만 프로그램을 제대로 실행할 수 있습니다.
실행 파일이 필요로 하는 동적 라이브러리 목록을 알고 싶으면
binutils 유틸리티 중 하나인 ldd 명령을 이용합니다:
$ ldd dvr-cms
linux-gate.so.1 => (0xb7f4c000)
libsndfile.so.1 => /usr/lib/libsndfile.so.1 (0xb7edc000)
libgtk-x11-2.0.so.0 => /usr/lib/libgtk-x11-2.0.so.0 (0xb7b2b000)
libgdk-x11-2.0.so.0 => /usr/lib/libgdk-x11-2.0.so.0 (0xb7a9d000)
libatk-1.0.so.0 => /usr/lib/libatk-1.0.so.0 (0xb7a82000)
libgdk_pixbuf-2.0.so.0 => /usr/lib/libgdk_pixbuf-2.0.so.0 (0xb7a68000)
libm.so.6 => /lib/tls/i686/cmov/libm.so.6 (0xb7a42000)
libpangocairo-1.0.so.0 => /usr/lib/libpangocairo-1.0.so.0 (0xb7a36000)
libcairo.so.2 => /usr/lib/libcairo.so.2 (0xb79bb000)
libpango-1.0.so.0 => /usr/lib/libpango-1.0.so.0 (0xb7978000)
libgobject-2.0.so.0 => /usr/lib/libgobject-2.0.so.0 (0xb793a000)
libglib-2.0.so.0 => /usr/lib/libglib-2.0.so.0 (0xb7882000)
libgthread-2.0.so.0 => /usr/lib/libgthread-2.0.so.0 (0xb787c000)
libasound.so.2 => /usr/lib/libasound.so.2 (0xb77b3000)
libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7650000)
libXext.so.6 => /usr/lib/libXext.so.6 (0xb7640000)
libX11.so.6 => /usr/lib/libX11.so.6 (0xb7551000)
libdl.so.2 => /lib/tls/i686/cmov/libdl.so.2 (0xb754d000)
libFLAC.so.8 => /usr/lib/libFLAC.so.8 (0xb74fa000)
libXrender.so.1 => /usr/lib/libXrender.so.1 (0xb74ef000)
libXinerama.so.1 => /usr/lib/libXinerama.so.1 (0xb74ec000)
libXi.so.6 => /usr/lib/libXi.so.6 (0xb74e2000)
libXrandr.so.2 => /usr/lib/libXrandr.so.2 (0xb74da000)
libXcursor.so.1 => /usr/lib/libXcursor.so.1 (0xb74d1000)
libXcomposite.so.1 => /usr/lib/libXcomposite.so.1 (0xb74cc000)
libXdamage.so.1 => /usr/lib/libXdamage.so.1 (0xb74c9000)
libXfixes.so.3 => /usr/lib/libXfixes.so.3 (0xb74c4000)
libgio-2.0.so.0 => /usr/lib/libgio-2.0.so.0 (0xb7456000)
libpangoft2-1.0.so.0 => /usr/lib/libpangoft2-1.0.so.0 (0xb742d000)
libfreetype.so.6 => /usr/lib/libfreetype.so.6 (0xb73b6000)
libz.so.1 => /lib/libz.so.1 (0xb739f000)
libfontconfig.so.1 => /usr/lib/libfontconfig.so.1 (0xb7372000)
libgmodule-2.0.so.0 => /usr/lib/libgmodule-2.0.so.0 (0xb736d000)
/lib/ld-linux.so.2 (0xb7f4d000)
libpixman-1.so.0 => /usr/lib/libpixman-1.so.0 (0xb732a000)
libdirectfb-1.0.so.0 => /usr/lib/libdirectfb-1.0.so.0 (0xb72c4000)
libfusion-1.0.so.0 => /usr/lib/libfusion-1.0.so.0 (0xb72ba000)
libdirect-1.0.so.0 => /usr/lib/libdirect-1.0.so.0 (0xb72a5000)
libpthread.so.0 => /lib/tls/i686/cmov/libpthread.so.0 (0xb728c000)
libpng12.so.0 => /usr/lib/libpng12.so.0 (0xb7266000)
libxcb-render-util.so.0 => /usr/lib/libxcb-render-util.so.0 (0xb7261000)
libxcb-render.so.0 => /usr/lib/libxcb-render.so.0 (0xb7258000)
libxcb.so.1 => /usr/lib/libxcb.so.1 (0xb723e000)
libpcre.so.3 => /lib/libpcre.so.3 (0xb720c000)
librt.so.1 => /lib/tls/i686/cmov/librt.so.1 (0xb7203000)
libXau.so.6 => /usr/lib/libXau.so.6 (0xb71ff000)
libogg.so.0 => /usr/lib/libogg.so.0 (0xb71f8000)
libselinux.so.1 => /lib/libselinux.so.1 (0xb71de000)
libexpat.so.1 => /usr/lib/libexpat.so.1 (0xb71b7000)
libXdmcp.so.6 => /usr/lib/libXdmcp.so.6 (0xb71b2000)
$
범위의 결정
ldd 실행 결과로 나오는 라이브러리 목록중 하나라도 빠지면 프로그램을 실행시킬 수 없습니다.
원칙적으로는 모든 모듈을 다 가지고 있어야하지만 대부분의 리눅스 시스템에 설치된
라이브러리도 있으므로 어떤 라이브러리를 포함할지 아니면 제외할지 결정해야 합니다.
linux-gate.so.1 의 경우 vdso (Virtual DSO) 라고하며 실제로 배포하기 위해서
공유 라이브러리를 수집할 때 따로 취할 필요가 없기 때문에 신경쓰지 않아도 됩니다.
ldd 는 의존 관계까지 고려해서 보여주므로 예를들어 dvr-cms 프로그램이 gtk 라이브러리에
의존해있다면 gdk 라이브러리까지 같이 보여줍니다.
그러므로 프로그램에서 확실하게 사용하지 않는 라이브러리가 있다면 제외할 수 있습니다.
dvr-cms 는 libsndfile 라이브러리에 의존하는데 libsndfile 가 의존하는 동적 라이브러리는
다음과 같습니다:
$ ldd /usr/lib/libsndfile.so.1
linux-gate.so.1 => (0xb80cc000)
libFLAC.so.8 => /usr/lib/libFLAC.so.8 (0xb8009000)
libm.so.6 => /lib/tls/i686/cmov/libm.so.6 (0xb7fe3000)
libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7e7f000)
libogg.so.0 => /usr/lib/libogg.so.0 (0xb7e79000)
/lib/ld-linux.so.2 (0xb80cd000)
$
그러므로 dvr-cms 프로그램 역시 libFLAC 과 libogg 라이브러리에 의존합니다. 하지만 dvr-cms 프로그램은 내부적으로 libsndfile 의 기능 중 FLAC 과 ogg 관련한 기능을 전혀 사용하지 않으므로 두 라이브러리는 배포할 때 없어도 됩니다.
또한 대부분의 리눅스 데스크탑은 X 윈도우즈를 기본으로 탑재하고 있기 때문에 X 관련 라이브러리까지 배포시 포함할 필요가 없습니다. 심지어 그놈 데스크탑 환경일 경우 gtk 와 glib 라이브러리 역시 기본으로 포함하므로 역시 필요없습니다. 하지만 dvr-cms 의 경우 최신 버전의 gtk 를 사용하기 때문에 오래된 리눅스 배포판에서 사용하는 낮은 버전의 gtk 로는 부족하므로 gtk 와 glib 관련 라이브러리를 포함할 것입니다. 이렇게 배포할 프로그램과 배포할 시스템의 환경을 고려해서 포함할 라이브러리의 종류를 적절하게 결정해야 합니다.
컴파일 중 링크 옵션을 보면 dvr-cms 프로그램의 Makefile 을 보면
다음과 같은 옵션을 사용하고 있습니다:
PROJECT_LDFLAGS := \
$(LDFLAGS) \
-Wl,-Bstatic \
$(EGTK_LDFLAGS) \
$(EDC_LDFLAGS) \
$(STW_LDFLAGS) \
$(PANASONIC_LDFLAGS) \
$(ICANTEK_LDFLAGS) \
$(ED_LDFLAGS) \
$(EDNET_LDFLAGS) \
$(FFMPEG_LDFLAGS) \
$(VNC_LDFLAGS) \
$(SQLITE_LDFLAGS) \
$(UNIXODBC_LDFLAGS) \
$(SEXY_LDFLAGS) \
$(XV_LDFLAGS) \
$(GOOCANVAS_LDFLAGS) \
-Wl,-Bdynamic \
$(SNDFILE_LDFLAGS) \
$(GTK_LDFLAGS) \
$(ALSA_LDFLAGS) \
-Wl,-Bstatic 은 정적 링크를 의미하며 -Wl,-Bdynamic 은 동적 링크를 의미합니다.
즉 크게 libsndfile 과 gtk, alsa 라이브러리를 동적으로 링크한다는 뜻입니다.
앞서 나온 ldd 의 결과 목록이 많은 이유는 이들 세 라이브러리가 의존하는
라이브러리까지 모두 포함해서 출력하기 때문입니다.
libc 버전 차이
최근의 리눅스 배포판은 대부분 문제가 없지만 간혹 libc 버전이 달라서 생기는 문제가 있습니다. 페도라 11, 우분투 9.04, 수세 11 모두 libc 2.9 버전을 사용하지만 현재 안정 버전 데비안 5.0 의 경우 libc 2.7 버전을 사용합니다. 데비안 5.0 의 gtk 와 glib 라이브러리 버전도 낮기 때문에 배포시 최신의 gtk 와 glib 을 함께 제공해야 합니다. 문제는 libc 2.9 버전 환경에서 glib 을 컴파일 했을 경우 실행 시점에 glib 이 libc 2.8 미만의 환경에서는 실행할 수 없다는 다음과 같은 메시지가 발생합니다.
./dvr-cms: /lib/i686/cmov/libc.so.6: version `GLIBC_2.8` not found (required by lib/libglib-2.0.so.0)
readelf 명령어로 glib 의 심볼을 확인해보면 GLIBC 2.8 의 심볼을 요구하기 때문에 발생하는 문제입니다:
$ readelf -s /usr/lib/libglib-2.0.so.0 | grep GLIBC_2.8
157: 00000000 0 FUNC GLOBAL DEFAULT UND __vasprintf_chk@GLIBC_2.8 (10)
$
근원적으로는 glib 에서 glibc 2.8 심볼을 요구하지 않도록 해야하지만 이 문제가 발생하는 원인과 이것을 해결하는 정확한 방법은 찾지 못한 상태입니다.
이런 경우 프로그램을 실행시키려면 glib 이 요구하는 glibc 버전을 맞추는 방법으로 해결할 수 있는데 이를 위해서는 libc 역시 배포시 포함하는 것입니다. libc 의 경우 binutils 등의 시스템 유틸리티와도 연관이 많기 때문에 일반적인 동적 라이브러리를 단순히 옮겨오는 것 보다는 번거롭습니다.
또한 ldd 명령으로 보면 나오는 ld-linux.so.2 항목의 경우 다음 처럼 표시가 됩니다:
$ ldd dvr-cms
linux-gate.so.1 => (0xb7f4c000)
...
/lib/ld-linux.so.2 (0xb7f4d000)
...
ld-linux.so.2 가 libc 가 제공하는 여러 파일 중 대표적인 한 파일 인데 이 항목이
절대 경로로 지정이 되어 있기 때문에 아무리 ld-linux.so.2 파일을 가지고 다니더라도
시스템 /lib 하부에 설치하지 않는 이상 자체 libc 를 사용할 수 없습니다.
glibc 소스 디렉터리 내부를 보면 FAQ 문서가 있는데 2.6 과 3.18 항목을 보면 libc 버전이 다른 환경에서 실행시키는 방법에 대해서 나와있습니다.
2.6. When I use GNU libc on my Linux system by linking against
the libc.so which comes with glibc all I get is a core dump.
3.18. After upgrading to glibc 2.1, I receive errors about
unresolved symbols, like '_dl_initial_searchlist' and can not
execute any binaries. What went wrong?
결론은 --dynamic-linker=/lib/ld-linux.so.2 옵션을 사용하는 것인데 gcc 에서 링커 옵션으로
넘겨줄 때는 다음 처럼 -Wl,--dynamic-linker=/lib/ld-linux.so.2 원하는 경로를 넣으면 됩니다.
절대 경로 상대 경로 모두 사용 가능합니다.
또는 다른 방법으로 컴파일 시 --dynamic-linker 옵션을 주지 않았을지라도
프로그램을 실행할 때 다음 처럼 입력함으로써 자체 libc 를 사용하는 것이 가능합니다:
LD_LIBRARY_PATH=<path-where-libc.so.6-lives> \
<path-where-corresponding-dynamic-linker-lives>/ld-linux.so.2 \
<path-to-binary>/binary
역시 경로는 절대 경로 상대 경로 모두 사용 가능합니다.
덤으로 LD_LIBRARY_PATH 는 -rpath 옵션에 상응하며 gcc 에 링크 옵션으로 넘겨줄 때는
-Wl,-rpath,lib,usr/lib 로 사용합니다.
역시 절대 경로 및 상대 경로 모두 사용 가능하며 여러 개의 디렉터리를 명시할 때는 쉼표로 구분합니다.
필요한 libc 의 위치는 위와 같은 방식으로 지정할 수 있으며
필요한 libc 라이브러리는 패키지 관리자를 이용해서 확인할 수 있습니다.
우분투 9.04 에서 libc 라이브러리는 libc6 라는 패키지로 존재합니다.
libc 패키지의 구성 파일을 확인해서 /lib 하부에 설치되는 파일 중에서
심볼릭 링크인 파일만 추려내면 그 목록은 다음과 같습니다:
$ dpkg-query -L libc6 | perl -nle 'm|^/lib| and -l and print'
/lib/ld-linux.so.2
/lib/libanl.so.1
/lib/libBrokenLocale.so.1
/lib/libc.so.6
/lib/libcidn.so.1
/lib/libcrypt.so.1
/lib/libdl.so.2
/lib/libm.so.6
/lib/libnsl.so.1
/lib/libnss_compat.so.2
/lib/libnss_dns.so.2
/lib/libnss_files.so.2
/lib/libnss_hesiod.so.2
/lib/libnss_nis.so.2
/lib/libnss_nisplus.so.2
/lib/libpthread.so.0
/lib/libresolv.so.2
/lib/librt.so.1
/lib/libthread_db.so.1
/lib/libutil.so.1
$
상기 목록의 공유 라이브러리가 libc 를 구성하는 최소한 파일입니다. 여타 다른 공유 라이브러리와 함께 libc 라이브러리도 같이 묶어서 배포하면 libc 버전이 다른 환경에서도 프로그램을 정상적으로 수행시킬 수 있습니다.
선별적 적재
시스템에 따라 설치된 공유 라이브러리의 버전이 충분히 높아서 시스템의 공유 라이브러리를 사용할 수 있는 경우에는 굳이 따로 지정한 라이브러리를 사용할 필요가 없습니다. 조건이 충족만 된다면 시스템에서 사용하는 라이브러리를 사용하는 편이 안정성이나 메모리 효율 측면에서 더 나을 수 있습니다. 그러므로 프로그램을 실행할 때 라이브러리를 종류별로 나누어서 꼭 필요한 라이브러리만 적재하는 것이 바람직할 것입니다.
예제의 dvr-cms 의 경우 크게 4 가지로 나누어서 적재를 할 수 있습니다.
- libc
- gtk
- asound
- sndfile
즉 libc(glibc) 라이브러리와 gtk(gtk, glib, gdk, ...) 라이브러리 ALSA 라이브러리와 sndfile 라이브러리로 나누어서 가능한 시스템의 라이브러리를 사용하도록 하는 것입니다.
이를 용이하게 하기 위해서 디렉터리 구조를 다음처럼 나누도록 합니다.
lib/
lib/libc/
lib/libgtk/
lib/libasound/
lib/libsndfile/
이렇게 디렉터리를 크게 4 개로 구분한 다음 각 라이브러리 디렉터리에 구분해서
라이브러리를 복사합니다.
프로그램을 실행할 때는 실행 파일을 직접 실행시키는 것이 아니라
dvr-cms.sh 와 같은 래퍼 스크립트를 만들어서 libc 버전을 확인한 후 적재 여부를 판단하고
다음으로 gtk 버전과 glib 버전을 확인합니다.
차례로 alsa 와 sndfile 의 버전 또는 존재 여부를 확인후 적재 여부를 판단합니다.
적재 여부를 판단한 후 적재를 할 디렉터리는 LD_LIBRARY_PATH 환경 변수에 추가하면
간단하게 선별적으로 적재하는 것이 가능합니다.
libc 를 적재한다면 LD_LIBRARY_PATH 에 추가하는 것 뿐만 아니라 실행시 다음처럼 ld-linux.so.2 경로도 입력해주어야 합니다:
LD_LIBRARY_PATH=lib lib/ld-linux.so.2 ./dvr-cms