Skip to content

Instantly share code, notes, and snippets.

@lethee
Last active December 27, 2022 00:52
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save lethee/a50d0cbb49cc95d5475dfdaea015556e to your computer and use it in GitHub Desktop.
Save lethee/a50d0cbb49cc95d5475dfdaea015556e to your computer and use it in GitHub Desktop.
Guide to pkg-config 한글 번역

Guide to pkg-config 한글 번역

Dan Nicholson

원문

https://people.freedesktop.org/~dbn/pkg-config-guide.html

개요

본 문서는 개발자와 사용자 양쪽에서 pkg-config 툴을 사용하는데 있어 큰 그림을 제시하고자 한다. 우선 pkg-config 툴이 내포하는 개념을 설명하고 프로젝트가 제공할 pkg-config 파일을 어떻게 작성하는지, 작성된 pkg-config 파일을 어떻게 프로젝트에 가져다 사용하는지 알아보려 한다.

pkg-config에 대한 자세한 내용는 https://pkg-config.freedesktop.org 이나 pkg-config(1) man 페이지에서 볼 수 있다.

본 문서는 pkg-config 툴을 리눅스나 유닉스 계열의 운영체제에서 사용하는 것을 가정한다. 다른 운영체제나 플랫폼에서는 사용법이 본 문서의 내용과 다를 수 있다.

왜죠?

요즘의 시스템이나 프레임워크는 다수의 컴포넌트를 계층적으로 구성해서 사용자에게 기능을 제공한다. 다수의 컴포넌트를 적절히 결합시키는 일은 짜증날 정도로 힘들다. 시스템에 설치된 컴포넌트 혹은 라이브러리의 메타정보를 pkg-config 가 관리하도록 하면 좀 더 쉽게 컴포넌트를 가져다 쓸 수 있다.

pkg-config 같은 메타정보 관리 툴이 없이 컴포넌트를 가져다 쓰려면 라이브러리가 어디에 위치해 있고 어떻게 가져다 쓸 수 있는지 자세한 내용을 알기 쉽지 않다. 컴포넌트를 배포하는 개발자 입장에서도 pkg-config 메타데이터를 함께 배포하면 가져다 쓰는 사람이 훨씬 쉽게 API를 호출할 수 있다.

개념

pkg-config의 핵심 내용은 라이브러리를 가져다 쓰는 사람에게 컴파일과 링크에 대한 적절한 정보를 쉽게 제공하는 것이다. 이 메타데이터 정보는 pkg-config 파일에 작성되어 있다. .pc 확장자를 가지며 pkg-config 툴이 알만한 특정 위치에 설치된다. 뒷부분에서 이 내용은 더 자세히 살펴본다.

파일 형식은 아래와 같은데 미리 정의된 몇 가지 메타데이터 키워드와 자유롭게 정의한 변수를 볼 수 있다.

prefix=/usr/local
exec_prefix=${prefix}
includedir=${prefix}/include
libdir=${exec_prefix}/lib

Name: foo
Description: The foo library
Version: 1.0.0
Cflags: -I${includedir}/foo
Libs: -L${libdir} -lfoo

메타데이터 키워드 정의는 Name: 과 같이 지정된 키워드와 : 기호로 시작하며 해당 키워드에 대한 적절한 값을 작성한다. 변수는 prefix= 처럼 일반적인 변수 할당 형식을 사용한다.

다음은 각 키워드가 어떤 값을 가져야 하는지에 대한 간단한 설명이다. 키워드를 효율적으로 사용하기 위한 자세한 디테일은 이어지는 'pkg-config 파일 작성' 부분에서 알아보자.

  • Name: 라이브러리나 패키지 이름으로 사람에게 읽힐 용도로 사용한다. pkg-config 툴에서 사용하는 .pc 파일의 파일이름과는 별개로 사용된다.
  • Description: 패캐지에 대한 간단한 설명이다.
  • URL: 웹사이트 URL로 패키지에 대한 자세한 정보가 알고 싶은 사람들을 위해 제공한다.
  • Version: 패키지의 버전을 정의한다.
  • Requires: 패키지가 의존하여 사용하는 다른 패키지의 이름의 목록으로 공백으로 구분한다. 비교 연산자(=, <, >, <=, >=)를 사용하여 버전을 지정할 수도 있다.
  • Requires.private: 패키지가 의존하여 사용하는 다른 패키지의 이름의 목록으로 공백으로 구분한다. 단 Requires와 다르게 패키지 안에서만 사용하며 이 패키지를 가져다가 사용하는 애플리케이션에는 사용할 필요 없을 패키지를 나열한다. 버전을 지정하는 형식은 Requires와 동일하다.
  • Conflicts: 옵션으로 정의하여 사용하는 키워드로 패키지와 충동을 일으키는 다른 패키지를 목록 형식으로 정의한다. 버전을 지정하는 형식은 Requires와 동일하다. Conflicts: bar < 1.2.3, bar >= 1.3.0 형식으로 같은 패키지를 두 번 정의하는 것도 가능하다.
  • Cflags: 외부에서 이 패키지를 가져다가 컴파일할 때 사용하는 컴파일러 옵션 플래그로, 이 패키지에서 의존하는 컴포넌트가 pkg-config 파일을 지원하지 않아서 컴파일러 옵션을 지정하도록 전달하기 위해 사용한다. 이 패키지에서 의존하는 컴포넌트가 pkg-config 파일을 지원한다면 RequiresRequires.private에 이름만을 나열해야 한다.
  • Libs: 외부에서 이 패키지를 가져다가 링크할 때 사용하는 링크 옵션 플래그다. Cflags와 마찬가지로 이 패키지에서 Public하게 의존하는 컴포넌트가 pkg-config 파일을 지원하지 않는 경우 링크 옵션을 지정하도록 전달하기 위해 사용한다. 이 패키지에서 의존하는 컴포넌트가 pkg-config 파일을 지원한다면 RequiresRequires.private에 지정해야 한다.
  • Libs.private: 외부에서 이 패키지를 가져다가 정적으로 링크할 때 사용하는 링크 옵션 플래그다. 이 패키지가 의존하여 사용하는 다른 패키지를 링크하는 옵션 플래그로 이 패키지를 가져다가 사용하는 애플리케이션에서 직접 사용할 필요 없을 패키지에 대한 링크 옵션 플래그이다. 이 패키지에서 의존하는 컴포넌트가 pkg-config 파일을 지원한다면 RequiresRequires.private에 지정해야 한다.

pkg-config 파일 작성

어떤 패키지에 대한 pkg-config 파일을 작성하려면 우선 패키지 혹은 라이브러리가 어떤 형태로 배포될 지 생각해봐야 한다. pkg-cnofig 파일이 라이브러리 하나만을 대표하는 것이 가장 좋으며 이 때에는 패키지 혹은 라이브러리 마다 pkg-config 파일 하나씩 생성해야 한다.

pkg-config 시스템에서 패키지 이름은 pkg-config 파일의 이름에서 가져와 사용된다. .pc 확장자 앞 부분 말이다. 일반적으로 라이브러리 이름은 .pc 앞의 파일 이름과 같은 것으로 맞추는 것이 좋다. 예를 들어 패키지가 설치하게 되는 라이브러리 파일 이름이 libfoo.so라면 libfoo.pc 라고 메타데이터 파일의 이름을 짓는 것이다. 반드시 이렇게 해야 하는 것은 아니다. .pc 파일의 이름이 내 패키지를 가리킬 수 있도록 다른 .pc 파일과 겹치지 않는 파일 이름을 가지면 된다. foo.pcfoolib.pc도 파일 이름으로 사용할 수 있다.

Name, Description, URL 항목은 패키지를 설명하는 부분으로 어렵지 않게 작성할 수 있으며 pkg-config 시스템에서 기술적으로 사용되는 부분은 아니다. Version 항목은 이 패키지를 가져다가 사용하는 다른 패키지에 영향을 줄 수 있는 부분이다. pkg-config 시스템은 RPM의 버전 비교 알고리즘을 사용한다. 버전 값은 점과 숫자로만 이루어진 형태를 사용해야 한다. 숫자가 아닌 알파벳 글자를 사용하면 제대로 동작하지 않을 수 있다. 버전 숫자는 증가하는 값을 사용하며 라이브러리의 변화를 구체적으로 반영해야 한다. 버전만으로 충분히 이 패키지를 가져다 사용하는 다른 패키지에 갱신 정보를 제공할 수 있다.

