conan-io / conan

Conan - The open-source C and C++ package manager
https://conan.io
MIT License
8.29k stars 985 forks source link

Cross-compiling ninja from Macos armv8 to Android armv8 #12610

Open TheSlackOne opened 1 year ago

TheSlackOne commented 1 year ago

I'm trying to cross-compile ninja v1.11.1 from my Macos M1 (armv8), Monterey 12.6 to Android armv8, using the NDK 22.1.7171670. I'm using conan-package-tools with the following settings:

builder.add(settings={
        "compiler.cppstd": "17",
        "os": "Android",
        "arch": "armv8",
        "build_type": "Release",
        "os.api_level": "24",
        "compiler": "clang",
        "compiler.version": "11",
        "compiler.libcxx": "libc++",
        "os_build": "Macos",
        "arch_build": "armv8"
    }, options={}, env_vars={
        "CONAN_CMAKE_ANDROID_NDK": "<path>/25.1.8937393"
    }, build_requires={})
% python --version
Python 3.10.8

Log snipet:

ninja/1.11.1@local/workbench:
ninja/1.11.1@local/workbench: Copying sources to build folder
ninja/1.11.1@local/workbench: Building your package in /Users/ernesto.messina/.conan/data/ninja/1.11.1/local/workbench/build/4cd8a0226669e0be90b7c9a8fcdc9ee41453a7d3
ninja/1.11.1@local/workbench: Generator txt created conanbuildinfo.txt
ninja/1.11.1@local/workbench: Calling generate()
ninja/1.11.1@local/workbench: WARN: Using the new toolchains and generators without specifying a build profile (e.g: -pr:b=default) is discouraged and might cause failures and unexpected behavior
ninja/1.11.1@local/workbench: Preset 'release' added to CMakePresets.json. Invoke it manually using 'cmake --preset release'
ninja/1.11.1@local/workbench: If your CMake version is not compatible with CMakePresets (<3.19) call cmake like: 'cmake <path> -G "Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=/Users/ernesto.messina/.conan/data/ninja/1.11.1/local/workbench/build/4cd8a0226669e0be90b7c9a8fcdc9ee41453a7d3/build/generators/conan_toolchain.cmake -DCMAKE_POLICY_DEFAULT_CMP0091=NEW -DCMAKE_BUILD_TYPE=Release'
ninja/1.11.1@local/workbench: Aggregating env generators
ninja/1.11.1@local/workbench: Calling build()
ninja/1.11.1@local/workbench: CMake command: cmake -G "Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE="/Users/ernesto.messina/.conan/data/ninja/1.11.1/local/workbench/build/4cd8a0226669e0be90b7c9a8fcdc9ee41453a7d3/build/generators/conan_toolchain.cmake" -DCMAKE_INSTALL_PREFIX="/Users/ernesto.messina/.conan/data/ninja/1.11.1/local/workbench/package/4cd8a0226669e0be90b7c9a8fcdc9ee41453a7d3" -DCMAKE_POLICY_DEFAULT_CMP0091="NEW" -DCMAKE_BUILD_TYPE="Release" "/Users/ernesto.messina/.conan/data/ninja/1.11.1/local/workbench/build/4cd8a0226669e0be90b7c9a8fcdc9ee41453a7d3/src"
-- Using Conan toolchain: /Users/ernesto.messina/.conan/data/ninja/1.11.1/local/workbench/build/4cd8a0226669e0be90b7c9a8fcdc9ee41453a7d3/build/generators/conan_toolchain.cmake
CMake Error at /Users/ernesto.messina/Library/Android/sdk/ndk/22.1.7171670/build/cmake/android.toolchain.cmake:381 (message):
  Invalid Android STL: libc++.
Call Stack (most recent call first):
  /Users/ernesto.messina/.conan/data/ninja/1.11.1/local/workbench/build/4cd8a0226669e0be90b7c9a8fcdc9ee41453a7d3/build/generators/conan_toolchain.cmake:27 (include)
  /opt/homebrew/Cellar/cmake/3.24.2/share/cmake/Modules/CMakeDetermineSystem.cmake:124 (include)
  CMakeLists.txt:6 (project)

CMake Error: CMake was unable to find a build program corresponding to "Unix Makefiles".  CMAKE_MAKE_PROGRAM is not set.  You probably need to select a different build tool.
CMake Error: CMAKE_C_COMPILER not set, after EnableLanguage
CMake Error: CMAKE_CXX_COMPILER not set, after EnableLanguage
-- Configuring incomplete, errors occurred!

I've tried changing the SDK version, but got the same error. Also tried compiler.libcxx to c++_static and c++_shared, but got code errors, like not detecting the compiler correctly.

memsharded commented 1 year ago

Hi @TheSlackOne

I am curious, do you plan to run ninja itself in an Android device? Will you be building in the Android device? I haven't seen many users doing that, everybody is cross-compiling to Android, but definitely not building on an Android device.

memsharded commented 1 year ago

Also, I'd recommend as a first step to validate a setup, to start from a template like conan new mylib/1.0 -m=cmake_lib (for a library) or conan new myapp/1.0 -m=cmake_exe (for an application). When it works, then we can move to more advanced recipes. It is possible that recipes like the one for Ninja provided in ConanCenter cannot support this scenario of cross-compiling itself for Android (many times the build scripts hardcode assumptions, need to be patched, etc, in order to be built for other non mainstream setups)

TheSlackOne commented 1 year ago

Hi @TheSlackOne

I am curious, do you plan to run ninja itself in an Android device? Will you be building in the Android device? I haven't seen many users doing that, everybody is cross-compiling to Android, but definitely not building on an Android device.

Hi @memsharded, thanks for your answer :)

Good point, I don't need to build on Android, but just to provide the binaries or executables. The reason why I'm trying to build ninja is because I'm trying to cross-compile minizip-ng, and it has pkgconfig as a building dependency, which depends on ninja.

def build_requirements(self):
        self.build_requires("pkgconf/1.7.4")

I couldn't just get rid of this pkgconfig because it generated the cmake files to build minizip-ng.

Do you know how should I approach this?

SSE4 commented 1 year ago

you actually may build ARMv8 ninja binary, e.g. for termux

memsharded commented 1 year ago

Good point, I don't need to build on Android, but just to provide the binaries or executables. The reason why I'm trying to build ninja is because I'm trying to cross-compile minizip-ng, and it has pkgconfig as a building dependency, which depends on ninja.

Yes, this is the idea of tool_requires/build_requires. If pkgconf has a tool_requires/build_requires dependency to ninja, this ninja should only be needed in the "build" context, that is, your current machine (not Android). But I don't see this dependency on conan-center-index repo, so I guess it is your private recipe?

memsharded commented 1 year ago

you actually may build ARMv8 ninja binary, e.g. for termux

Of course, cross-building for ARMv8 is totally fine. The weird thing is building Ninja for Android, this is the thing that in my opinion should probably never be necessary.

SpaceIm commented 1 year ago

I guess your configuration of conan-package-tools for a cross-build of a mixture of conan v1 & v2 recipes is incorrect:

And finally I don't understand why you force cross-build of ninja. This recipe is an internal detail of other recipes you really want to cross-build, and ninja is in build requirements of these recipes so unless you are trying to trick conan client (or maybe it's a consequence of this incorrect configuration of conan-package-tools), it shouldn't be cross-built.

TheSlackOne commented 1 year ago

Good point, I don't need to build on Android, but just to provide the binaries or executables. The reason why I'm trying to build ninja is because I'm trying to cross-compile minizip-ng, and it has pkgconfig as a building dependency, which depends on ninja.

Yes, this is the idea of tool_requires/build_requires. If pkgconf has a tool_requires/build_requires dependency to ninja, this ninja should only be needed in the "build" context, that is, your current machine (not Android). But I don't see this dependency on conan-center-index repo, so I guess it is your private recipe?

Hi @memsharded, thanks, I believe you are right, I'm just trying to get rid of the building dependency now.

Here the minizip-ng recipie that defines pkgconf as a building dependency. I understand this is why it tries to build pkgconfig (and ninja so) when cross-compiling, but I can't just remove it.

FYI: @SpaceIm

SpaceIm commented 1 year ago

Here the minizip-ng recipie that defines pkgconf as a building dependency. I understand this is why it tries to build pkgconfig (and ninja so) when cross-compiling, but I can't just remove it.

Sure, and my answer is still the same: your configuration of conan-package-tools is likely wrong. Since pkgconf is in build requirements of minizip-ng, conan should try to do a native build of pkgconf (and its requirements & build requirements), but you don't specify a build profile, so how conan can know which native compiler to use?

TheSlackOne commented 1 year ago

Sure, and my answer is still the same: your configuration of conan-package-tools is likely wrong. Since pkgconf is in build requirements of minizip-ng, conan should try to do a native build of pkgconf (and its requirements & build requirements), but you don't specify a build profile, so how conan can know which native compiler to use?

@SpaceIm The native build works perfectly just by running conan create. Finishes without any error.

My cross-building script looks pretty much like this

if __name__ == "__main__":
    if os.getenv("ANDROID_NDK"):
        android_ndk_root = os.environ["ANDROID_NDK"]
    else:
        print("Need to define ANDROID_NDK environment variable")
        exit()

    builder = ConanMultiPackager(username=username, channel=channel)
    builder.build_policy = "missing"

    builder.add(settings={
        "compiler.cppstd": "17",
        "os": "Android",
        "arch": "armv8",
        "build_type": "Release",
        "os.api_level": "24",
        "compiler": "clang",
        "compiler.version": "13",
        "compiler.libcxx": "libc++",
        "os_build": build_os, #"Macos",
        "arch_build": "armv8"
    }, options={}, env_vars={}, build_requires={})

    builder.run()

I expect Conan will get the building settings from the above, not from my Conan profile. Here my Conan default profile

[settings]
os=Macos
os_build=Macos
arch=armv8
arch_build=armv8
compiler=apple-clang
compiler.version=13
compiler.libcxx=libc++
build_type=Release
[options]
[env]
[conf]
tools.android:ndk_path=/Users/ernesto.messina/Library/Android/sdk/ndk/22.1.7171670

I still don't know how to avoid cross-compiling ninja when cross-compiling minizip-ng :(

SpaceIm commented 1 year ago

Your build profile should not refer to Android NDK, it should be a simple profile for your build machine:

[settings]
os=Macos
arch=armv8
compiler=apple-clang
compiler.version=13
compiler.libcxx=libc++
build_type=Release
[options]
[env]
[conf]

Your host profile should be something like this:

[settings]
os=Android
os.api_level=24
arch=armv8
compiler=clang
compiler.version=14
compiler.libcxx=c++_shared
build_type=Release
[options]
[env]
# Recipes based on conan v1 helpers listen this env var (but not conf below)
CONAN_CMAKE_ANDROID_NDK=<path/to/android-ndk>
[conf]
# Recipes based on conan v2 helpers listen this conf (but not env var above)
tools.android:ndk_path=<path/to/android-ndk>

Then in conan-package-tools, there are 2 environment variables to set build & host profile: CONAN_BASE_PROFILE_BUILD for build profile and CONAN_BASE_PROFILE for host profile. see https://github.com/conan-io/conan-package-tools#specifying-build-context-for-cross-building

It's worth noting that you may have to explicilty set CC, CXX, AR, AS, LD, STRIP, RANLIB, STRIP, OBJCOPY, OBJDUMP, READELF, ELFEDIT in [env] and [buildenv] of your host profile, because not all recipes are CMake based, so underlying build system may need some help.

TheSlackOne commented 1 year ago

Your build profile should not refer to Android NDK, it should be a simple profile for your build machine:

Thanks, @SpaceIm. I've set the default profile as you described, but compiler.version=13, I've set compiler.version=11 Also created a android profile as you've mentioned.

Then I've added to my cross-building script builder.run(base_profile_name="android", base_profile_build_name="default")

Now it reports missing minizipConfig.cmake


