반응형
자바및 XML 기반의 빌드 툴인 Ant에 대해서 살펴본다.
Ant!
유닉스/리눅스 시스템에서 C나 C++로 프로그래밍을 해본 개발자라면 make라는 프로그램에 대해서 알 것이다. make는 makefile 이라는 설정 파일을 기반으로 소스 코드를 컴파일 해 주고 컴파일된 오브젝트 코드를 모아서 실행 가능한 코드로 만들어주는 등의 많은 작업들을 한번의 배치성 작업으로 처리할 수 있도록 해 주는 프로그램이다. 자바의 경우도 자바 소스 코드를 컴파일해주고, 컴파일된 클래스 파일을 배포본으로 변경해주고 그리고 소스 코드로부터 API 문서를 생성하는 작업을 한번의 배치 작업으로 처리할 수 있도록 해주는 프로그램이 있다. 그것은 Tomcat으로 유명한 아파치의 자카르타 프로젝트에서 진행되고 있는 Ant라는 것이다.
Ant는 자바 기반의 빌드툴로서 make와 비슷한 기능을 제공하고 있다. 하지만 Ant는 다음과 같은 면에서 make와 같은 쉘기반의 빌드툴과는 다르다.
Ant의 설치
Ant를 사용하려면 먼저 Ant를 컴퓨터에 설치해야 한다. Ant는 자카르타의 홈페이지인 http://jakarta.apache.org에서 구할 수 있다. 현재 Ant는 1.4 버전이 출시된 상태이며 자카르타의 다른 프로젝트들과 마찬가지로 바이너리와 소스의 두 가지 형태의 배포본을 제공하고 있다. 이 두 가지 배포본 중 바이너리 배포본은 설치하는 데 큰 어려움이 없으므로 바이너리 배포본을 설치하는 것에 대해서 살펴보도록 하자.
바이너리 배포본은 현재 윈도우즈 배포본인 jakarta-ant-1.4-bin.zip과 유닉스/리눅스 배포본인 jakarta-ant-1.4-bin.tar.gz의 두 파일이 있다. 이 두 파일은 실제 내용은 같다. 운영체제에 따라 알맞은 파일을 다운로드 받은 후 알맞은 디렉토리에 압축을 풀도록 하자. 필자의 경우 윈도우즈 시스템의 C:\에 jakarta-ant-1.4-bin.zip 파일의 압축을 풀어서 사용하고 있다.
c:\에 바이너리 배포본의 압축을 풀면 다음 표1과 같은 디렉토리가 생성될 것이다.
환경변수 설정
Ant의 설치가 완료되었다면, Ant가 올바르게 실행되도록 환경변수를 지정해주어야 한다. 환경 변수는 다음과 같이 지정해주면 된다.
그리고 유닉스나 리눅스 시스템일 경우에는 다음과 같이 지정해주면 된다.
이 중 JAVA_HOME 환경변수는 선택사항이며 필수사항은 아니다. JAVA_HOME 환경변수는 몇몇 특수한 태스크를 사용하는 경우에만 지정해주면 되며 일반적인 경우에는 지정해주지 않아도 무방하다.
윈도우즈 98이나 95를 사용할 경우 ANT_HOME 디렉토리 이름이 길 경우 운영체제의 제한 때문에 Ant의 실행파일인 ant.bat 파일이 제대로 실행되지 않게 된다. 따라서 윈도우즈 98이나 95를 사용할 경우에는 ANT_HOME 디렉토리의 이름을 jakarta-ant-1.4가 아닌 ant와 같이 짧은 이름으로 변경해주기 바란다.
Ant의 실행
Ant의 실행은 매우 간단하다. 앞에서 설명한 환경변수를 올바르게 설정하였다면 단순히 커맨드 프롬프트에서 다음과 같이 입력하면 된다.
ant.bat(또는 ant.sh)을 실행하면 Ant의 설정 파일인 build.xml 파일을 현재 디렉토리부터 시작해서 상위 디렉토리로 이동해가면서 검색하게 된다. build.xml 파일이 발견되면 Ant는 build.xml 파일을 분석하여 build.xml 파일에 정의되어 있는 태스크에 따라 알맞은 배치 작업을 수행한다.
단순히 위와 같이 'ant'만 입력해도 Ant가 실행되지만, ant는 개발자가 명령어 프롬프트에서 옵션을 입력해서 여러 내용들을 직접 설정할 수 있도록 하고 있다. ant는 기본적으로 다음과 같은 형태로 명령행 인자를 입력받는다.
여기서 [옵션]에는 빌드 파일, 로그 파일 그리고 프로퍼티 등의 값을 지정할 수 있는 옵션이 오게 되며 [타겟]에는 빌드 파일에 지정되어 있는 <target> 태그의 name 속성의 값이 온다. [타겟]을 지정하지 않을 경우 빌드 파일에서 <project> 태그의 default 속성의 값을 작업 대상으로 사용한다. 이에 대한 내용은 <project> 태그와 <target> 태그를 설명할 때 보다 자세히 살펴보기로 하자.
표2는 Ant를 실행할 때 사용할 수 있는 주요 옵션 목록이다.
예를 들어, 빌드 파일로 build.xml이 아닌 testbuild.xml을 사용하고 작업 대상(target)을 main으로 하고 싶다면 다음과 같이 실행하면 된다. (작업 대상에 대한 내용은 잠시 뒤에 살펴볼 것이다.)
표2에서 설명한 옵션 외에도 몇몇 옵션이 더 있는데, 이 옵션은 -help 옵션을 사용하여 확인할 수 있다.
Ant의 사용
make를 사용하기 위해서 makefile을 알맞게 작성해야 하듯이 Ant를 올바르게 활용하기 위해서는 Ant가 사용할 빌드 파일을 알맞게 작성해야만 한다. Ant의 빌드 파일은 XML 문서의 구조를 갖고 있으며 Ant가 작업을 수행할 프로젝트에 대한 정보를 담고 있으며, 다음과 같은 형태를 취하고 있다.
위 코드를 보면 크게 project, property, target, 태스크의 네가지 요소로 빌드 파일이 구성되어 있는 것을 알 수 있다. 위에서 '태스크명n'으로 표시된 태그는 target 태그가 나타내는 대상이 수행할 작업을 나타내는 것으로서 소스 코드를 컴파일해주는 태스크, Jar를 사용하여 압축해주는 태스크 등 다양한 종류의 태스크가 미리 개발되어 있다.
이 네 가지 요소가 Ant의 모든 기능을 사용할 수 있도록 해 주며, 네 가지 요소를 알맞게 조합함으로써 개발자는 원하는 작업을 배치 작업으로 처리할 수 있게 된다. 이 글의 나머지 부분에서는 빌드 파일에서 사용되는 project, target, property 그리고 태스크 태그를 어떻게 설정해주는지 구체적으로 살펴보도록 하자.
project
<project> 태그는 빌드 파일의 루트 태그로서 프로젝트 자체를 정의해준다. 모든 빌드 파일은 한 개의 project 태그를 가지며, project 태그는 프로젝트를 설명하기 위해서 다음과 같은 속성을 사용한다.
Ant는 Ant를 실행할 때 가장 먼저 실행할 태스크를 지정할 수 있는 두가지 방법을 갖고 있는데, 한 가지는 앞에서 Ant 실행할 때 [타겟]을 지정해주는 것이며 다른 하나는 <project> 태그의 default 속성을 사용하여 첫번째로 실행할 태스크를 지정해주는 것이다.
default 속성의 값은 반드시 지정해주어야 하는 것으로서 Ant를 실행할 때 [타겟]을 지정해주지 않을 경우 default 속성의 값을 사용하여 첫번째로 수행할 [타겟]을 선택하게 된다.
target
<target> 태그는 <project> 태그에 포함되며, 실제로 프로젝트가 수행하게 될 작업(태스크)을 지정한다. <target> 태그의 기본 구조는 다음과 같다.
하나의 <project> 태그는 여러 개의 <target< 태그를 포함할 수 있으며, 각각의 <target> 태그는 그 타겟을 통해서 수행하고자 하는 태스크(작업)를 명시한다. 즉, 실제 작업의 처리는 <target> 태그 내에 명시되어 있는 태스크를 통해서 이루어지는 것이다. 이때 각각의 <target> 태그는 상호간 의존관계가 있을 수 있다. 예를 들어, 일반적인 어플리케이션의 빌드 순서는 '컴파일->API 문서생성->배포판 생성'인데, 이 경우 빌드 파일에는 이 각각의 작업에 대한 <target>이 있을 것이다. 즉, 컴파일을 위한 <target> 태그, API 문서 생성을 위한 <target> 태그 그리고 배포판 생성을 위한 <target> 태그 등 3개의 <target> 태그가 존재하게 되며 다음과 같이 빌드 파일이 구성되어 있다고 해 보자.
이 때, 각각의 타겟의 수행순서는 compile 타겟, apidoc 타겟, makeproduct 타겟이어야 한다. 즉, apidoc 타겟은 compile 타겟에 의존하고 makeproduct 타겟은 apidoc 타겟에 의존하게 되는 것이다. 이러한 의존관계는 <target> 태그의 depends 속성을 통해서 지정할 수 있다. 예를 들어, 다음과 같이 depends 속성이 명시되어 있다고 해 보자.
여기서 여러분이 최종적으로 실행하고자 타겟이 D라고 해 보자. 이 때, 타겟 D의 depends 속성의 값은 "C,B,A"인데 이는 타겟 D를 실행하기 위해서는 먼저 타겟 C, B, A가 수행되어야 함을 의미한다. 이때 타겟 C, B, A가 수행되는 순서는 depends 속성에 표시된 순서대로 수행되게 된다. 즉, 타겟 D를 실행하면 타겟 C가 먼저 실행되고, 그 다음 타겟 B, 그 다음 타겟 A, 그리고 마지막으로 타겟 D가 실행된다.
그런데, 다음과 같이 타겟 B와 타겟 C가 depends 속성을 갖고 있다고 해 보자.
이 경우에는 타겟 C가 타겟 B에 의존하고 있고, 타겟 B는 타겟 A에 의존하고 있기 때문에 타겟 C가 가장 먼저 실행되기 보다는 타겟 A가 가장 먼저 실행되고, 그 다음에 타겟 B, 그 다음에 타겟 C, 그리고 마지막으로 타겟 D가 실행되게 된다. 이러한 depends 속성을 이용하여 앞의 빌드 순서를 표시한 타겟을 다시 표현하면 다음과 같이 변경된다.
여러 개의 <target> 태그의 depends 속성이 같은 타겟의 이름을 값으로 가질 수도 있으나 이에 상관없이 각각의 타겟은 오직 한번만 실행된다.
Ant의 빌드 파일에서는 프로퍼티를 사용할 수 있는데, <target> 타겟 태그는 if 속성을 사용하여 특정한 프로퍼티가 지정되어 있을 경우에만 작업을 수행하도록 할 수 있다. 또한, unless 속성을 사용하여 프로퍼티가 지정되어 있지 않을 경우에만 작업을 수행하도록 할 수도 있다. 예를 들어, 테스트 단계와 배포판 생성 단계에서 각각 다른 태스크를 수행해야 할 경우 다음과 같이 "test" 프로퍼티를 사용할 수 있을 것이다.
<target> 태그는 그 타겟에 대한 설명을 description 속성을 통해서 할 수 있다. description 속성에 명시한 값은 ant.bat을 실행할 때 명령행 옵션인 -projecthelp를 통해서 볼 수 있다.
태스크
타켓이 각 수행할 작업간의 의존관계나 수행 조건 등을 표시한다면 태스크는 타켓 내에서 실제로 수행할 작업을 나타낸다. 예를 들어, 여러분은 태스크를 통해서 소스 코드를 컴파일하고 파일을 복사/삭제하고 API 문서를 생성할 수 있다. 태스크 <target> 태그에 중첩되어 표시되며 다음과 같은 구조를 갖고 있다.
'태스크명'은 태스크의 이름을 나타내고 '속성n'과 '값n'은 각각 태스크를 처리할 때 사용할 속성값을 나타낸다.
태스크는 이미 만들어져 있는 빌트인(built-in) 태스크를 사용할 수도 있고, 추가적으로 제공되는 옵션(optional) 태스크를 사용할 수도 있다. 또한 직접 작성한 태스크를 사용할 수도 있다. 예를 들어, 빌트인 태스크인 javac를 사용하여 소스 코드를 컴파일 할 때는 다음과 같이 <target> 태그에 javac 태스크를 중첩시키면 된다.
위 코드에서 <javac src="${src}" ... /> 태그가 <target> 태그에 중첩되어 있는 것을 알 수 있는데, 이때 javac는 태스크명을 나타내며, srcdir과 destdir, classpath는 각각 javac 태스크에서 사용되는 속성을 나타낸다. 그리고 각 속성의 값 중, "${"로 시작하고 "}"로 끝나는 것이 있는데 이는 프로퍼티를 나타내는 것으로서 "${"와 "}" 사이에 있는 문자열의 프로퍼티의 이름이다.
property
프로젝트를 진행하다보면 다양한 프로퍼티를 사용하게 된다. 컴파일할 소스 코드의 위치, 컴파일된 클래스 파일을 저장할 디렉토리, 압축한 파일을 위치시킬 디렉토리 등 유연한 개발을 위해서는 다양한 프로퍼티의 사용이 필수적이라 할 수 있다. Ant는 이처럼 빌드 과정에서 사용되는 다양한 프로퍼티를 지정할 수 있도록 하고 있으며, 또한 몇몇 개의 빌트인 프로퍼티를 제공하고 있다.
프로퍼티의 지정은 property 태스크를 통해서 할 수 있으며, property 태스크는 표4와 같은 속성을 갖고 있다.
예를 들어, "buildno" 속성의 값을 "356"으로 지정하고 싶다면 다음과 같이 하면 된다.
또한, 특정한 파일에 저장된 프로퍼티를 사용하고 싶다면 다음과 같이 file 속성을 사용하면 된다.
Ant의 빌드 파일에서는 자바에서 기본적으로 제공하는 "java.version"과 같은 시스템 프로퍼티를 기본적으로 사용할 수 있으며, 추가적으로 Ant 자체에서 기본적으로 제공하는 프로퍼티를 사용할 수 있다. Ant가 기본적으로 제공하는 프로퍼티 목록은 다음표와 같다.
프로퍼티는 특정한 타켓에서만 사용되도록 지정할 수도 있고 전체에서 사용되도록 지정할 수도 있다. 다음 예를 살펴보자.
위 빌드 파일을 보면 build, dist 프로퍼티는 프로젝트 전체에서 사용될 수 있는 프로퍼티로서 빌드 파일의 어떤 곳에서든지 사용될 수 있다. 반면에 init 타켓 내에 정의된 src 프로퍼티는 init 타켓에 직간접적으로 의존하고 있는 타켓에서만 사용될 수 있다. 위 코드에서는 init 타켓에 의존하고 있는 compile 타켓에서 src 프로퍼티를 사용할 수 있음을 알 수 있다. 또한 간접적으로 init 타겟에 의존하고 있는 dist 타겟 역시 src 프로퍼티를 사용하고 있다.
파일과 디렉토리 지정을 위한 패턴 관련 태그
어플리케이션을 빌드하는 과정에서 흔히 필요한 작업이 다음과 같은 것들이다.
특정 디렉토리/파일의 지정은 일정한 패턴을 통해서 표현된다. 패턴에서 사용되는 글자는 '*'와 '?'이다. 아마 많은 개발자들이 도스나 리눅스에서 dir이나 ls 명령어를 사용할텐데, '*'와 '?' 글자는 dir이나 ls 명령어를 입력할 때 사용되는 와일드캐릭터인 '*'와 '?'와 비슷한 의미를 지닌다. Ant의 설정 파일에서 이 두 글자는 다음과 같은 의미를 지닌다.
디렉토리를 지정할 때도 이 두 글자를 사용할 수 있다. 예를 들어, 'mywork/src?/*/*.java'와 같이 지정했을 경우 'mywork/src1/jcore/ABC.java'나 'mywork/srcA/java/MySome.java'과 같은 파일을 지정할 수 있게 된다. 또한, 좀더 유연한 방법을 제공하기 위해 Ant는 '**'를 사용하여 디렉토리를 지정할 수 있도록 하고 있다. 디렉토리 지정에서 '*'를 사용할 경우 디렉토리 트리에서 단 하나의 계층만을 가리키게 되는 반면에 '**'는 다계층을 나타낸다. 즉, 'mywork/*/A.java'라고 하면 'mywork/some/A.java'나 'mywork/src/A.java'와 같이 '*'가 명확하게 하나의 디렉토리 레벨만을 나타내지만, 'mywork/**/A.java'라고 할 경우 'mywork/some/A.java'는 물론 'mywork/some/jcore/A.java'나 'mywork/com/javacan/test/A.java'와 같이 여러 레벨의 디렉토리를 나타내는 것이다.
'**'로 패턴이 끝나게 되면 디렉토리 뿐만 아니라 모든 파일을 나타낸다. 예를 들어, 'xyz/**' 패턴은 'xyz/A.java', 'xyz/some/A.java'를 포함하게 된다. 그리고 패턴의 마지막이 디렉토리를 구분해주는 '/'나 '\'일 경우에는 자동으로 맨 뒤에 '**'가 붙게 된다. 즉, 'xyz/' 패턴은 'xyz/**' 패턴과 동일한 의미를 지닌다.
참고사항으로, 다음 패턴들은 특별히 지정하지 않는 한 태그 또는 태스크가 처리할 파일 또는 디렉토리 패턴에서 자동으로 제외된다.
patternset
<patternset> 태그는 패턴을 집합을 지정할 때 사용되며, 다음과 같은 구조를 갖고 있다.
위에서 patternset 태그의 id 속성을 지정했는데, 패턴을 사용하는 다른 태스크나 태그들은 id 속성의 값을 사용하여 patternset을 참조할 수 있게 된다. 예를 들어, 파일의 집합을 지정할 때 사용되는 fileset 태그에서 위 patternset을 참조하고 싶다면 다음과 같이 하면 된다.
<patternset> 태그를 보면 안에 <include> 태그, <exclude> 태그가 중첩된 것을 알 수 있는데, 이 두 태그는 각각 <patternset> 태그가 포함할 패턴과 포함하지 않을 패턴을 나타낸다. 이 두 태그 뿐만 아니라 <includesfile> 태그와 <excludesfile> 태그가 존재한다. 이 네 태그는 각각 다음의 의미를 지닌다.
이 네 태그는 특정 패턴 및 파일을 포함시켜야 하는 모든 태스크에도 중첩되어 사용될 수 있다. 이 네 태그는 모두 같은 속성을 갖고 있으며, 그 속성은 다음과 같다.
fileset
<fileset> 태그는 파일의 집합을 나타낼 때 사용되며 <patternset> 태그를 중첩하여 파일의 집합을 표시할 수 있다. 또한 <patternset> 태그를 사용하지 않고 직접적으로 <include>나 <exclude>와 같은 태그를 사용하여 파일을 명시할 수도 있다. 예를 들어, <filest>은 다음과 같이 지정될 수 있다.
첫번째와 두번째는 각각 <patternset>을 중첩한 예이고, 세번째는 <patternset>을 중첩하지 않고 직접적으로 <include> 태그를 사용하여 포함할 파일을 명시한 예이다. 이러한 <fileset> 태그는 javac 태스크나 javadoc 태스크와 같이 파일/디렉토리 기반의 태스크에 중첩되어 처리할 파일을 지정하는 데 사용된다. 예를 들어, Jar 파일을 생성해주는 jar 태스크에서 다음과 같이 사용될 수 있다.
<fileset> 태그는 다음과 같은 속성을 제공하고 있다.
앞에서 기본적으로 포함되지 않는 패턴 목록을 보여줬었는데, 만약 그 패턴들을 모두 처리하고자 한다면 <fileset> 태그의 defaultexcludes 속성의 값을 "no"라고 지정하면 된다.
주요 빌트인 태스크
주로 사용되는 빌트인 태스크에는 다음과 같은 것들이 있다.
표8에 표시한 것 중에서 property는 이미 앞에서 알아 보았으므로, 이 절에서는 가장 많이 사용되는 javac, javadoc 태스크를 사용하는 방법에 대해서 간략하게 살펴보도록 하자. Ant가 제공하는 빌트인 태스크에 대한 자세한 내용은 Ant의 사용자 매뉴얼을 참고하기 바란다.
컴파일을 위한 javac 태스크
javac 태스크는 자바 소스 코드를 컴파일할 때 사용되며, 일반적으로 다음과 같은 형태를 취한다.
srcdir 속성은 소스 코드를 포함하고 있는 디렉토리를 명시하는 것으로서, 여러개의 디렉토리를 지정할 수 있으며 각 디렉토리는 ':'나 ';'로 구분된다. 이때 지정한 디렉토리에 존재하는 .java 파일 뿐만 아니라 하위 디렉토리에 있는 .java 파일까지 컴파일하게 된다. srcdir 속성 대신에 <src> 태그를 중첩하여 소스 코드의 위치를 지정할 수도 있다. 예를 들어, 위 예제를 <src> 태그를 사용하여 변경하면 다음과 같이 된다.
destdir 속성은 컴파일한 클래스 파일을 위치시킬 디렉토리를 지정하는 것으로 지정한 디렉토리부터 컴파일한 클래스의 패키지에 알맞게 배치된다. classpath 속성은 컴파일할 때 사용할 클래스패스를 나타내며, 컴파일할 때 사용하는 CLASSPATH 환경변수나 -classpath 옵션을 지정한 것과 같은 의미를 지닌다.
srcdir 속성이나 <src> 속성에 지정한 디렉토리의 하위에 있는 소스 파일 중 일부만 컴파일하고 싶을 때는 <include> 태그와 <exclude> 태그를 사용하면 된다. 예를 들어, 기본 디렉토리를 기준으로 package/project1 디렉토리와 package/project2 디렉토리 밑에 있는 소스 코드만을 컴파일하고 싶을 때에는 다음과 같이 하면 된다.
이 상태에서 package/project1/test 디렉토리에 있는 소스 코드는 컴파일 목록에서 제외하고 싶다면 다음과 같이 하면 된다.
API 문서 생성을 위한 javadoc 태스크
API 문서의 생성은 javadoc 태스크를 통해서 처리되며, 다음과 같이 사용된다.
위의 각 속성은 실제 JDK의 javadoc을 실행시켰을 때 지정해주는 명령행 인자와 유사하기 때문에 특별한 설명은 하지 않겠다. 따라서 컴파일 API 문서를 생성해주는 빌드 파일을 작성하고 싶다면 다음과 같은 형식을 취하면 될 것이다.
결론
여러분은 이 글을 읽어나가면서 비록 일부분이긴 하지만 Ant의 강력함을 느꼈을 것이다. 비록 이 글에서는 javac 태스크와 javadoc 태스크만을 예로 들었지만, Ant는 이 두 태스크 분만 아니라 더욱 다양한 빌트인 태스크를 제공하고 있으며 또한 추가적으로 확장 태스크를 제공하고 있다. 이러한 태스크를 사용함으로써 개발자들은 매우 손쉽게 어플리케이션의 빌드 과정을 정의할 수 있고, 변경할 수 있고, 실행할 수 있게 되었다.
Ant가 제공하는 태스크 목록은 Ant의 사용자 매뉴얼에 나와 있으므로 Ant에 대한 보다 많은 정보를 필요로 하는 독자는 Ant 매뉴얼을 참고하기 바란다.
관련링크:
Ant!
유닉스/리눅스 시스템에서 C나 C++로 프로그래밍을 해본 개발자라면 make라는 프로그램에 대해서 알 것이다. make는 makefile 이라는 설정 파일을 기반으로 소스 코드를 컴파일 해 주고 컴파일된 오브젝트 코드를 모아서 실행 가능한 코드로 만들어주는 등의 많은 작업들을 한번의 배치성 작업으로 처리할 수 있도록 해 주는 프로그램이다. 자바의 경우도 자바 소스 코드를 컴파일해주고, 컴파일된 클래스 파일을 배포본으로 변경해주고 그리고 소스 코드로부터 API 문서를 생성하는 작업을 한번의 배치 작업으로 처리할 수 있도록 해주는 프로그램이 있다. 그것은 Tomcat으로 유명한 아파치의 자카르타 프로젝트에서 진행되고 있는 Ant라는 것이다.
Ant는 자바 기반의 빌드툴로서 make와 비슷한 기능을 제공하고 있다. 하지만 Ant는 다음과 같은 면에서 make와 같은 쉘기반의 빌드툴과는 다르다.
- Ant는 자바기반이기 때문에 플랫폼에 독립적으로 실행된다.
- 복잡할 쉘 명령어 대신에 XML 기반의 설정 파일을 사용하기 때문에 작업을 설정하는 것이 복잡하지 않다.
- 미리 정의된 태스크(Task)를 사용하여 매우 쉽고 빠르게 배치 작업을 설정할 수 있다.
- 새로운 태스크의 추가를 통해서 처리 가능한 배치 작업의 확장이 가능하다.
Ant의 설치
Ant를 사용하려면 먼저 Ant를 컴퓨터에 설치해야 한다. Ant는 자카르타의 홈페이지인 http://jakarta.apache.org에서 구할 수 있다. 현재 Ant는 1.4 버전이 출시된 상태이며 자카르타의 다른 프로젝트들과 마찬가지로 바이너리와 소스의 두 가지 형태의 배포본을 제공하고 있다. 이 두 가지 배포본 중 바이너리 배포본은 설치하는 데 큰 어려움이 없으므로 바이너리 배포본을 설치하는 것에 대해서 살펴보도록 하자.
바이너리 배포본은 현재 윈도우즈 배포본인 jakarta-ant-1.4-bin.zip과 유닉스/리눅스 배포본인 jakarta-ant-1.4-bin.tar.gz의 두 파일이 있다. 이 두 파일은 실제 내용은 같다. 운영체제에 따라 알맞은 파일을 다운로드 받은 후 알맞은 디렉토리에 압축을 풀도록 하자. 필자의 경우 윈도우즈 시스템의 C:\에 jakarta-ant-1.4-bin.zip 파일의 압축을 풀어서 사용하고 있다.
c:\에 바이너리 배포본의 압축을 풀면 다음 표1과 같은 디렉토리가 생성될 것이다.
디렉토리 | 설명 |
C:\jakarta-ant-1.4 | Ant 1.4의 홈 디렉토리 |
C:\jakarta-ant-1.4\bin | Ant 1.4의 실행 파일 |
C:\jakarta-ant-1.4\docs | Ant 1.4의 문서 파일. 사용자 매뉴얼과 FAQ 문서 등을 포함하고 있다. |
C:\jakarta-ant-1.4\lib | Ant 1.4의 핵심 패키지를 비롯하여 XML 파서 등 Ant를 실행하는 데 필요한 클래스 라이브러리를 포함하고 있다. |
환경변수 설정
Ant의 설치가 완료되었다면, Ant가 올바르게 실행되도록 환경변수를 지정해주어야 한다. 환경 변수는 다음과 같이 지정해주면 된다.
- Ant를 실행할 수 있도록 [Ant 홈]\bin 디렉토리를 PATH 환경변수에 추가한다.
- 환경변수 ANT_HOME의 값을 Ant의 홈디렉토리로 지정한다.
- 환변변수 JAVA_HOME의 값을 Java의 홈디렉토리로 지정한다.
set ANT_HOME=c:\ant
set JAVA_HOME=c:\jdk1.2.2
set PATH=%PATH%;%ANT_HOME%\bin
set JAVA_HOME=c:\jdk1.2.2
set PATH=%PATH%;%ANT_HOME%\bin
그리고 유닉스나 리눅스 시스템일 경우에는 다음과 같이 지정해주면 된다.
export ANT_HOME=/usr/local/ant
export JAVA_HOME=/usr/local/jdk-1.2.2
export PATH=${PATH}:${ANT_HOME}/bin
export JAVA_HOME=/usr/local/jdk-1.2.2
export PATH=${PATH}:${ANT_HOME}/bin
이 중 JAVA_HOME 환경변수는 선택사항이며 필수사항은 아니다. JAVA_HOME 환경변수는 몇몇 특수한 태스크를 사용하는 경우에만 지정해주면 되며 일반적인 경우에는 지정해주지 않아도 무방하다.
윈도우즈 98이나 95를 사용할 경우 ANT_HOME 디렉토리 이름이 길 경우 운영체제의 제한 때문에 Ant의 실행파일인 ant.bat 파일이 제대로 실행되지 않게 된다. 따라서 윈도우즈 98이나 95를 사용할 경우에는 ANT_HOME 디렉토리의 이름을 jakarta-ant-1.4가 아닌 ant와 같이 짧은 이름으로 변경해주기 바란다.
Ant의 실행
Ant의 실행은 매우 간단하다. 앞에서 설명한 환경변수를 올바르게 설정하였다면 단순히 커맨드 프롬프트에서 다음과 같이 입력하면 된다.
c:\myproject>ant
ant.bat(또는 ant.sh)을 실행하면 Ant의 설정 파일인 build.xml 파일을 현재 디렉토리부터 시작해서 상위 디렉토리로 이동해가면서 검색하게 된다. build.xml 파일이 발견되면 Ant는 build.xml 파일을 분석하여 build.xml 파일에 정의되어 있는 태스크에 따라 알맞은 배치 작업을 수행한다.
단순히 위와 같이 'ant'만 입력해도 Ant가 실행되지만, ant는 개발자가 명령어 프롬프트에서 옵션을 입력해서 여러 내용들을 직접 설정할 수 있도록 하고 있다. ant는 기본적으로 다음과 같은 형태로 명령행 인자를 입력받는다.
ant [옵션] [타겟 [타겟2 [타겟3] ...]]
여기서 [옵션]에는 빌드 파일, 로그 파일 그리고 프로퍼티 등의 값을 지정할 수 있는 옵션이 오게 되며 [타겟]에는 빌드 파일에 지정되어 있는 <target> 태그의 name 속성의 값이 온다. [타겟]을 지정하지 않을 경우 빌드 파일에서 <project> 태그의 default 속성의 값을 작업 대상으로 사용한다. 이에 대한 내용은 <project> 태그와 <target> 태그를 설명할 때 보다 자세히 살펴보기로 하자.
표2는 Ant를 실행할 때 사용할 수 있는 주요 옵션 목록이다.
옵션 | 설명 |
-help | 도움말을 출력한다. |
-projecthelp | 프로젝트의 도움말 정보를 출력한다. |
-version | 버전 정보를 출력한다. |
-quiet | 적은 양의 메시지를 출력한다. |
-verbose | 추가적인 메시지를 출력한다. |
-debug | 디버깅 정보를 출력한다. |
-logfile file | 로그 메시지를 file에 기록한다. |
-buildfile file | 지정한 file을 빌드 파일로 사용한다. |
-Dproperty=value | 이름이 property인 프로퍼티의 값을 value로 지정한다. |
예를 들어, 빌드 파일로 build.xml이 아닌 testbuild.xml을 사용하고 작업 대상(target)을 main으로 하고 싶다면 다음과 같이 실행하면 된다. (작업 대상에 대한 내용은 잠시 뒤에 살펴볼 것이다.)
ant -buildfile testbuild.xml main
표2에서 설명한 옵션 외에도 몇몇 옵션이 더 있는데, 이 옵션은 -help 옵션을 사용하여 확인할 수 있다.
Ant의 사용
make를 사용하기 위해서 makefile을 알맞게 작성해야 하듯이 Ant를 올바르게 활용하기 위해서는 Ant가 사용할 빌드 파일을 알맞게 작성해야만 한다. Ant의 빌드 파일은 XML 문서의 구조를 갖고 있으며 Ant가 작업을 수행할 프로젝트에 대한 정보를 담고 있으며, 다음과 같은 형태를 취하고 있다.
<project name="프로젝트이름" default="기본타겟이름" basedir="." >
<target name="타겟이름">
<property name="프로퍼티이름1" value="프로퍼티값1"/>
<property name="프로퍼티이름2" value="프로퍼티값2"/>
</target>
<target name="타겟이름1">
<태스크명/>
<태스크명1 dir="${build}"/>
<property name="프로퍼티이름3" value="프로퍼티값3"/>
</target>
<target name="타겟이름2" depends="타겟이름1">
<태스크명2 속성1="값1" 속성2="값2"/>
</target>
</project>
<target name="타겟이름">
<property name="프로퍼티이름1" value="프로퍼티값1"/>
<property name="프로퍼티이름2" value="프로퍼티값2"/>
</target>
<target name="타겟이름1">
<태스크명/>
<태스크명1 dir="${build}"/>
<property name="프로퍼티이름3" value="프로퍼티값3"/>
</target>
<target name="타겟이름2" depends="타겟이름1">
<태스크명2 속성1="값1" 속성2="값2"/>
</target>
</project>
위 코드를 보면 크게 project, property, target, 태스크의 네가지 요소로 빌드 파일이 구성되어 있는 것을 알 수 있다. 위에서 '태스크명n'으로 표시된 태그는 target 태그가 나타내는 대상이 수행할 작업을 나타내는 것으로서 소스 코드를 컴파일해주는 태스크, Jar를 사용하여 압축해주는 태스크 등 다양한 종류의 태스크가 미리 개발되어 있다.
이 네 가지 요소가 Ant의 모든 기능을 사용할 수 있도록 해 주며, 네 가지 요소를 알맞게 조합함으로써 개발자는 원하는 작업을 배치 작업으로 처리할 수 있게 된다. 이 글의 나머지 부분에서는 빌드 파일에서 사용되는 project, target, property 그리고 태스크 태그를 어떻게 설정해주는지 구체적으로 살펴보도록 하자.
project
<project> 태그는 빌드 파일의 루트 태그로서 프로젝트 자체를 정의해준다. 모든 빌드 파일은 한 개의 project 태그를 가지며, project 태그는 프로젝트를 설명하기 위해서 다음과 같은 속성을 사용한다.
속성 | 설명 | 필수여부 |
name | 프로젝트의 이름 | 필수아님 |
default | ant.bat 파일을 실행할 때 [타겟]이 지정되지 않을 때 기본적으로 사용할 타겟 | 필수 |
basedir | 경로 계산을 할 때 사용할 기본 디렉토리. basedir 프로퍼티를 지정했을 경우, 그 값을 이 속성에서 지정한 값으로 대체한다.만약 이 속성도 지정하지 않고 basedir 프로퍼티도 지정하지 않았을 경우에는 빌드 파일이 위치하는 디렉토리를 기본 디렉토리로 사용한다. | 필수아님 |
Ant는 Ant를 실행할 때 가장 먼저 실행할 태스크를 지정할 수 있는 두가지 방법을 갖고 있는데, 한 가지는 앞에서 Ant 실행할 때 [타겟]을 지정해주는 것이며 다른 하나는 <project> 태그의 default 속성을 사용하여 첫번째로 실행할 태스크를 지정해주는 것이다.
default 속성의 값은 반드시 지정해주어야 하는 것으로서 Ant를 실행할 때 [타겟]을 지정해주지 않을 경우 default 속성의 값을 사용하여 첫번째로 수행할 [타겟]을 선택하게 된다.
target
<target> 태그는 <project> 태그에 포함되며, 실제로 프로젝트가 수행하게 될 작업(태스크)을 지정한다. <target> 태그의 기본 구조는 다음과 같다.
<target name="타겟이름1">
<태스크명/>
<태스크명1 dir="${build}"/>
<property name="프로퍼티이름3" value="프로퍼티값3"/>
</target>
<태스크명/>
<태스크명1 dir="${build}"/>
<property name="프로퍼티이름3" value="프로퍼티값3"/>
</target>
하나의 <project> 태그는 여러 개의 <target< 태그를 포함할 수 있으며, 각각의 <target> 태그는 그 타겟을 통해서 수행하고자 하는 태스크(작업)를 명시한다. 즉, 실제 작업의 처리는 <target> 태그 내에 명시되어 있는 태스크를 통해서 이루어지는 것이다. 이때 각각의 <target> 태그는 상호간 의존관계가 있을 수 있다. 예를 들어, 일반적인 어플리케이션의 빌드 순서는 '컴파일->API 문서생성->배포판 생성'인데, 이 경우 빌드 파일에는 이 각각의 작업에 대한 <target>이 있을 것이다. 즉, 컴파일을 위한 <target> 태그, API 문서 생성을 위한 <target> 태그 그리고 배포판 생성을 위한 <target> 태그 등 3개의 <target> 태그가 존재하게 되며 다음과 같이 빌드 파일이 구성되어 있다고 해 보자.
<target name="compile">
...
</target>
<target name="apidoc">
...
</target>
<target name="makeproduct">
...
</target>
...
</target>
<target name="apidoc">
...
</target>
<target name="makeproduct">
...
</target>
이 때, 각각의 타겟의 수행순서는 compile 타겟, apidoc 타겟, makeproduct 타겟이어야 한다. 즉, apidoc 타겟은 compile 타겟에 의존하고 makeproduct 타겟은 apidoc 타겟에 의존하게 되는 것이다. 이러한 의존관계는 <target> 태그의 depends 속성을 통해서 지정할 수 있다. 예를 들어, 다음과 같이 depends 속성이 명시되어 있다고 해 보자.
<target name="A"/>
<target name="B"/>
<target name="C"/>
<target name="D" depends="C,B,A"/>
<target name="B"/>
<target name="C"/>
<target name="D" depends="C,B,A"/>
여기서 여러분이 최종적으로 실행하고자 타겟이 D라고 해 보자. 이 때, 타겟 D의 depends 속성의 값은 "C,B,A"인데 이는 타겟 D를 실행하기 위해서는 먼저 타겟 C, B, A가 수행되어야 함을 의미한다. 이때 타겟 C, B, A가 수행되는 순서는 depends 속성에 표시된 순서대로 수행되게 된다. 즉, 타겟 D를 실행하면 타겟 C가 먼저 실행되고, 그 다음 타겟 B, 그 다음 타겟 A, 그리고 마지막으로 타겟 D가 실행된다.
그런데, 다음과 같이 타겟 B와 타겟 C가 depends 속성을 갖고 있다고 해 보자.
<target name="A"/>
<target name="B" depends="A"/>
<target name="C" depends="B"/>
<target name="D" depends="C,B,A"/>
<target name="B" depends="A"/>
<target name="C" depends="B"/>
<target name="D" depends="C,B,A"/>
이 경우에는 타겟 C가 타겟 B에 의존하고 있고, 타겟 B는 타겟 A에 의존하고 있기 때문에 타겟 C가 가장 먼저 실행되기 보다는 타겟 A가 가장 먼저 실행되고, 그 다음에 타겟 B, 그 다음에 타겟 C, 그리고 마지막으로 타겟 D가 실행되게 된다. 이러한 depends 속성을 이용하여 앞의 빌드 순서를 표시한 타겟을 다시 표현하면 다음과 같이 변경된다.
<target name="compile">
...
</target>
<target name="apidoc" depends="compile">
...
</target>
<target name="makeproduct" depends="apidoc">
...
</target>
...
</target>
<target name="apidoc" depends="compile">
...
</target>
<target name="makeproduct" depends="apidoc">
...
</target>
여러 개의 <target> 태그의 depends 속성이 같은 타겟의 이름을 값으로 가질 수도 있으나 이에 상관없이 각각의 타겟은 오직 한번만 실행된다.
Ant의 빌드 파일에서는 프로퍼티를 사용할 수 있는데, <target> 타겟 태그는 if 속성을 사용하여 특정한 프로퍼티가 지정되어 있을 경우에만 작업을 수행하도록 할 수 있다. 또한, unless 속성을 사용하여 프로퍼티가 지정되어 있지 않을 경우에만 작업을 수행하도록 할 수도 있다. 예를 들어, 테스트 단계와 배포판 생성 단계에서 각각 다른 태스크를 수행해야 할 경우 다음과 같이 "test" 프로퍼티를 사용할 수 있을 것이다.
<target name="test" if="test" depends="compile" >
...
</target>
<target name="distribute" unless="test" depends="compile">
...
</target>
...
</target>
<target name="distribute" unless="test" depends="compile">
...
</target>
<target> 태그는 그 타겟에 대한 설명을 description 속성을 통해서 할 수 있다. description 속성에 명시한 값은 ant.bat을 실행할 때 명령행 옵션인 -projecthelp를 통해서 볼 수 있다.
태스크
타켓이 각 수행할 작업간의 의존관계나 수행 조건 등을 표시한다면 태스크는 타켓 내에서 실제로 수행할 작업을 나타낸다. 예를 들어, 여러분은 태스크를 통해서 소스 코드를 컴파일하고 파일을 복사/삭제하고 API 문서를 생성할 수 있다. 태스크 <target> 태그에 중첩되어 표시되며 다음과 같은 구조를 갖고 있다.
<태스크명 속성1="값1" 속성2="값2" ... />
'태스크명'은 태스크의 이름을 나타내고 '속성n'과 '값n'은 각각 태스크를 처리할 때 사용할 속성값을 나타낸다.
태스크는 이미 만들어져 있는 빌트인(built-in) 태스크를 사용할 수도 있고, 추가적으로 제공되는 옵션(optional) 태스크를 사용할 수도 있다. 또한 직접 작성한 태스크를 사용할 수도 있다. 예를 들어, 빌트인 태스크인 javac를 사용하여 소스 코드를 컴파일 할 때는 다음과 같이 <target> 태그에 javac 태스크를 중첩시키면 된다.
<target name="compile" depends="init">
<javac srcdir="${src}"
destdir="${build}"
classpath="jcorelogging.jar" />
</target>
<javac srcdir="${src}"
destdir="${build}"
classpath="jcorelogging.jar" />
</target>
위 코드에서 <javac src="${src}" ... /> 태그가 <target> 태그에 중첩되어 있는 것을 알 수 있는데, 이때 javac는 태스크명을 나타내며, srcdir과 destdir, classpath는 각각 javac 태스크에서 사용되는 속성을 나타낸다. 그리고 각 속성의 값 중, "${"로 시작하고 "}"로 끝나는 것이 있는데 이는 프로퍼티를 나타내는 것으로서 "${"와 "}" 사이에 있는 문자열의 프로퍼티의 이름이다.
property
프로젝트를 진행하다보면 다양한 프로퍼티를 사용하게 된다. 컴파일할 소스 코드의 위치, 컴파일된 클래스 파일을 저장할 디렉토리, 압축한 파일을 위치시킬 디렉토리 등 유연한 개발을 위해서는 다양한 프로퍼티의 사용이 필수적이라 할 수 있다. Ant는 이처럼 빌드 과정에서 사용되는 다양한 프로퍼티를 지정할 수 있도록 하고 있으며, 또한 몇몇 개의 빌트인 프로퍼티를 제공하고 있다.
프로퍼티의 지정은 property 태스크를 통해서 할 수 있으며, property 태스크는 표4와 같은 속성을 갖고 있다.
속성 | 설명 | 필수여부 |
name | 설정할 프로퍼티의 이름 | 필수아님 |
value | 프로퍼티의 값 | name 속성을 사용할 경우 세 속성중의 하나를 지정해야 한다. |
location | 프로퍼티를 주어진 파일의 절대 파일명으로 지정한다. 이 속성의 값이 절대 경로일 경우, '/'나 '\'와 같은 구분자는 현재 플래폼에 알맞게 처리된다. 절대 경로가 아닐 경우 프로젝트의 basedir에 상대적인 경로로 처리된다. | |
refid | 다른 곳에서 정의된 객체를 참조한다. | |
resource | 프로퍼티 파일을 나타내는 자원의 이름 | name 속성을 사용하지 않을 때 이 세 속성중의 한 개를 사용해야 한다. |
file | 프로퍼티 파일의 파일 이름 | |
environment | 환경 변수를 읽을 때 사용할 접두어. 만약 environment="myenv"라고 지정했다면, OS에 종속적인 환경변수인 PATH나 TEMP와 같은 값을 "myenv.PATH"나 "myenv.TEMP"와 같은 프로퍼티 이름을 사용하여 구할 수 있다. | |
classpath | 자원을 검색할 때 사용할 클래스패스 | 필수아님 |
classpathref | 다른 곳에서 정의된 PATH에 대한 참조로서 주어진 자원을 검색할 때 사용할 클래스패스 | 필수아님 |
예를 들어, "buildno" 속성의 값을 "356"으로 지정하고 싶다면 다음과 같이 하면 된다.
<property name="buildno" value="356"/>
또한, 특정한 파일에 저장된 프로퍼티를 사용하고 싶다면 다음과 같이 file 속성을 사용하면 된다.
<property file="build355.properties" />
Ant의 빌드 파일에서는 자바에서 기본적으로 제공하는 "java.version"과 같은 시스템 프로퍼티를 기본적으로 사용할 수 있으며, 추가적으로 Ant 자체에서 기본적으로 제공하는 프로퍼티를 사용할 수 있다. Ant가 기본적으로 제공하는 프로퍼티 목록은 다음표와 같다.
프로퍼티 | 설명 |
basedir | 프로젝트의 기본 디렉토리에 대한 절대 경로. <project> 태그의 basedir 속성값을 사용한다. |
ant.file | 빌드 파일의 절대 경로 |
ant.version | Ant의 버전 |
ant.project.name | 현재 실행중인 프로젝트의 이름. <project> 태그의 name 속성값을 사용한다. |
ant.java.version | Ant가 발견한 Java의 버전 |
프로퍼티는 특정한 타켓에서만 사용되도록 지정할 수도 있고 전체에서 사용되도록 지정할 수도 있다. 다음 예를 살펴보자.
<project name="MyProject" default="dist" basedir=".">
<!-- 빌드를 위한 글로벌 프로퍼티 -->
<property name="build" value="build"/>
<property name="dist" value="dist"/>
<target name="init">
<!-- init 타켓 내에서 프로퍼티 지정 -->
<property name="src" value="."/>
<mkdir dir="${build}"/>
</target>
<target name="compile" depends="init">
<javac srcdir="${src}" destdir="${build}"/>
</target>
<target name="dist" depends="compile">
<mkdir dir="${dist}/lib"/>
<jar jarfile="${dist}/lib/MyProject-${DSTAMP}.jar" basedir="${build}"/>
</target>
<target name="clean">
<delete dir="${build}"/>
<delete dir="${dist}"/>
</target>
</project>
<!-- 빌드를 위한 글로벌 프로퍼티 -->
<property name="build" value="build"/>
<property name="dist" value="dist"/>
<target name="init">
<!-- init 타켓 내에서 프로퍼티 지정 -->
<property name="src" value="."/>
<mkdir dir="${build}"/>
</target>
<target name="compile" depends="init">
<javac srcdir="${src}" destdir="${build}"/>
</target>
<target name="dist" depends="compile">
<mkdir dir="${dist}/lib"/>
<jar jarfile="${dist}/lib/MyProject-${DSTAMP}.jar" basedir="${build}"/>
</target>
<target name="clean">
<delete dir="${build}"/>
<delete dir="${dist}"/>
</target>
</project>
위 빌드 파일을 보면 build, dist 프로퍼티는 프로젝트 전체에서 사용될 수 있는 프로퍼티로서 빌드 파일의 어떤 곳에서든지 사용될 수 있다. 반면에 init 타켓 내에 정의된 src 프로퍼티는 init 타켓에 직간접적으로 의존하고 있는 타켓에서만 사용될 수 있다. 위 코드에서는 init 타켓에 의존하고 있는 compile 타켓에서 src 프로퍼티를 사용할 수 있음을 알 수 있다. 또한 간접적으로 init 타겟에 의존하고 있는 dist 타겟 역시 src 프로퍼티를 사용하고 있다.
파일과 디렉토리 지정을 위한 패턴 관련 태그
어플리케이션을 빌드하는 과정에서 흔히 필요한 작업이 다음과 같은 것들이다.
- /project/src 디렉토리 및 그 하위 디렉토리에 존재하는 모든 파일을 /dist/src 디렉토리에 복사한다. 단, /project/src 디렉토리 및 그 하위 디렉토리에서 디렉토리 이름이 'CVS' 디렉토리는 복사하지 않는다.
- /src 디렉토리에 있는 *.java 파일을 컴파일한다. 단, Test로 시작하는 .java 파일은 컴파일하지 않는다.
- A 디렉토리에 있는 파일은 모두 컴파일하고 B 디렉토리에 있는 파일은 컴파일하지 않는다.
특정 디렉토리/파일의 지정은 일정한 패턴을 통해서 표현된다. 패턴에서 사용되는 글자는 '*'와 '?'이다. 아마 많은 개발자들이 도스나 리눅스에서 dir이나 ls 명령어를 사용할텐데, '*'와 '?' 글자는 dir이나 ls 명령어를 입력할 때 사용되는 와일드캐릭터인 '*'와 '?'와 비슷한 의미를 지닌다. Ant의 설정 파일에서 이 두 글자는 다음과 같은 의미를 지닌다.
- '*' - 0개 또는 그 이상의 글자가 매칭된다.
- '?' - 한 글자와 매칭된다.
디렉토리를 지정할 때도 이 두 글자를 사용할 수 있다. 예를 들어, 'mywork/src?/*/*.java'와 같이 지정했을 경우 'mywork/src1/jcore/ABC.java'나 'mywork/srcA/java/MySome.java'과 같은 파일을 지정할 수 있게 된다. 또한, 좀더 유연한 방법을 제공하기 위해 Ant는 '**'를 사용하여 디렉토리를 지정할 수 있도록 하고 있다. 디렉토리 지정에서 '*'를 사용할 경우 디렉토리 트리에서 단 하나의 계층만을 가리키게 되는 반면에 '**'는 다계층을 나타낸다. 즉, 'mywork/*/A.java'라고 하면 'mywork/some/A.java'나 'mywork/src/A.java'와 같이 '*'가 명확하게 하나의 디렉토리 레벨만을 나타내지만, 'mywork/**/A.java'라고 할 경우 'mywork/some/A.java'는 물론 'mywork/some/jcore/A.java'나 'mywork/com/javacan/test/A.java'와 같이 여러 레벨의 디렉토리를 나타내는 것이다.
'**'로 패턴이 끝나게 되면 디렉토리 뿐만 아니라 모든 파일을 나타낸다. 예를 들어, 'xyz/**' 패턴은 'xyz/A.java', 'xyz/some/A.java'를 포함하게 된다. 그리고 패턴의 마지막이 디렉토리를 구분해주는 '/'나 '\'일 경우에는 자동으로 맨 뒤에 '**'가 붙게 된다. 즉, 'xyz/' 패턴은 'xyz/**' 패턴과 동일한 의미를 지닌다.
참고사항으로, 다음 패턴들은 특별히 지정하지 않는 한 태그 또는 태스크가 처리할 파일 또는 디렉토리 패턴에서 자동으로 제외된다.
**/*~
**/#*#
**/.#*
**/%*%
**/CVS
**/CVS/**
**/.cvsignore
**/SCCS
**/SCCS/**
**/vssver.scc
**/#*#
**/.#*
**/%*%
**/CVS
**/CVS/**
**/.cvsignore
**/SCCS
**/SCCS/**
**/vssver.scc
patternset
<patternset> 태그는 패턴을 집합을 지정할 때 사용되며, 다음과 같은 구조를 갖고 있다.
<patternset id="sources" >
<include name="std/**/*.java"/>
<include name="prof/**/*.java" if="professional"/>
<exclude name="**/*Test*"/>
</patternset>
<include name="std/**/*.java"/>
<include name="prof/**/*.java" if="professional"/>
<exclude name="**/*Test*"/>
</patternset>
위에서 patternset 태그의 id 속성을 지정했는데, 패턴을 사용하는 다른 태스크나 태그들은 id 속성의 값을 사용하여 patternset을 참조할 수 있게 된다. 예를 들어, 파일의 집합을 지정할 때 사용되는 fileset 태그에서 위 patternset을 참조하고 싶다면 다음과 같이 하면 된다.
<fileset dir="${client.src}" >
<patternset refid="sources"/>
</fileset>
<patternset refid="sources"/>
</fileset>
<patternset> 태그를 보면 안에 <include> 태그, <exclude> 태그가 중첩된 것을 알 수 있는데, 이 두 태그는 각각 <patternset> 태그가 포함할 패턴과 포함하지 않을 패턴을 나타낸다. 이 두 태그 뿐만 아니라 <includesfile> 태그와 <excludesfile> 태그가 존재한다. 이 네 태그는 각각 다음의 의미를 지닌다.
태그 | 설명 |
include | 특정한 패턴이 나타내는 디렉토리 및 파일을 포함한다. |
exclude | 특정한 패턴이 나타내는 디렉토리 및 파일을 포함시키지 않는다. |
includesfile | 특정 파일을 포함한다. |
excludesfile | 특정 파일을 포함시키지 않는다. |
이 네 태그는 특정 패턴 및 파일을 포함시켜야 하는 모든 태스크에도 중첩되어 사용될 수 있다. 이 네 태그는 모두 같은 속성을 갖고 있으며, 그 속성은 다음과 같다.
속성 | 설명 | 필수여부 |
name | 패턴(include, exclude) 또는 파일(includesfile, excludesfile)의 지정 | 필수 |
if | 특정 프로퍼티가 지정되어 있을 경우 수행한다. | 필수아님 |
unless | 특정 프로퍼티가 지정되어 있지 않을 경우 수행한다. | 필수아님 |
fileset
<fileset> 태그는 파일의 집합을 나타낼 때 사용되며 <patternset> 태그를 중첩하여 파일의 집합을 표시할 수 있다. 또한 <patternset> 태그를 사용하지 않고 직접적으로 <include>나 <exclude>와 같은 태그를 사용하여 파일을 명시할 수도 있다. 예를 들어, <filest>은 다음과 같이 지정될 수 있다.
<fileset dir="${server.src}" >
<patternset id="non.test.sources" >
<include name="**/*.java"/>
<exclude name="**/*Test*"/>
</patternset>
</fileset>
<fileset dir="${client.src}" >
<patternset refid="non.test.sources"/>
</fileset>
<fileset dir="src/share">
<include name="**/*.properties"/>
</fileset>
<patternset id="non.test.sources" >
<include name="**/*.java"/>
<exclude name="**/*Test*"/>
</patternset>
</fileset>
<fileset dir="${client.src}" >
<patternset refid="non.test.sources"/>
</fileset>
<fileset dir="src/share">
<include name="**/*.properties"/>
</fileset>
첫번째와 두번째는 각각 <patternset>을 중첩한 예이고, 세번째는 <patternset>을 중첩하지 않고 직접적으로 <include> 태그를 사용하여 포함할 파일을 명시한 예이다. 이러한 <fileset> 태그는 javac 태스크나 javadoc 태스크와 같이 파일/디렉토리 기반의 태스크에 중첩되어 처리할 파일을 지정하는 데 사용된다. 예를 들어, Jar 파일을 생성해주는 jar 태스크에서 다음과 같이 사용될 수 있다.
<jar jarfile="${catalina.deploy}/lib/naming-factory.jar">
<fileset dir="${catalina.build}/classes">
<include name="org/apache/naming/factory/**" />
<exclude name="org/apache/naming/factory/Constants.class" />
</fileset>
</jar>
<fileset dir="${catalina.build}/classes">
<include name="org/apache/naming/factory/**" />
<exclude name="org/apache/naming/factory/Constants.class" />
</fileset>
</jar>
<fileset> 태그는 다음과 같은 속성을 제공하고 있다.
속성 | 설명 | 필수여부 |
dir | 파일셋 디렉토리 트리의 루트 | 필수 |
defaultexcludes | 기본적으로 포함되지 않는 패턴을 exclude의 요소로 사용할지의 여부(yes, no)를 지정한다. 이 속성을 지정하지 않을 경우 기본적으로 포함되지 않는 패턴을 exclude의 요소로 사용하게 된다. | 필수아님 |
includes | 포함할 파일의 패턴 목록을 지정한다. 각 패턴은 콤마로 구분한다. 생략할 경우 모든 파일을 포함한다. | 필수아님 |
includesfile | 포함할 파일명을 지정한다. | 필수아님 |
excludes | 포함하지 않을 파일의 패턴 목록을 지정한다. 각 패턴은 콤마로 구분한다. 생략할 경우 기본적으로 포함되지 않는 패턴을 제외한 모든 파일은 제외되지 않는다. | 필수아님 |
excludesfile | 포함하지 않을 파일명을 지정한다. | 필수아님 |
앞에서 기본적으로 포함되지 않는 패턴 목록을 보여줬었는데, 만약 그 패턴들을 모두 처리하고자 한다면 <fileset> 태그의 defaultexcludes 속성의 값을 "no"라고 지정하면 된다.
주요 빌트인 태스크
주로 사용되는 빌트인 태스크에는 다음과 같은 것들이 있다.
속성 | 설명 |
Ant | 현재 빌드 과정에서 또 다른 Ant 프로세스를 수행 |
Copydir | 전체 디렉토리를 복사 |
Copyfile | 한 파일을 복사할 때 사용 |
Cvs | CVS 리포지토리에서 읽어온 패키지/모듈을 처리할 때 사용 |
Delete | 파일을 삭제할 때 사용. 한 파일을 지우거나 또는 특정 디렉토리와 그 하위 디렉토리에 있는 모든 파일을 삭제할 수 있다. |
Deltree | 특정 디렉토리를 삭제 |
Exec | 시스템 명령어를 실행할 때 사용. |
get | URL로부터 파일을 구함 |
jar | 파일 집합을 Jar를 사용하여 압축한다. |
java | 현재 Ant를 실행중인 가상 머신 또는 다른 가상 머신에서 자바 클래스를 실행한다. |
javac | 소스 트리를 컴파일한. |
javadoc (또는 javadoc2) | javadoc을 사용하여 API 문서를 생성한다. |
mkdir | 디렉토리를 생성한다. |
property | 프로젝트에 사용될 프로퍼티를 지정한다. |
tstamp | 현재 프로젝트의 DSTAMP, TSTAMP, TODAY 프로퍼티를 지정한다. |
style | xslt를 사용하여 문서 집합을 처리한다. |
표8에 표시한 것 중에서 property는 이미 앞에서 알아 보았으므로, 이 절에서는 가장 많이 사용되는 javac, javadoc 태스크를 사용하는 방법에 대해서 간략하게 살펴보도록 하자. Ant가 제공하는 빌트인 태스크에 대한 자세한 내용은 Ant의 사용자 매뉴얼을 참고하기 바란다.
컴파일을 위한 javac 태스크
javac 태스크는 자바 소스 코드를 컴파일할 때 사용되며, 일반적으로 다음과 같은 형태를 취한다.
<javac srcdir="/mywork/project/src1:/mywork/project/src2"
destdir="${build}"
classpath="lib/mail.jar:lib/activation.jar"
debug="on"
/>
destdir="${build}"
classpath="lib/mail.jar:lib/activation.jar"
debug="on"
/>
srcdir 속성은 소스 코드를 포함하고 있는 디렉토리를 명시하는 것으로서, 여러개의 디렉토리를 지정할 수 있으며 각 디렉토리는 ':'나 ';'로 구분된다. 이때 지정한 디렉토리에 존재하는 .java 파일 뿐만 아니라 하위 디렉토리에 있는 .java 파일까지 컴파일하게 된다. srcdir 속성 대신에 <src> 태그를 중첩하여 소스 코드의 위치를 지정할 수도 있다. 예를 들어, 위 예제를 <src> 태그를 사용하여 변경하면 다음과 같이 된다.
<proejct name="dist" default="compile" >
<target name="compile">
<javac destdir="${build}"
classpath="lib/mail.jar:lib/activation.jar"
debug="on" >
<src path="/mywork/project/src1">
<src path="/mywork/project/src2">
</javac>
</target>
</project>
<target name="compile">
<javac destdir="${build}"
classpath="lib/mail.jar:lib/activation.jar"
debug="on" >
<src path="/mywork/project/src1">
<src path="/mywork/project/src2">
</javac>
</target>
</project>
destdir 속성은 컴파일한 클래스 파일을 위치시킬 디렉토리를 지정하는 것으로 지정한 디렉토리부터 컴파일한 클래스의 패키지에 알맞게 배치된다. classpath 속성은 컴파일할 때 사용할 클래스패스를 나타내며, 컴파일할 때 사용하는 CLASSPATH 환경변수나 -classpath 옵션을 지정한 것과 같은 의미를 지닌다.
srcdir 속성이나 <src> 속성에 지정한 디렉토리의 하위에 있는 소스 파일 중 일부만 컴파일하고 싶을 때는 <include> 태그와 <exclude> 태그를 사용하면 된다. 예를 들어, 기본 디렉토리를 기준으로 package/project1 디렉토리와 package/project2 디렉토리 밑에 있는 소스 코드만을 컴파일하고 싶을 때에는 다음과 같이 하면 된다.
<javac destdir="${build}"
classpath="lib/mail.jar:lib/activation.jar"
debug="on" >
<src path="src">
<include name="package/project1/**"/>
<include name="package/project2/**"/>
</javac>
classpath="lib/mail.jar:lib/activation.jar"
debug="on" >
<src path="src">
<include name="package/project1/**"/>
<include name="package/project2/**"/>
</javac>
이 상태에서 package/project1/test 디렉토리에 있는 소스 코드는 컴파일 목록에서 제외하고 싶다면 다음과 같이 하면 된다.
<javac destdir="${build}"
classpath="lib/mail.jar:lib/activation.jar"
debug="on" >
<src path="src">
<include name="package/project1/**"/>
<include name="package/project2/**"/>
<exclude name="package/project1/test/**"/>
</javac>
classpath="lib/mail.jar:lib/activation.jar"
debug="on" >
<src path="src">
<include name="package/project1/**"/>
<include name="package/project2/**"/>
<exclude name="package/project1/test/**"/>
</javac>
API 문서 생성을 위한 javadoc 태스크
API 문서의 생성은 javadoc 태스크를 통해서 처리되며, 다음과 같이 사용된다.
<javadoc packagenames="${packages}"
sourcepath="${basedir}/${src.dir}"
destdir="${build.javadocs}"
author="true"
version="true"
windowtitle="${Name} API"
doctitle="${Name}"
bottom="Copyright ⓒ 2001 SomeMane. All Rights Reserved."
/>
sourcepath="${basedir}/${src.dir}"
destdir="${build.javadocs}"
author="true"
version="true"
windowtitle="${Name} API"
doctitle="${Name}"
bottom="Copyright ⓒ 2001 SomeMane. All Rights Reserved."
/>
위의 각 속성은 실제 JDK의 javadoc을 실행시켰을 때 지정해주는 명령행 인자와 유사하기 때문에 특별한 설명은 하지 않겠다. 따라서 컴파일 API 문서를 생성해주는 빌드 파일을 작성하고 싶다면 다음과 같은 형식을 취하면 될 것이다.
<project name="build" default="api" basedir=".">
<target name="compile">
<javac srcdir="src" ... >
...
</javac>
</target>
<target name="api" depends="compile">
<javadoc .. >
</target>
</project>
<target name="compile">
<javac srcdir="src" ... >
...
</javac>
</target>
<target name="api" depends="compile">
<javadoc .. >
</target>
</project>
결론
여러분은 이 글을 읽어나가면서 비록 일부분이긴 하지만 Ant의 강력함을 느꼈을 것이다. 비록 이 글에서는 javac 태스크와 javadoc 태스크만을 예로 들었지만, Ant는 이 두 태스크 분만 아니라 더욱 다양한 빌트인 태스크를 제공하고 있으며 또한 추가적으로 확장 태스크를 제공하고 있다. 이러한 태스크를 사용함으로써 개발자들은 매우 손쉽게 어플리케이션의 빌드 과정을 정의할 수 있고, 변경할 수 있고, 실행할 수 있게 되었다.
Ant가 제공하는 태스크 목록은 Ant의 사용자 매뉴얼에 나와 있으므로 Ant에 대한 보다 많은 정보를 필요로 하는 독자는 Ant 매뉴얼을 참고하기 바란다.
관련링크: