안드로이드에서 실행가능한 프로그램을 만들기 위해서는 반드시 필요한 몇가지가 있다. 그중에 하나가 여기서 설명할 Android.mk 파일이다.

안드로이드에서 돌아가는 프로그램은 기본적으로 안드로이드에서 제공하는 라이브러리를 참조한다. 또한 안드로이드 플랫폼 위에서 실행되어야 하기 때문에 안드로이드용 프로그램이라면, 안드로이드가 정하는 규칙을 따라야 할 필요가 있다.
기본적으로는 Makefile 의 문법을 따르지만, 안드로이드에서 사용하는 특별한 규칙을 알아볼 것이다.
여기에 기술된 내용은 '안드로이드의 모든것 NDK' 라는 서적에 있는 것을 발췌한 것이다.

반드시 Android.mk 파일을 만들어야 할까?

앞서 말했듯이, 안드로이드에서 실행되는 프로그램을 만들기 위해서는 Android.mk 가 필요하다고 했는데, 반드시 그럴까?

반드시는 아니다. Android.mk 가 필요한 경우는 빌드할 프로그램이 안드로이드가 제공하는 라이브러리를 참조한다는 가정하에서다. 만일 프로그램이 필요한 모든 라이브러리를 미리 포함하여 빌드한다면(static linking), 굳이 Android.mk 파일이 없어도 된다. 대신 바이너리 사이즈가 커질 수 있다.

예를 들어 기존 임베디드 리눅스에서 사용하던 프로그램을 안드로이드로 포팅하기 위해서는 Android.mk 를 사용하여 안드로이드에서 제공하는 라이브러리를 사용하는 방법과 자체 라이브러리를 bulit-in 하여 빌드하여 사용하는 방법이 있다고 하겠다.

하지만, 일반적으로 권장하는 방법은 안드로이드에서 제공하는 라이브러리를 사용하는 것이므로 여기서는 Android.mk 에 대해 설명하겠다.

Android.mk 의 개요

안드로이드 Makefile 은 리눅스에서 쓰는 GNU Makefile 과 비슷한데다가 사용법이 훨씬 간단하다. 리눅스에서 Makefile 로 컴파일하면 소스코드 지정이나 헤더 지정, 라이브러리 링크 등등의 과정을 자동으로 할 수 있고 코드를 수정할 때에는 수정된 코드만 재컴파일하여 결과물을 생성하기에 매우 편리하다.

안드로이드에서는 Makefile 대신 Android.mk 로 소스를 컴파일한다(NDK 에서도 마찬가지이고 안드로이드 전체 소스에서도 마찬가지로 Android.mk 를 사용한다). 이것을 규칙에 맞게 작성해야만 소스코드를 컴파일할 수 있다.

안드로이드 시스템은 안드로이드를 위한 Makefile 양식을 가지고 있다. NDK로 개발하려면 Applicantion.mk 와 Android.mk 를 작성해야하며, NDK 의 버전이 변경되면 Makefile 의 옵션에도 추가사항이 생긴다.
이에 대한 내용은 NDK 의 docs 디렉토리안에 문서(ANDROID-MK.html, APPLICATION-MK.html, HOWTO.txt)를 참고한다.

Android.mk 예제

안드로이드 소스코드에 있는 실제 코드를 통해 설명하겠다.

#cd /root/WORKING_DIRECTORY/external/helloandroid
#vi Android.mk

내용은 아래와 같다.

LOCAL_PATH := $(call my-dir)           // Android.mk 파일은 반드시 LOCAL_PATH 변수를 정의하면서 시작해야 한다. 이것은 소스파일의 위치를 지정하는 것이다. my-dir(현재 디렉토리를 리턴) 매크로를 사용한다
 
include $(CLEAR_VARS)  // CLEAR_VARS 변수는 특정 GNU Makefile 을 가리킨다. 이것은 LOCAL_PATH 이외의 다른 LOCAL_XXX 변수를 지운다.
                                     // (예. LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES 등)
                                     // 라이브러리를 여러 개 사용하는 것과 같이, 먼저 읽은 Android.mk 의 LOCAL_XXX 값이 중복되어 사용되는 것을 방지하기 위해 반드시 정의한다 
 
