resolve_all 의 유용한 키워드(ENVI 프로그램 컴파일)

resolve라는 단어는 “심도있게 파헤쳐 문제를 근원적으로 해결하다”라는 어감을 내포하고 있다고 합니다. 그런가 보죠. ^^. 우리 IDL 개발자들에게는 resolve_all 이라는 프로시저로 친숙합니다. 이 프로시저의 역할은, “현재 컴파일되어 있는 프로그램을 실행하는데 필요한 프로시저나 함수를 모두 찾아들어가 컴파일”하는 것입니다. 즉, 1단계 실행 프로그램 단계에서는 컴파일 되어 있는데, 그 내부적으로 아직 컴파일되지 않아 있는 루틴들을 더 찾아 모두 컴파일하고자 할 때 사용합니다.

저는 배포용 IDL 프로그램인 sav 포맷을 만들 때 다음과 같이 기계적으로 사용을 합니다. 아마 대부분의 사용자가 그러실 겁니다.

IDL> .reset
IDL> .compile testprogram            ;testprogram은 프로시저 이름으로, 실제 상황에서는 프로그래머가 작명한 이름이 사용됨
IDL> resolve_all
…. 그동안 컴파일 되어 있지 않았으나 testprogram 내에서 사용될, 현재 컴파일 되지 않은 루틴들을 샅샅이 찾아 컴파일합니다.

예를 들면 아래와 같이 사용하지요(cgdrawshapes는 David Fanning 박사님의 Coyote Graphics 프로시저입니다).

IDL> .reset
IDL> .compile cgdrawshapes
% Compiled module: CGDRAWSHAPES_DRAWENTITY.
% Compiled module: CGDRAWSHAPES.
IDL> resolve_all
% Compiled module: RESOLVE_ALL.
% Compiled module: CGCOLORFILL.
% Compiled module: CGDISPLAY.
% Compiled module: CGPLOT.
% Compiled module: CGPLOTS.
% Compiled module: CGWINDOW.
% Compiled module: ASPECT.
% Compiled module: CGCHECKFORSYMBOLS.
% Compiled module: CGCOLOR.
… 이 아래로도 많은 루틴들이 컴파일 됩니다…

IDL> save, filename=’cgdrawshapes.sav’, /routines

이렇게 하면 cgdrawshapes.sav 라는 파일이 생성되고, 이 파일은 IDL 컴파일러 없이도 Virtual Machine에서도 돌릴 수 있게 됩니다. 윈도 상에서 더블클릭하면 실행이 됩니다(물론 IDL은 설치되어 있어야 합니다). 자, 이런 배포판을 만들고자 할 때 resolve_all이 흔히 쓰이는데, 이 프로시저도 키워드(옵션들)을 가지고 있습니다.  이 옵션들을 이해하는데는 저 위에 보여드린 단계를 다시 한번 보는 것이 좋습니다.

1) save, filename=’cgdrawshapes.sav’, /routines
이 행은, 현재 컴파일 되어 있는 모든 루틴을 cgdrawshapes.sav 라는 파일 안에 저장하는 것입니다. 컴파일된 채로 말이죠. 물론 메인 프로그램(더블클릭하면 실행되는 프로시저)은 cgdrawshapes 입니다. 이 때, 어떤 루틴이 cgdrawshapes 와 관련 있는지 없는지는 IDL이 판단할 수 없습니다. 그냥 모두 한 파일에 넣는 거예요. 아마도 필요 없는 파일이 함께 저장될 수도 있고, 결과적으로 파일이 커질 겁니다. 실행에는 지장 없겠지만… 그래서 이런 비효율을 정리하기 위해 처음에 메모리를 비우죠.

2) .reset
위와 같이 배포판을 만들기 전에 .reset을 실행하는 것은 이런 이유 때문입니다.  그 이후에 메인 프로그램을 컴파일하는 것은 당연합니다. 그런데, 메인 프로그램을 컴파일해도 메인 프로그램 안에서 호출하거나 호출할 가능성이 있는(분기문의 움직임에 따라 호출할 수도 안할 수도 있습니다) 루틴들은 컴파일되지 않은 채로 남아 있습니다. IDL 컴파일러는 이런 루틴들을 실행하는 과정에서 필요할 때마다 컴파일합니다. 그런데, 배포판을 만들 때는, 최종 사용자가 컴파일을 하지 않고 사용할 수 있는 상태로 만들어야 합니다. 즉, 컴파일러 라이센스가 없을 수도 있는 것을 가정해야합니다. 과학자들 세계에서 소스코드를 그냥 주는 것이 대부분인데(IDL의 .pro) 이 것이 IDL Full License가 없는 사용자에게는 하나도 고맙지 않은 상황일 수 있습니다. 그래서 가능한 모든 필요한 루틴을 컴파일해서 배포해야 합니다. 이 때 사용하는 것이 resolve_all 프로시저입니다. “메인프로그램을 보고, 그 안에서 필요로 하는 모든 프로시저/함수를 지금 당장 사용하지 않더라도 찾아내서 컴파일 하라”는 프로시저입니다.

그런데, 모든 프로시저가 컴파일되어 한데 묶이면 불편한 경우도 있습니다.

1) 예를 들어, IDL로 만들어진 원격탐사 소프트웨어 ENVI는 소스코드 공개없이 .sav 덩어리들로 배포가 되는데요, 우리가 이들 ENVI 라이브러리를 이용해서 프로그램을 만든다면, 우리가 만든 프로그램에 ENVI 루틴들을 모두 포함해서 배포해야 할까요? 그러면 덩치가 너무 커지는 거 아닐까요?
2) 또 다른 예로, 우리가 의도에 따라 sav 파일을 한 덩어리로 합치지 않고, 체계별로 여러 덩어리로 나누어 배포하고자 할 경우도 있습니다. 예를 들면, File 처리하는 부분을 한 덩어리로, GUI 정의 부분을 한 덩어리로, 각각 핵심 알고리즘마다 한 덩어리로 나누어 여러 덩어리로 프로그램을 배포하고자 할 경우도 있습니다(ENVI가 그렇게 만들어져 있어요.). 이 때는 무작정 resolve_all 을 하여 한 덩어리로 가는 것은 원치 않는 방향일 수 있습니다.

resolve_all의 키워드들은 이런 상황을 대비하여 존재합니다. 다만, 우리가 만드는 프로그램들이 아주 거대하거나 하지 않다 보니 옵션들을 사용할 기회가 많지 않을 뿐이죠.

1) /QUIET 옵션 : 제일 간단한 얘기여서 앞에 설명합니다. 컴파일된 내용들을 화면에 쭉 보여주는데, 그럴 필요 없다는 거예요.

2) /CONTINUE_ON_ERROR : 아마도 많이 쓰일 옵션입니다. 실행은 되는데, resolve_all을 하다가 에러가 날 때(뭐가 없다고 하면서 컴파일 못한다고 에러를 냅니다) 흔히 사용될 옵션입니다. 우리가 ENVI(classic입니다) 라이브러리를 이용하여 프로그래밍을 했는데, 이 프로그램의 실행은 ENVI가 설치된 것을 전제로 하거든요. 그러므로 ENVI 루틴들을 끌고 들어갈 수 없어요. 그리고 많은 ENVI 루틴들은 프로시저 이름/함수이름으로 되어 있지 않습니다. 필요할 때 호출하는 것이 아니라, ENVI를 시작하면 관련 라이브러리들을 일괄 메모리에 올리는 방식이지요. 그러므로 ENVI 루틴들, 예를 들어, ENVI_PROJ_CREATE 같은 루틴을 프로그램 안에서 사용한다고 해도, 얘만 따로 배포판에 끌고 가는 것은 불가능합니다. resolve_all이 찾지도 못해요. 왜냐하면 ENVI_PROJ_CREATE.sav 라는 파일은 없으니까요. ENVI_PROJ_CREATE는 거대한 ENVI 체계 안에 어딘가에 숨어 있으니까요. 그럼, 에러나면 그냥 못하는 거냐? 그게 아니고, /CONTINUE_ON_ERROR 키워드를 사용하면, 이런 에러(ENVI_PROJ_CREATE가 없어요… 같은)를 그냥 넘어갑니다.
참고로, 쓰이지 않을 함수나 프로시저(분기문 구조상 절대 그쪽으로는 안가는 프로시저)가 프로그램 안에 들어 있어서 문제가 되는 경우에도 이 옵션을 사용하면 그냥 눈감고 넘어갑니다.

3) SKIP_ROUTINES=[‘aaa’, ‘bbb’]   : 문자열이나 문자열 배열로 지정하는데, 그냥 건너뛸 루틴들의 이름을 지정합니다. 프로시저와 펑션을 가리지 않습니다. 2) /CONTINUE_ON_ERROR 를 대체하여 종종 사용됩니다. 특히, “얘는 나중에 따로 컴파일 해 줄테니까, 지금은 건너뛰세요”라는 의미로 사용됩니다.

4) RESOLVE_FUNCTION=[‘aaa’, ‘bbb’] : 문자열이나 문자열 배열로 지정하는데, “얘만 컴파일해 주세요”라는 의미입니다. 당연히 함수여야 하구요

5) RESOLVE_PROCEDURE=[‘aaa’, ‘bbb’] : 문자열이나 문자열 배열로 지정하는데, 프로시저라는 점이 RESOLVE_FUNCTION과 다른 점입니다.

6) RESOLVE_EITHER=[‘aaa’, ‘bbb’] : 펑션인지 프로시저인지 구분하지 않을 때, 사용합니다.

7) UNRESOLVED=변수 : 해당 변수에, 컴파일하지 않은 루틴 이름들이 넘어옵니다. CONTINUE_ON_ERROR 옵션에 따라 그냥 건너뛴 것들과, SKIP_ROUTINES 옵션에 따라 건너뛴 것들의 목록이 배열로 저장되겠지요.

정리하자면,

A) RESOLVE_ALL은 알아서 “모두” 찾아 컴파일해 주니까 편리합니다.

B) 그런데, 지금 단계에서 모두 준비되어 있지 않은 경우도 있습니다. 나중에 따로 제공하고 싶은 라이브러리도 있는 거구요.

C) 이런 상황을 대비하여, 지정한 것만 하거나, 지정한 것만 빼거나, 아니면 준비되지 않은 것을 눈감고 넘어가거나 하는 그런 옵션들이 준비되어 있는 것입니다.