termux-packages icon indicating copy to clipboard operation
termux-packages copied to clipboard

Clang should specify __ANDROID_API__ to current device version

Open easyaspi314 opened this issue 7 years ago • 12 comments

Some C functions require __ANDROID_API__ to be set.

For example, the following will not compile without 'implicit declaration' errors on Nougat+:

#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>

int main(void) {
    const char *hello_world = "Hello, world!";
    char *world = strchrnul(hello_world, 'w');
    if (world)
        puts(world);
    return 0;
}
$ clang test.c
test.c:7:16: warning: implicit declaration of function 'strchrnul' is invalid in      C99 [-Wimplicit-function-declaration]
        char *world = strchrnul(hello_world, 'w');
                      ^
test.c:7:8: warning: incompatible integer to pointer conversion initializing
      'char *' with an expression of type 'int' [-Wint-conversion]
        char *world = strchrnul(hello_world, 'w');
              ^       ~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 warnings generated.

Compile with __ANDROID_API__ defined to 24 or higher (assuming, again, that you are running Nougat or above), and it works fine.

Clang does have a config file that can do that, but in order for the file to be loaded, you would have to set the CMake flag -DCLANG_CONFIG_FILE_SYSTEM_DIR while compiling Clang to a valid path, then it will automatically load the config file.

After that, all you would need to do is add a shell script to the postinstall script that does something like this:

#!/data/data/com.termux/files/usr/bin/dash
API_STR="-D__ANDROID_API__="
API_LEVEL="$(getprop ro.build.version.sdk)"
CONFIG_FILE="<config file path>"

echo "Updating Clang's config file..."

# Check if the file exists and it contains "-D__ANDROID_API__="
if [ -f "${CONFIG_FILE}" ] && [ "$(busybox grep -qs "${API_STR}" "${CONFIG_FILE}")" ]
then
    # Check if this config file is from an older Android version, if so, update it
    if [ ! "$(busybox grep -qs "${API_STR}${API_LEVEL}" "${CONFIG_FILE}")" ]
    then
        busybox sed -i'' -e "s#${API_STR}[0-9]\*#${API_STR}${API_LEVEL}#g" "${CONFIG_FILE}"
    fi
    # Otherwise, do nothing as the file has the latest version
else
    # We now can just append __ANDROID_API__ to the file.
    echo "${API_STR}${API_LEVEL}" >> "${CONFIG_FILE}"
fi

Make sure it gets tested, I just wrote this blindly. I am not the greatest at shell scripts or sed, and I don't know much about dpkg either.

If this works properly, Clang should automatically define __ANDROID_API__, and that source file should compile without issues, again, assuming you are running Nougat or later.

easyaspi314 avatar May 29 '18 18:05 easyaspi314

In Termux the file <android/api-level.h> (which gets included from <sys/cdefs.h>, which in turn is included in almost all libc include files) contains:

#ifndef __ANDROID_API__
#define __ANDROID_API__ 21

So __ANDROID_API__ already gets defined if not passed explicitly - but to a default value of 21, which is currently the minimum supported api level of Termux (soon to be bumped to 23).

I guess you expect this to be set to the api level of the currently running device, which I guess is not unreasonable?

fornwall avatar Jun 04 '18 00:06 fornwall

Yes.

easyaspi314 avatar Jun 04 '18 12:06 easyaspi314

I think the current situation is actually buggy: android/api-level.h defines __ANDROID_API__ to a different value from the libc that is used by default.

This causes problems when for example trying to build code that tests (e.g. with autoconf) for functions in libc. These can pass (e.g. on my Oreo phone, the test for mempcpy passes), but then compilation is incorrect, because mempcpy requires __ANDROID_API__ to be at least 23. In fact this only causes a warning (for undeclared function), but the resulting code crashes.

So whatever solution is chosen (whether to set an arbitrary minimum API level, or to set the maximum supported by the device), the __ANDROID_API__ value should match the default libc.

rrthomas avatar Nov 04 '18 22:11 rrthomas

So __ANDROID_API__ already gets defined if not passed explicitly - but to a default value of 21, which is currently the minimum supported api level of Termux (soon to be bumped to 23).

@fornwall I humbly ask you, and would even beg you, to please keep compatibility with API level 21, there are many of us, especially in third world countries, who can't afford to buy a new device in order to get to the new api level.

erlanger avatar Jan 25 '19 19:01 erlanger

I'd really love to have this feature. As @easyaspi314 said, the compilation often fails due to "implicit declaration", especially when a configure script is used.

Typically, ./configure checks if a function is "linkable" or not. However, the compilation requires a prototype declaration for the function, which is sometimes disabled when __ANDROID_API__ is lower.

For example, my Android 9 Pie (API version 28) provides a function pthread_attr_setinheritsched. When running the configure script on Termux, the function is actually linkable, so it defines a macro namely HAVE_PTHREAD_ATTR_SETINHERITSCHED. However, pthread.h declares the function only when __ANDROID_API__ >= 28:

#if __ANDROID_API__ >= 28
int pthread_attr_setinheritsched(pthread_attr_t* __attr, int __flag) __INTRODUCED_IN(28);
#endif /* __ANDROID_API__ >= 28 */

Currently, it is not declared by default because __ANDROID_API__ is 21. So, the following code leads to an "implicit declaration" error. (Decent projects tend to use -Werror=implicit-function-declaration.)

#ifdef HAVE_PTHREAD_ATTR_SETINHERITSCHED
  pthread_attr_setinheritsched(...);
#endif

I think that @erlanger's concern about the API version is less relevant in this particular case. I believe that almost all Termux users just want to build a program only for themselves. Few people will distribute a binary compiled on Termux. If they want to do so seriously, they should use a cross-compiler and create a portable package, I think.

mame avatar Aug 19 '19 17:08 mame

Currently, it is not declared by default because ANDROID_API is 21

We have split repositories for legacy (Android 5/6) and Android 7+ devices. Android 7 repo has API 24.

https://wiki.termux.com/wiki/New_package_repository

Anyway I agree that we should set __ANDROID_API__ to which device have. The only problem is best/reliable way to determine current API from clang.

For now I think we can just export env variable like TERMUX_ANDROID_API_LEVEL and attempt to read it from clang but it obviously may be unset somehow (e.g. via build script or whatever else).

ghost avatar Aug 19 '19 18:08 ghost

This issue/PR has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Nov 18 '21 18:11 stale[bot]

Not stale.

xtkoba avatar Nov 23 '21 08:11 xtkoba

This issue/PR has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Jan 07 '22 09:01 stale[bot]

From api-level.h:

For NDK and APEX builds, this macro will always be defined. It is set automatically by Clang using the version suffix that is a part of the target name. For example, __ANDROID_API__ will be 24 when Clang is given the argument -target aarch64-linux-android24.

So Android determines __ANDROID_API__ from the target triplet. Specifying . If you don't want to specify target everytime one alternative will be to substitute the default target in libLLVM.so with the target corresponding to the device's API level

For example:

sed --follow-symlinks -i 's/aarch64-unknown-linux-android24/aarch64-unknown-linux-android28/g' /data/data/com.termux/files/usr/lib/libLLVM.so

Will set the __ANDROID_API__ to 28 on an aarch64 device.

Also note that this modification is not needed for commands prefixed with target triplets like aarch64-linux-android-clang. They use the __ANDROID_API__ value in android/api-level.h. Modifying libLLVM.so is only needed when using commands like cc,clang etc. That too when you don't want to specify target triplet every time.

zorro avatar Sep 17 '22 10:09 zorro

There has been talk about this again. Currently, we set a default API of 24 when building with clang:

> clang --version
clang version 16.0.0
Target: aarch64-unknown-linux-android24
Thread model: posix
InstalledDir: /data/data/com.termux/files/usr/bin

That will also set __ANDROID_API__ by the compiler, as the API level in the triple sets that. That means changing the API level is as simple as supplying --target=aarch64-unknown-linux-android33 to your builds, whether through CFLAGS/CXXFLAGS or creating a clang wrapper script.

I would rather not shift the default from 24 for everyone now because those who want a higher API level can always set it for themselves, but I'm fine with moving the default to whatever the consensus is.

finagolfin avatar Mar 27 '23 09:03 finagolfin

I've been using a wrapper script saved in ~/.local/bin/clang like:

#!/data/data/com.termux/files/usr/bin/bash

PREFIX=/data/data/com.termux/files/usr

tuple=$HOSTTYPE-linux-android
tool=${0##*/}
tool=${tool#"$tuple-"}
sdk=$(getprop ro.build.version.sdk)

exec -a "$PREFIX/bin/$tuple$sdk-$tool" -- \
    "$PREFIX/bin/$tool" "$@"

and linking ~/.local/bin/{clang,gcc}, etc. to it:

cd "$PREFIX/bin" &&
for i in *; do
    # find all symlinks of clang
    if [[ $i != clang && $i -ef clang ]]; then
        ln -sfv -- clang ~/.local/bin/"$i"
    fi
done

This sets argv[0] to e.g. $PREFIX/bin/aarch64-linux-android33-clang and then exec's the corresponding tool so no shell process is left hanging around.

The executable at that argv[0] value doesn't actually exist but it doesn't matter because clang just needs to parse the value correctly to figure out its InstallDir and target.

If any --target argument is passed it will override the derived value so this wrapper is safe even if you want to cross-compile.

algorythmic avatar Apr 02 '23 06:04 algorythmic

this is a workaround for cmake

set(CMAKE_CXX_COMPILER_TARGET aarch64-unknown-linux-android30)

rhjdvsgsgks avatar Nov 06 '23 01:11 rhjdvsgsgks