minizip-ng/3.0.7@local/workbench (test package): Calling build()
-- Android: Targeting API '24' with architecture 'arm64', ABI 'arm64-v8a', and processor 'aarch64'
-- Android: Selected unified Clang toolchain
-- The C compiler identification is Clang 11.0.5
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /Users/ernesto.messina/Library/Android/sdk/ndk/22.1.7171670/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Conan: called by CMake conan helper
-- Conan: Adjusting output directories
-- Conan: Using cmake targets configuration
-- Library minizip-ng found /Users/ernesto.messina/.conan/data/minizip-ng/3.0.7/local/workbench/package/0c10f645557980957d61516fe6df5a1f72644c63/lib/libminizip-ng.a
-- Library z found /Users/ernesto.messina/.conan/data/zlib/1.2.12/_/_/package/2b2f1542da8962686483ada8f65b191b8b9dc9da/lib/libz.a
-- Library lzma found /Users/ernesto.messina/.conan/data/xz_utils/5.2.5/_/_/package/2b2f1542da8962686483ada8f65b191b8b9dc9da/lib/liblzma.a
-- Library ssl found /Users/ernesto.messina/.conan/data/openssl/1.1.1k-6bcaff/release/workbench/package/2b2f1542da8962686483ada8f65b191b8b9dc9da/lib/libssl.a
-- Library crypto found /Users/ernesto.messina/.conan/data/openssl/1.1.1k-6bcaff/release/workbench/package/2b2f1542da8962686483ada8f65b191b8b9dc9da/lib/libcrypto.a
-- Library iconv found /Users/ernesto.messina/.conan/data/libiconv/1.17/_/_/package/2b2f1542da8962686483ada8f65b191b8b9dc9da/lib/libiconv.a
-- Library charset found /Users/ernesto.messina/.conan/data/libiconv/1.17/_/_/package/2b2f1542da8962686483ada8f65b191b8b9dc9da/lib/libcharset.a
-- Conan: Adjusting default RPATHs Conan policies
-- Conan: Adjusting language standard
-- Conan setting CPP STANDARD: 17 WITH EXTENSIONS OFF
-- This project seems to be plain C, using 'Clang' compiler
-- Conan: Compiler Clang>=8, checking major version 11
-- Conan: Checking correct version: 11
-- Conan: C++ stdlib: libc++
CMake Error at CMakeLists.txt:7 (find_package):
  Could not find a package configuration file provided by "minizip" with any
  of the following names:

    minizipConfig.cmake
    minizip-config.cmake

  Add the installation prefix of "minizip" to CMAKE_PREFIX_PATH or set
  "minizip_DIR" to a directory containing one of the above files.  If
  "minizip" provides a separate development package or SDK, be sure it has
  been installed.

-- Configuring incomplete, errors occurred!
SpaceIm commented 1 year ago

I don't know, maybe you have a modified minizip-ng test package, or some weirdness in conan-package-tools. At least minizip-ng and all its dependency graph builds fine now for you.

All I can say is that I can build from source minizip-ng, its dependency graph, and build its test package on my mac Intel with:

default profile (build)

[settings]
os=Macos
arch=x86_64
compiler=apple-clang
compiler.version=14
compiler.libcxx=libc++
build_type=Release
[options]
[tool_requires]
[env]
[buildenv]
[conf]
tools.apple:sdk_path=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk

android-24-armv8-clang profile (host)

[settings]
os=Android
os.api_level=24
arch=armv8
compiler=clang
compiler.version=14
compiler.libcxx=c++_shared
build_type=Release
[options]
[tool_requires]
android-ndk/r25
[env]
[buildenv]
[conf]
conan create . minizip-ng/3.0.7@ -pr:b default -pr:h android-24-armv8-clang -b missing
SpaceIm commented 1 year ago

Its worth noting that android-ndk recipe in conan-center forces CMAKE_FIND_ROOT_PATH_MODE_PACKAGE to BOTH as a workaround to conan v1 helpers limitations. Since you manually inject android-ndk and not rely on android-ndk recipe, this might be root cause of this failure to find minizip-config.cmake in test package.