APK Build Environment

APK Build Environment

- How to Fix Windows React Native APK Path Length Limitations -

1. Summary

On Windows, building an Android APK for an Expo CNG-based React Native app may fail as follows.

ninja: error: Stat(...): Filename longer than 260 characters

This issue is caused not by the app code, but by Windows path length limitations and the path structure of native build artifacts generated by CMake/Ninja.

The three recommended solutions are as follows.

  1. Create a Junction at the project root to shorten the path used to run the build.

  2. Move CMake's buildStagingDirectory to a short absolute path.

  3. Delete the existing .cxx cache and perform a full rebuild.

Junction 생성 → buildStagingDirectory 지정 → android/app/.cxx 삭제 → 재빌드

subst virtual drives can shorten paths, but they are not recommended because Metro, realpath, and Gradle cache paths can become mixed and cause other problems.

2. Cause

The general Win32 path length limit on Windows is MAX_PATH, which is 260 characters. Even if Windows Long Path support is enabled, tools that are not prepared to use that feature may still fail when handling long paths.

React Native's New Architecture or automatically linked native modules build C++ code through CMake and Ninja. In this process, object file paths can become extremely long.

<빌드 스테이징 경로>/<빌드 타입>/<해시>/<ABI>/
<target>_autolinked_build/CMakeFiles/<target>.dir/
<원본 소스 경로를 포함한 경로>

In particular, long C++ source paths under node_modules are included again in the object file paths. When the project path is deeply nested and native packages such as react-native-screens, safe-area-context are added, the limit of 260 characters can easily be exceeded.

The following are examples of the longest object file paths for each staging location.

Build path

Longest object path

Result

Regular project path + default .cxx

331 characters

Failure

Junction path + default .cxx

290 characters

Failure

Junction path + short staging path

248 characters

Success

Shortening only the project root is not enough; the location of CMake's intermediate artifacts must also be shortened.

3. Implementation

3.1 Creating a Junction

If the project is located in a long path, link the monorepo root to a short path using a Junction.

cmd /c mklink /J C:\v "C:\Users\<사용자>\<긴 경로>\<프로젝트 루트>"

Run the native build from the Junction path afterward.

C:\v\<프로젝트 경로>

A Junction operates as an actual directory entry on the same volume. substUnlike a virtual drive root created separately by , it provides more consistent file system path resolution.

3.2 Shortening the CMake staging path

Add the following setting to the android {} block in android/app/build.gradle.

externalNativeBuild {
    cmake {
        buildStagingDirectory = file("C:/cxx-build")
    }
}

buildStagingDirectoryspecifies the location where CMake external native build artifacts and Ninja-related files are generated. Instead of the default .cxx path, use a shorter root path to reduce the total length of object file paths.

3.3 Delete the Existing CMake Cache

After changing the path settings, be sure to delete the existing cache.

rm -rf android/app/.cxx

In Windows PowerShell, you can run the following command.

Remove-Item -Recurse -Force android\app\.cxx

The CMake cache may contain the canonical paths from the initial configuration. If you do not delete the cache, the previous long paths may continue to be used even after applying the Junction and shorter staging paths.

3.4 Run the Build

Run the native build from the Junction path.

.\gradlew.bat assembleRelease --max-workers=2

Before a release build, it is recommended to shut down Metro and any web development servers, because memory contention can cause Gradle Worker Daemon errors.

4. Operating Principles and Precautions

Use Junction instead of subst

substcan map a path to a short drive letter, but when the virtual drive path and the actual C: path are used together, the following problems may occur.

  • Different root path errors in Codegen

  • Failure to resolve workspace packages or aliases in Metro

  • Conflicts with canonical paths stored in the Gradle global cache

  • The mapping disappears after a reboot and must be configured again each time

Therefore, use Junction for path shortening.

Run Metro from the canonical path

Run native Gradle builds from the Junction path, but run Metro from the canonical project path.

Task

Execution path

Native C++ build (gradlew, expo run:android)

Junction path

Metro bundler (expo start)

Canonical C: path

Some Expo/React Native projects rely on TypeScript paths, Babel- or Metro-configuration-based aliases, and workspace package resolution. Running Metro from the Junction path can cause the project root and the realpath of files to differ, resulting in alias or workspace package resolution problems.

Recognize that these are Expo CNG-generated files

android/ directory is generated by Expo CNG. If you run expo prebuild --clean, the android/app/build.gradle setting added directly to buildStagingDirectory may be removed.

This setting only changes the path for intermediate artifacts in Windows local builds and does not affect the resulting APK. Therefore, it can be managed by having the person responsible for native builds on Windows apply it manually.

If automation is needed in the long term, consider a Gradle Property-based opt-in approach that allows only Windows users to provide the value selectively. Do not force an absolute path into all operating systems and EAS builds through a Config Plugin.

5. Separating the Development Environment and Deployment

Java and Android Studio

When opening the android/ project in Android Studio, if Gradle Sync fails, check the compatibility between the project's Gradle Wrapper, Android Gradle Plugin, and Kotlin plugin combination and the JDK version.

Use the JDK version verified for the project as the Gradle JDK. For example, in an environment using Java 21, you can configure it as follows.

$env:JAVA_HOME = "C:\Program Files\Microsoft\jdk-21"
$env:ANDROID_HOME = "C:\Users\<사용자>\AppData\Local\Android\Sdk"

Android Studio's android/ folder is a CNG-generated artifact, not permanent source code. Use Android Studio to check native logs and Gradle status, and place settings that need to persist in app.json or a Config Plugin.

Debug APK and Release APK

Category

Debug

Release

JS bundle

Loaded in real time from Metro

Included inside the APK

Execution conditions

Requires access to Metro on the development PC

Can run independently

Purpose

Local development and debugging

Distribution to colleagues and testers

Build time

Relatively short

Can be longer because all ABIs are included

APK artifacts can generally be found at the path below.

android/app/build/outputs/apk/debug/app-debug.apk
android/app/build/outputs/apk/release/app-release.apk

Hybrid apps often load most screens from a server through a WebView.

  • Web screen changes: Fully close and relaunch the app after deploying the web content

  • RN shell changes: Changes to the tab bar, deep links, bridges, native SDKs, and so on require rebuilding and reinstalling the APK

Justin

Site footer