태그 보관물: Ant

안드로이드(Android) 프로젝트에서 의존 라이브러리 때문에 Ant(build.xml) 빌드가 실패하는 경우

안드로이드 프로젝트를 Ant로 빌드하다가 의존 라이브러리 때문에 빌드 실패가 발생하는 경우, 의존 라이브러리가 build.xml을 가지고 있지 않은 것을 볼 수 있다. 위 문제에 대한 해결책으로, 라이브러리 프로젝트 폴더에서 아래의 명령을 실행하면 된다. android-sdk-xxx/tools 폴더가 PATH에 등록되어 있지 않으면, 등록하고 실행한다.

android update project –path ./

위 명령을 실행해 보자.

mcsong@mcsong-ubuntu:~/dev/works/lib$ android update project --path ./
 Updated local.properties
 Updated file ./proguard-project.txt
 It seems that there are sub-projects. If you want to update them please use the --subprojects parameter.

이 방법으로, 프로젝트에서 의존 라이브러리를 빌드할 수 있는 Ant 파일들을 만들어서, build.xml을 성공적으로 돌릴 수 있다.

최근버전의 SDK에서는 아래의 명령을 사용하게 바뀌었네요. ^^

mcsong@mcsong-ubuntu:~/dev/works/project-name$ android update project -p ./
 Updated local.properties
 No project name specified, using project folder name 'project name'.
 If you wish to change it, edit the first line of build.xml.
 Added file ./build.xml
 Updated file ./proguard-project.txt
 It seems that there are sub-projects. If you want to update them please use the --subprojects parameter.

Android에서 Ant(build.xml)로 마켓 별 바이너리 빌드하기..

안드로이드 마켓은 애플 앱스토어와 다르게, 통신사나 회사에서 마켓을 만들어서 서비스 할 수 있다. 그래서 여러 안드로이드 마켓을 확인할 수 있다. 안드로이드 앱을
개발하면, Play 마켓 과 더불어 아마존 이나 티스토어 등의 여러 마켓에 런칭하는 경우가 있다. 개별 앱 마켓에 앱을 런칭하는 경우, 개별 마켓은 고유한 정책을 가지고 있고, 정책에 위배되면, 위배되는 내용을 수정해서 다시 요청(Play 마켓을 제외한 다른 마켓들은 거의 리뷰를 한다)해야 한다.

개별 마켓이 가지고 있는 정책중에 하나가, 앱에 대한 설명이나 앱의 주소가 적인 URL(http://play.google.com과 같은 주소는 다른 마켓에서 반려 사유가 된다)이 문제가 되는 경우가 있다. 그리고 개별 마켓에서 설치한 앱은 마켓 별로 다른 메세지를 보여줄 필요도 있다.

그래서, 앤트(Ant, build.xml)를 사용해서 마켓별로 앱을 패키징 하는 방법을 살펴보자.

1. 기본적인 구조

기본적인 패키지의 구조는 좌측과 같습니다..

2.3 버전부터 지원할 수 있는 형태로 프로젝트를 생성했습니다.

좌측의 패키지 구조를 살펴보면…

\libs-ext : 마켓의 분리를 위해서 필요한 Market.java를 폴더별로 가지고 있습니다.

build.properties : 릴리즈를 하기 위한 인증서의 위치와 정보를 저장하는 파일..

build.xml : 빌드 스크립트 파일..

local.properties : 로컬의 SDK 위치를 가리치는 파일..

project.properties : target project의 버전정보를 가지고 있는 파일..

 

참고로, 검은색으로 칠해버린 넘은 인증서 파일입니다..

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2. 코드 보기..

 

 

 

 

 

 

 

2.1 MainActivity.java

– 화면에 마켓의 정보를 보여주는 클래스..

package net.sjava.buildtest;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.TextView;

import net.sjava.buildtest.util.Constants;

public class MainActivity extends Activity {
	private TextView tv;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        tv = (TextView)findViewById(R.id.main_textview);
        tv.setText(Constants.getMarketString(MainActivity.this));
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
}

2.2 Constants.java

– 마켓에 대한 정보를 넘겨주는 클래스..

package net.sjava.buildtest.util;

import android.content.Context;
import net.sjava.buildtest.R;
import net.sjava.buildtest.util.config.Debug;
import net.sjava.buildtest.util.config.Market;

public class Constants {
	public static final boolean DEBUG = Debug.DEBUG;
	public static final int MARKET = Market.MARKET;
	
	public static String getMarketString(Context c) {
		if(c == null)
			return "";
		
		if(MARKET == 0)
			return c.getString(R.string.mk_play);
					
		if(MARKET == 1)
			return c.getString(R.string.mk_amazon);
		
		if(MARKET == 2)
			return c.getString(R.string.mk_tstore);
		
		return "";
	}
}

 

2. 마켓의 정보를 구분하기..

마켓의 정보는 위의 폴더에서 자신의 상수값을 가지고 있게 된다..

따라서, 첨부한 소스를 확인해 보면, mk-play 폴더의 Market.java는 상수값으로 1을 가지고 있다..

 

3. 빌드 스크립트..

– 빌드 스크립트인 build.xml은 구글의 프레임웍이 빌드를 하기 위한 스크립트를 제공하기 때문에 매우 간단하게 작성을 할 수 있다.. 떙큐.. ^^, 아래의 스크립트는 dev/rc/live 같은 개발/라이브의 테스트/라이브 별도 바이너리를 릴리즈 하는 스크립트는 포함하지 않지만, 이것도 쉽게 추가할 수 있다.. 또한, 포함되어 있지 않지만, obfuscate를 위해서  proguard가 포함되어 있어, obfucate를 쉽게 지원한다.

 

3.1 build.properties

#
# Set the keystore properties for signing the application.
# 맞게 수정하시면 됩니다.. ^^
key.store=./libs-ext/xxxx
key.alias=xxxx

key.store.password=xxxx
key.alias.password=xxxx

3.2 local.properties

# location of the SDK. This is only used by Ant
# For customization when using a Version Control System, please read the
# header note.
sdk.dir=C:\\Program Files\\android\\android-sdk

 

3.3 project.properties

# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt

# Project target.
target=android-16

 

3.4 build.xml

– 아래의 빌드 스크립트의 buildtest-release-all 타켓을 실행해서, 마켓별 바이너리를 뽑아낼 수 있다.

 

<?xml version=”1.0″ encoding=”UTF-8″?>
<project name=”BuildTest” default=”help”>
<property file=”local.properties”/>
<property file=”build.properties” />
<property file=”project.properties” />

<!–property name=”config-target-dir-path” value=”net/sjava/buildtest/util/config” /–>
<property name=”market-target-dir-path” value=”net/sjava/buildtest/util/config” />

<property name=”project.name” value=”BuildTest”/>
<property name=”dist.dir” value=”./dist/” />
<property name=”release.file” value=”./bin/BuildTest-release.apk” />
<property name=”dist.backup” value=”c:/dev/dist” />

<path id=”android.antlibs”>
<fileset dir=”${sdk.dir}/tools/lib/” includes=”*.jar” />
</path>

    <!– 안드로이드 SDK가 제공하는 build.xml을 사용하기 위해서 import 한다.. –>
    <import file=”${sdk.dir}/tools/ant/build.xml”/>

    <!–AndroidManifest에서 xpath를 이용해서 선언된 versionName 값을 가져온다. –>
    <xpath input=”AndroidManifest.xml” expression=”/manifest/@android:versionName” output=”versionName” default=”1.0.0″/>

<tstamp>
<format property=”touch.time” pattern=”yyyyMMdd_HHmm” />
</tstamp>

<target name=”switch-market”>
<echo>Market Configuration file: ${market.filename}</echo>
<property name=”market-target-path” value=”${source.dir}/${market-target-dir-path}” />
<copy file=”${market.filename}” todir=”${market-target-path}” overwrite=”true” encoding=”utf-8″ />
</target>

<target name=”buildtest-backup”>
<copy todir=”${dist.backup}/${project.name}/${versionName}_${touch.time}”>
<fileset dir=”./dist”/>
</copy>
</target>

<target name=”buildtest-release-mk-play”>
<echo>Selected release configuration</echo>
<antcall target=”clean” />

<antcall target=”switch-market”>
<param name=”market.filename” value=”libs-ext/mk-play/Market.java” />
</antcall>

<property name=”proguard.enabled” value=”false”/>
<antcall target=”release” />
<copy file=”${release.file}” tofile=”${dist.dir}/${project.name}_live_play_${versionName}_${touch.time}.apk”/>
</target>

<target name=”buildtest-release-mk-amazon”>
<echo>Selected release configuration</echo>
<antcall target=”clean” />

<antcall target=”switch-market”>
<param name=”market.filename” value=”libs-ext/mk-amazon/Market.java” />
</antcall>

<property name=”proguard.enabled” value=”false”/>
<antcall target=”release” />
<copy file=”${release.file}” tofile=”${dist.dir}/${project.name}_live_amazon_${versionName}_${touch.time}.apk”/>
</target>

<target name=”buildtest-release-mk-tstore”>
<echo>Selected release configuration</echo>
<antcall target=”clean” />

<antcall target=”switch-market”>
<param name=”market.filename” value=”libs-ext/mk-tstore/Market.java” />
</antcall>

<property name=”proguard.enabled” value=”false”/>
<antcall target=”release” />
<copy file=”${release.file}” tofile=”${dist.dir}/${project.name}_live_tstore_${versionName}_${touch.time}.apk”/>
</target>

<target name=”buildtest-release-all”>
<echo>Start distribute binaries all of the market</echo>
<antcall target=”buildtest-release-mk-play” />
<antcall target=”buildtest-release-mk-amazon” />
<antcall target=”buildtest-release-mk-tstore” />
</target>

</project>

 

4. 결과

– 위의 코드를 통해서 마켓별로 필요한 URL이나, 리소스를 구분해서 보여줄 수 가 있겠습니다.. 아래의 화면은 만들어진 바이너리가 마켓별로 구분된 메세지를 보여주는지 테스트한 화면입니다..

 

아래의 명령을 통해서 각 바이너리를 에뮬레이터에 설치해서 테스트 하시면 됩니다.

> adb install BuildTest_live_amazon_1.0_20120918_2132.apk

> adb install BuildTest_live_tstore_1.0_20120918_2132.apk

 

 

5. 소스

cfile27.uf.1534F3375059F12A030C14.zip

 

 

 

ant를 이용해서, lib 폴더의 denendency jar file을 manifest의 class-path에 손쉽게 추가하기…

자바로 프로젝트를 하다보면, 몇개의 라이브러리는 기본적으로 사용을 합니다.
좀 큰 프로젝트를 하다보면, 적게는 10개이상의 라이브러리를 사용할 텐데요.. 빌드를 하면서 의존 라이브러리를 일일히 manifest 파일에 기입을 해 주기는 매우 불편한 일이 되겠죠..
하지만, lib 폴더의 jar파일을 동적으로 로딩해서 jar로 배포할 때 넣어주면, 위 불편함이 순식간에 가시겠죵??ㅋㅋ

사용방법은 아래처럼 사용하면 됩니다.

<attribute name=”Class-Path” value=”lib/ conf/ ${lib-manifest-path}” />

아래 코드를 build.xml에 넣으시면 됩니다.

    <target name=”gen-manifest” description=”Generate CLASSPATH to use in manifest”>
        <pathconvert dirsep=’${file.separator}’ property=”full-lib-dir”>
            <path>
                 <pathelement path=”lib”/>
            </path>
        </pathconvert>

        <echo>full-lib-dir: ${full-lib-dir}</echo>

        <pathconvert pathsep=” ” dirsep=”/” property=”manifest-path”>
            <path>
                <fileset dir=”lib” includes=”*”/>
            </path>
            <map from=”${full-lib-dir}${file.separator}” to=””/>
        </pathconvert>

        <echo>manifest-path: ${manifest-path}</echo>
       
        <pathconvert pathsep=” ” dirsep=”/” property=”lib-manifest-path”>
            <path>
                <fileset dir=”lib” includes=”*”/>
            </path>
            <map from=”${full-lib-dir}${file.separator}” to=”lib/”/>
        </pathconvert>

        <echo>lib-manifest-path: ${lib-manifest-path}</echo>
    </target>

위 코드는 아래 링크에서 가져왔습니다. 너무 감사드립니다.. ^^

Ant: construct manifest classpath 

build.xml refactoring using macrodef tag

The ThoughtWorks Anthology 라는 책의 11 챕터의 빌드 아케텍트인 줄리안 심슨님의 내용은, build.xml 즉, ant 파일이 refactoring에 대한 내용입니다.  그 중에서, <macrodef> 태그를 이용한 build.xml 파일의 리펙토링 예제가 나오는데, 유용할것 같아서 개인적으로 사용하고 있는 dist target에 대해서 리펙토링을 적용해 봤습니다.

기존

<!– distribute compiled binary to dist folder –>
    <target name=”dist” depends=”compile” description=”distribute library”>
        <echo>dist target started</echo>
        <jar destfile=”${dist.dir}/${project.name}-${project.version}.jar”>
            <fileset dir=”${build.main.classes}”></fileset>
           
            <manifest id=”MANIFEST.MF”>
                 <attribute name=”Built-By” value=”${user.name}”/>
                 <!–attribute name=”Class-Path” value=”${class-path}” /–>   
             </manifest>
        </jar>
       
        <copy todir=”${dist.dir}”>
            <fileset dir=”${lib.dir}”>
                <exclude name=”junit*.jar” />
            </fileset>
        </copy>
       
        <!– compress binary –>
        <zip destfile=”${dist.dir}/${project.name}-${project.version}.zip” >
            <fileset dir=”${dist.dir}” includes=”**” />
        </zip>
        <echo>dist target completed</echo>
    </target>   

리펙토링된 후

    <macrodef name=”dist_jar”>
        <sequential>
            <jar destfile=”${dist.dir}/${project.name}-${project.version}.jar”>
                <fileset dir=”${build.main.classes}”></fileset>
                <manifest id=”MANIFEST.MF”>
                    <attribute name=”Built-By” value=”${user.name}”/>
                    <!–attribute name=”Class-Path” value=”${class-path}” /–>   
                </manifest>
            </jar>
        </sequential>
    </macrodef>
   
    <macrodef name=”dist_copy”>
        <sequential>
            <copy todir=”${dist.dir}”>
                <fileset dir=”${lib.dir}”>
                    <exclude name=”junit*.jar” />
                </fileset>
            </copy>
        </sequential>
    </macrodef>
   
    <macrodef name=”dist_zip”>
        <sequential>
            <!– compress binary –>
            <zip destfile=”${dist.dir}/${project.name}-${project.version}.zip” >
                <fileset dir=”${dist.dir}” includes=”**” />
            </zip>       
        </sequential>
    </macrodef>
   
    <!– distribute compiled binary to dist folder –>
    <target name=”dist” depends=”compile” description=”distribute library”>
        <echo>dist target started</echo>
        <dist_jar />
        <dist_copy />
        <dist_zip />
        <echo>dist target completed</echo>
    </target>   

위 예제처럼, 리펙토링된 내용을 살펴보면, <macrodef>태그를 통해서 기능에 대한 내용을 작게 쪼개서 빼고(<macrodef>) , 사용하는 <target>에서는 필요한 각 기능을 호출하는 템플릿 메쏘드(?)처럼 기술하게 됩니다. 어디서 많이 본 느낌인데.. 위는 기능을 작게 나눠서 메쏘드로 뽑는 extract method 리렉토링 기법과 동일하네요.. 역시 대가들에게서 많은 내용을 배우게 되는것 같습니다. ^^

이클립스에서 Ant 빌드시 javadoc task 에러

이클립스에서 Ant로 빌드시에 javadoc 툴을 사용해서 document를 만들고 있는데, Ant cannot run program javadoc.exe 에러가 발생을 하네요.. ^^;;;

그래서 확인해 봤더니..
java.home이 잘못 세팅이 되어 있습니다. ^^;;

<echo>${java.home}</echo>
C:\Program Files\Java\jre6

eclipse에서 아래처럼 ant의 설정에 java.home을 추가해서 위치를 조정해 주면 쉽게 해결이 됩니다. ^^