728x90


이제 외부 라이브러리를 사용해서 빌드하는 방법을 알아보자.

일단 외부 라이브러리를 포함한다는 이해가 선행되어야 한다.

자바에서 결국에 혼자서 모든 프로그램을 만드는건 힘들다.

누군가가 만들어 놓은 외부 라이브러리를 사용해서 빌드 하는게 바람직하다는 것이다.

여러분들은 IDE를 사용하지 않고 라이브러리를 추가할줄 아는가?

이를 이용해서 빌드를 하고 실행을 하는것을 할줄 안다는 전제가 깔려야 ant를 이해할 수 있다.

가령 아래의 예를 보자.


package net.theceres.cal;

public class Sum {
public Sum() {
}

public static final int sumMethod(int var0, int var1) {
return var0 + var1;
}
}
package net.theceres.cal;

public class Sub {
public Sub() {
}

public static final int subMethod(int var0, int var1) {
return var0 - var1;
}
}

Sum과 Sub이라는 클래스이다.



이를 기반으로 만든 old.jar이다.

자 여러분은 이제 java와 javac를 이용해서 위를 빌드하고 실행시켜보아라.


필자가 누누히 강조하지만 java는 컴파일언어도, 인터프리터언어도 아닌(혹은 둘다이다.) 바이트코드 언어이다.

즉 컴파일을 한번 거치고 인터프리터도 거치는 언어라는 것이다.

두가지의 프로세서를 하면서 javac으로 java->class로 바꾸고 java로 실행한다.

즉 컴파일(javac)하는 단계에서도 "라이브러리가 존재함을 확인"해야하며 실행(java)하는 단계에서도 "라이브러리가 존재"해야한다는 것이다.

그렇기에 일반적으로 보통은 아래와 같이 사용한다.


1.find ./src -name *.java > sources_list.txt

2.ex)javac -d <결과물이 들어갈 디렉터리> -cp <참고할 라이브러리경로>/*.jar @sources_list.txt

ex)javac -d bin -cp lib/*.jar @sources_list.txt

3.java -cp <클래스가 들어있는 폴더 혹은 jar파일, 둘 이상은 :로 묶음> <실행시킬 클래스, 메인이 아니라도 됨>

ex)java -cp bin:lib/* net.theceres.anttest.Main 


위의 결과 실행하면 제대로 결과가 나오게 된다.

여기서 주요한 관점은 컴파일 할때도, 실행을 할때도 둘다 라이브러리가 필요하다는 것이다.

자 이제 build.xml을 수정하여 보자.


<?xml version="1.0"?>
<project name="AntTest" default="main" basedir=".">
<property name="Name" value="Ant Test"></property>
<property name="name" value="anttest"></property>
<property name="groupid" value="net.theceres.anttest"></property>
<property name="project.version" value="1.0.0"></property>

<property name="src.dir" value="src"></property>
<property name="build.dir" value="build"></property>
<property name="classes.dir" value="${build.dir}/classes"></property>
<property name="jar.dir" value="${build.dir}/jar"></property>
<property name="lib.dir" value="lib"></property>

<path id="classpath">
<fileset dir="${lib.dir}" includes="**/*.jar"></fileset>
</path>

<target name="clean">
<delete dir="${build.dir}"></delete>
</target>
<target name="init">
<mkdir dir="${build.dir}"></mkdir>
<mkdir dir="${classes.dir}"></mkdir>
</target>
<target name="compile" depends="init">
<javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="classpath" includeantruntime="false">
</javac>
</target>
<target name="jar" depends="compile">
<jar destfile="${jar.dir}/${name}-${project.version}.jar" basedir="${classes.dir}">
<manifest>
<attribute name="Main-Class" value="net.theceres.anttest.Main"></attribute>
</manifest>
</jar>
</target>
<target name="run" depends="compile,jar">
<java fork="true" classname="net.theceres.anttest.Main">
<classpath>
<path refid="classpath"></path>
<path location="${jar.dir}/${name}-${project.version}.jar"/>
</classpath>
</java>
</target>
<target name="main" depends="clean,jar">
</target>
</project>

생각보다는 길어졌다. 이까지 오는데에 대해서 감개 무량하지 않은가?

실제로 새로 추가된 부분은 아래와 같다.


<path id="classpath">
<fileset dir="${lib.dir}" includes="**/*.jar"></fileset>
</path>

먼저 추가할 classpath를 선언해 준다.

include는 일종의 필터링역활을 하는데 안에 있는 모든 jar를 긁어서 컴파일 하는데 보탬이 될 것이다.


<target name="compile" depends="init">
<javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="classpath" includeantruntime="false">
</javac>
</target>

먼저 compile부분에서 classpathref를 보면 우리가 선언한 classpath를 마치 변수처럼 쓰고 있다.

여기서 우리가 선언한 classpath를 적어준다.

이 선언으로 우리는 컴파일 할 때 이 classpath를 참조해서 컴파일 하게된다.


<target name="run" depends="compile,jar">
<java fork="true" classname="net.theceres.anttest.Main">
<classpath>
<path refid="classpath"></path>
<path location="${jar.dir}/${name}-${project.version}.jar"/>
</classpath>
</java>
</target>

그 다음 run 부분에서 추가를 해준다. 우리가 참조해야할 클래스패스는 총 2가지이다.

먼저 main이 담긴 클래스패스와 그 이외에 서드파티 라이브러리이다.

따라서 한번에 담을 수 없으므로 쪼개서 담는다.

refid는 말그대로 참조하는 라이브러리를 의미한다. 즉 주가 되는것은 아니다.

그리고 location은 Main을 실행할 디렉터리(혹은 jar 혹은 zip)을 의미한다.

이제 실행을 하면 의도대로 실행 되는 것을 확인 할 수 있다.



일단 안정적이게 jar파일이 나왔다.

이제 run을 해보자.

중요한건 lib가 항상 저 위치에 있어야한다.

jar안에 lib를 넣은게 아니기때문에 build.xml이 위치한곳에 있어야한다(물론 본인이 위치를 옮겼다면 다른 이야기이지만.)



보다시피 제대로 작동한다.


jar파일 하나에 다 넣기


위의 방법은 전형적인 방법이지만 만약에 lib의 jar나 class파일을 몽땅 클래스안에 넣고 싶을 때가 있다.

위의 경우 jar는 실행하려면 외부 lib에 의존해야한다. 사실 실행가능한 jar를 만든다면 이렇게 만드는게 좋지 않다.

즉 jar하나에 필요한 모든 클래스를 다 넣어주는게 좋다는 이야기이다.

이 경우 어떻게 해야할까?


일단 ant를 하기 앞서서 일반적인 java와 jar에 대해서 이야기 해보도록 하자.

jar는 일종의 package를 담은 directory의 일종이다.

따라서 jar는 마치 directory처럼 사용할 수 있다.

그럼 jar가 라이브러리에 구애받지 않으려면 라이브러리를 jar에 넣으면 되지 않을까?

물론 훌륭한 생각이다. 정답이긴한데... 문제는 jar를 jar에 넣어봤자 작동하지 않는다는 것이다.

해답은 jar를 풀어서 다시 class화 시키고 해당 클래스를 바탕으로 다시 통합된 jar를 만드는 것이다.

방법은 아래와 같다.


<?xml version="1.0"?>
<project name="AntTest" default="main" basedir=".">
<property name="Name" value="Ant Test"></property>
<property name="name" value="anttest"></property>
<property name="groupid" value="net.theceres.anttest"></property>
<property name="project.version" value="1.0.0"></property>

<property name="src.dir" value="src"></property>
<property name="build.dir" value="build"></property>
<property name="classes.dir" value="${build.dir}/classes"></property>
<property name="jar.dir" value="${build.dir}/jar"></property>
<property name="lib.dir" value="lib"></property>

<path id="classpath">
<fileset dir="${lib.dir}" includes="**/*.jar"></fileset>
</path>

<target name="clean">
<delete dir="${build.dir}"></delete>
</target>
<target name="init">
<mkdir dir="${build.dir}"></mkdir>
<mkdir dir="${classes.dir}"></mkdir>
</target>
<target name="extract.jar" depends="init">
<unjar dest="${classes.dir}">
<fileset dir="${lib.dir}" includes="**/*.jar"></fileset>
</unjar>
</target>
<target name="compile" depends="extract.jar,init">
<javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="classpath" includeantruntime="false">
</javac>
</target>
<target name="jar" depends="compile">
<jar destfile="${jar.dir}/${name}-${project.version}.jar" basedir="${classes.dir}">
<manifest>
<attribute name="Main-Class" value="net.theceres.anttest.Main"></attribute>
</manifest>
</jar>
</target>
<target name="run" depends="compile,jar">
<java fork="true" classname="net.theceres.anttest.Main">
<classpath>
<path refid="classpath"></path>
<path location="${jar.dir}/${name}-${project.version}.jar"/>
</classpath>
</java>
</target>
<target name="main" depends="clean,jar">
</target>
</project>

위의 소스에서 보면 extract.jar라는 target이 추가되었고 compile target은 extract.jar가 필요하게 되었다.

그럼 extract.jar의 내용을 한번 보도록 하자.


<target name="extract.jar" depends="init">
<unjar dest="${classes.dir}">
<fileset dir="${lib.dir}" includes="**/*.jar"></fileset>
</unjar>
</target>

unjar를 통해서 jar를 풀어주는 과정이 추가되었다.

이제 jar파일을 만들때 class파일들이 추가되었으므로 class들이 jar파일에 추가되게 된다.

그럼 한번 테스트 해보자.



제대로 작동되는 것을 확인할 수 있다.


+ Recent posts