Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[bug] creating iOS packages using simple toolchains broken as of conan 1.26 #7493

Closed
DoDoENT opened this issue Aug 4, 2020 · 9 comments · Fixed by #7512
Closed

[bug] creating iOS packages using simple toolchains broken as of conan 1.26 #7493

DoDoENT opened this issue Aug 4, 2020 · 9 comments · Fixed by #7512
Assignees
Milestone

Comments

@DoDoENT
Copy link
Contributor

DoDoENT commented Aug 4, 2020

Consider this simple iOS toolchain (based on this toolchain):

cmake_minimum_required( VERSION 3.14 )

set( CMAKE_C_COMPILER_ID   AppleClang )
set( CMAKE_CXX_COMPILER_ID AppleClang )

if( NOT ${CMAKE_GENERATOR} MATCHES "Xcode" )
    message( FATAL_ERROR "iOS toolchain supports only XCode generator" )
endif()

# Standard settings
set( CMAKE_SYSTEM_NAME      iOS    )
set( CMAKE_OSX_SYSROOT      "iphoneos" )
set( CPACK_SYSTEM_NAME      iOS    )
set( CMAKE_SYSTEM_VERSION   8.0    )
set( CMAKE_SYSTEM_PROCESSOR arm    )
set( CMAKE_OSX_ARCHITECTURES armv7 armv7s arm64 i386 x86_64 )

# Skip the platform compiler checks for cross compiling (or not)...
set( CMAKE_CXX_COMPILER_WORKS true CACHE STRING "Skip CMake compiler detection (requires a functioning code signing identity and provisioning profile)." )
set( CMAKE_C_COMPILER_WORKS   ${CMAKE_CXX_COMPILER_WORKS} )
set( CMAKE_XCODE_EFFECTIVE_PLATFORMS "-universal;-iphonesimulator;-iphoneos;" )
set( CMAKE_IOS_DEVELOPER_ROOT        "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer" )
set( CMAKE_XCODE_ATTRIBUTE_SDKROOT   iphoneos ) # "Latest iOS"

set( XCODE_ATTRIBUTE_CFLAGS_armv7  "-mcpu=cortex-a9 -mfpu=neon" ) 
set( XCODE_ATTRIBUTE_CFLAGS_armv7s "-mcpu=swift"                )
set( CMAKE_XCODE_ATTRIBUTE_OTHER_CFLAGS[arch=armv7]          "${XCODE_ATTRIBUTE_CFLAGS_armv7}  $(OTHER_CFLAGS)"         )
set( CMAKE_XCODE_ATTRIBUTE_OTHER_CFLAGS[arch=armv7s]         "${XCODE_ATTRIBUTE_CFLAGS_armv7s} $(OTHER_CFLAGS)"         )
set( CMAKE_XCODE_ATTRIBUTE_OTHER_CPLUSPLUSFLAGS[arch=armv7]  "${XCODE_ATTRIBUTE_CFLAGS_armv7}  $(OTHER_CPLUSPLUSFLAGS)" )
set( CMAKE_XCODE_ATTRIBUTE_OTHER_CPLUSPLUSFLAGS[arch=armv7s] "${XCODE_ATTRIBUTE_CFLAGS_armv7s} $(OTHER_CPLUSPLUSFLAGS)" )

Let's create a simple package that contains this toolchain:

import os

from conans import ConanFile


class IostoolchainConan(ConanFile):
    name = "IosToolchain"
    version = "1.2.0"
    # obtain sources, etc.

    def package(self):
        self.copy('ios.toolchain.cmake')

    def package_info(self):
        self.env_info.CONAN_CMAKE_TOOLCHAIN_FILE = os.path.join(self.package_folder, 'ios.toolchain.cmake')
        self.env_info.CONAN_CMAKE_CUSTOM_iOS_ALLOW_UNIVERSAL_BUILD = 'ON'

    def package_id(self):
        self.info.header_only()

Also, let's create a conan profile using this toolchain:

[settings]
os=iOS
os.version=8.0
os_build=Macos
arch=ios_fat
arch_build=x86_64
compiler=apple-clang
compiler.libcxx=libc++
build_type=Release
compiler.version=11.0.3

[build_requires]
IosToolchain/1.2.0@microblink/stable

[env]
CONAN_CMAKE_GENERATOR = Xcode

Now, let's use this profile to create some package

conan create . user/testing -pr ios-profile

With Conan 1.25.2 and earlier, everything works. With Conan 1.26.1 and later (also tested with 1.28.0), I get the following CMake configuration error:

CMake Error at /Applications/CMake.app/Contents/share/cmake-3.18/Modules/Platform/iOS-Initialize.cmake:4 (message):
  
  /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk
  is not an iOS SDK
...
-- Configuring incomplete, errors occurred!

The problem can be traced to how conan's CMake build helper invokes cmake:

cmake -G "Xcode" -DCMAKE_OSX_ARCHITECTURES="None" -DCMAKE_OSX_SYSROOT="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk" -DCMAKE_TOOLCHAIN_FILE="/Users/dodo/.conan/data/IosToolchain/1.2.0/microblink/stable/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/ios.toolchain.cmake" -DCONAN_IN_LOCAL_CACHE="ON" -DCONAN_COMPILER="apple-clang" -DCONAN_COMPILER_VERSION="11.0.3" -DCONAN_LIBCXX="libc++" ...

The problematic part is -DCMAKE_OSX_SYSROOT="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk" - this overrides the CMAKE_OSX_SYSROOT variable as set by the toolchain. Also problematic is -DCMAKE_OSX_ARCHITECTURES="None" that does the same.

When executed with CONAN_PRINT_RUN_COMMANDS=1 by using conan 1.25.2 (or earlier), it can be seen that those two variables are not given via command-line prior to CMAKE_TOOLCHAIN_FILE.

I know that from v1.26 there is support for new toolchain mechanism, but I didn't get how should I modify my toolchain files to work with the new conan.

@memsharded
Copy link
Member

This change was introduced in #7164, by @SSE4

Could you please have a look to this issue, @SSE4 ? Many thanks

@memsharded memsharded added this to the 1.29 milestone Aug 4, 2020
@memsharded
Copy link
Member

Maybe this would be a reasonable fix?

diff --git a/conans/client/build/cmake_flags.py b/conans/client/build/cmake_flags.py
index ca91d250b..439517110 100644
--- a/conans/client/build/cmake_flags.py
+++ b/conans/client/build/cmake_flags.py
@@ -298,7 +298,8 @@ class CMakeDefinitionsBuilder(object):
             definitions["CMAKE_OSX_ARCHITECTURES"] = tools.to_apple_arch(arch)
             # xcrun is only available on macOS, otherwise it's cross-compiling and it needs to be
             # set within CMake toolchain
-            if platform.system() == "Darwin":
+            toolchain_file = os.getenv("CONAN_CMAKE_TOOLCHAIN_FILE")
+            if platform.system() == "Darwin" and not toolchain_file:
                 definitions["CMAKE_OSX_SYSROOT"] = tools.XCRun(self._conanfile.settings).sdk_path

Also @DoDoENT we would be interested in your feedback for the toolchain() feature. This will be the proposed future integration with build systems in Conan 2.0.

We are preparing a patch release Conan 1.28.1, are you planning to upgrade to 1.28 or to which other version?

@memsharded memsharded modified the milestones: 1.29, 1.28.1 Aug 4, 2020
@SSE4
Copy link
Contributor

SSE4 commented Aug 5, 2020

I think it requires a bit different fix. CMAKE_OSX_ARCHITECTURES="None" is definitely wrong and should never be passed.
sysroot computed for iOS also looks incorrect, might be a bug in our XCRun helper as well.

@SSE4
Copy link
Contributor

SSE4 commented Aug 5, 2020

copying comment from @anuraaga also:

I found this broke a build workflow I was using with conan. With careful setting of environment variables like SDKROOT, I've found it's possible to build packages on Mac without even installing X-Code or the command line tools, only using Anaconda and its toolchain. However, now conan is always calling the command line tools even when SDKROOT is set, which is different from the cmake behavior. Can we tweak this so xcrun is only executed if sdkroot is not available?

@memsharded
Copy link
Member

Ok, would be then an easy fix?, do you think you could prepare a patch for 1.28.1 @SSE4 ?

@DoDoENT
Copy link
Contributor Author

DoDoENT commented Aug 5, 2020

We are preparing a patch release Conan 1.28.1, are you planning to upgrade to 1.28 or to which other version?

