pkg_mkIndex — 패키지 자동 로딩용 인덱스 만들기

pkg_mkIndex — 패키지 자동 로딩용 인덱스 만들기

배포할 패키지를 만들었다면, 사용자가 package require만 호출해도 해당 패키지를 자동으로 찾아 로드하게 만들고 싶을 거예요. pkg_mkIndex는 바로 그런 인덱스 파일을 만들어 주는 표준 Tcl 라이브러리의 유틸리티 프로시저예요.

출처: Tcl Commands — pkg_mkIndex

본문

pkg_mkIndex ?options...? dir ?pattern pattern ...?

pkg_mkIndex는 표준 Tcl 라이브러리의 일부인 유틸리티 프로시저예요. package require 명령이 실행될 때 패키지를 자동으로 로드할 수 있게 해 주는 인덱스 파일을 만드는 데 사용돼요. pkg_mkIndex를 쓰려면 다음 단계를 따라요:

  1. 패키지를 만든다. 각 패키지는 하나 이상의 Tcl 스크립트 파일이나 바이너리 파일로 구성될 수 있어요. 바이너리 파일은 load 명령으로 단일 인수와 함께 로드하기에 적합해야 해요. 예를 들어 파일이 test.so라면 load test.so 명령으로 로드할 수 있어야 해요. 각 스크립트 파일에는 패키지와 버전 번호를 선언하는 package provide 명령이 있어야 하고, 각 바이너리 파일에는 Tcl_PkgProvide 호출이 있어야 해요.

  2. pkg_mkIndex를 호출해 인덱스를 만든다. dir 인수는 디렉토리 이름을, 각 pattern 인수는 dir에서 스크립트·바이너리 파일을 선택하는 glob 스타일 패턴이에요. 기본 패턴은 *.tcl*.[info sharedlibextension]이에요.

pkg_mkIndex는 패턴 인수로 주어진 모든 파일에 대한 패키지 정보를 담은 pkgIndex.tcl 파일을 dir에 만들어요. 각 파일을 자식 인터프리터에 로드해서 어떤 패키지와 새 명령이 나타나는지 봄으로써 수행돼요(그래서 위에서 설명한 대로 파일에 package provide 명령이나 Tcl_PkgProvide 호출이 있는 것이 필수적이에요). 패키지가 스크립트와 바이너리 파일에 나뉘어 있거나 파일들 사이에 의존성이 있다면, -load 옵션을 쓰거나 pkg_mkIndex가 파일을 처리하는 순서를 조정해야 할 수 있어요. 아래 "복잡한 경우"를 참고해요.

  1. 패키지를 tcl_pkgPath 변수가 주는 디렉토리 중 하나의 하위 디렉토리로 설치한다. $tcl_pkgPath에 둘 이상의 디렉토리가 있으면, 머신 의존 패키지(바이너리 공유 라이브러리를 포함하는 것들)는 보통 첫 번째 디렉토리 아래에, 머신 무관 패키지(Tcl 스크립트만 포함하는 것들)는 두 번째 디렉토리 아래에 설치해야 해요. 하위 디렉토리는 패키지의 스크립트·바이너리 파일과 pkgIndex.tcl 파일을 포함해야 해요. 패키지가 $tcl_pkgPath의 디렉토리 하위 디렉토리로 설치되는 한, package require 명령 중에 자동으로 발견돼요.

  2. 패키지를 다른 곳에 설치했다면, 패키지가 담긴 디렉토리가 auto_path 전역 변수에 있거나, auto_path 디렉토리 중 하나의 바로 하위 디렉토리인지 확인한다. auto_path는 auto-loader와 패키지 로더 둘 다 검색하는 디렉토리 목록을 담아요. 기본적으로 $tcl_pkgPath를 포함해요. 패키지 로더는 auto_path의 디렉토리들의 모든 하위 디렉토리도 확인해요. 애플리케이션에서 auto_path에 디렉토리를 명시적으로 추가하거나, TCLLIBPATH 환경 변수에 디렉토리를 추가할 수 있어요. 이 환경 변수가 있으면 Tcl은 애플리케이션 시작 중에 그것으로 auto_path를 초기화해요.

위 단계를 마치면 패키지를 쓰려면 package require를 호출하기만 하면 돼요. 예를 들어 패키지 Test의 버전 2.1, 2.3, 3.1이 pkg_mkIndex로 인덱스되었다면, package require Test는 버전 3.1을 사용 가능하게 만들고, package require -exact Test 2.1은 버전 2.1을 사용 가능하게 만들어요. auto_path의 다양한 인덱스 파일에 패키지의 여러 버전이 있을 수 있지만, 주어진 인터프리터에서는 package require의 첫 호출을 기준으로 하나만 실제로 로드돼요. 패키지의 다른 버전들이 다른 인터프리터에서 로드될 수는 있어요.

옵션

선택 스위치는 다음과 같아요.

-direct 생성된 인덱스가 package require 시 패키지를 직접 로드하게 구현해요. 이것이 기본값이에요.

-lazy 생성된 인덱스가 package require 시 즉시 로드하는 대신, 패키지가 제공하는 명령 중 하나를 사용할 때까지 패키지 로드를 지연하게 관리해요. 이것은 auto_reset의 사용과 호환되지 않으므로 사용이 권장되지 않아요.

-load pkgPat 인덱스 과정이 현재 인터프리터에 존재하는 pkgPat과 일치하는 패키지를, 인덱스를 생성하는 데 쓰는 자식 인터프리터에 미리 로드해요. 패턴 일치는 string match 규칙을 사용하되 대소문자를 구분하지 않아요. 아래 "복잡한 경우"를 참고해요.

-verbose 인덱싱 과정 중 출력을 생성해요. 출력은 기본적으로 stderr에 인쇄하는 tclLog 프로시저를 통해 이뤄져요.

-- dir이 대시로 시작하는 경우를 대비한 플래그의 끝을 표시해요.

패키지와 auto-loader

패키지 관리 기능은 on-demand로 파일을 로드하게 한다는 점에서 auto-loader와 다소 겹쳐요. 하지만 패키지 관리는 로딩 과정의 마지막 단계에 auto-loader를 사용하는 더 높은 수준의 메커니즘이에요. auto_mkindex보다 pkg_mkIndex로 패키지를 인덱스하는 것이 일반적으로 더 좋아요. 패키지 메커니즘이 버전 제어를 제공하기 때문이에요. 여러 버전의 패키지를 인덱스 파일에서 사용 가능하게 만들 수 있고, 다른 애플리케이션은 package require 명령을 기준으로 다른 버전을 사용할 수 있어요. 반면 auto_mkindex는 버전을 이해하지 못해서 각 패키지의 단일 버전만 처리할 수 있어요. 주어진 패키지를 pkg_mkIndexauto_mkindex 둘 다로 인덱스하는 것은 아마 좋은 생각이 아니에요. pkg_mkIndex로 패키지를 인덱스하면 버전을 선택하는 package require가 사용될 때까지 그 명령들을 호출할 수 없어요. 반면 auto_mkindex로 인덱스한 패키지는 버전 제어가 없으므로 즉시 사용할 수 있어요.

동작 원리

pkg_mkIndexpackage unknown 명령, package ifneeded 명령, 그리고 auto-loader에 의존해요. package require 명령이 처음 호출되면 package unknown 스크립트가 호출돼요. 이것은 Tcl 초기화가 auto_path의 모든 pkgIndex.tcl 파일을 평가하는 스크립트로 설정해요. pkgIndex.tcl 파일은 사용 가능한 각 패키지의 각 버전에 대한 package ifneeded 명령을 담고 있어요. 이 명령들은 패키지의 제공을 알리는 package provide 명령을 호출하고, 패키지의 파일을 로드하도록 auto-loader 정보를 설정해요.

pkgIndex.tcl 생성 시 -lazy 플래그가 주어졌다면, 주어진 패키지의 주어진 버전의 주어진 파일은 그 명령 중 하나가 처음 호출될 때까지 실제로 로드되지 않아요. 따라서 package require 호출 후 인터프리터에 패키지의 명령이 보이지 않을 수 있지만, 명령을 호출할 수 있고 자동으로 로드될 거예요.

직접 로딩

네임스페이스를 사용하고 명령을 내보내는 패키지나 특별한 초기화가 필요한 패키지 같은 일부 패키지는, 패키지 명령 중 하나의 첫 사용까지 실제 로딩을 미루는 대신 package require 시 패키지 파일을 즉시 로드하도록 선택할 수 있어요. 이것이 패키지 인덱스를 생성할 때의 기본 모드예요. -lazy 인수를 지정해 덮어쓸 수 있어요.

복잡한 경우

스크립트와 바이너리 파일 사이, 그리고 스크립트와 바이너리 파일에 나뉜 패키지 사이의 대부분의 의존성 복잡한 경우는 잘 처리돼요. 하지만 pkg_mkIndex가 파일을 처리하는 순서를 조정해야 할 수도 있어요. 이런 문제들은 아래에 자세히 설명돼요.

각 스크립트나 파일이 하나의 패키지를 담고, 패키지가 한 파일에만 담겨 있다면 간단해요. 인덱스할 모든 파일을 몇몇 glob 패턴으로 어떤 순서로든 지정하기만 하면 돼요.

일반적으로 스크립트가 다른 패키지에 의존하는 것은 괜찮아요. 스크립트가 package require 명령을 포함하면, 스크립트를 처리하는 인터프리터에서 그것이 스텁 처리되므로 문제가 생기지 않아요. 스크립트가 전역 코드에서 다른 패키지를 호출하면 그러한 호출은 스텁 unknown 명령으로 처리돼요. 하지만 스크립트가 전역 코드에서 다른 패키지 변수를 참조하면 오류가 생겨요. 그것은 나쁜 코딩 스타일이기도 해요.

바이너리 파일이 다른 패키지에 의존하면, 바이너리 파일을 로드할 때 Tcl_PkgRequire API 같은 C 레벨 API를 스텁 처리할 수 없기 때문에 까다로워질 수 있어요. 예를 들어 BLT 패키지가 Tk를 요구하고, Blt_Init 루틴에서 Tcl_PkgRequire 호출로 표현한다고 해 보죠. 이를 지원하려면 Tk가 로드된 인터프리터에서 pkg_mkIndex를 실행해야 해요. 이것을 -load pkgPat 옵션으로 달성할 수 있어요. 이 옵션을 지정하면 pkg_mkIndexinfo loaded에 나열되고 pkgPat과 일치하는 패키지를 파일 처리용 인터프리터에 로드해요. 대부분의 경우 이것이 바이너리 파일이 하는 Tcl_PkgRequire 호출을 충족시켜요.

두 바이너리 파일을 인덱스하는데 하나가 다른 것에 의존한다면, 의존성이 있는 것을 마지막에 지정해야 해요. 이렇게 하면 의존성이 없는 것이 로드·인덱스되고, 두 번째 파일을 처리할 때 그것이 제공하는 패키지를 사용할 수 있게 돼요. -load 플래그로 첫 패키지를 인덱스를 만드는 데 쓰는 임시 인터프리터에 로드해야 할 수도 있어요. 아직 로드되지 않은 패키지 패턴을 지정해도 해가 되지 않아요.

스크립트와 바이너리 파일에 나뉜 패키지를 갖고 있다면 -load 플래그를 피해야 해요. 문제는 인덱스를 계산하기 전에 패키지를 로드하면 같은 패키지의 일부를 제공하는 다른 파일을 가려서(mask) 버린다는 점이에요. -load를 꼭 써야 한다면 스크립트를 먼저 지정해야 해요. 그렇지 않으면 바이너리 파일에서 로드된 패키지가 스크립트가 정의한 패키지를 가릴 수 있어요.

더 알아보기

  • package — 패키지 로딩·버전 관리