LOCAL_MODULE    := helloandroid    // 모듈 이름을 지정한다. Android.mk 에 반드시 설정해줘야 하는 변수이다. 이 이름은 중복되어서도 안되고, 빈 공간을 포함해서도 안된다.
                                               // NDK 빌드 시스템은 여기에서 주어진 이름에 접두사와 접미사를 자동으로 부여하며 libHelloNDK.so 공유 라이브러리를 생성한다(전체 소스에서는 지정한 이름 그대로 생성된다).
                                     // 만약 여기서 libHelloNDK 이름을 지정하면, liblibHelloNDK.so 가 아니라 libHelloNDK.so 를 공유라이브러리로 생성한다. 이것은 원본 안드로이드 플랫폼 소스의 Android.mk 를 지원하기 위함이다.
 
LOCAL_MODULE_TAGS  := eng   # lunch insignal_origen-eng
 
LOCAL_SRC_FILES := helloandroid.c   // C 또는 C++ 소스 파일 목록을 지정한다. 헤더는 포함하지 않는다. C++ 소스파일의 확장명은 .cpp 가 기본이다. 
 
LOCAL_C_INCLUDE := 
 
include $(BUILD_SHARED_LIBRARY)  // BUILD_SHARED_LIBRARY include $(CLEAR_VARS)를 선언한 이후의 LOCAL_XXX 변수를 모두 모아서 적용하고 빌드할 것을 결정한다.
                                                     // BUILD_STATIC_LIBRARY 변수를 사용해서 정적 라이브러리를 만들 수도 있다. 
                                                     // NDK 에서는 결과물은 so 로만 나온다. BUILD_STATIC_LIBRARY 로 지정한다고해서 .a 파일이 생성되는 것은 아니다. 단지 Shared Object 를 만들기 위한 정적라이브러리를 지원한다

위에서 선언한 것은 Android.mk 를 구성하는 최소한의 요소이며 Android.mk 파일을 구성하는데 반드시 정의되어 있어야 네이티브 라이브러리 빌드가 가능하다.
이제 소스코드(helloandroid.c)를 작성할 차례다.

#include <stdio.h>
 
char * krrr_func(){
    printf("Krrrrrrrrrrrr ..... :-) \n");
    return "chlrbgh0";
}
 
int main()
{
    printf("Hello, Android!!  \n");
    printf("Hello %s!!\n", krrr_func());
    return 0;
}

이제 빌드해보자.

#mm
Import includes file: out/target/product/arndale/obj/SHARED_LIBRARIES/helloandroid_intermediates/import_includes
target thumb C: helloandroid <= external/helloandroid/helloandroid.c
target SharedLib: helloandroid (out/target/product/arndale/obj/SHARED_LIBRARIES/helloandroid_intermediates/LINKED/helloandroid.so)      // shared library 생성
target Symbolic: helloandroid (out/target/product/arndale/symbols/system/lib/helloandroid.so)
Export includes file: external/helloandroid/Android.mk -- out/target/product/arndale/obj/SHARED_LIBRARIES/helloandroid_intermediates/export_includes
target Strip: helloandroid (out/target/product/arndale/obj/lib/helloandroid.so)                        // shared library strip
Install: out/target/product/arndale/system/lib/helloandroid.so                              // library 설치 경로
make: Leaving directory `/root/WORKING_DIRECTORY'

helloandroid.so 라는 공유 라이브러리가 생성되었다.

앞서 Android.mk 파일에서 아래의 부분을 수정하여 다시 빌드해보자!

#include $(BUILD_SHARED_LIBRARY)   // 주석처리
include $(BUILD_STATIC_LIBRARY)

수정한 것은 shared library 가 아닌 static library 를 선언한 것이다.

#mm
Import includes file: out/target/product/arndale/obj/STATIC_LIBRARIES/helloandroid_intermediates/import_includes
target thumb C: helloandroid <= external/helloandroid/helloandroid.c
Export includes file: external/helloandroid/Android.mk -- out/target/product/arndale/obj/STATIC_LIBRARIES/helloandroid_intermediates/export_includes
target StaticLib: helloandroid (out/target/product/arndale/obj/STATIC_LIBRARIES/helloandroid_intermediates/helloandroid.a)

정적 라이브러리 helloandroid.a 파일이 생성되었다.

앞서 Android.mk 파일에서 아래의 부분을 수정하여 다시 빌드해보자!

#include $(BUILD_SHARED_LIBRARY)
include $(BUILD_EXECUTABLE)

실행가능한 바이너리를 생성하도록 선언했다.

#mm
Import includes file: out/target/product/arndale/obj/EXECUTABLES/helloandroid_intermediates/import_includes
target thumb C: helloandroid <= external/helloandroid/helloandroid.c
target Executable: helloandroid (out/target/product/arndale/obj/EXECUTABLES/helloandroid_intermediates/LINKED/helloandroid)
target Symbolic: helloandroid (out/target/product/arndale/symbols/system/bin/helloandroid)
Export includes file: external/helloandroid/Android.mk -- out/target/product/arndale/obj/EXECUTABLES/helloandroid_intermediates/export_includes
target Strip: helloandroid (out/target/product/arndale/obj/EXECUTABLES/helloandroid_intermediates/helloandroid)
Install: out/target/product/arndale/system/bin/helloandroid

또한 빌드 옵션에 따라 컴파일 소스를 선택하고 싶을 때는 다음과 같이 정의해서 사용할 수도 있다. 하지만 LOCAL_ 이나 PRIVATE_, NDK_, APP_ 등은 빌드시스템에 예약된 이름일 수 있으니 MY_ 등으로 시작하는 이름을 짓는 것이 좋다.
Android.mk 파일을 아래와 같이 수정한다.

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
 
 
MY_SOURCES := helloandroid.c
    ifneq ($(MY_CONFIG_BAR),)             // MY_CONFIG_BAR 변수가 선언되면, in.c 파일을 추가한다
        MY_SOURCES += in.c
    endif
 
    LOCAL_SRC_FILES += $(MY_SOURCES)
 
LOCAL_MODULE := helloandroid
LOCAL_MODULE_TAGS := eng # lunch insingal_origen-eng
LOCAL_C_INCLUDES := frameworks/base/include \
                   system/core/include
#LOCAL_CFLAGS := -G
include $(BUILD_SHARED_LIBRARY)
#include $(BUILD_STATIC_LIBRARY)

그리고 in.c 파일을 추가하고 아래와 같이 작성한다.

#include <stdio.h>
 
void last(void)
{
    printf("this is last\n");
}

마지막으로 helloandroid.c 에 다음을 추가한다.

#include <stdio.h>
 
extern void last(void);           // 추가
 
char * krrr_func(){
    printf("Krrrrrrrrrrrr ..... :-) \n");
    return "chlrbgh0";
}
 
int main()
{
    printf("Hello, Android!!  \n");
    printf("Hello %s!!\n", krrr_func());
    last();
    return 0;
}

이제 빌드해보자!

#mm  -B
make: Entering directory `/root/WORKING_DIRECTORY'
Import includes file: out/target/product/arndale/obj/SHARED_LIBRARIES/helloandroid_intermediates/import_includes
target thumb C: helloandroid <= external/helloandroid/helloandroid.c
target SharedLib: helloandroid (out/target/product/arndale/obj/SHARED_LIBRARIES/helloandroid_intermediates/LINKED/helloandroid.so)
/root/WORKING_DIRECTORY/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.6/bin/../lib/gcc/arm-linux-androideabi/4.6.x-google/../../../../arm-linux-androideabi/bin/ld: out/target/product/arndale/obj/SHARED_LIBRARIES/helloandroid_intermediates/helloandroid.o: in function main:external/helloandroid/helloandroid.c:14: error: undefined reference to 'last'
collect2: ld returned 1 exit status
make: *** [out/target/product/arndale/obj/SHARED_LIBRARIES/helloandroid_intermediates/LINKED/helloandroid.so] 오류 1
make: Leaving directory `/root/WORKING_DIRECTORY'

last 함수를 참조할 수 없다고 에러가 발생했다.
이번에는 아래와 같이 빌드해보자!

#MY_CONFIG_BAR=1 mm -B
make: Entering directory `/root/WORKING_DIRECTORY'
Import includes file: out/target/product/arndale/obj/SHARED_LIBRARIES/helloandroid_intermediates/import_includes
target thumb C: helloandroid <= external/helloandroid/helloandroid.c
target thumb C: helloandroid <= external/helloandroid/in.c
target SharedLib: helloandroid (out/target/product/arndale/obj/SHARED_LIBRARIES/helloandroid_intermediates/LINKED/helloandroid.so)
target Symbolic: helloandroid (out/target/product/arndale/symbols/system/lib/helloandroid.so)
Export includes file: external/helloandroid/Android.mk -- out/target/product/arndale/obj/SHARED_LIBRARIES/helloandroid_intermediates/export_includes
target Strip: helloandroid (out/target/product/arndale/obj/lib/helloandroid.so)
Install: out/target/product/arndale/system/lib/helloandroid.so
make: Leaving directory `/root/WORKING_DIRECTORY'

에러없이 빌드된 것을 볼 수 있다.

PREBUILT_SHARED_LIBRARY 미리 컴파일된 공유 라이브러리를 지정한다
PREBUILT_STATIC_LIBRARY 미리 컴파일된 정적 라이브러리를 지정한다
TARGET_ARCH 타겟의 CPU 아키텍처 이름을 지정한다. 안드로이드 전체 소스에 기술되어 있다. 여기에서 'arm' 이라고만 하면 모든 arm 에서 호환된다
TARGET_PLATFORM 대상 안드로이드 플랫폼을 정의한다. android-3 라고 하면 안드로이드 1.5 버전을 지정한다
TARGET_ARCH_ABI armeabi 와 arm v7 을 위한 armeabi-v7a 를 지정할 수 있다. arm v7 은 VFP hardware FPU 명령이 있고 thmb-2 명령어셋 확장이 가능하다. armv7 으로 컴파일하면 하위 호환되지 않을 수 있다
TARGET_ABI 실제로 $(TARGET_PLATFORM)-$(TARGET_ARCH_ABI) 와 같이 지정된다. 기본값으로는 android-3-armeabi 가 된다
LOCAL_CPP_EXTENSION C소스 파일 확장명을 지정한다. 기본 확장명은 cpp 다 소스 파일 컴파일 플래그를 지정한다
LOCAL_CPPFLAGS C 소스파일과 C++ 소스 파일 모두를 위해 컴파일 플래그를 지정한다
LOCAL_STATIC_LIBRARY BUILD_STATIC_LIBRARY 에서 지정한 모듈에 연결하고 싶은 것을 목록으로 지정한다. 이 사양은 공유 라이브러리를 만드는 경우에만 지정할 수 있다
LOCAL_SHARED_LIBRARY 이 모듈을 실행할 때 참조되는 공유 라이브러리다
LOCAL_LDLIBS 라이브러리를 빌드할 때 필요한 추가 링크 플래그를 지정한다. '-l<library>' 에서 링커에 지시하는 방법은 동일하다
LOCAL_ALLOW_UNDEFINED_SYMBOLS 기본 동작으로 정의되지 않는 무언가를 참조하는 경우 공유 라이브러리를 만들 때 'undefined symbol' 에러가 발생한다. 어떤 이유로든 오류를 생성하지 않으려면, 이 정의를 true 로 설정한다
LOCAL_ARM_MODE ARM target 바이너리는 기본적으로 thumb 모드로 생성된다. 하지만 이 변수를 arm 으로 지정하면 32bit 명령어 형태로 만들 수 있다(LOCAL_ARM_MODE := arm). 또한 원하는 코드만 arm 으로 컴파일할 수 있는데 파일이름 뒤에 '.arm' 을 붙여주면 된다(LOCAL_SRC_FILES := foo.c bar.c.arm)
LOCAL_ARM_NEON 이 값을 true 로 하면 ARM Advanced SIMD(a.k.a.NEON) 가 활성화된다. 이 옵션은 반드시 'armeabi-v7a' 타겟이 지정되어야만 한다. 이것 역시 파일 하나하나에 지정할 수 있고 파일 이름 뒤에 '.neon' 을 붙여주면 된다. 예를 들자면 다음과 같이 사용할 수 있다(LOCAL_SRC_FILES = foo.c.neon bar.c zoo.c.arm.neon). foo.c 는 thumb+neon 모드, bar.c 는 thumb 모드, zoo.c 는 arm+neon 모드로 컴파일된다
  • computer/embedded/안드로이드_핵심가이드_-_4.makefile_이해하기.txt
  • Last modified: 4 years ago
  • by likewind