tm — Tcl 모듈의 위치 탐색과 로딩 기능

tm — Tcl 모듈의 위치 탐색과 로딩 기능

복잡한 프로그램을 여러 파일로 나누기 위해 패키지를 쓰는데, 그중 한 파일로만 구성된 패키지를 'Tcl 모듈'이라고 불러요. tm은 이런 Tcl 모듈을 어디서 찾고 어떻게 불러올지 관리하는 기능이에요. 경로 목록을 다루는 명령들과 모듈의 정의, 탐색 규칙을 함께 설명해요.

출처: Tcl 공식 문서 - tm

본문

::tcl::tm::path add ?path...?

::tcl::tm::path remove ?path...?

::tcl::tm::path list

::tcl::tm::roots paths

이 문서는 Tcl 모듈의 위치 탐색과 로딩 기능을 설명해요(모듈의 정의는 MODULE DEFINITION 절 참조). 지원되는 명령은 다음과 같아요.

  • ::tcl::tm::path add ?path...?: 경로들이 나타나는 순서대로 모듈 경로 목록의 맨 앞에 추가돼요. 즉 마지막 인자가 목록의 새 맨 앞이 돼요. 이 명령은 어떤 경로도 목록의 다른 경로의 조상 디렉터리가 될 수 없다는 제한을 강제해요. 새 경로 중 하나라도 이 제한을 위반하면, 어떤 경로도 추가되기 전에 오류가 발생해요. 다시 말해 단 하나의 경로 인자만 위반해도 아무것도 추가되지 않아요. 경로가 이미 그대로 있으면 오류가 발생하지 않고 아무런 조치도 취하지 않아요. 경로는 나중에 목록에 나타난 순서대로 검색돼요. 목록의 앞에 추가되므로 역순으로 검색되는데, 즉 나중에 추가된 경로가 먼저 봐요.
  • ::tcl::tm::path remove ?path...?: 모듈 경로 목록에서 경로를 제거해요. 목록에 없는 경로는 조용히 무시해요.
  • ::tcl::tm::path list: 등록된 모든 모듈 경로를, 모듈을 검색하는 순서대로 담은 리스트를 반환해요.
  • ::tcl::tm::roots paths: path add와 비슷하지만 그 위에 층을 얹은 명령이에요. 단일 인자로 경로 리스트를 받아, 각각을 Tcl 인터프리터의 주 버전 X와 그 이하의 부 버전 y에 대해 "tclX/site-tcl""tclX/X.y"로 확장하고, 결과 경로 집합을 검색할 경로 목록에 추가해요. 이 명령은 시스템이 시스템별 기본 경로를 설정할 때 내부적으로 사용해요. 이 문서가 설명하는 것 너머의 추가 루트 경로를 정의할 수 있도록 빌드 시스템에 노출되어 있어요.

모듈 정의(Module Definition)

Tcl 모듈은 단일 파일에 담긴 Tcl 패키지이고, 그 외 필요한 파일은 없어요. 이 파일은 소스 가능(sourceable)해야 해요. 다시 말해 Tcl 모듈은 항상 다음을 통해 불러와져요.

source module_file

load 명령은 직접 사용되지 않아요. 이 제한은 어떤 이들이 생각하는 것만큼 실질적인 제약이 아니에요. 8.4 이후로 Tcl source 명령은 첫 ^Z 문자까지만 읽어요. 이를 통해 임의의 Tcl 스크립트와 임의의 바이너리 데이터를 한 파일에 결합할 수 있어요. 스크립트가 그 첨부 데이터를 원하는 방식으로 처리해 패키지를 완전히 불러와 활성화해요.

모듈 파일의 이름은 다음 정규 표현식과 일치해야 해요.

([_[:alpha:]][:_[:alnum:]]*)-([[:digit:]].*)\.tm

첫 번째 캡처 괄호가 패키지 이름을, 두 번째 절이 버전을 제공해요. 패턴 일치 외에도 추출된 버전 번호는 다음 명령에서 오류를 일으키지 않아야 해요.

package vcompare $version 0

모듈 찾기(Finding Modules)

Tcl 모듈을 저장하는 디렉터리 트리는 파일시스템의 다른 부분과 분리되어 있고 auto_path와 독립적이에요. Tcl 모듈은 ::tcl::tm::path list 명령의 결과에 나열된 모든 디렉터리에서 검색돼요. 이를 모듈 경로(Module path)라고 불러요. auto_pathtcl_pkgPath 변수는 사용되지 않아요. 모듈 경로의 모든 디렉터리는 한 가지 제한을 지켜야 해요. 즉 임의의 두 디렉터리에서 서로가 다른 쪽의 조상 디렉터리가 아니어야 해요. 이는 패키지 이름의 모호함을 피하기 위해 필요해요. 예를 들어 "foo/""foo/cool"이 경로에 있다면 cool::ice라는 패키지를 cool::ice 또는 ice 이름으로 찾을 수 있어서, 후자가 비정규화된 ice 패키지를 가릴 수 있어요.

검색을 시작하기 전에 요청된 패키지의 이름은 다음 알고리즘으로 부분 경로로 변환돼요. 패키지 이름의 모든 "::"는 플랫폼에 맞는 디렉터리 구분자로 교체돼요(Unix에서는 /). 예: 요청 패키지가 encoding::base64면 생성된 부분 경로는 "encoding/base64"예요.

