Blob Blame History Raw
<?xml version="1.0" encoding="utf-8"?>
<page xmlns="http://projectmallard.org/1.0/" xmlns:its="http://www.w3.org/2005/11/its" type="topic" id="parallel-installability" xml:lang="ko">

  <info>
    <link type="guide" xref="index#maintainer-guidelines"/>

    <credit type="author copyright">
      <name>Havoc Pennington</name>
      <email its:translate="no">hp@pobox.com</email>
      <years>2002</years>
      <!-- Heavily based off Havoc’s original article about parallel
           installability: http://ometer.com/parallel.html.
           Licence CC-BY-SA 3.0 confirmed by e-mail with him. -->
    </credit>
    <credit type="author copyright">
      <name>Philip Withnall</name>
      <email its:translate="no">philip.withnall@collabora.co.uk</email>
      <years>2015</years>
    </credit>

    <include xmlns="http://www.w3.org/2001/XInclude" href="cc-by-sa-3-0.xml"/>

    <desc>동시 설치 방식으로 나중에 검증할 라이브러리를 작성합니다</desc>
  
    <mal:credit xmlns:mal="http://projectmallard.org/1.0/" type="translator copyright">
      <mal:name>조성호</mal:name>
      <mal:email>shcho@gnome.org</mal:email>
      <mal:years>2016, 2017.</mal:years>
    </mal:credit>
  </info>

  <title>동시 설치 가능성</title>

  <synopsis>
    <title>요약</title>

    <p>두 패키지를 동시에 설치할 수 있다면, 일반적인 파일 이름이 붙어있지 않으며 패키지를 개발한 사람은 원하는 버전에 따라 컴파일합니다. 이 방식은 헤더 파일, 라이브러리 이진 파일에 적용하는 방식대로 데몬, 유틸리티 프로그램, 설정 파일에 적용합니다.</p>

    <list>
      <item><p>모든 라이브러리 버전을 동시에 설치할 수 있는지 확인하십시오(<link xref="#justification"/>).</p></item>
      <item><p>라이브러리로 설치한 모든 파일에 버전을 부여하십시오(<link xref="#solution"/>).</p></item>
      <item><p>so 이름 또는 libtool 버전 번호와는 별개로 패키지 버전 번호를 관리하십시오. 어떤 패키지 버전 번호 부분이 API와 바뀌는지 분명히 하십시오(<link xref="#version-numbers"/>).</p></item>
      <item><p><file><var>$(includedir)</var>/lib<var>library</var>-<var>version</var>/<var>library</var>/</file>에 C 헤더 파일을 설치하십시오(<link xref="#header-files"/>).</p></item>
      <item><p><file><var>$(libdir)</var>/lib<var>library</var>-<var>version</var>.so.<var>soname</var></file>에 라이브러리 파일을 설치하십시오(<link xref="#libraries"/>).</p></item>
      <item><p><file><var>$(libdir)</var>/pkgconfig/<var>library</var>-<var>version</var>.pc</file>에 pkg-config 파일을 설치하십시오(<link xref="#pkg-config"/>).</p></item>
      <item><p>설정 파일을 전후 호환성에 맞추든지 <file><var>$(sysconfdir)</var>/<var>library</var>-<var>version</var>/</file>에 설치하십시오(<link xref="#configuration-files"/>).</p></item>
      <item><p><code>GETTEXT_PACKAGE</code> 변수 값을 <code><var>library</var>-<var>version</var></code>에 설정하십시오(<link xref="#gettext"/>).</p></item>
      <item><p>모든 D-Bus 인터페이스 이름, 서비스 이름, 객체 경로에 버전 번호를 넣으십시오. 이를 테면 <code>org.domain.<var>Library</var><var>Version</var>.<var>Interface</var></code>, <code>org.domain.<var>Library</var><var>Version</var></code>, <code>/org/domain/<var>Library</var><var>Version</var>/</code> 과 같이 넣으십시오(<link xref="#dbus"/>).</p></item>
      <item><p>데몬 바이너리를 <file><var>$(libexecdir)</var>/<var>library</var>-daemon-<var>version</var></file>에 설치하십시오(<link xref="#programs"/>).</p></item>
      <item><p>유틸리티 바이너리를 <file><var>$(bindir)</var>/<var>library</var>-utility-<var>version</var></file> and install symbolic links to <file><var>$(bindir)</var>/<var>library</var>-utility</file>에 설치하십시오(<link xref="#programs"/>).</p></item>
    </list>
  </synopsis>

  <section id="justification">
    <title>명분</title>

    <p>모든 공개 라이브러리는 라이브러리의 생명 주기 이후 API 구조 변경이 쉽게 함께 설치할 수 있도록 설계해야합니다. 라이브러리를 여러 프로젝트에서 사용하는 상황에서 API 구조를 깨야 한다면, 모든 프로젝트를 동시에 설치한 새 API에 맞춰 이식해야 하며, 이렇게 하지 않으면 일부 프로젝트는 의존 관계상 라이브러리 버전이 맞지 않는 문제로 더이상 라이브러리와 설치할 수 없습니다.</p>

    <p>이 상태가 바로 관리할 수 없는 상태이며, 대부분의 API가 이식의 동기가 될 눈에 띄는 새 기능을 넣지 않기에, 모든 프로젝트에 새 API에 맞춰 동시에 이식을 요청하기 어렵고, 이식의 의욕을 꺾어버립니다.</p>

    <p>API의 이전 버전과 새 버전을 동시에 설치하고 컴파일 할 수 있게끔 모든 라이브러리를 동시에 설치할 수 있게 했는지 확인하면 이 문제가 처리됩니다. 동시에 설치할 수 있게 빌드하면, 굳이 근본 부분을 건드리지 않고도 프로젝트의 시작단계를 쉽게 처리할 수 있습니다.</p>

    <p>이 해결 방식은 또한 라이브러리의 어떤 버전과 그 다음 버전을 설치하는 문제에서 여러 프로그램을 이식하는 ‘닭이 먼저냐 달걀이 먼저냐’의 문제를 해결하며, 라이브러리 관리 집단이 원한다면 개발 주기를 더 빠르게 진행할 수 있으며 개발 과정상 새 기능을 넣을 수 있도록 API 구조를 훨씬 쉽게 바꿀 수 있습니다.</p>

    <p>이와 동일한 라이브러리 대상 해결책은 API 구조를 그냥 안바꾸는 방법입니다. 이 접근 방식은 <code>libc</code>에서 왔습니다.</p>
  </section>

  <section id="solution">
    <title>해결책</title>

    <p>문제 해결책은 근본적으로 라이브러리의 이름을 바꾸는 방식이며, 대부분의 경우 모든 파일을 설치하는 경로의 버전 번호를 넣는 방식입니다. 이는 라이브러리의 여러 버전을 동시에 설치할 수 있다는 의미입니다.</p>

    <p>예를 들어 <code>Foo</code> 라이브러리에서 기존에 설치한 파일이 다음과 같다고 하겠습니다:</p>
    <list>
      <item><p><file>/usr/include/foo.h</file></p></item>
      <item><p><file>/usr/include/foo-utils.h</file></p></item>
      <item><p><file>/usr/lib/libfoo.so</file></p></item>
      <item><p><file>/usr/lib/pkgconfig/foo.pc</file></p></item>
      <item><p><file>/usr/share/doc/foo/foo-manual.txt</file></p></item>
      <item><p><file>/usr/bin/foo-utility</file></p></item>
    </list>

    <p>이 파일을 설치하려면 위 대신 <code>Foo</code> 버전 4로 수정해야합니다:</p>
    <list>
      <item><p><file>/usr/include/foo-4/foo/foo.h</file></p></item>
      <item><p><file>/usr/include/foo-4/foo/utils.h</file></p></item>
      <item><p><file>/usr/lib/libfoo-4.so</file></p></item>
      <item><p><file>/usr/lib/pkgconfig/foo-4.pc</file></p></item>
      <item><p><file>/usr/share/doc/foo-4/foo-manual.txt</file></p></item>
      <item><p><file>/usr/bin/foo-utility-4</file></p></item>
    </list>

    <p>수정하고 나면 버전 5로 동시에 설치할 수 있습니다:</p>
    <list>
      <item><p><file>/usr/include/foo-5/foo/foo.h</file></p></item>
      <item><p><file>/usr/include/foo-5/foo/utils.h</file></p></item>
      <item><p><file>/usr/lib/libfoo-5.so</file></p></item>
      <item><p><file>/usr/lib/pkgconfig/foo-5.pc</file></p></item>
      <item><p><file>/usr/share/doc/foo-5/foo-manual.txt</file></p></item>
      <item><p><file>/usr/bin/foo-utility-5</file></p></item>
    </list>

    <p><link href="http://www.freedesktop.org/wiki/Software/pkg-config/"> <cmd>pkg-config</cmd></link>에서 쉽게 기능을 빌려 쓸 수 있습니다. <file>foo-4.pc</file> 파일은 include 경로에 <file>/usr/include/foo-4</file> 경로를 추가하고 링크할 라이브러리 목록에 <file>libfoo-4.so</file> 파일을 추가합니다. <file>foo-5.pc</file> 파일은 <file>/usr/include/foo-5</file> 경로와 <file>libfoo-5.so</file> 파일을 추가합니다.</p>
  </section>

  <section id="version-numbers">
    <title>버전 번호</title>

    <p>파일 이름에 붙은 버전 번호는 <em>ABI/API</em> 버전입니다. 패키지의 전체 버전 번호는 아닙니다. API 유지 상태가 깨졌는지 여부를 나타내는 부분일 뿐입니다. 프로젝트 버전 부여시 <code><var>주</var>.<var>부</var>.<var>마이크로</var></code> 표준 버전 부여 형식을 사용한다면 API 버전은 보통 주 버전 번호입니다.</p>

    <p>부 버전 출시판(보통 API를 추가하지만 바꾸거나 제거하지 <em>않음</em>)과 마이크로 버전 출시판(보통 버그 수정)은 <link xref="api-stability">API 이전 호환성</link>에 영향을 주지 않으므로 모든 파일을 옮길 필요가 없습니다.</p>

    <p>다음 부분의 예제는 다음 코드를 활용하여 <file>configure.ac</file> 파일에서 API 버전과 so 이름을 내보낸다고 가정합니다:</p>
    <listing>
      <title>autoconf의 API 버전 부여</title>
      <desc><file>configure.ac</file> 파일에 API 버전 및 so 이름을 내보낼 코드를 작성하십시오</desc>
      <code># Before making a release, the <var>LIBRARY</var>_LT_VERSION string should be modified.
# The string is of the form c:r:a. Follow these instructions sequentially:
#
#  1. If the library source code has changed at all since the last update,
#     then increment revision (‘c:r:a’ becomes ‘c:r+1:a’).
#  2. If any interfaces have been added, removed, or changed since the last update,
#     increment current, and set revision to 0.
#  3. If any interfaces have been added since the last public release,
#     then increment age.
#  4. If any interfaces have been removed or changed since the last public release,
#     then set age to 0.
AC_SUBST([<var>LIBRARY</var>_LT_VERSION],[1:0:0])

AC_SUBST([<var>LIBRARY</var>_API_VERSION],[4])</code>
    </listing>
  </section>

  <section id="header-files">
    <title>C 헤더 파일</title>

    <p>헤더 파일은 C 컴파일러의 <cmd>-I</cmd> 플래그가 요구하는 버전이 붙은 하위 디렉터리에 항상 설치해야합니다. 예를 들어, <file>foo.h</file> 헤더 파일이 있다면 프로그램에서는 다음과 같이 씁니다:</p>
    <code mime="text/x-csrc">#include &lt;foo/foo.h&gt;</code>
    <p>그 다음 이 파일을 설치하겠습니다:</p>
    <list>
      <item><p><file>/usr/include/foo-4/foo/foo.h</file></p></item>
      <item><p><file>/usr/include/foo-5/foo/foo.h</file></p></item>
    </list>

    <p>프로그램에서는 <cmd>-I/usr/include/foo-4</cmd> 또는 <cmd>-I/usr/include/foo-5</cmd> 플래그를 C 컴파일러에 보내야합니다. 다시 말하지만, <cmd>pkg-config</cmd>를 활용하면 됩니다.</p>

    <p><file>foo/</file> 추가 하위 디렉터리를 살펴보십시오. 다른 라이브러리의 파일 이름이 겹치는 일을 피하도록 <code mime="text/x-csrc">#include</code> 절에 이름 영역을 부여합니다. 예를 들자면, 서로 다른 두 라이브러리에서 <file>utils.h</file> 헤더 파일을 설치한다면 <code mime="text/x-csrc">#include &lt;utils.h&gt;</code> 헤더를 사용할 때 무얼 넣을까요?</p>

    <p>어떤 하위 디렉터리 외에 있는 헤더 파일 중 하나를 두어야 할 지 망설임이 옵니다:</p>
    <list>
      <item><p><file>/usr/include/foo.h</file></p></item>
      <item><p><file>/usr/include/foo-5/foo.h</file></p></item>
    </list>

    <p>문제는 사용자가 언제나 부주의하게 잘못된 헤더를 참조하는데, 컴파일 명령행에 <cmd>-I/usr/include</cmd>를 넣는걸 규칙인 것 처럼 생각하기 때문입니다. 만약 이렇게 해야 한다면, 최소한 라이브러리를 초기화할 때 잘못된 헤더 파일을 사용하여 프로그램을 감지하는 라이브러리가 있는지 검사하는 부분을 추가하십시오.</p>

    <p>버전을 부여한 헤더 파일은 다음 코드로 automake에서 설치할 수 있습니다:</p>
    <listing>
      <title>automake의 헤더 파일 처리</title>
      <desc><file>Makefile.am</file> 파일에 버전을 부여한 헤더 파일을 설치할 코드를 작성하십시오</desc>
      <code><var>library</var>includedir = $(includedir)/lib<var>library</var>-@<var>LIBRARY</var>_API_VERSION@/<var>library</var>
<var>library</var>_headers = \
	lib<var>library</var>/example1.h \
	lib<var>library</var>/example2.h \
	$(NULL)

# The following headers are private, and shouldn't be installed:
private_headers = \
	lib<var>library</var>/example-private.h \
	$(NULL)
# The main header simply #includes all other public headers:
main_header = lib<var>library</var>/<var>library</var>.h
public_headers = \
	$(main_header) \
	$(<var>library</var>_headers) \
	$(NULL)

<var>library</var>include_HEADERS = $(public_headers)</code>
    </listing>

    <p>버전을 올바르게 부여하는 것처럼, 설치한 헤더에 있는 모든 API의 <link xref="namespacing">공간 이름을 올바르게 부여</link>해야합니다.</p>
  </section>

  <section id="libraries">
    <title>라이브러리</title>

    <p>라이브러리 객체 파일은 다음과 같이 버전을 붙인 이름을 달고 있어야합니다:</p>
    <list>
      <item><p><file>/usr/lib/libfoo-4.so</file></p></item>
      <item><p><file>/usr/lib/libfoo-5.so</file></p></item>
    </list>

    <p>이 과정으로 컴파일을 진행하는 동안 원하는 프로그램이 제대로 나오며, 일반적으로 버전 4 및 버전 5에 파일이 없는지 확인합니다.</p>

    <p>버전을 매긴 라이브러리는 다음 코드로 automake에서 빌드하고 설치할 수 있습니다:</p>
    <listing>
      <title>automake의 라이브러리 처리</title>
      <desc><file>Makefile.am</file>에 버전을 매긴 파일을 빌드하고 설치하는 코드를 작성하십시오</desc>
      <code>lib_LTLIBRARIES = lib<var>library</var>/lib<var>library</var>-@<var>LIBRARY</var>_API_VERSION@.la

lib<var>library</var>_lib<var>library</var>_@<var>LIBRARY</var>_API_VERSION@_la_SOURCES = \
	$(private_headers) \
	$(<var>library</var>_sources) \
	$(NULL)
lib<var>library</var>_lib<var>library</var>_@<var>LIBRARY</var>_API_VERSION@_la_CPPFLAGS = …
lib<var>library</var>_lib<var>library</var>_@<var>LIBRARY</var>_API_VERSION@_la_CFLAGS = …
lib<var>library</var>_lib<var>library</var>_@<var>LIBRARY</var>_API_VERSION@_la_LIBADD = …
lib<var>library</var>_lib<var>library</var>_@<var>LIBRARY</var>_API_VERSION@_la_LDFLAGS = \
	-version-info $(<var>LIBRARY</var>_LT_VERSION) \
	$(AM_LDFLAGS) \
	$(NULL)</code>
    </listing>

    <section id="library-sonames">
      <title>라이브러리 so 이름</title>

      <p>라이브러리 so 이름(libtool 버전 번호로도 알려짐)은 앞서 컴파일한 프로그램을 연결하는 실행시 문제만을 해결합니다. 이전 버전에서 필요한 프로그램을 컴파일하는 문제를 바로잡을 수 없으며, 다른 라이브러리의 문제는 바로 잡지 않습니다.</p>

      <p>이 때문에, so 이름을 활용하지만, <em>추가로</em> 라이브러리의 이름에 버전을 붙입니다. 각각의 문제를 두가지 해결책으로 바로 잡습니다.</p>
    </section>
  </section>

  <section id="pkg-config">
    <title>pkg-config 파일</title>

    <p>다음과 같이 pkg-config 파일에 버전을 매긴 이름을 넣어야합니다:</p>
    <list>
      <item><p><file>/usr/lib/pkgconfig/foo-4.pc</file></p></item>
      <item><p><file>/usr/lib/pkgconfig/foo-5.pc</file></p></item>
    </list>

    <p>각각의 pkg-config 파일에 라이브러리 이름과 수반 경로의 버전 정보가 있기 때문에 라이브러리에 의존하는 어떤 프로젝트든지간에 <file>foo-4</file> 파일에서 <file>foo-5</file> 파일로 pkg-config check를 바꾸(고 API 이식 필요 과정을 진행하)는 방법만으로 버전을 바꿀 수 있어야합니다.</p>

    <p>버전을 부여한 pkg-config 파일은 다음 코드로 autoconf 및 automake에서 설치할 수 있어야합니다:</p>
    <listing>
      <title>autoconf와 automake의 pkg-config 파일 처리</title>
      <desc><file>configure.ac</file>파일과 <file>Makefile.am</file> 파일에 버전을 부여한 pkg-config 파일을 설치하는 코드를 작성하십시오</desc>

      <code>AC_CONFIG_FILES([
lib<var>library</var>/<var>library</var>-$<var>LIBRARY</var>_API_VERSION.pc:lib<var>library</var>/<var>library</var>.pc.in
],[],
[<var>LIBRARY</var>_API_VERSION='$<var>LIBRARY</var>_API_VERSION'])</code>

      <code># Note that the template file is called <var>library</var>.pc.in, but generates a
# versioned .pc file using some magic in AC_CONFIG_FILES.
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = lib<var>library</var>/<var>library</var>-$(<var>LIBRARY</var>_API_VERSION).pc

DISTCLEANFILES += $(pkgconfig_DATA)
EXTRA_DIST += lib<var>library</var>/<var>library</var>.pc.in</code>
    </listing>
  </section>

  <section id="configuration-files">
    <title>설정 파일</title>

    <p>사용자 관점에서, <link xref="api-stability">전후 호환성</link>(정확히 동일한 설정 파일 및 문맥을 각 라이브러리 버전에서 이해)에 따라 설정 파일 형식을 유지하는게 좋습니다. 그 다음 동일한 설정 파일을 모든 라이브러리 버전에서 활용할 수 있으며, 설정 파일 자체를 버전 관리할 필요가 없습니다.</p>

    <p>이렇게 할 수 있다면, 간단하게 설정 파일의 이름을 바꿔야 하며, 사용자는 각 라이브러리 버전 별로 따로 설정해야합니다.</p>
  </section>

  <section id="gettext">
    <title>gettext 번역</title>

    <p>autoconf와 automake의 번역 기반으로 gettext를 사용한다면 보통 <file>/usr/share/locale/<var>lang</var>/LC_MESSAGES/<var>package</var></file> 경로에 번역 파일을 설치합니다. 여기서 <var>package</var> 변수를 추가해야합니다. 그놈에서는 <file>configure.ac</file>에 이 변수를 넣는 방식을 활용합니다:</p>

    <code>GETTEXT_PACKAGE=foo-4
AC_SUBST([GETTEXT_PACKAGE])
AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE],["$GETTEXT_PACKAGE"])</code>

    <p>그 다음 <code mime="text/x-csrc">bindtextdomain()</code>, <code mime="text/x-csrc">textdomain()</code>, <code mime="text/x-csrc">dgettext()</code>에 패키지 이름을 전달할 목적으로 <code>GETTEXT_PACKAGE</code>를 활용합니다.</p>
  </section>

  <section id="dbus">
    <title>D-Bus 인터페이스</title>

    <p>D-Bus 인터페이스는 컴파일 시간이 아닌 실행 시간에 버전 관리를 처리할 때를 제외하고는 C API와 유사한 API의 한 유형입니다. D-Bus 인터페이스 버전 부여는 다른 C API와 별반 차이가 없습니다. 버전 숫자는 인터페이스 이름, 서비스 이름, 객체 경로에 있어야합니다.</p>

    <p>이를테면, 다음과 같이 <code>org.example.Foo</code> 서비스에는 <code>Controller</code>와 <code>Client</code>의 <code>A</code> 와 <code>B</code>, D-Bus API 버전 4와 5가 나타나야합니다:</p>
    <list>
      <title>서비스 이름</title>
      <item><p><code>org.example.Foo4</code></p></item>
      <item><p><code>org.example.Foo5</code></p></item>
    </list>
    <list>
      <title>인터페이스 이름</title>
      <item><p><code>org.example.Foo4.InterfaceA</code></p></item>
      <item><p><code>org.example.Foo4.InterfaceB</code></p></item>
      <item><p><code>org.example.Foo5.InterfaceA</code></p></item>
      <item><p><code>org.example.Foo5.InterfaceB</code></p></item>
    </list>
    <list>
      <title>객체 경로</title>
      <item><p><code>/org/example/Foo4/Controller</code></p></item>
      <item><p><code>/org/example/Foo4/Client</code></p></item>
      <item><p><code>/org/example/Foo5/Controller</code></p></item>
      <item><p><code>/org/example/Foo5/Client</code></p></item>
    </list>
  </section>

  <section id="programs">
    <title>프로그램, 데몬, 유틸리티</title>

    <p>데스크톱 프로그램은 보통 다른 모듈과 의존 관계가 없기 때문에 버전을 매길 필요가 없습니다. 그러나 데몬과 유틸리티 프로그램은 시스템의 다른 부분과 함께 동작하므로 버전을 매겨야 합니다.</p>

    <p>다음의 데몬 및 유틸리티 프로그램을:</p>
    <list>
      <item><p><file>/usr/libexec/foo-daemon</file></p></item>
      <item><p><file>/usr/bin/foo-lookup-utility</file></p></item>
    </list>
    <p>다음과 같이 버전을 부여합니다:</p>
    <list>
      <item><p><file>/usr/libexec/foo-daemon-4</file></p></item>
      <item><p><file>/usr/bin/foo-lookup-utility-4</file></p></item>
    </list>

    <p>사용자가 편리하게 쓸 수 있게 <file>/usr/bin/foo-lookup-utility</file>를 유틸리티의 추천 버전 사본으로 심볼릭 링크 사본을 설치하고 싶을 수도 있습니다.</p>
  </section>
</page>