독립 실행형 툴체인

Android NDK와 함께 독립적으로 제공되거나 기존 IDE와 함께 플러그인으로 제공되는 툴체인을 사용할 수 있습니다. 이미 자체적인 빌드 시스템이 있고 이 시스템을 위한 Android에 지원을 추가하기 위해 교차 컴파일러를 호출하는 기능만 필요한 경우 이러한 유연성이 유용할 수 있습니다.

툴체인 선택

무엇보다도 먼저, 독립 실행형 툴체인이 대상으로 삼을 프로세서 아키텍처를 결정해야 합니다. 이 작업은 --arch 플래그로 수행됩니다.

Sysroot 선택

다음에 할 일은 sysroot를 정의하는 것입니다. sysroot는 대상에 대한 시스템 헤더 및 라이브러리를 담고 있는 디렉토리입니다. sysroot를 정의하려면 네이티브 지원을 위해 대상으로 삼으려는 Android API 레벨을 알아야 합니다. 사용 가능한 네이티브 API는 Android API 레벨에 따라 다릅니다.

Android API 레벨에 대한 네이티브 API의 라이브러리는 $NDK/platforms/ 아래에 있고, 각 API 레벨 디렉토리는 다양한 CPU와 아키텍처에 대한 하위 디렉토리를 포함합니다. 헤더는 $NDK/sysroot에 있습니다.

Android API 레벨과 이러한 레벨에서 지원하는 각 네이티브 API에 대한 자세한 내용은 네이티브 API를 참조하세요.

툴체인 생성

NDK는 명령줄에서 사용자 지정 툴체인 설치를 수행할 수 있는 make_standalone_toolchain.py 스크립트를 제공합니다.

이 스크립트는 이전의 make-standalone-toolchain.sh를 대체하는 새로운 도구입니다. 이 도구는 Windows 사용자가 실행을 위해 Cygwin 또는 MSYS를 설치하지 않아도 되도록 Python에 다시 구현되었습니다.

이 스크립트는 $NDK/build/tools/ 디렉토리에 있으며, 여기서 $NDK는 NDK의 설치 루트 디렉토리입니다.

스크립트의 사용 예는 아래에 나와 있습니다.

$NDK/build/tools/make_standalone_toolchain.py \
    --arch arm --api 21 --install-dir /tmp/my-android-toolchain

이 명령어를 실행하면 /tmp/my-android-toolchain/이라는 이름의 디렉토리가 생성되고, 이 디렉토리에는 android-21/arch-arm sysroot의 복사본과 32비트 ARM 대상 툴체인 바이너리의 사본이 포함됩니다.

툴체인 바이너리는 호스트별 경로에 종속되지 않거나 이러한 경로를 포함하지 않습니다. 즉, 어떤 위치에든 이러한 바이너리를 설치하거나, 필요하다면 이동할 수도 있습니다.

--arch 인수는 필수이지만, API 레벨은 주어진 아키텍처에 대한 최소 지원 레벨로 기본 설정됩니다(현재 32비트 아키텍처는 레벨 16, 64비트 아키텍처는 레벨 21).

r18 이후로 모든 독립 실행형 툴체인은 Clang 및 libc++를 사용합니다. 정적 실행 파일을 빌드하지 않는 이상 libc++ 공유 라이브러리가 기본으로 사용됩니다. 정적 라이브러리 사용을 강제 적용하려면 링크 시 -static-libstdc++를 전달하세요. 이 동작은 일반 호스트 툴체인의 동작과 일치합니다.

C++ 라이브러리 지원에서 언급한대로, libc++에 대해 링크할 때는 -latomic를 전달해야 하는 경우가 있습니다.

참고로 --install-dir 옵션을 빠뜨리면 도구가 $TOOLCHAIN_NAME.tar.bz2라는 현재 디렉토리에 tarball을 생성합니다. 이 tarball을 다른 디렉토리에 배치하려면 --package-dir을 사용하면 됩니다.

추가적인 옵션과 세부정보를 보려면 --help를 사용하세요.

Clang을 사용한 작업

Clang 바이너리는 자동으로 독립 실행형 툴체인에 포함됩니다.

래퍼 스크립트도 두 개 있습니다. 하나는 clang이고 다른 하나는 clang++로, 이들은 <install-dir>/bin 아래에 있습니다. 이들 스크립트는 올바른 아키텍처 플래그를 포함한 clang 바이너리를 호출합니다. 다시 말해, 위 두 스크립트는 어떤 수정 없이도 작동해야 하고, 이러한 스크립트를 포인팅하도록 CCCXX 환경 변수를 설정하는 것만으로도 빌드에서 두 스크립트를 사용할 수 있어야 합니다.

Clang을 호출하는 gccg++라는 또 다른 래퍼 스크립트도 있습니다. 이것은 NDK에 GCC가 없더라도 GCC를 명시적으로 참조하는 빌드 파일에 일정 수준의 호환성을 제공하기 위해서입니다. 당연히 빌드 파일이 Clang에서 지원되지 않는 명령 줄 옵션을 사용한다면 이를 삭제하거나 교체해야 합니다.

ARM이 있는 Clang 대상

ARM용으로 빌드할 때, Clang은 -march=armv7-a 및/또는 -mthumb 컴파일러 플래그의 존재 여부에 따라 대상을 변경합니다.

표 1. 지정 가능한 -march 값과 결과 대상

-march 결과 대상
-march=armv7-a armv7-none-linux-androideabi
-mthumb thumb-none-linux-androideabi
-march=armv7-a-mthumb 모두 thumbv7-none-linux-androideabi

원한다면 자신의 -target으로 재정의할 수도 있습니다.

clangclang++는 메이크파일에서 별도의 구성이나 변경 없이 gccg++를 대체할 수 있어야 합니다. 의구심이 드는 경우에는 컴파일러를 시작할 때 다음 옵션을 사용하여 올바르게 작동하고 있는지 확인하세요.

  • -v: 컴파일러 드라이버 문제와 관련된 명령어 덤프
  • -###: 명시적으로 미리 정의된 것을 포함한 명령 줄 옵션 덤프
  • -x c < /dev/null -dM -E: 미리 정의된 전처리기 정의 덤프
  • -save-temps: *.i 또는 *.ii 전처리 파일 비교

ABI 호환성

기본적으로 ARM Clang 독립 실행형 툴체인은 armeabi-v7a ABI를 대상으로 합니다. 기본값은 적절한 -march 또는 -target 옵션을 전달하여 재정의할 수 있습니다.

16비트 Thumb-2 명령 생성을 강제하려는 경우, -mthumb 컴파일러 플래그를 사용하는 것을 권장합니다. 이 항목이 누락되면 툴체인이 32비트 ARM 명령을 방출합니다.

NEON 명령을 사용하려면 -mfpu 컴파일러 플래그인 -mfpu=neon을 사용해야 합니다.

이 설정은 ARM 사양에 따라 VFPv3-D32의 사용을 강제 적용합니다.

또한, 링커에 다음 두 개의 플래그를 제공해야 합니다. -march=armv7-a -Wl,--fix-cortex-a8

첫 번째 플래그는 링커가 armv7-a에 맞춤화된 툴체인 라이브러리를 선택하도록 지시합니다. 몇몇 Cortex-A8 구현에서는 CPU 버그에 대한 해결 방법으로 두 번째 플래그가 필요합니다.

다른 ABI를 대상으로 할 때는 특정 컴파일러 플래그를 사용할 필요가 없습니다.

ABI 지원에 대해 자세히 알아보려면 ABI를 참조하세요.

경고 및 제한 사항

Windows 지원

Windows 바이너리는 Cygwin에 종속되지 않습니다. 이처럼 종속성이 없으므로 Windows 바이너리의 속도가 더 빨라집니다. 하지만 C:/foo/bar와는 반대로 cygdrive/c/foo/bar같은 Cygwin 경로 사양을 알 수 없다는 단점이 있습니다.

예외, RTTI, STL

툴체인 바이너리는 기본적으로 C++ 예외와 RTTI를 지원합니다. 소스를 빌드할 때 C++ 예외와 RTTI를 비활성화하려면(예: 더욱 가벼운 기계어 코드를 생성하려는 경우) -fno-exceptions-fno-rtti를 사용하세요.

C++ STL 지원

독립 실행형 툴체인은 C++ 표준 템플릿 라이브러리(STL) 구현을 포함합니다.

  • libc++의 정적 라이브러리 버전을 가져오려면 -static-libstdc++를 사용하세요. 그렇게 하면 필요한 C++ STL 코드가 전부 최종 바이너리에 포함됩니다. 이 방법은 단일 공유 라이브러리나 실행 파일만 생성하려는 경우에 이상적이며, 권장되는 방법입니다.

  • libc++의 공유 라이브러리 버전이 기본값으로 사용됩니다. 이 공유 라이브러리에 대해 링크할 때는 별도의 플래그가 필요하지 않습니다. libc++_shared.so를 앱에 패키징해야 합니다. 그렇지 않으면 코드가 로드되지 않습니다.

    표 2는 각 아키텍처에 대한 이 파일의 위치를 보여줍니다.

    표 2. 지정 가능한 -march 값과 결과 대상.

    툴체인 위치
    arm $TOOLCHAIN/arm-linux-androideabi/lib/
    arm64 $TOOLCHAIN/aarch64-linux-android/lib/
    x86 $TOOLCHAIN/i686-linux-android/lib/
    x86_64 $TOOLCHAIN/x86_64-linux-android/lib/

독립 실행형 툴체인을 사용하여 오픈소스 프로젝트 빌드하기

다음 예시 툴체인이 주어진 경우를 생각해 보겠습니다.

# Create an arm64 API 26 libc++ toolchain.
$NDK/build/tools/make_standalone_toolchain.py \
  --arch arm64 \
  --api 26 \
  --install-dir=my-toolchain

다음은 이 툴체인을 사용하여 기존의 오픈소스 프로젝트를 빌드하기 위해 환경을 설정하는 방법입니다.

# Add the standalone toolchain to the search path.
export PATH=$PATH:`pwd`/my-toolchain/bin

# Tell configure what tools to use.
target_host=aarch64-linux-android
export AR=$target_host-ar
export AS=$target_host-clang
export CC=$target_host-clang
export CXX=$target_host-clang++
export LD=$target_host-ld
export STRIP=$target_host-strip

# Tell configure what flags Android requires.
export CFLAGS="-fPIE -fPIC"
export LDFLAGS="-pie"

맞춤 빌드 시스템이 있는 프로젝트

예를 들어, 이전 단계를 수행한 후 Toybox를 빌드하는 방법은 다음과 같습니다.

git clone https://github.com/landley/toybox.git
cd toybox
make defconfig && make

autoconf를 사용한 프로젝트

한편, autoconf 기반 프로젝트는 다음과 같습니다.

tar zxvf make-4.2.tar.gz
cd make-4.2
./configure --host=$target_host && make

autoconf 기반 프로젝트는 교차 컴파일에 대한 지원이 매우 다양하다는 점에 유의해야 합니다. 또한, autoconf 기반 프로젝트를 git clone하는 경우, 체크인된 configure 스크립트가 있을 가능성이 낮습니다. 따라서 해당 프로젝트의 문서를 따라 부트스트랩해야 합니다.