다른 키워드 항목을 살펴보기 전에 우선 변수를 정의하는 것을 살펴보는 것이 도움이 된다. 메타데이터 파일에서 주로 설치 경로등을 지정할때 사용하며 변수를 사용하면 파일 내용이 지저분해지는 것을 다소 막아줄 수 있다. 변수는 재귀적으로 적용이 가능하므로 autoconf 등으로부터 가져온 변수와 결합하셔 사용할 때 매우 유용하다.

prefix=/usr/local
includedir=${prefix}/include

Cflags: -I${includedir}/foo

Requires, Requires.private, Cflags, Libs, Libs.private 항목은 pkg-config 메타데이터에서 가장 중요한 정보를 다룬다. 이 패키지를 가져다가 사용하는 다른 패키지에서 컴파일하거나 링크할 때 여기에 작성한 정보를 가져다 사용하기 때문이다.

Requires, Requires.private 항목은 이 패키지가 필요로 하는 다른 패키지를 설명한다. 이 패키지를 링크하여 사용하는데 굳이 노출시키지 않아도 될 의존 라이브러리라면 Requires.private 항목에 작성하는 것이 좋다. 이 패키지를 가져다 사용하는 다른 패키지에서 이 패키지가 의존하는 패키지의 심볼을 직접 사용하는 것이 아니라면 굳이 링크를 하지 않아도 되기 때문이다. 이어지는 내용에서 이 .private 붙은 항목을 왜 써야하는 지를 설명하고자 한다.

pkg-config 시스템은 항상 Requires 항목에 작성된 라이브러리의 Link 플래그를 노출시킨다. 이는 이 라이브러리를 가져다가 사용하는 패키지에 직접적인 의존관계를 설정해주게 된다. Requires.private 항목에 작성된 라이브러리의 Link 플래그는 정적 Link를 할 때에만 노출이 된다. 이런 설정에 따라 이 패키지를 사용하는 다른 패키지에서 Requires에 적은 이 패키지의 이름이 동일하더라도 상황에 따라서 적절한 Link 플래그가 생성될 수 있게 된다.

Libs 항목은 이 패키지를 링크하기 위해 필요한 Link 플래그를 작성한다. Libs, Libs.private의 Link 플래그는 Requires로 가져오지 못한 이 패키지가 의존하는 라이브러리에 대한 Link 플래그 정보도 포함한다. Requires 항목과 마찬가지로 반드시 Libs 에 작성해야 할 내용이 아니라면 Libs.private 항목을 사용하는 것이 직접적인 의존관계를 만들지 않기 때문에 좋다.

마지막으로 Cflags 항목은 이 패키지의 라이브러를 가져다 사용할 때 컴파일러 옵션으로 전달되는 내용이다. 여기에는 .private 키워드가 붙은 항목이 없다. 데이터 형식이나 매크로 정의는 링크와 관련없이 컴파일 할 때 필요하기 때문이다.

pkg-config 파일 사용

.pc 파일이 시스템에 적절히 설치되어 있다면 pkg-config 툴로 메타데이터를 적절하게 추출하여 사용하는 것이 가능합니다. pkg-config --help 명령으로 각 옵션을 어떻게 사용할 수 있는지에 대한 간단한 도움말을 살펴볼 수 있습니다. 자세한 내용은 pkg-config(1) man 페이지에서 살펴볼 수 있습니다. 이 페이지에서는 주로 사용하는 간단한 예제로 설명합니다.

예를 들어 시스템에 foobar라는 모듈이 설치되어 있고 .pc 파일이 아래와 같은 내용을 담고 있습니다.

foo.pc:
prefix=/usr
exec_prefix=${prefix}
includedir=${prefix}/include
libdir=${exec_prefix}/lib

