A module for generation of ndk-build Android.mk files for Premake 5.
- Providing the same level of multi-ABI building capabilities as hand-written Android.mk files, allowing for building for multiple ABIs from one ndk-build invocation and supporting ABI filtering for most of the project settings.
- Supporting all languages accepted by ndk-build — C, C++, the GNU Assembler, Yasm, RenderScript, as well as prebuilt libraries and external Android.mk files.
- Focused on supporting as many Premake project settings as possible, with attention to coverage of edge cases such as allowed characters.
- Preferring exposing Android.mk settings through existing Premake project settings over adding new ones where semantically appropriate to allow porting of projects from other targets with minimal changes.
Available under the Unlicense, or, similar to Premake, the BSD 3-Clause “New” or “Revised” License.
Clone or download the module, and require("path/to/premake-androidndk/androidndk")
in your workspace's Premake script. Use the androidndk
action to perform generation.
All projects — which represent ndk-build modules — in the application must be located in a single workspace, as a workspace corresponds to an Application.mk file and the root Android.mk.
Add Android platform definitions to the platforms
of your workspace or the projects in it. This is especially important in two cases — if your workspace targets other operating systems alongside Android, and if you want to specify ABI-specific settings. Using configurations
for this purpose also works, but generally it's recommended to use the configuration axis for build flavors such as debug/release, while doing all platform and architecture filtering via the platform axis — and due to the nature of specifying platforms and configurations to be built by ndk-build for Android.mk files generated by this module, mixing the two may result in complicated setup and unnecessary duplication of arguments you'll need to pass to ndk-build.
Specify the system
as "android"
in your Android platforms and configurations. While this module doesn't check the value of system
anywhere internally, if it's not specified, Premake will assume that the host OS is the target, which may be undesirable — for instance, build libraries may not have the "lib"
prefix if the system
is selected as "windows"
.
If you need to specify settings that apply only to certain ABIs, you can specify separate platforms
(or configurations
) with different architecture
values. The supported architectures are "ARM"
(corresponds to the armeabi-v7a
ABI), "ARM64"
(arm64-v8a
ABI), "x86"
(x86
ABI) and "x86_64"
(x86_64
ABI).
For example:
configurations({"Debug", "Release")
platforms({"Windows-x86_64", "Android-ARM64", "Android-x86_64"})
filter({"platforms:Windows-*"})
system("windows")
systemversion("10.0")
filter({"platforms:Android-*"})
system("android")
systemversion("24")
cppstl("c++")
filter({"platforms:*-x86_64"})
architecture("x86_64")
filter({"platforms:*-ARM64"})
architecture("ARM64")
filter({})
It's also possible to specify both architecture-specific and architecture-agnostic (with a nil or "universal"
architecture) platforms in the same project. In this case, the architecture specialization will be chosen when ndk-build is building for an ABI for which one available, and the architecture-independent settings will be used as a fallback if one is not:
platforms({"AndroidOther", "AndroidARMv7", "AndroidARMv8"})
filter({"platforms:AndroidARMv7"})
architecture("ARM")
filter({"platforms:AndroidARMv8"})
architecture("ARM64")
filter({"architecture:ARM or ARM64"})
files({"neonmath.cpp"})
filter({})
-- `armeabi-v7a` ABI will use the AndroidARMv7 platform with neonmath.cpp.
-- `arm64-v8a` ABI will use the AndroidARMv8 platform with neonmath.cpp.
-- `x86` and `x86_64` ABIs will use the AndroidOther platform without neonmath.cpp.
Note that deprecated ABIs such as armeabi
, armeabi-v7a-hard
, mips
, mips64
are not supported. armeabi-v7a
builds by default will have NEON enabled and will use the Thumb instruction set for release configurations, this can be changed via settings listed in the section below.
It is important to consider that Premake settings such as architecture
are assigned to configuration–platform pairs. So, if you want to use filter({"architecture:"})
, you must create a platform or a configuration for that architecture, otherwise it will not work.
To exclude a project from building for certain ABIs, you can set its kind
to None
with the required filter.
Most settings allow GNU make variable expansion and function call passthrough, so you can use environment variables, or variables and functions provided by ndk-build like $(TARGET_ARCH_ABI)
, in your projects, and they will be expanded at build time. You need to be careful not to reference variables or functions that may result in disallowed characters in the specific context, such as whitespaces in settings that end up being written to a list variable, however — it's not possible for the module to validate the value in this case. To use the dollar sign as a character, specify $$
instead. Paths starting with a dollar sign (even if it's $$
denoting an escaped raw $
character) are treated as absolute by Premake, allowing for the usage of paths relative to directories such as $(NDK_ROOT)
. If you want to override this behavior, explicitly prefix the path with ./
.
Normally, this module provides functionality for building of projects from source code.
However, it also exposes the prebuilt library functionality of ndk-build. This is the recommended way of including non-system library binaries in the workspace and linking projects against them, as it lets ndk-build properly ensure that the library is copied into the destination directory properly and that it's loaded from the correct path. ndk-build will generate warnings if the project is linked against non-system libraries via system links
or via linkoptions
.
In addition, it's also possible to create a project that will use an external Android.mk file instead of its own settings (certain settings still must be correctly specified for linkage purposes, however).
To create a project using a prebuilt library or an external Android.mk file, specify just one .a
, .so
or .mk
file and no other source files in the files
setting for the desired configurations/platforms. Also, you must specify the correct kind
of the project — "SharedLib"
for .so
, "StaticLib"
for .a
, or the kind that matches the actual build script included in the external Android.mk.
Projects using an external Android.mk files have special rules regarding their usage, specifically:
- The external Android.mk must contain settings for only at most one project (module).
- The
kind
of the project must match the build script used in the external Android.mk —"SharedLib"
forBUILD_SHARED_LIBRARY
orPREBUILT_SHARED_LIBRARY
,"StaticLib"
forBUILD_STATIC_LIBRARY
orPREBUILT_STATIC_LIBRARY
,"ConsoleApp"
forBUILD_EXECUTABLE
. Do not use the"Makefile"
kind — it has a completely different purpose, and is not supported by this module. The module must know the actualkind
for linkage. - The name of the project must be the same as
LOCAL_MODULE
in the external Android.mk.
The following settings have effect when used in a prebuilt library project or an external Android.mk project:
configurations
flags
"ExcludeFromBuild"
"LinkTimeOptimization"
(for static libraries, if any object files in it are built with-flto
)
linkoptions
(the only supported options are-u
or-Wl,--undefined
exported from static libraries)links
(must matchLOCAL_SHARED_LIBRARIES
andLOCAL_STATIC_LIBRARIES
/LOCAL_WHOLE_STATIC_LIBRARIES
for external Android.mk projects referencing other Premake projects, external or not; also, for static libraries, must include the system libraries fromLOCAL_EXPORT_LDLIBS
)location
(orbasedir
if not provided)kind
platforms
project
(must matchLOCAL_MODULE
in the external Android.mk)wholelib
For example, to add the Android Native App Glue to your workspace, you can create a project with the following settings:
project("android_native_app_glue")
kind("StaticLib")
files({"$(NDK_ROOT)/sources/android/native_app_glue/Android.mk"})
links({"log", "android"})
linkoptions({"-u ANativeActivity_onCreate"})
To link projects with RenderScript sources, you may need to add some of these prebuilt library projects to your workspace (most importantly "RScpp_static"
) and to specify them in links
of the projects that use RenderScript:
project("RSSupport")
kind("SharedLib")
files({"$(RENDERSCRIPT_TOOLCHAIN_PREBUILT_ROOT)/platform/$(TARGET_ARCH)/libRSSupport.so"})
project("RSSupportIO")
kind("SharedLib")
files({"$(RENDERSCRIPT_TOOLCHAIN_PREBUILT_ROOT)/platform/$(TARGET_ARCH)/libRSSupportIO.so"})
project("blasV8")
kind("SharedLib")
files({"$(RENDERSCRIPT_TOOLCHAIN_PREBUILT_ROOT)/platform/$(TARGET_ARCH)/libblasV8.so"})
project("RScpp_static")
kind("StaticLib")
files({"$(RENDERSCRIPT_TOOLCHAIN_PREBUILT_ROOT)/platform/$(TARGET_ARCH)/libRScpp_static.a"})
The basic setup is different depending on whether you're using the externalNativeBuild
functionality of Gradle to build the application.
The module generates an Application.mk file containing basic settings for the entire application, with some support for Premake configuration and platform filtering that may be useful, for instance, for choosing APP_DEBUG
and APP_OPTIM
based on the selected configuration. However, as ABI filtering is not available in Application.mk, it will be more coarse than for settings that go to Android.mk — it is assumed that all Application.mk-level settings should not depend on the target ABI, you can see the exact list of the settings that go to Application.mk in the list of supported project settings.
If you're launching ndk-build as a custom task, or not using Gradle at all, you need to specify the path to the Application.mk in the ndk-build command arguments, as NDK_APPLICATION_MK:=path/to/WORKSPACE_NAME.Application.mk
, where WORKSPACE_NAME
is the name of the Premake workspace for the application. The Application.mk file is written to the location
directory of the workspace.
Gradle's externalNativeBuild
, however, causes most Application.mk settings to be ignored and overridden by the settings specified in the Gradle script itself. Because of this, using the Application.mk file generated by this module may not be absolutely necessary, but for correctness, you should still set the NDK_APPLICATION_MK
variable in ndk-build arguments, otherwise it will try to use the Application.mk from the default path — it's better to express the intentions explicitly than to rely on the file not currently existing. However, you'll need to specify the correct settings in the Gradle script manually anyway. Because Gradle scripts contain settings not only for NDK, but also for all aspects of the application, and since the layout of configurations — build types and product flavors — may vary greatly between applications, Gradle setup is out of the scope of this module.
Here is an example of configuring the application settings relevant to the usage of files generated by this module:
android {
// The Android NDK version to build the application with.
// If omitted, the latest installed version will be used.
ndkVersion '23.0.7599858'
// Per-configuration Gradle settings.
// May be specified for defaultConfig, as well as for buildTypes and productFlavors.
defaultConfig {
// Minimum Android SDK version supported by the application.
// Corresponds to APP_PLATFORM := android- in Application.mk (Premake `systemversion`).
minSdkVersion 14
externalNativeBuild {
ndkBuild {
// See com.android.build.api.dsl.ExternalNativeNdkBuildOptions documentation.
// Application.mk path - specifying the correct one is recommended.
arguments 'NDK_APPLICATION_MK:=path/to/WORKSPACE_NAME.Application.mk',
// Premake platforms to build for - see the section about configuration and platform selection below.
'PREMAKE_ANDROIDNDK_PLATFORMS:=Android-ARM',
'PREMAKE_ANDROIDNDK_PLATFORMS+=Android-ARM64'
}
}
ndk {
// See com.android.build.api.dsl.Ndk documentation for more useful options such as `jobs`.
// The list of the ABIs to build this application for.
// Corresponds to APP_ABI in Application.mk.
// Should match the actual list of ABIs supported by at least some of the projects in the workspace.
// If omitted, ndk-build will try to build for all ABIs supported by the used NDK version.
// In this case, you should provide an architecture-agnostic platform/configuration for the projects.
// It may be fine not to specify this if you target only a subset of APIs, but ndk-build will be emitting warnings.
abiFilters 'armeabi-v7a', 'arm64-v8a'
// The C++ STL to use.
// Corresponds to APP_STL in Application.mk.
// If omitted, APP_STL (Premake `cppstl` and `staticruntime`) from the Application.mk will be used.
// If that's not specified too, the default STL for the current API version will be selected.
stl 'c++_static'
}
}
buildTypes {
release {
debuggable false
externalNativeBuild {
ndkBuild {
// Premake configurations to build for - see the section about configuration and platform selection below.
arguments 'PREMAKE_ANDROIDNDK_CONFIGURATIONS:=Release'
}
}
}
debug {
debuggable true
externalNativeBuild {
ndkBuild {
// Premake configurations to build for - see the section about configuration and platform selection below.
arguments 'PREMAKE_ANDROIDNDK_CONFIGURATIONS:=Debug'
}
}
}
}
externalNativeBuild {
ndkBuild {
// See com.android.build.api.dsl.NdkBuild documentation.
// This is required to initiate native code building at all.
// The Android.mk file to use - the generated workspace Android.mk in the `location` of the workspace.
// Corresponds to APP_BUILD_SCRIPT in Application.mk.
path file('path/to/WORKSPACE_NAME.wks.Android.mk')
}
}
}
Because a single ndk-build invocation builds for multiple ABIs, the usage of the generated files is slightly unusual compared to most build systems targeted by Premake where only a single configuration and platform pair is selected for building.
Instead of letting you select just one configuration and platform, this module allows you to execute ndk-build for multiple platforms and configurations.
For this purpose, the module provides two variables that you need to set in the arguments of the ndk-build invocation (externalNativeBuild.ndkBuild.arguments
for a Gradle build configuration):
PREMAKE_ANDROIDNDK_CONFIGURATIONS
— Premake configurations to build, at least one must be specified.PREMAKE_ANDROIDNDK_PLATFORMS
— Premake platforms to build for. Optional, if none are specified, will be building for all platforms specified in any project.
To set a variable to just one value, you can use the VARIABLE:=value
syntax of the ndk-build argument. For multiple values, you need to provide each in a separate argument as VARIABLE+=value
(preferably the first with :=
still to ensure it's overwritten if there's an environment variable with the same name). The Gradle example above shows how you can enable building for multiple platforms representing different ABIs, and for a configuration depending on the Gradle build type.
This, however, does not mean that you can create debug and release builds at once — rather, this architecture is designed specifically to provide a way for building for multiple ABIs as usual while being able to use an architecture
filter in Premake. Because of this, you must ensure that for every ABI, only at most one configuration–platform pair will be selected for every project. An ambiguous selection of platforms and configurations will result in an undefined behavior.
Platform-agnostic projects — those that have no platforms
inherited from the workspace or defined — will be built regardless of the value of PREMAKE_ANDROIDNDK_PLATFORMS
(only configuration selection will be used for them).
Please carefully verify that the settings you're using in your projects are handled in a way that's supported by this module and by ndk-build itself. Most importantly, please check the settings that are listed as per-file or per-extension here, especially if you have source files of different languages in your projects.
There is a small number of settings for which it's possible to specify the value for individual source files using the files
filter.
armisa
(new) ="A32"
/"T32"
(default)- ARMv7 instruction set to use. By default, Thumb (
"T32"
) will be used for release builds. Debug builds, however, always use A32. - At the project configuration scope, it controls
LOCAL_ARM_MODE
. - At the file scope, it can be used to build the specified files for A32 in a Thumb project, but not vice versa, by adding the
.arm
suffix to the file inLOCAL_SRC_FILES
. Alternatively, you can use the.arm
suffix (if needed, as a part of.arm.neon
, but not.neon.arm
) directly in thefiles
setting, which will take precedence.
- ARMv7 instruction set to use. By default, Thumb (
flags
ExcludeFromBuild
- Also supported for excluding entire projects on specified configurations/platforms.
vectorextensions
(for the"ARM"
architecture) ="ARMv7"
(new) /"NEON"
(default, new)- Whether to allow the compiler to generate instructions from the NEON SIMD instruction set as part of its optimizations. Specifying
"ARMv7"
(analogous to"IA32"
on x86) disables NEON, specifying"NEON"
enables it. By default, for consistency with NDK versions starting with r21, NEON will be enabled, even if the project doesn't explicitly specifyvectorextensions
(so on pre-r21, NEON will be used for Premake projects too). - At the project configuration scope, it controls
LOCAL_ARM_NEON
. - At the file scope, it can be used to build the specified files with NEON code generation in a project with NEON disabled, but not vice versa, by adding the
.neon
suffix to the file inLOCAL_SRC_FILES
. Alternatively, you can use the.neon
suffix (if needed, as a part of.arm.neon
, but not.neon.arm
) directly in thefiles
setting, which will take precedence. - For the allowed values on other architectures, see the per-configuration/per-platform settings section.
- Whether to allow the compiler to generate instructions from the NEON SIMD instruction set as part of its optimizations. Specifying
While ndk-build doesn't provide a way to specify compiler flags and most other settings for individual source files, it supports multiple source languages with different compilers, which accept different build options. Premake, however, exposes most build settings under language-agnostic names.
To specify which compiler should receive the needed value, you can use file extension filters.
Because the module supports .arm
, .neon
and .arm.neon
suffixes, as well as many C++ extensions, it's not recommended to list file extensions manually. Instead, built-in filter constants provided by the module should be preferred, as they include the ARM suffixes and all the supported extensions.
For including a specific language, the following variables are available (note that they must be used directly, and if needed, via ..
concatenation — %{}
string interpolation will not locate them):
premake.modules.androidndk.filefilters.as
for the GNU Assembler (GAS)premake.modules.androidndk.filefilters.asm
for Yasmpremake.modules.androidndk.filefilters.c
for Cpremake.modules.androidndk.filefilters.cpp
for C++premake.modules.androidndk.filefilters.rs
for RenderScript
Each of those filters is a single string which already has the "files:"
prefix (for instance, premake.modules.androidndk.filefilters.c
is "files:**.c or files:**.c.arm or files:**.c.neon or files:**.c.arm.neon"
).
To exclude a language from a file filter, filefilters
also includes filters prefixed with not
, such as premake.modules.androidndk.filefilters.notc
. Unlike the inclusive filters, the exclusive are tables, because Premake exposes the logical and
as separate table elements. premake.modules.androidndk.filefilters.notc
is { "files:not **.c", "files:not **.c.arm", "files:not **.c.neon", "files:not **.c.arm.neon" }
, for example. Premake filter tables can be nested, you don't need to perform flattening manually if you want to combine the language filters with additional terms.
In addition, raw combinations of extensions and ARM suffixes are available in filefilters
with the extensions
suffix, such as premake.modules.androidndk.filefilters.cextensions
, which is { ".c", ".c.arm", ".c.neon", ".c.arm.neon" }
(without the **
recursive wildcard). You can do your own processing of them using functions like table.translate
and table.concat
, for instance, to attach all the possible extensions to a specific file name.
Note that ndk-build treats the uppercase .C
extension as C++. To avoid imposing additional constraints, this module allows using the .C
extension for C++, even though other Premake actions may treat it as C, especially considering that path.hasextension
in Premake is case-insensitive. However, Premake filters are case-insensitive, and "files:**.c"
or "files:**.C"
will match both C .c
and C++ .C
sources, leading to the specified settings being used for both C and C++. Therefore, it's recommended to completely avoid using the uppercase .C
extension. The .C
extension is also not included in the premake.modules.androidndk.filefilters
constants for this reason.
It is important that you must not try to derive a language
filter from an extension filter because the language
setting has a "project"
scope in Premake rather than "config"
, and thus it doesn't support file-specific or even configuration- or platform-specific overrides, in addition to not having allowed values for GAS, Yasm and RenderScript, so the following will not work:
filter(premake.modules.androidndk.cfilefilter)
language("C") -- This WILL NOT WORK!
filter(premake.modules.androidndk.cppfilefilter)
language("C++") -- This WILL NOT WORK!
filter("language:C")
defines({"MY_NULL=((void *)0)"})
filter("language:C++")
defines({"MY_NULL=0"})
In addition, if the NoPCH
flag is not enabled in the project for the configuration–platform pair, and the file specified in pchheader
is also listed in files
, any file-filtered settings for it will be treated as C++ settings.
With some exceptions, most language-specific settings can be set independently for each language supported by this module.
buildoptions
(C, C++, GAS, Yasm, RenderScript)- One build option may contain one or multiple compiler arguments separated with whitespaces.
- Double quotation marks (
"
) may be used to specify a single compiler argument containing whitespaces. - If double quotation marks need to be escaped, they need to be prefixed with
\
, as"\\\""
according to Lua string literal escaping rules. - The backslash character (
\
) itself also needs to be escaped with another\
, as"\\\\"
, so the module can distinguish between a backslash used for escaping the double quote character and an actual backslash character. - All other characters after GNU make
$
variable or function reference expansion are allowed, and other shell-interpreted characters will be escaped automatically at build time. - Because of the way the values are gathered from the used filters, duplicate
buildoptions
are eliminated. For this reason, always specify multiple-argument options as{ "-prefix value1", "-prefix value2" }
, not{ "-prefix", "value1", "-prefix", "value2" }
(as the latter will become"-prefix value1 value2"
). - See the documentation for the
"LinkTimeOptimization"
flag here for details about the handling of"-flto=…"
.
cdialect
(C)cppdialect
(C++)flags
"FatalCompileWarnings"
(C, C++, Yasm)"NoBufferSecurityCheck"
(C, C++)"ShadowedVariables"
(C, C++)"UndefinedIdentifiers"
(C, C++)
defines
(C, C++, Yasm)- All
defines
are passed to the compiler beforeundefines
even if they're specified both within and without an extension filter.
- All
disablewarnings
(C, C++, Yasm)enablewarnings
(C, C++, Yasm)fatalwarnings
(C, C++, Yasm)- For Yasm, this is treated as
enablewarnings
.
- For Yasm, this is treated as
floatingpoint
(C, C++)"Strict"
is treated as"Default"
.
floatingpointexceptions
(C, C++)forceincludes
(C, C++, Yasm)- For C and C++, double quotation mark characters (
"
) are disallowed, as they would terminate the#include "path"
statement inserted by the compiler. - For Yasm,
"
,'
,;
characters are disallowed.
- For C and C++, double quotation mark characters (
includedirs
(C, C++, GAS and Yasm combined, RenderScript separately)- C, C++, GAS and Yasm use the same list of include directories (that is written to
LOCAL_C_INCLUDES
). - All
includedirs
are passed to the compiler beforesysincludedirs
. - Whitespaces and
#
,$
(after GNU make reference expansion) characters are disallowed.
- C, C++, GAS and Yasm use the same list of include directories (that is written to
inlinesvisibility
(C, C++)- Unlike in other Premake actions using
premake.clang
orpremake.gcc
, this is also supported for C, not only C++.
- Unlike in other Premake actions using
omitframepointer
(C, C++)- To use the Address Sanitizer, this needs to be set to
Off
— see the Address Sanitizer guide on Android Developers.
- To use the Address Sanitizer, this needs to be set to
strictaliasing
(C, C++)sysincludedirs
(C, C++, GAS and Yasm combined, RenderScript separately)- See
includedirs
.
- See
undefines
(C, C++, Yasm)- All
defines
are passed to the compiler beforeundefines
even if they're specified both within and without an extension filter.
- All
unsignedchar
(C, C++)visibility
(C, C++)- Unlike in other Premake actions using
premake.clang
orpremake.gcc
, this is also supported for C, not only C++.
- Unlike in other Premake actions using
warnings
(C, C++, Yasm)- For Yasm, only
"Off"
has effect — other values are treated as warnings enabled as normal.
- For Yasm, only
architecture
- If an architecture is specified, the settings for this configuration–platform pair will be used will be used while building for the respective ABI if the configuration and the platform are selected for building via
PREMAKE_ANDROIDNDK_CONFIGURATIONS
andPREMAKE_ANDROIDNDK_PLATFORMS
. In this case, thearchitecture
filter may be used to provide ABI-specific settings."ARM"
for thearmeabi-v7a
ABI."ARM64"
for thearm64-v8a
ABI."x86"
for thex86
ABI."x86_64"
for thex86_64
ABI.
- Leave unspecified or set to
"universal"
to provide fallback settings that will be used for the project for ABIs without a specialization. - Generally
architecture
should be specified under aplatforms
filter, or, depending on your configuration structure, under aconfigurations
one. See the “Usage” section for more information ABI filtering of settings.
- If an architecture is specified, the settings for this configuration–platform pair will be used will be used while building for the respective ABI if the configuration and the platform are selected for building via
exceptionhandling
="Off"
/"On"
(default)files
- For a built Premake project:
- The following languages are supported:
- C (
.c
) - C++ (
.cc
,.cp
,.cxx
,.cpp
,.CPP
,.c++
,.C
)- The list of allowed extensions is broader than the default for Premake v5.0.0-alpha16, but matches the default for the NDK r23).
- Note that while ndk-build (and therefore this setting) is case-sensitive, Premake filters are not. Therefore, a
files:**.c
or afiles:**.C
filter will cause both C.c
and C++.C
to pass, and extension-filtered language-specific settings such asbuildoptions
intended only for C++ will be used for C as well if both are present in the project, for example — see the section about per-language settings. It's better to avoid using the.C
extension completely.
- GNU Assembler (
.s
,.S
) - Yasm (
.asm
)"x86"
and"x86_64"
architectures only — will be ignored automatically while building for other ABIs, including in architecture-agnostic projects.
- RenderScript (
.rs
,.fs
)
- C (
.arm
,.neon
and.arm.neon
(but not.neon.arm
) suffixes are allowed — see the documentation for per-filearmisa
andvectorextensions
settings, which can be used as an alternative (using file-filteredarmisa
andvectorextensions
is recommended instead so filters like"files:**.c"
can still be used rather than"files:**.c or **.c.arm or **.c.neon or **.c.arm.neon"
, however — though for extension filtering, constants likepremake.modules.androidndk.filefilters.c
are recommended, which include the.arm
and.neon
suffixes).- Whitespaces, backtick, and
"
,#
,$
(after GNU make reference expansion),&
,'
,(
,)
,,
,;
,<
,>
,|
characters are disallowed. The backslash is allowed as a path separator on Windows, but disallowed on Linux.
- The following languages are supported:
- To create a prebuilt library project or to use an external Android.mk file, specify only one
.so
(for the"SharedLib"
kind),.a
(for the"StaticLib"
kind) or.mk
file, and no other source files. See the “Usage” section for more information about the required and supported settings for prebuilt and external projects.- In prebuilt library paths, whitespaces, and
"
,#
,$
(after GNU make reference expansion),%
,&
,*
,;
,<
,>
,?
,|
characters are disallowed on Windows. On other operating systems, backtick,'
,(
,)
,\
characters are not supported. - In external Android.mk paths, whitespaces are not disallowed.
- In prebuilt library paths, whitespaces, and
- All irrelevant file types (such as headers) will be ignored automatically.
- For a built Premake project:
flags
"ExcludeFromBuild"
- Also supported for excluding individual source files from building.
"FatalLinkWarnings"
- For shared libraries and executables only.
- Unlike in ndk-build itself, fatal link warnings are disabled by default (
LOCAL_DISABLE_FATAL_LINKER_WARNINGS := true
).
"LinkTimeOptimization"
- If used in a static library, shared libraries and executables linked against it will also be linked with link-time optimization (the transitivity ensured for this options, however, doesn't apply to building of source files).
- It's possible to override the default link-time optimization type by specifying the desired link-time optimizations type, such as
-flto=thin
, in bothbuildoptions
andlinkoptions
. The specific-flto=
type will also be propagated fromlinkoptions
of static library dependencies (though in this case, all linked static libraries must have the same LTO type, and static library dependency analysis only checkslinkoptions
, notbuildoptions
).
"NoPCH"
- Causes
pchheader
to be ignored — seepchheader
for more information.
- Causes
formatstringchecks
(new) ="Off"
/"On"
(default)- Corresponds to
LOCAL_DISABLE_FORMAT_STRING_CHECKS
(but enabling instead of disabling), whether to checkprintf
format strings in the sources.
- Corresponds to
isaextensions
- This should be used only in libraries with code used conditionally based on the information provided by
cpuid
, or for emulation purposes, otherwise it may prevent the app from working on physical devices. - All
"x86"
and"x86_64"
architectureisaextensions
normally supported by Premake actions targeting Clang are supported.
- This should be used only in libraries with code used conditionally based on the information provided by
libdirs
- Search paths for library binaries specific directly via
links
. - Not recommended as using library binaries directly via linker arguments is discouraged by ndk-build — prefer using prebuilt library projects, and see
links
for more information. - All
libdirs
gathered from the project and its static library dependencies are passed to the linker beforesyslibdirs
.
- Search paths for library binaries specific directly via
linkoptions
- Most options are supported only in shared libraries and executables, but there are some exceptions which are exported from static libraries as well.
- See
buildoptions
for information about passing multiple arguments, characters that need to be escaped manually, deduplication, disallowed characters. - Going to Clang++ rather than directly to LLD —
-Wl
may be needed for many options. - Unlike in other Premake actions, some parsing is done:
- Options prefixed with
-l
,-L
(with or without-Wl
),-Wl,--library
,-Wl,--library-path
will go toLOCAL_LDLIBS
instead ofLOCAL_LDFLAGS
, and will be exported from static libraries to dependent shared libraries, similarly tolinks
. This may be useful if you have a project named the same as a system library, but you want to link against that system library (for instance,-llog
if there is a project named"log"
). Seelinks
andlibdirs
for more information. - Options prefixed with
-u
(with or without-Wl
) or-Wl,--undefined
will also be exported from static libraries transitively to allow preserving “unused” symbols imported from static libraries in the dependent shared libraries, primarily JNI native function implementations in static libraries — similar towholelib
, but more granular, for individual symbols. - See the documentation for the
"LinkTimeOptimization"
flag here for details about the handling of"-flto=…"
.
- Options prefixed with
links
- Premake projects and system libraries to link against — usable in all kinds of projects (in shared libraries and executables, as well as in static libraries for transitive linkage).
- If a shared library or an executable is linked against a static library project, it will automatically be linked against all system libraries referenced by its static library dependencies, even if static library dependencies are chained via
links
. Transitivity across shared libraries is not provided, however, as it's not necessary and may be undesirable — instead of usingLOCAL_EXPORT_LDLIBS
, the module traverses the dependency tree by itself. This is done for consistency with other Premake actions such as Visual Studio. - See
wholelib
andwholelibs
for information about importing static libraries as whole archives. - If you have a project named the same as a system library, but you still want to link against the system library, you can use a
buildoptions
entry with the-l
prefix (for instance,-llog
if there is a project named"log"
). - Generally shouldn't be used for anything but linking to other projects and to the NDK system libraries — ndk-build will generate warnings for non-system libraries. Prebuilt library projects (see the “Usage” section) should be preferred for linking against non-system library binaries directly. However, the module still provides the functionality for linking against arbitrary library files:
- Full paths,
libdirs
andsyslibdirs
can be used for specifying search paths (however, there's no way to link against two libraries with the same name under different paths). - Both short library names and full file names are allowed. If the file name ends with
.a
or.so
, it's treated as a full name — the"lib"
prefix must be explicitly specified if needed. - The
":"
prefix can be added to the file name explicitly to force handling of it as a full file name.
- Full paths,
kind
"SharedLib"
or"StaticLib"
for a built or a prebuilt shared or static library."ConsoleApp"
for an executable."None"
to exclude the project from building for the current configuration/platform.- Note that
"Makefile"
is not a supported option — an external Android.mk file must be used throughfiles
, and the project for it must have thekind
that matches the actual ndk-build script included by it. - For more information about the usage of prebuilt libraries and external Android.mk files, see the section about them in “Usage”.
renderscriptcompatibility
(new) ="Off"
(default) /"On"
- Corresponds to
LOCAL_RENDERSCRIPT_COMPATIBILITY
.
- Corresponds to
renderscriptincludedirsoverride
(new) ="Off"
(default) /"On"
- Corresponds to the usage of
LOCAL_RENDERSCRIPT_INCLUDES_OVERRIDE
instead ofLOCAL_RENDERSCRIPT_INCLUDES
, whether to ignore the NDK built-in platform and toolchain RenderScript include search paths.
- Corresponds to the usage of
pchheader
- Suffixes such as
.arm
and.neon
are not supported — handling of ARMv7 ISA overrides is performed internally in ndk-build. - Whitespaces, backtick, and
"
,#
,$
(after GNU make reference expansion),&
,'
,(
,)
,,
,;
,<
,=
,>
,|
characters are disallowed (same as infiles
for sources, but additionally=
). The backslash is allowed as a path separator on Windows, but disallowed on Linux. - ndk-build treats the precompiled header as C++ code. The module will configure variables necessary for building C++ code (such as
LOCAL_CPPFLAGS
) if the precompiled header is present, however, if you have any C++-specific settings with a file extension filter, you need to provide at least one source file with the respective extension, otherwise this module will not be able to access those settings. If the file specified inpchheader
is listed amongfiles
, any file-filtered settings applied to it will also be treated as C++ settings. - Ignored if the
"NoPCH"
flag is enabled for the project configuration/platform.
- Suffixes such as
rtti
="Off"
/"On"
(default)targetprefix
,targetname
,targetsuffix
- Combined into
LOCAL_MODULE_FILENAME
. - Overriding the binary path or extension is not supported.
- Whitespaces and
#
,$
(after GNU make reference expansion),%
,/
,;
,|
characters are disallowed. - Only removing the
lib
prefix works when invoking ndk-build from Gradle as of Gradle 7.0.2 because it gathers the names of the targets to build fromLOCAL_MODULE_FILENAME
instead ofLOCAL_MODULE
. If executing ndk-build manually without a list of targets, or with a correctly specified list of them, other values are supported.
- Combined into
undefinedsymbols
(new) ="Off"
(default) /"On"
- For shared libraries and executables only.
- Corresponds to
LOCAL_ALLOW_UNDEFINED_SYMBOLS
, if enabled, errors will not be thrown for undefined symbols, instead, the symbols will be resolved at runtime.
shortcommands
(new) ="Off"
(default) /"On"
- Corresponds to
LOCAL_SHORT_COMMANDS
, whether to execute build commands from a file rather than directly via the shell, as if the command line is too long, it may not fit in the 8191 character limit on Windows.
- Corresponds to
syslibdirs
- See
libdirs
.
- See
system
- Must be
"android"
for the correct library name prefix ("lib"
).
- Must be
thinarchive
(new) ="Off"
(default) /"On"
- For static libraries only.
- Corresponds to
LOCAL_THIN_ARCHIVE
, whether to store only object file paths instead of the object themselves in the static library being built.
vectorextensions
(for the"x86"
and"x86_64"
architectures)- This should be used only in libraries with code used conditionally based on the information provided by
cpuid
, or for emulation purposes, otherwise it may prevent the app from working on physical devices. - Allowed values on the
"x86"
architecture:"SSE4.1"
,"SSE4.2"
(new),"AVX"
,"AVX2"
(SSSE3 and below are required by Android and are always supported). - Allowed values on the
"x86_64"
architecture:"AVX"
,"AVX2"
(SSE4.2 and below are required by Android and are always supported). - For the allowed values on the
"ARM"
architecture, see the per-file settings section.
- This should be used only in libraries with code used conditionally based on the information provided by
wholelib
(new) ="Off"
(default) /"On"
- For static libraries only.
- Whether all object files from this static library should be included in the shared libraries (but not executables) referencing it (and this library will be added to
LOCAL_WHOLE_STATIC_LIBRARIES
instead ofLOCAL_STATIC_LIBRARIES
). This is useful, for instance, when exporting JNI native function implementations from a static library. - This setting is for exporting the requirement that the static library must be linked as a whole archive — for the equivalent for importing, see
wholelibs
(a static library will be linked viaLOCAL_WHOLE_STATIC_LIBRARIES
rather thanLOCAL_STATIC_LIBRARIES
if it enableswholelib
for itself, or if it's specified inwholelibs
of the project importing it — you only need either to specifywholelib
in the dependency or to add it towholelibs
of the dependent projects; though specifying both the same time is fine, that's redundant).
wholelibs
(new)- List of static library project names among the
links
of the project to link as whole archives. - For more information about whole static libraries, see
wholelib
— this setting provides the same functionality, but for importing static library dependencies as whole archives (only one ofwholelib
orwholelibs
is enough). - If a project is listed in
wholelibs
, it still needs to be added tolinks
to be linked — this setting is merely a modifier, and therefore it's fine to just list all static library projects that need to be linked as whole archives once for the entire workspace (thoughwholelib
is more suited for this purpose).
- List of static library project names among the
These settings have effect on APP
variables in the Application.mk rather than LOCAL
variables in projects Android.mk files.
There is a single Application.mk file for the entire workspace, and the same values of the APP
variables are used regardless of the current target ABI. Thus, for all configurations and platforms selected in PREMAKE_ANDROIDNDK_CONFIGURATIONS
and PREMAKE_ANDROIDNDK_PLATFORMS
, the values of these settings must be the same for:
- All projects in the workspace;
- All target ABIs (
architecture
s).
For all selected configurations (which are visited in an undefined order), the module tries to locate the value in:
- The workspace itself.
- The
startproject
. - For all selected platforms, visited in an undefined order, platform-specific projects, also in an undefined order.
- Platform-agnostic projects, in an undefined order.
You can use, for instance, optimize("Off")
in the "Debug"
configuration, and optimize("On")
in the "Release"
one, as long as you build the workspace with either PREMAKE_ANDROIDNDK_CONFIGURATIONS:=Debug
or PREMAKE_ANDROIDNDK_CONFIGURATIONS:=Release
, but not both at once. However, you can't disable optimizations for specific projects in the "Release"
configuration in this case.
cppstl
(new) ="Default"
/"c++"
/"none"
/"system"
- The C++ standard library to use.
- NDK versions before r18 also support
"gabi++"
,"gnustl"
,"stlport"
. - Use
staticruntime
to specify whether to link against the static standard library or against the shared one.
optimize
- Handled in a way that approximates the behavior of other Premake actions.
- Unspecified,
"Off"
,"Debug"
result inAPP_OPTIM := debug
. - Anything else (
"Size"
,"Speed"
,"Full"
) results inAPP_OPTIM := release
, and also force-disablesAPP_DEBUG
.
symbols
- Handled in a way that approximates the behavior of other Premake actions.
- Ignored if
optimize
is set to a value that results in optimization enabled. - Unspecified,
"Off"
,"Default"
result inAPP_DEBUG := true
. - Anything else (
"On"
,"FastLink"
,"Full"
) results inAPP_DEBUG := false
.
staticruntime
="Default"
/"Off"
/"On"
(default)- Whether to use the static C++ standard library (
cppstl
) as opposed to a shared one. - Static by default, consistent with the default CMake behavior (where
c++_static
is the default standard library). - Ignored for
"none"
and"system"
standard libraries.
- Whether to use the static C++ standard library (
systemversion
- Minimum Android SDK version required by the application, as a string representation of a number (corresponds to
APP_PLATFORM := android-systemversion
).
- Minimum Android SDK version required by the application, as a string representation of a number (corresponds to
basedir
configurations
- Whitespaces and
%
,(
,)
,,
characters are disallowed (configuration and platform names are used in GNU make list function calls).
- Whitespaces and
location
- The directory where the Android.mk file for the project or the Application.mk and the root Android.mk for the workspace will be placed (if provided — otherwise
basedir
will be used). - For projects, it is heavily recommended to set this to a folder that doesn't contain header files. This is the path that will be used as
LOCAL_PATH
, and ndk-build internally addsLOCAL_PATH
as the last include directory, causingLOCAL_PATH
to be treated as a more “system” include search path than the system paths themselves. This causes issues when theLOCAL_PATH
directory contains header files named the same as C and C++ standard library headers — the LLVM libc++ uses#include_next
in a few files (such asmath.h
), causing project's files to included from system headers; this is especially dangerous if the local"math.h"
itself#include
s<math.h>
.
- The directory where the Android.mk file for the project or the Application.mk and the root Android.mk for the workspace will be placed (if provided — otherwise
platforms
- See
configurations
.
- See
project
- Whitespaces and
#
,$
,%
,/
,:
,;
,=
,|
characters are disallowed. - GNU make variable or function references (
$
) are disallowed.
- Whitespaces and
startproject
- The start project will have a higher priority when locating setting values for Application.mk variables.
workspace
- Whitespaces and
#
,$
characters are disallowed. - GNU make variable or function references (
$
) are disallowed.
- Whitespaces and
Several features were left temporarily or intentionally unimplemented in the current version of the module, however, they may be useful in certain cases.
If your project depends on something missing from the module, feel free to open an issue, and we'll discuss how the needed functionality can be added to the module in a convenient and flexible way — or submit a pull request with the implementation!
Note that the general preference is to use existing Premake settings wherever possible — potentially twisting them and adding the necessary constraints while still trying to keep the original semantic concepts — instead of adding new ones. Don't hesitate to exploit filters, to interpret a variable in different ways depending on the target ABI and overall context, to use any opportunity to make the module more compatible with existing projects for other targets. However, if new settings need to be added, prefer using kind = "boolean"
settings rather than flags as the former have three states, making it easier to provide default behavior, especially if the new settings are promoted to the Premake core or used in other modules in the future, potentially on platforms with different defaults.
If you're adding functionality that involves strings in a new place in the generated Android.mk and Application.mk files, make sure to check which non-letter ASCII characters, including different types of whitespaces, are supported by ndk-build in the specific context, especially if it's a file name or a path, on both Linux (which allows any characters in paths) and Windows (including UNC paths), with attention to backslash-escaping of shell-interpreted characters (see androidndk.shellEscapedCharactersPostQuotes
), but not only them. All user input must be passed through p.esc
, preferably early (and functions expecting output of p.esc
as arguments must be named with the PostEsc
suffix), to ensure the integrity of GNU make variable or function references (also make sure that paths including $
references are handled correctly as absolute or as relative depending on the context, especially when joining — usually paths beginning with $
should be considered absolute, and that's how Premake itself treats them, while a relative path needs to start with ./$
in this case) and of escaping of the comment character #
. After p.esc
, the invalid character check may be done for the non-reference part of the string if needed — see the usage androidndk.staticallyHasPatternPostEsc
(build-time validation would be an overkill if there are references in the string, but to catch potential errors when porting projects from other platforms, generation-time checks are useful). In many places, strings are used in shell commands invoked by ndk-build — see how checkers and escapers starting with androidndk.shellEscape
are used in the module; also shell character escaping needs to be done at build time, not at Premake invocation time, since it must be performed after expanding GNU make references. Also, if you're passing user input to a GNU make function, use an intermediate variable (see androidndk.getTemporaryVariableReference
), as the user-provided string may contain brackets or commas, which would break the parsing of the function call.
The following functionality has been omitted currently, but may be nice to have for reasons like compatibility:
- Unit tests. This is a large module with complex input handling with a large number of edge cases for many settings that, in some cases, depend on the kind of the project, the target ABIs, source languages used in the project — written in a dynamically-typed language. Many errors and typos may occur. Tests verifying both supported and disallowed input need to be added — covering platform-dependent and platform-agnostic projects, ABI-dependent and ABI-agnostic projects, different kinds of binaries, prebuilt libraries, external Android.mk projects, source files written in various languages with extension filters and
.arm
and.neon
suffixes, link settings and transitivity of them across chains of static libraries where needed, GNU make variable and function references, disallowed characters, shell-interpreted character escaping, and other cases handled by this module. - Building for deprecated ABIs removed from the NDK —
armeabi
,armeabi-v7a-hard
,mips
,mips64
. While simply adding a newarchitecture
is trivial (though for choosingarmeabi-v7a-hard
, a special setting for the floating-point argument convention may be more suitable), that wouldn't imply complete support for the details of them — such as instruction set extensions (like the MXU vector extensions for MIPS), hardware floating-point unit support. MIPS-powered Android devices are extremely rare, and ARMv5 deprecation began in Android 4.0, having been completed in Android 4.4. - Support for obsolete toolchains removed from the NDK, such as GCC, as well as toolchain version selection (as of r23, the NDK only offers one version of the Clang toolchain).
- Clang-Tidy — needs to be integrated into the architecture of Premake itself.
- Post-processing of the generated machine code and assembly source files via
LOCAL_FILTER_ASM
— needs to be added in a way friendly to the Premake architecture. - Compatibility with the Visual Studio actions for Android (the built-in
android
module). It defines many settings in ways that aren't consistent with the Premake core itself, as well as adding redundant options:- Values are generally lowercase, while the Premake core uses PascalCase internally.
- The additions to the list of the allowed
architecture
values are chaotic. Forarmeabi
, it allows both"arm"
and"armv5"
(which are registered as separate options rather than aliases of each other), while forarmeabi-v7a
, it uses"armv7"
. Premake itself provides an"ARM"
option, which, given the removal of ARMv5 support from the NDK, is more likely to be expected to correspond to ARMv7 as opposed to ARMv5.arm64-v8a
is exposed as"aarch64"
instead of"ARM64"
used in the Premake core. - ARMv5 and ARMv7 instruction sets are presented as two options: a
"Thumb"
flag, and a"thumbmode"
setting, with one of the allowed values being"disabled"
rather than"Default"
commonly used in Premake. stl
is a new setting in it, but it uses misleading names. The"gnustl"
and"c++"
standard libraries provided by ndk-build are called"gnu"
and"libc++"
instead, which is likely not what users expect. For"system"
, the value"none"
is used — however, the NDK has both"system"
(which is deprecated) and"none"
, the difference between the two beingnew
anddelete
being supported in"system"
, while"none"
providing no C++ standard library functionality at all. The namestl
also doesn't imply C++ directly, which may cause issues if Premake gets support for a different language that also has the standard library referred to by the same acronym — unlike settings likebuildoptions
, this may be even workspace-level (and in ndk-build, it is), and thus the value is not file- or extension-filterable.androidapilevel
is redundant due to the existence ofsystemversion
.- The
"posix"
system tag is not added for Android.
- The “call array” architecture is not used in the code as certain settings are handled in multiple places in different ways, and various kinds of preprocessing are done. Replacing parts of the generation code in project scripts is unlikely to be useful or convenient for this reason.