이 변환 후 패키지는 모든 모듈 경로에서, 그 경로들을 첫 번째부터 마지막까지 부분 경로와 하나씩 결합해 완전한 검색 패턴을 만들어 찾아져요. 검색 알고리즘은 MODULE DEFINITION 절에 주어진 정규 표현식과 일치하지 않는 파일은 모두 거부해요. 남은 파일들에 대해 provide 스크립트가 생성되고 패키지 ifneeded 데이터베이스에 추가돼요. 찾은 모듈 파일 중 어떤 것도 요청을 충족하지 못하면 알고리즘은 이전의 unknown 핸들러로 넘어가요. 요청이 충족되면 폴백은 무시돼요.

모듈 형태의 패키지는 그들을 위해 패키지 데이터베이스에 들어간 index와 provide 스크립트를 통제하지 못해요. 모듈 파일 MF에 대해 index 스크립트는 항상 다음과 같아요.

package ifneeded PNAME PVERSION [list source MF]

그리고 위에 포함된 provide 스크립트는 다음과 같아요.

source MF

패키지 이름 PNAME과 패키지 버전 PVERSION은 둘 다 다음 정의에 따라 파일 이름 MF에서 추출돼요.

MF = /module_path/PNAME′-PVERSION.tm

여기서 PNAME′는 FINDING MODULES 절에서 정의한 모듈의 부분 경로이고, 모든 디렉터리 구분자를 "::"로 바꿔 PNAME으로 변환되며, module_path는 모듈 파일을 찾은 (검색할 경로 목록의) 경로예요.

여기서 우리는 패키지 이름과 경로 사이에 연결을 만들고 있다는 점도 주목하세요. Tcl은 패키지 이름을 비교할 때 대소문자를 구분하지만, NTFS처럼 대소문자를 구분하지 않는 파일시스템도 있어요. 다행히 이런 파일시스템은 비교할 때 정보를 사용하지 않아도 이름의 대소문자를 저장해요. 그래서 우리는 Tcl 모듈의 패키지 이름에 대소문자를 섞는 것을 허용하되, 대소문자 구분 없이 비교할 때 충돌이 없어야 한다고 요구해요. 다시 말해, 패키지 Foo가 Tcl 모듈 형태로 배포되면 foo, fOo 같은 패키지는 더 이상 허용되지 않아요.

기본 경로(Default Paths)

모듈 경로의 기본 목록은 tclsh가 X를 Tcl 인터프리터의 주 버전, y를 부 버전 이하로 하여 다음과 같이 계산해요. 기본 경로는 모두 모듈 경로에 추가돼요. 존재하지 않는 경로도요. 존재하지 않는 경로는 실제 검색 중에 걸러져요. 이를 통해 사용자는 필요할 때 검색되는 경로 중 하나를 만들 수 있고, 실행 중인 모든 애플리케이션이 그곳에 놓인 모듈을 자동으로 가져가게 돼요. 경로는 아래 나열된 순서대로 추가되고, 환경 변수로 정의된 경로 목록은 변수에서 발견되는 순서대로 추가돼요.

시스템별 경로(System Specific Paths)

  • file normalize [info library]/../tclX/X.y: 다시 말해 인터프리터는 주 버전이 지정한 디렉터리 중 부 버전이 인터프리터의 부 버전 이하인 것을 봐요. 예를 들어 Tcl 8.4의 검색 경로는 다음과 같아요.
    [info library]/../tcl8/8.4
    [info library]/../tcl8/8.3
    [info library]/../tcl8/8.2
    [info library]/../tcl8/8.1
    [info library]/../tcl8/8.0
    
    이 정의는 Tcl X.y용으로 정의된 패키지가 같은 주 번호 X를 갖고 부 번호가 y보다 큰 모든 인터프리터에서도 사용될 수 있다고 가정해요.
  • file normalize EXEC/tclX/X.y: 여기서 EXEC는 file normalize [info nameofexecutable]/../lib 또는 file normalize [::tcl::pkgconfig get libdir,runtime]이에요. 이 경로 집합은 앞 경로 집합과 동일하게 처리되지만, EXEC_PREFIX에 고정된다는 점이 달라요. PREFIX = EXEC_PREFIX인 빌드에서는 두 집합이 동일해요.

사이트별 경로(Site Specific Paths)

  • file normalize [info library]/../tclX/site-tcl: X는 항상 특정 값(현재 Tcl의 주 버전)이므로 이는 항상 단일 항목이라는 점에 주의하세요.

사용자별 경로(User Specific Paths)

  • $::env(TCLX_y_TM_PATH): :(Unix) 또는 ;(Windows)로 구분된 경로 목록이에요. 이 환경 변수는 사용자 프로필뿐 아니라 시스템 구성 스크립트로도 설정될 수 있으므로 사용자·사이트별이에요.
  • $::env(TCLX.y_TM_PATH): 이전 변수와 같은 의미와 내용이에요. 다만 주·부 버전 번호를 구분하는 데 점 .을 쓰는 이 이름은 덜 이식성이 좋아서 사용을 권장하지 않아요. 이 변수의 지원은 원래 명세(TIP 189)와의 하위 호환을 위해서만 유지되고 있어요.

이 경로들은 사용자의 $::env(PATH) 안의 모든 Tcl 셸이 보고 따라서 공유해요. X와 y는 위의 일반 규칙을 따르므로, Tcl 8.4는 예를 들어 이 10개의 환경 변수를 봐요.

$::env(TCL8.4_TM_PATH)  $::env(TCL8_4_TM_PATH)
$::env(TCL8.3_TM_PATH)  $::env(TCL8_3_TM_PATH)
$::env(TCL8.2_TM_PATH)  $::env(TCL8_2_TM_PATH)
$::env(TCL8.1_TM_PATH)  $::env(TCL8_1_TM_PATH)
$::env(TCL8.0_TM_PATH)  $::env(TCL8_0_TM_PATH)

더 알아보기