Name: foo
Description: The foo library
Version: 1.0.0
Cflags: -I${includedir}/foo
Libs: -L${libdir} -lfoo

bar.pc:
prefix=/usr
exec_prefix=${prefix}
includedir=${prefix}/include
libdir=${exec_prefix}/lib

Name: bar
Description: The bar library
Version: 2.1.2
Requires.private: foo >= 0.7
Cflags: -I${includedir}
Libs: -L${libdir} -lbar

각 모듈의 버전은 --modversion 옵션을 사용하여 다음과 같이 확인할 수 있습니다.

$ pkg-config --modversion foo
1.0.0
$ pkg-config --modversion bar
2.1.2

각 모듈의 링크 옵션 플래그는 --libs 옵션을 사용하여 다음과 같이 확인할 수 있습니다.

$ pkg-config --libs foo
-lfoo
$ pkg-config --libs bar
-lbar

pkg-config 툴이 Libs 부분에서 일부 정보를 가져오지 않았음을 확인할 수 있습니다. 가져오지 않은 -L 옵션을 좀 특별하게 다루게 되는데, -L 플래그에서 지정한 ${libdir} 실제 값인 /usr/lib 디렉토리가 시스템 link 명령의 검색 디렉토리 목록 안에 포함되어 있음을 알기 때문입니다. 이런식으로 시스템의 기본 Link 도구를 방해하지 않도록 pkg-config는 동작합니다.

그리고 bar 라이브러리가 foo 라이브러리를 필요로 하지만 링크 옵션 플래그로 출력되지 않았습니다. bar 라이브러리를 가져다가 사용하는 애플리케이션이 bar의 API만 사용하고 foo의 API를 사용하지 않는다면 이러한 출력이 적절하기 때문입니다. 하지만 정적(static)으로 애플리케이션을 컴파일하려면 반드시 두 라이브러리의 링크 옵션이 필요합니다.

$ pkg-config --libs --static bar
-lbar -lfoo

pkg-config--static 옵션을 통해 정적 컴파일에서 필요로 하는 모든 심볼을 찾을 수 있는 링크 옵션 플래그를 출력할 수 있습니다. 이와는 다르게 Cflags는 동적이든 정적이든 상관없이 항상 전달됩니다.

$ pkg-config --cflags bar
-I/usr/include/foo
$ pkg-config --cflags --static bar
-I/usr/include/foo

--exists 옵션을 사용하여 찾고자 하는 모듈이 시스템에 설치되어 있는지 확인할 수 있습니다.

$ pkg-config --exists foo
$ echo $?
0

pkg-config 툴을 사용하면 버전 정보를 사용하여 적절한 버전이 설치되어 있는지 확인할 수도 있습니다. 필요로 하는 버전 이상의 모듈이 설치되어 있는지 확인하려면 다음과 같이 실행합니다.

$ pkg-config --libs "bar >= 2.7"
Requested 'bar >= 2.7' but version of bar is 2.1.2

pkg-config를 사용하여 오류가 발생할 옵션을 실행하는 경우 --print-errors 옵션을 같이 사용하면 좀 더 자세한 내용의 오류 메시지를 확인할 수 있습니다.

$ pkg-config --exists --print-errors xoxo
Package xoxo was not found in the pkg-config search path.
Perhaps you should add the directory containing `xoxo.pc'
to the PKG_CONFIG_PATH environment variable
No package 'xoxo' found

위의 오류메시지를 확인하면 PKG_CONFIG_PATH 환경 변수를 참조한다는 것을 볼 수 있습니다. PKG_CONFIG_PATH 변수는 pkg-config 파일을 검색하는데 사용합니다. 일반적인 유닉스나 리눅스 시스템에서라면 /usr/lib/pkgconfig, /usr/share/pkgconfig 디렉토리를 참조합니다. 대부분 시스템에 설치된 라이브러리 모듈은 보통 위의 디렉토리에 pkg-config 파일을 위치시킵니다. 다만 일부 모듈의 경우 설치 디렉토리가 /usr/local 인 경우가 있습니다. 이런 경우 pkg-config가 파일을 잘 찾을 수 있도록 PKG_CONFIG_PATH 환경변수를 설정하게 됩니다.

$ pkg-config --modversion hello
Package hello was not found in the pkg-config search path.
Perhaps you should add the directory containing `hello.pc'
to the PKG_CONFIG_PATH environment variable
No package 'hello' found
$ export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
$ pkg-config --modversion hello
1.0.0

autoconf 에서 아래와 같은 추가적인 매크로를 사용해서 pkg-config 툴의 기능을 결합하여 활용할 수 있습니다.

  • PKG_PROG_PKG_CONFIG([MIN-VERSION]): pkg-config 프로그램의 위치를 확인하며 해당 버전 이상의 버전이 설치되어 있는지 확인합니다.
  • PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]): 찾고자 하는 모듈이 설치되어 있는지 확인합니다.
  • PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]): 찾고자 하는 모듈이 설치되어 있는지 확인합니다. 설치되어 있다면 pkg-config --cflags 결과를 <VARIABLE-PREFIX>_CFLAGS변수에, pkg-config --libs 결과를 <VARIABLE-PREFIX>_LIBS 변수에 설정합니다.

FAQ 퐈큐

내 프로그램이 라이브러리 x를 쓰려고 합니다. 이제 뭘 하죠?

pkg-config 툴의 출력을 컴파일러 명령 옵션으로 어렵지 않게 사용할 수 있습니다. 라이브러리 x가 pkg-config를 지원하여 x.pc 파일이 존재한다면 다음과 같이 컴파일러 옵션에 사용할 수 있습니다.

cc `pkg-config --cflags --libs x` -o myapp myapp.c

autoconfautomake 툴을 사용한다면 좀 더 든든한 형식으로 컴파일러 옵션 플래그 결합을 시킬 수 있습니다. PKG_CHECK_MODULES 매크로를 사용하여 빌드 프로세스에서 메타데이터를 쉽게 가져다 사용할 수 있습니다.

configure.ac:
PKG_CHECK_MODULES([X], [x])

Makefile.am:
myapp_CFLAGS = $(X_CFLAGS)
myapp_LDADD = $(X_LIBS)

pkg-config 툴이 라이브러리 x에 관련된 메타정보를 찾은 경우 X_CFLAGS 변수와 X_LIBS 변수에 관련 내용이 설정됩니다. 라이브러리 x가 설치되지 않았거나 어떤 이유든 간에 찾을 수 없는 경우에는 설정 과정에서 오류가 발생합니다. PKG_CHECK_MODULES 매크로의 3번째와 4번째 인자를 통해 찾고자 하는 라이브러리를 찾지 못했을 때의 상황을 제어할 수 있습니다.

내 라이브러리 z에서 설치하는 헤더 파일이 libx의 헤더파일을 include 해서 쓰고 있어요. z.pc 파일을 뭔 내용으로 작성해야 하죵?

libx가 pkg-config 형식을 지원한다면 Requires.privatex를 추가하세요. 그렇지 않은 경우에는 Cflags에 libx의 헤더를 사용하기 위한 적절한 컴파일러 옵션을 적어주세요. 이렇게 설정한 내용은 --static 옵션을 사용하든 안하든 컴파일 옵션으로 출력됩니다.

내 라이브러리 z에서 libx를 호출하지만 public API로 libx에 관련된 내용을 노출시키지는 않습니다. z.pc파일을 내용으로 작성해야 하죵?

마찬가지로 libx가 pkg-config 형식을 지원한다면 Requires.privatex를 추가하세요. 이 경우 컴파일러 플래그 옵션은 전달되지 않지만 정적 링크를 할 때 Link 옵션 플래그는 전달됩니다. libxpkg-config를 지원하지 않는다면 Libs.privatelibx를 링크할 수 있는 링크 옵션 플래그를 작성합니다.

Info

Dan Nocholson <dbn.lists (at) gmail (dot) com>

Copyright (C) 2010 Dan Nicholson

번역: https://github.com/lethee

This docuemtn is licensed under GNU General Public License, Version 2 the or any last version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment