Maven은 빌드에 필요한 라이브러리를 아래와 같이 설정합니다.

 <dependencies>
  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>3.8.2</version>
   <scope>test</scope>
  </dependency>
  <dependency>
   <groupId>commons-fileupload</groupId>
   <artifactId>commons-fileupload</artifactId>
   <version>1.1.1</version>
  </dependency>
  <dependency>
   <groupId>commons-lang</groupId>
   <artifactId>commons-lang</artifactId>
   <version>2.1</version>
  </dependency>
  ... ...
 </dependencies>

Buildr에서는 compile.with 메소드에 라이브러리 목록을 넘겨주어 의존성을 추가할 수 있습니다.

compile.with "org.apache.axis2:axis2:jar:1.2", "commons-fileupload:commons-fileupload:jar:1.1.1"

compile.with 메소드가 받아들이는 의존 라이브러리의 몇가지의 타입이 존재합니다.

1. 아티팩트(artifact) 스펙
위의 메이븐 pom에서 xml 형식으로 지정한 coordinates를 다음의 형식으로 지정합니다.

groupId:artifactId:packaging:version
groupId:artifactId:packaging:classifier:version

* 위의 형식 뿐 아니라 다양한 포맷으로 스펙을 지정할 수 있습니다. 여기를 참조하세요.

이렇게 아티팩트 스펙 형식으로 의존성을 추가하면 Buildr은 메이븐 레포지토리에서 해당 라이브러리를 다운받아 로컬에 설치합니다.

2. 다른 프로젝트
만일 다른 프로젝트의 패키징 결과물에 의존하고 있다면 그 프로젝트를 넘겨주면 됩니다.

compile.with projects('another-project', 'other-project')

3. 파일/디렉토리 경로
로컬에 존재하는 파일/디렉토리의 경로를 지정하여 의존성을 추가할 수 있습니다.
메이븐에서는 로컬 파일을 의존성에 추가하기 위해서 스코프를 system으로 잡고 파일의 절대경로를 지정하였습니다.

  <dependency>
   <groupId>weblogic</groupId>
   <artifactId>weblogic-api</artifactId>
   <version>10.0</version>
   <scope>system</scope>
   <systemPath>C:/bea/weblogic/xxx/wls-api.jar</systemPath>
  </dependency>

Buildr에서는 프로젝트에 대한 상대경로 또는 절대경로 모두 가능합니다.

compile.with 'C:/bea/weblogic/xxx/wls-api.jar' , 'lib/my-api.jar',

경로 지정시 path_to 메소드를 활용하는 것이 좋습니다.

compile.with path_to(:source,:main,:webapp,'WEB-INF/lib/my-api.jar')

또한 glob 패턴을 적용하여 파일을 지정할 수 있습니다. 메이븐도 이런게 되면 참 좋겠습니다.

compile.with FileList[path_to(:source,:main,:webapp,'WEB-INF/lib/*.jar'), 'lib/**/*.jar']

여기서 Rake::FileList를 사용했습니다. FileList 없이 그냥 패턴 문자열을 그대로 compile.with 메소드에 넘겨줄 수도 있습니다. 그러나 그렇게 되면 패턴 문자열이 해석없이 그대로 compile.dependencies에 할당되기 때문에 (단위테스트 등에서) 예상치못한 문제가 발생할 수 있습니다. 그래서 FileList를 통해 패턴을 해석한 뒤에 compile.with에 넘겨주는 것을 권장합니다.

Buildr에서는 놀랍게도 파일뿐 아니라 디렉토리도 의존성에 추가하는게 가능합니다.

compile.with '../AAA/classes'

다만, 이렇게 디렉토리를 의존성에 추가하고 war로 패키징하게 되면 war에 그 디렉토리가 포함이 됩니다. 이때는 그 디렉토리를 아래처럼 패키지 libs에서 빼주면 됩니다.

DEPEND_CLASSES =  '../AAA/classes'
compile.with DEPEND_CLASSES
package(:war).libs -= artifacts(DEPEND_CLASSES)

Posted by 에코지오
,
Maven에서는 자바 소스 컴파일시 옵션을 아래와 같이 추가할 수 있었습니다.

  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
      <source>1.5</source>
      <target>1.5</target>
      <encoding>UTF-8</encoding>

    </configuration>
  </plugin>

Buildr에서는 compile 타스크의 options 속성을 통하거나

compile.options.target = '1.5'
compile.options.source = '1.5'
compile.options.other = ['-encoding', 'UTF-8']

또는 using 메소드를 사용하여 컴파일 옵션을 설정할 수 있습니다.
 
compile.using :target=>'1.5', :source=>'1.5', :other=>['-encoding', 'UTF-8']

옵션 설정에 사용할 수 있는 심볼은 다음과 같습니다.

  • :warnings — Issue warnings when compiling. True when running in verbose mode.
  • :debug — Generates bytecode with debugging information. Set from the debug              
  • :deprecation — If true, shows deprecation messages. False by default.
  • :source — Source code compatibility.
  • :target — Bytecode compatibility.
  • :lint — Lint option is one of true, false (default), name (e.g. ‘cast’) or array.
  • :other — Array of options passed to the compiler (e.g. ’-implicit:none’)
  • Posted by 에코지오
    ,
    Buildr의 기본 프로젝트 구조는 Maven2의 그것과 거의 동일합니다.

      src
      |__main
      |    |__java              <-- Source files to compile
      |    |__resources      <-- Resources to copy
      |    |__webapp         <-- For WARs
      |__test
      |    |__java              <-- Source files to compile (tests)
      |    |__resources      <-- Resources to copy (tests)
    target                       <-- Packages created here
      |__classes             <-- Generated when compiling
      |__resources          <-- Copied (and filtered) from resources
      |__test/classes       <-- Generated when compiling tests
      |__test/resources    <-- Copied (and filtered) from resources
    reports                     <-- Test, coverage and other reports

    Maven에서 기본 프로젝트 디렉토리 구조를 아래처럼 세팅하여 변경할 수 있었습니다.

     <build>
      <directory>target</directory>
      <sourceDirectory>src</sourceDirectory>
      <outputDirectory>web/WEB-INF/classes</outputDirectory>
      <resources>
       <resource>
        <directory>src</directory>
        <excludes>
         <exclude>**/*.java</exclude>
        </excludes>
       </resource>
      </resources>
        ... ....
       <plugin>
        <artifactId>maven-war-plugin</artifactId>
        <version>2.1-alpha-2</version>
        <configuration>
         <warSourceDirectory>web</warSourceDirectory>
         <webappDirectory>${project.build.directory}/webapp</webappDirectory>
        </configuration>
       </plugin>


    Buildr도 디렉토리 구조를 바꿀 수 있습니다. 프로젝트 정의 메소드에 Layout 인스턴스를 전달해주면 됩니다.
    ( http://incubator.apache.org/buildr/extending.html#using_alternative_layouts )

    my_layout = Layout.new
    my_layout[:source, :main, :java] = 'src'
    my_layout[:source, :main, :resources] = 'src'
    my_layout[:source, :test, :java] = 'test'
    my_layout[:source, :test, :resources] = 'test'
    my_layout[:source, :main, :webapp] = 'web'

    my_layout[:target, :main, :classes] = 'web/WEB-INF/classes'
    my_layout[:target, :main, :resources] = 'web/WEB-INF/classes'
    my_layout[:target, :test, :classes] = 'target/test-classes'
    my_layout[:target, :test, :resources] = 'target/test-classes'
    my_layout[:target, :main, :webapp] = 'target/webapp'
    my_layout[:target, :main, :htdocs] = 'target/htdocs'

    my_layout[:reports] = 'target/reports'

    define "MyProject", :layout=>my_layout do

      project.version = VERSION_NUMBER
      project.group = GROUP
      manifest["Implementation-Vendor"] = COPYRIGHT
      ... ...
      resources.exclude '**/*.java'
      test.resources.exclude '**/*.java'
      ... ...
    end


    (위에서 :htdocs는 제가 임의로 사용한 심볼입니다)

    'Build&Deploy > Buildr' 카테고리의 다른 글

    Buildr : 리소스 필터링  (0) 2008.12.09
    Buildr : 파일집합 복사하기  (0) 2008.12.05
    Buildr : 파일 집합 선택  (0) 2008.12.01
    Buildr : 의존성(dependencies) 설정하기  (4) 2008.11.19
    Buildr : 컴파일 옵션 설정하기  (2) 2008.11.18
    Posted by 에코지오
    ,
    (웹)어플리케이션을 '개발' 서버에 배포(deploy)할 때는 소스저장소의 중심개발축(HEAD)에서 전체 최신 개발소스를 가져와서 빌드하고, 빌드결과 파일들을 일괄적으로 통째로 전부 배포해도 대부분 큰 문제가 없다.

    배포코스 설계에서 설명한 것처럼 빌드서버중심 코스에서는 빌드서버에서 빌드 후 개발서버로 전송하여 배포하면 될 것이고, 소스저장소중심 코스에서는 개발서버에서 빌드 및 배포를 모두 처리하면 될 것이다.

    그러나 배포 대상 서버가 '운영' 서버라면 개발소스 전체를 무차별적으로 배포하는 것은 문제가 된다. 버그가 고쳐지지 않은 모듈, 검증이 덜된 모듈, 아직 공개해서는 안될 모듈 등이 함께 반영될 수 있기 때문이다.

    따라서 중심개발축의 개발소스 중에서 stable하고 production-ready 상태의 소스만 선별해서 운영에 반영해야 한다

    그럼 어떻게 선별할 것인가? (내가 아는 한) 실현 가능한 선별방법 2가지가 있다(이전 글 참조).

    ○ 중심개발축으로부터 빌드된 전체 파일들 중에서 반영할 파일들을 하나하나 수작업으로 골라내는 방법
    ○ 소스저장소에 릴리스 브랜치를 만들어서 여기에 반영할 소스만 모아놓는 방법

    전자는 컴파일,테스트,코드검사,패키징,배포까지의 흐름 중간에 사람이 개입하게 되어 자동화가 불가능하는 점, 배포모듈의 정합성이 깨질 가능성이 큰 점 등 여러가지 문제점을 안고 있기 때문에 권장하지 않는 방식이다.

    대개는 널리 알려진 후자의 방법을 택한다.(어느 나라? 누구? 어느 분야에서?)
    외국의 자료(사이트, 책 등)를 살펴보면 보통 아래와 같은 내용이 포함된다(주관적인 판단일 수 있음).

    - 어플리케이션은 외부에 릴리스하는 방식으로 배포(distribution)됨
    - 브랜치는 릴리스 브랜치(Release Branch)로 불림
    - 주요 마일스톤/릴리스 마다 새로운 브랜치를 생성
    - 주로 릴리스 브랜치에서 버그를 픽스하고 중심 개발축으로 파일을 병합함
    - 동시에 여러 릴리스 브랜치가 유지보수됨
    - 주로 릴리스 브랜치 -> 중심 개발축으로 소스가 병합됨

    그러나 이러한 설명은 솔루션/프레임워크/라이브러리의 개발,유지보수 환경에 치우쳐 있다는 느낌을 갖는다(솔직히 Agile, TDD, Maven에 대해서도 마찬가지 느낌을 갖고 있다).
    외국에서 '개발'은 솔루션 개발이 default일지 모르나, 국내에서는 개발이라고 하면 (SI/SM프로젝트에서) 웹시스템 개발이 default라고 생각한다.

    다음은 내가 생각하는 국내 개발환경에 적합한 배포 방식이다(웹시스템 개발/유지보수에 초점).

    - 어플리케이션은 외부에 릴리스되는 것이 아니라 운영서버에 배포(deployment)됨
    - 그래서 릴리스 브랜치 대신 배포 브랜치(Deploy Branch)라고 이름 바꿈(또는 운영브랜치?)
    - 기능추가시 중심 개발축에서 배포브랜치로 운영에 반영할 소스파일을 추가
    - 기능수정시 중심 개발축의 소스 수정 후 배포브랜치의 소스와 병합
    - 개발 도중 발견된 버그는 중심개발축의 소스를 고친 후 배포브랜치의 소스에 병합시킴
    - 운영에서 발견된 버그는 일단 배포브랜치의 소스를 고쳐서 운영에 반영한 뒤에 중심개발축에 병합함
    - 하나의 배포 브랜치만 생성하여 유지보수
    - 주로 중심개발축 -> 배포 브랜치로 소스가 병합됨



    고백하건데 이렇게 하는게 국내환경에서 좀더 실용적일 거라고 생각은 하지만, 아직은 실전 프로젝트에서 이를 적용해본 적은 없다. 해보지도 않고 떠들다니...살짝 부끄럽군... -.-;;

    태클 대환영!
    Posted by 에코지오
    ,

    메이븐에서는 기본적으로 자바 소스 폴더를 한 개만 지정할 수 있습니다.

     <build>
        <sourceDirectory>src/main/java</sourceDirectory>
     ....
     </build>

    하지만 때로는 자바 소스가 여러 폴더에 흩어져 있는 경우도 있습니다.
    메이븐에서는 그럴 경우 프로젝트를 분리하라고 권장합니다.
    그러나 프로젝트를 분리할 형편(?)이 안되거나 분리하기 싫다면 어떻게 할까요?

    Build Helper Maven 플러그인을 이용하면 됩니다.
    build-helper 플러그인의 add-source 모조는 POM에 소스 디렉토리를 추가해줍니다. 아래처럼 사용할 있습니다.

      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>build-helper-maven-plugin</artifactId>
        <version>1.2</version>
        <executions>
         <execution>
          <id>add-source-dir</id>
          <phase>generate-sources</phase>
          <goals>
           <goal>add-source</goal>
          </goals>
          <configuration>
           <sources>
             <source>another/src/main/java</source>
             <source>others/src</source>
           </sources>
          </configuration>
         </execution>
        </executions>
      </plugin>

     

    Posted by 에코지오
    ,

    메이븐에서 이게 가능한가요? 

    Ant에서는 classpath 엘리먼트를 통해서 클래스가 포함된 디렉토리를 컴파일 패스에 추가할 수 있습니다.

        <classpath>
          <pathelement location="mydir/classes"/>
        </classpath>

    이것과 동일한 기능을하는 메이븐 설정/플러그인이 있나요?

    아무리 구글링하고 찾아봐도 방법을 모르겠습니다.

    아시는 분은 좀 알려주시길...

    Posted by 에코지오
    ,

    메이븐의 특징 중 하나는 메이븐이 참조 라이브러리 의존관계를 체계적으로 관리해준다는 것이다. 메이븐이 의존성을 다루는 의존성 관리 메커니즘에는 의존성 전이(Transitive Dependencies)라는 게 있는데,  
    A가 B에 의존하고 B가 C에 의존한다고 할 때, 즉 A -> B -> C 관계에서 A는 B에 대한 의존관계만 설정하면 메이븐이 알아서 C까지도 가져와서 적절한 스코프에 포함시켜 준다는 것이다.

    그러나, B의 type이 war 이면 의존성 전이 메커니즘은 작동하지 않는다.

     <dependency>
       <groupId>mycom</groupId>
       <artifactId>B</artifactId>
       <version>1.0</version>
       <type>war</type>
     </dependency>

    그러니까 메이븐이 자동으로 B.war가 의존하는 C.jar를 포함시켜주지 않는다는 얘기다.
    게다가 B.war에 포함된 클래스들(WEB-INF/classes) 또한 A의 의존성에 포함되지 않는다. A의 소스가 B.war의 클래스를 참조한다면 컴파일시 에러가 발생할 것이다.

    이클립스에서 A프로젝트의 빌드패스에 B 웹프로젝트를 추가하면 이클립스는 B 웹프로젝트의 빌드패스에 포함된 C.jar와 B 웹프로젝트의 클래스들을 A프로젝트에 자동으로 추가해준다.

    아쉽지만 메이븐에서는 이게 자동으로 안된다.

    Posted by 에코지오
    ,
    메이븐에서 ear 패키지를 만들어 주는 플러그인은 maven-ear-plugin이다.
    ear 파일에 포함되어 함께 패키징될 아카이브 모듈들은 <modules> 엘리먼트 하위에 <ejbModule>, <jarModule>, <webModule> 식으로 설정해서 끼워 넣는다.

    그런데 xxxModule 설정 옵션을 보자면, 모두가 groupId와 artifactId를 통해서 끼워넣을 모듈을 지정하고 있다. 아무리 찾아봐도 모듈 파일의 경로를 설정하는 건 없다 ! 내가 원하는 건 임의의 경로에 있는 아카이브를 ear에 포함하고 싶은건데.... ㅜ.ㅜ

    여기까지만 보면, 메이븐 저장소에 등록된 모듈만 ear에 포함시킬 수 있다는 결론이 나온다. 내가 만든 모듈을 ear에 포함시키고 싶으면 먼저 모듈을 로컬이나 리모트 저장소에 등록해주어야 한다는 거다. 이 무슨 시츄에이션인가?

    그러나 저장소에 등록하지 않고 포함시킬수 있는 방법이 있다. 포함되는 모듈의 의존성 scope를 system으로 잡고, 파일의 절대경로를 지정하면 된다.

    <plugin>
     <groupId>org.apache.maven.plugins</groupId>
     <artifactId>maven-ear-plugin</artifactId>
     <version>2.3.1</version>
     <configuration>
      <modules>
       <ejbModule>
        <groupId>mycom</groupId>
        <artifactId>myapp-ejb</artifactId>
       </ejbModule>
       <webModule>
        <groupId>mycom</groupId>
        <artifactId>myapp-web</artifactId>
       </webModule>
      </modules>
     </configuration>
    </plugin>
    .....
    ......
    <dependencies>
     <dependency>
      <groupId>mycom</groupId>
      <artifactId>myapp-web</artifactId>
      <version>1.0</version>
      <type>war</type>
      <scope>system</scope>
      <systemPath>${project.basedir}/myapp-web.war</systemPath>
     </dependency>
     <dependency>
      <groupId>mycom</groupId>
      <artifactId>myapp-ejb</artifactId>
      <version>1.0</version>
      <type>ejb</type>
      <scope>system</scope>
      <systemPath>${project.basedir}/myapp-ejb.jar</systemPath>

     </dependency>
     .....
    </dependencies>

    이렇게 잡아놓고 mvn package를 날리면 저장소에 등록되지 않은 myapp-ejb.jar, myapp-web.war 파일이 ear에 함께 패키징되는 것을 확인할 수 있다.

    Posted by 에코지오
    ,
    WAS를 start/stop/restart하거나 웹어플리케이션을 deploy/undeploy/redeploy하는 작업을 자동으로 처리하기 위해 쓸 수 있는 방법은 어떤게 있을까?

    1. 스크립트 실행
    작업을 정의한 스크립트를 실행한다. 아마 이게 제일 속편한 방법일지도 모른다.
    Ant에서는 telnet, exec, sshexec 등의 타스크를 이용하여 실행하면 된다.

    Ant sshexec 타스크를 통해서 원격지의 Tomcat을 start하는 예제

    <target name="remote-tomcat-start">
      <sshexec host="${ssh.hostname}"
      port="${ssh.port}"
      username="${ssh.username}"
      passphrase=""
      trust="true"
      keyfile="${ssh.key.file}"
      command="${tomcat.home}/bin/startup" />
      <sleep seconds="${sleep.time}" />
    </target>


    2. WAS에서 제공하는 Ant 타스크 또는 Maven 플러그인 이용
    대부분의 WAS가 어드민화면을 통하지 않고 여러가지 작업을 수행할 수 있는 수단을 제공한다.

    Tomcat에서 제공하는 deploy 타스크를 이용하여 war를 deploy하는 Ant 예제

        <target name="install" description="Install application in Tomcat"
            depends="package-web">
            <deploy url="${tomcat.manager.url}"
                username="${tomcat.username}"
                password="${tomcat.password}"
                path="/${webapp.name}"
                war="file:${webapp.dist}/${webapp.war}"/>
        </target>


    3. Cargo 이용
    Cargo는 WAS 마다 다른 형태의 API를 래핑하여 표준적인 방법으로 WAS를 핸들링할 수 있게 해준다. 그러나 아직 지원하는 WAS가 많지 않다.

    Ant에서 cargo 타스트를 이용하여 Tomcat을 start하는 예제

      <cargo containerId="tomcat5x" home="${tomcat.home}" output="${tomcatlog.dir}/output.log"
          log="${tomcatlog.dir}/cargo.log" action="start">
        <configuration home="${tomcatconfig.dir}">
          <property name="cargo.servlet.port" value="8080"/>
          <property name="cargo.logging" value="high"/>
          <deployable type="war" file="${mywarfile}"/>
        </configuration>
      </cargo>
    Posted by 에코지오
    ,
    Ant는 세밀하게 빌드를 설정하기 좋지만 미리 정해진 규칙이나 프로세스가 없기 때문에 알파부터 오메가까지 모든걸 내가 다 정의해야한다. Ant는 나에게 거의 모든 자유를 주며, 복잡한 빌드환경에서도 적절히 대처할 수 있는 온갖 task를 제공해준다. 그러나 자유에는 책임이 따르듯이 규칙과 절차를 만드는 몫은 나에게 있다. 이렇게 수립된 규칙과 절차는 프로젝트마다 제각각이어서 대부분 호환되지 않는다.

    Maven은 Ant에 비해 자유도는 낮지만 규칙을 잘 따른다면 작업을 쉽게 처리할 수 있다. 널리 통용되는 빌드 프로세스(phase)와 수행할 작업이 내장되어 있어서 설정을 최소화해주며, 의존관계의 라이브러리를 체계적으로 다루는 능력이 뛰어나다.(원래 ant는 의존성 관리를 하지 않으나 ivy를 이용하면 ant에서도 충분히 maven처럼 의존성 관리를 할 수 있다고 함) 그러나 Ant만큼 풍부하고 작은 단위의 task는 제공하지 않고 대신, plugin이라는 다소 큰 덩어리의 도구를 제공한다. 딱딱한 빌드절차, 묵직한 plugin 등 커스터마이징이 극히 제한적이어서 국내 SI프로젝트처럼 국제적인 상식(?)을 잘 따르지 않는 복잡한 빌드환경에서는 적합하지 않을 수 있다.

    그럼 Ant의 높은 자유도와 Maven의 다듬어진 프로세스를 두루 갖는 그런 빌드 도구는 없는가?
    최근 Ant와 Maven을 뛰어넘기 위해 스크립트 언어 기반으로 빌드를 설정하려는 시도가 많아지고 있다.

    찾아보면 BuildrGradle, Raven 대충 이정도 나온다.

    Raven과 Buildr은 ruby 언어로, Gradle은 groovy 언어로 빌드를 작성한다.
    Raven은 Ant 스타일과 비슷하고 Buildr과 Gradle은 Maven의 사상이 들어있다.

    자 이쯤에서 하나 골라잡아 공부해야 하는건가?
    Posted by 에코지오
    ,