We are currently stuck at 1.25.2 due to this bug (I've actually discovered it soon after 1.26.1 was released, by was unfortunately too busy to test the behaviour thoroughly, make sure that it really is a bug and not something on my side and write a proper bug report - sorry about that).

As soon this gets fixed, we will upgrade to 1.28.1, provided that we don't find some other new bugs that would stop us in upgrade along the way 😛

Also @DoDoENT we would be interested in your feedback for the toolchain() feature. This will be the proposed future integration with build systems in Conan 2.0.

I didn't play with this feature, but I've read the documentation as part of trying to find a workaround for this very issue and this got me confused. Basically the current toolchain feature of conan is the same thing as CMake build helper, with the difference that all cmake flags are encoded into a conan_toolchain.cmake file, instead of being given directly via command line. This looks very useful during the development of the package, but the name toolchain is misleading. Yes, it abuses the CMAKE_TOOLCHAIN_FILE of CMake to achieve its goal, but I would argue that this is not a "toolchain", but merely a "package-specific serialized build options".

Here is what I expect from the toolchain (as a word): a set of tools, utilities, libraries and configuration of those needed to build any package. This includes:

  • compilers (gcc, clang, msvc, ...)
  • tools (cmake, bazel, automake, ninja, ...)
  • utilities (ar vs llvm-ar, ranlib vs llvm-ranlib, ...)
  • libraries
    • STL: libc++, libstdc++, libstdc++11, MS STL, ...
    • libc: glibc by versions, muslc by versions, bionic by versions
    • system libraries, for example:
      • Windows SDK version: WinXP, Win7, Win10, ...
      • MacOS deployment target
      • iOS deployment target
      • Android minimum API level
      • Emscripten SDK version
      • ...
  • configuration
    • compile flags (e.g. I want to have different toolchains for centos-gcc-generic-x86-64 and centos-gcc-haswell - in the haswell case I want to enforce -march=haswell across entire dependency graph and make sure the package ID are different than in generic x64 case)
    • LTO (if I enable LTO, I want all packages in my dependency graph to be built with -flto or the equivalent /ltcg is msvc case - note that enabling/disabling such feature affects binary compatibility between packages depending on the compiler being used. For example, if LTO is not used, it's possible to link together packages built with slightly different compiler version (e.g. gcc 9.2 and gcc 9.3; msvc 19.23 and msvc 19.24, apple-clang 11.0.0 and apple-clang 11.0.3), but if LTO is used this is no longer possible (except for Linux clang, which has compatible bitcode between, e.g., 10.0.0 and 10.0.1))
    • PIC
    • sanitizers (address, leak, memory, undefined behaviour, ...)
    • some custom flags (for example I want to have different clang toolchains for -Os and -Oz optimization-for-size flags, in order to compare impacts of those flags on the entire codebase/dependency graph)
    • tool configuration (e.g. CMake variables)

Ideally, a proper toolchain should be a conan package itself, with its own options (for example, clang toolchain may have options to toggle LTO, AddressSanitizer, ControlFlowSanitizer, ...). The only difference to the "normal" should be the ability to impose settings to the rest of the dependency graph at the cost of having only a single toolchain per build context in the graph. At the moment, this is only possible via the command line or by using profiles.

As you can see, the toolchains are more related to profiles and build requirements than the concrete packages - this is what first confused me when I saw toolchain method as part of the package and why I argue that the name is misleading.

Actually, to be fair, most of the abilities I've described above are already possible with the current conan model, although it requires some orchestration between profiles and conan packages which requires some conan experience and can be very intimidating for the beginners. But it works quite well when configured properly. Here is how we currently do it at Microblink:

  • there is a single conan package called CMakeBuild which contains common cmake utilities and compile flags we want to impose to all our packages while building them. Specific packages may override some flags, but this is clearly visible in the PR diff and requires the approval of the senior developer (this is rarely used). This package is basically a wrapper around this open source project, but we also added some features that are specific for our in-house procedures.
  • every package is required to have a direct dependency to CMakeBuild so that whenever package ID of CMakeBuild changes, the package gets rebuilt
  • CMakeBuild is required to follow semantic versioning scheme, i.e. whenever a compile flag is changed that would alter link-compatibility or if we want to impose the new flag on the entire codebase, a major version needs to be raised. This is problematic for cases when you want to make a change to a single platform only. For example, I want to enable LTO for release builds for Windows: I add the flag into a cmake file, create a new CMakeBuild package with major version bumped and override the CMakeBuild package version at the most downstream level. This ensures that the entire dependency graph is rebuilt using LTO on windows, but it also causes rebuilds on other platforms (iOS, Android, Emscripten, ...) which had no changes. This wastes time and resources. In the ideal case, I would only want to make a new "windows" toolchain.
  • versions of the compiler, min SDK levels, STL versions and OS versions are managed by the profile files that need to be kept in sync with CMakeBuild versions. This is the most tricky part, as it's very easy to have different information in CMakeBuild and in profile (e.g. we had an internal bug where our CMakeBuild ensured fat iOS binaries to be produced with iOS deployment target 8.0, while the ios profile stated that architecture is x86_64 and deployment target 10.0).
  • we ensure that everything is in sync by using a custom cmake script that detects a profile from cmake settings, but this still requires that during development the developer correctly sets the cmake toolchain during cmake invocation. We have other helper scripts for that, but this is actually the part where the feature that conan currently calls a toolchain could come handy.
  • for cross-compilations, profiles additionally have a build_requires on conan package that actually contains the cross-toolchain (Emscripten or Android NDK). For iOS case, the "toolchain" is basically a repackaged CMakeBuild, but only those that bootstrap cmake into "iOS-mode", without those that may actually make binary different (this must be carefully repackaged because conan build_requirements do not affect package ID, and in this case, we want that).

So, I would seek-out to implement toolchains as an upgrade to profiles. Just like packages allow conanfile.txt for simple usages and conanfile.py for more complex use-cases (creating a package or more complex package consumer), I think that profiles should also be possible to exist in text-only simple form (just as is today) and in more complex "toolchain-package" form, as special conan packages that:

  • can impose settings on the dependency graph
  • may have build_requires and python_requires, optionally even requires
    • for using common python utilities and for depending on already-existing conan packages, such as cmake_installer, emsdk_installer, android_ndk, ...
    • if we don't allow requires for such packages, then the build procedure will have to ensure that all tools are repackaged into the toolchain (which makes sense for repackaging emsdk_installer, android_ndk and similar, but I'm not so sure about the purpose of repackaging cmake - for such case it may make sense to allow requires, but in some kind of "restricted" form. We need to discuss this further).
  • are optionally configurable
    • for example, a specific toolchain may have an option to enable/disable sanitizers, LTO, etc. The changed option would require different package IDs of the entire graph built using the toolchain-profile. Alternatively, we could require different toolchains for each change. This is how currently @vector-of-bool's dds does it, but I find this pretty annoying - I would prefer configurability.
  • should be able to update settings.yml
    • this is a bit tricky, but it may be required for some toolchains to add specific architectures, OS options or compiler versions that do not exist in the default settings.yml
    • for example, let's consider an iOS toolchain that ensures that produced binaries are all fat. It's obviously wrong to advertise package architecture as x86_64 as it will also contain arm64 and other slices. At Microblink, we use the ios_fat architecture, which does not exist in the default settings.yml, but we needed to manually add that to settings.yml and distribute it. Ideally, an iOS toolchain should be able to register new architecture to settings.yml during its installation.
    • another example: Emscripten. Emscripten does not allow for link-compatibility between their minor versions, while the used clang compiler advertises the same version. For example, binaries built with emsdk 1.39.16 are not link-compatible with binaries built with emsdk 1.39.11, while both emsdk versions advertise clang 6.0.2 for their fastcomp backend and clang 11.0.0 for their upstream backend. We currently address this issue by introducing special clang versions 6.0-emsdk-1.39.11, 6.0-emsdk-1.39.16, 11-emsdk-1.39.11, 11-emsdk-1.39.16, ..., in our settings.yml. Additionally, our emscripten internal toolchain allows for choosing whether you want to enable emscripten threads and SIMD support. We modelled that with OS options for the "Emscripten" OS in our settings.yml, but ideally a toolchain should be able to register these new possible compiler versions and OS settings entries to user's settings.yml upon toolchain installation.
  • can be distributed via Artifactory
    • currently, we are distributing profiles and custom settings.yml via zip files built by our Jenkins and CMakeBuild and ios/android/emsdk "toolchain packages" via artifactory. It would be much easier if a toolchain could be deployed to a conan repository on Artifactory and installed as "config-package/toolchain/smart-profile/call-it-as-you-wish" with conan config install. The installation would do the same as it currently does, but it would also trigger hooks in "toolchain packages" that will allow for custom updates of settings.yml as described above.

I hope this gives some good feedback about what I think about current conan's "toolchain" feature. Also, since conan tribe 2.0 messaging hasn't started yet, feel free to reference this comment there as soon as it starts.

@memsharded
Copy link
Member

Hi @DoDoENT

There is a proposed fix in #7512, if you would like to run it from sources and check everything is ok. The goal is to merge it and release it asap in Conan 1.28.1

@DoDoENT
Copy link
Contributor Author

DoDoENT commented Aug 6, 2020

There is a proposed fix in #7512, if you would like to run it from sources and check everything is ok.

I've just tried creating an iOS package with this patch and it works correctly - just like in 1.25.2. Thank you!

@a4z
Copy link
Contributor

a4z commented Nov 15, 2020

I wonder if this whole issue could have been avoided when this
conan-io/conan-center-index#1137
would have been pulled in and put into the documentation on how to build for iOS with conan

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants