leetal / ios-cmake

A CMake toolchain file for iOS/iPadOS, visionOS, macOS, watchOS & tvOS C/C++/Obj-C++ development
BSD 3-Clause "New" or "Revised" License
1.87k stars 446 forks source link

Building guidance #214

Open TheVitya opened 3 weeks ago

TheVitya commented 3 weeks ago

Hello everyone!

I am about to build a simple 2 file test c++ library and than use it as a Cocoa Pod. The library does some basic logging.

I am struggling to find the right resources to what and how should I approach this problem. I am confused whether I should build a framework or build always from source.

I would be really really grateful if you could give me some advice or some examples what should I check or read. Also this might be also helpful for others.

What I do:

The file structure looks like this:

build/ --- MyFramework.framework include/ --- MyClass.hpp --- module.modulemap src/ --- MyClass.cpp CMakeLists.txt MyFramework.podspec

The CMake file looks like this:

CMAKE_MINIMUM_REQUIRED(VERSION 3.15)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

PROJECT(MyFramework)

set(SOURCES
  src/MyClass.cpp
)

set(HEADERS
  include/MyClass.hpp
  include/module.modulemap
)

add_library(MyFramework STATIC ${HEADERS} ${SOURCES})

target_include_directories(MyFramework
    PUBLIC
        ${CMAKE_CURRENT_SOURCE_DIR}/include
    PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/src
)

set_target_properties(MyFramework PROPERTIES
  FRAMEWORK TRUE
  VERSION 1.0.0
  SOVERSION 1.0.0
  PUBLIC_HEADER "${HEADERS}"
  FRAMEWORK_VERSION A
  XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET 13.0
  XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT "dwarf-with-dsym"
)

The podspec looks like this:

Pod::Spec.new do |s|
  s.name         = "MyFramework"
  s.version      = "1.0.0"
  s.summary      = "Core"
  s.homepage     = "Core"
  s.license      = "MIT"
  s.authors      = "Author"
  s.source       = { :git => 'https://example.com/MyFramework.git', :tag => s.version.to_s }

  s.platform     = :ios, '13.0'

  s.module_map = 'include/module.modulemap'

  s.vendored_frameworks = 'build/MyFramework.framework'
  s.public_header_files = 'build/MyFramework.framework/Headers/*'
  s.source_files = 'build/MyFramework.framework/Headers/*'

  s.libraries = 'c++'

end

Thank you in advance!

kambala-decapitator commented 3 weeks ago

if your C++ library doesn't require to use cmake, then you could describe it using podspec only, i.e. like you'd normally do for objc/swift libraries. And that'd build from source, of course.

what is your exact goal? Are you going to distribute this C++ library/framework in a binary form to other users?

TheVitya commented 3 weeks ago

Thank you for your reply!

Makes sense.

My end goal would be to create a library for general utilities for applications. For example, I would like to use a thirdparty HTTP client in C++ and link it with CMake using FetchContent. I know there are libraries that cannot be built for iOS, but lets assume that I want to link such a library.

So the general idea would be to have a library that holds some public APIs that I can import in Swift, ObjC or Java. Those APIs are defined by me, something like ApplicationUtils.postRequest, but that uses under the hood some other C++ library that is being linked by CMake.

Yes, I would like to distribute to others via Cocoa Pods if it is possible.

kambala-decapitator commented 3 weeks ago

I wouldn't recommend using FetchContent to get a dependency on popular libraries, rather use package manager like Conan or vcpkg (if that library doesn't provide Package.swift / podspec file) and use the standard find_package calls. BTW Conan can generate integration files for Xcode directly (xcconfig), so you might not even need CMake.

Yes, I would like to distribute to others via Cocoa Pods if it is possible.

I still wonder if you're going to distribute in binary or source form. Also you might want to add objc/swift wrappers right into your library to make lives of library users easier.

P.S. This project (ios-cmake toolchain) doesn't really have much to do with your question. It can be used to build CMake library / simplify generation of Xcode project from CMake, but everything else is really out of scope and more belongs to some CMake chat. Also maybe this example will help you: https://github.com/kambala-decapitator/cmake-ios-rpath-example

TheVitya commented 3 weeks ago

Thank you for your reply!

Conan absolutely makes sense, I would take a look at it. Also thank you for the example.

Please correct me if I am wrong, so basically what I would need are private C++ source files, a public header file for the library/framework, and some additional wrappers for easier usage.

Let's say, I would like distribute the pod in binary form using a framework, then I will use the ios-toolchain to create the framework, than I will add the built framework to my podspec. If the framework itself builds in Xcode, than that should be it.

Am I wrong?

kambala-decapitator commented 3 weeks ago

Please correct me if I am wrong, so basically what I would need are private C++ source files, a public header file for the library/framework, and some additional wrappers for easier usage.

yes, correct. Wrapper sources can also be made private if you wish so.

then I will use the ios-toolchain to create the framework

if you decide to use just cmake to describe/build your framework, then yes. But you could also create your C++ framework as Xcode project and use Conan to generate xcconfig files for dependencies, no cmake involved. Basically it's up to you / your requirements.

After that you'd simply vendor the built framework in your podspec like in your example. (btw in such case you don't need to specify source_files property iirc)

TheVitya commented 1 week ago

Thank you for your reply!

I've read a few articles since last week about how it might be done.

Mainly this one: chuck

As far as I correctly understood everything, this example should compile for iOS. Here is a quick example that I have just made to demonstrate what I am about to do. I would really appreciate if you could take a look at it: repo

Am I on a completely wrong path?

P.S. Sorry for the delay!

kambala-decapitator commented 1 week ago

Mainly this one: chuck

looks quite good. But I have a couple of recommendations where you shouldn't follow those articles:

  1. use Conan v2 and read Conan docs, ignore most of the info in that article as it's quite old
  2. most probably you don't need to create C API for iOS app:
    • if you go with objc wrappers, there's objc++ mode that can work with C++ directly
    • if you go with Swift, since Swift 5.9 there's interoperability with C++. Although it might look rather crippled as it currently lacks a lot of useful features, for simple cases it should be sufficient.

Here is a quick example that I have just made to demonstrate what I am about to do.

looks good to me. For distribution you'll probably need to adjust podspec to point to some remote location.

You can also check this Russian article that describes something similar, although it's a bit more advanced as it also needs bazel to build a dependency: https://habr.com/ru/companies/ru_mts/articles/838926/