Skip to content

Introduce CMake support for SwiftFoundationICU #22

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

Merged
merged 1 commit into from
Jun 14, 2024

Conversation

iCharlesHu
Copy link
Contributor

@iCharlesHu iCharlesHu commented Apr 24, 2024

Add Cmake support for SwiftFoundationICU which includes:

  • CMakeLists.txt in major directories
  • module.modulemap for each include directory (these files will be automatically generated during export)

Unfortunately we also had to merge all ICU modules into one as part of this change.

@iCharlesHu iCharlesHu force-pushed the charles/cmake-support branch from 2643f35 to d91484a Compare April 24, 2024 17:29
@iCharlesHu iCharlesHu marked this pull request as ready for review April 24, 2024 17:33
@iCharlesHu iCharlesHu force-pushed the charles/cmake-support branch from 104b73f to 847a8ad Compare April 30, 2024 21:33
@iCharlesHu
Copy link
Contributor Author

@swift-ci build toolchain

Copy link

@etcwilde etcwilde left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, that should be enough of an initial review.

The layout of the include directories are confusing me a bit and I'm not sure what the goal is. They're labelled as not propagating to things that depend on the target, but then you turn around and target_include_directories across the directories to get to them again, so they're still treated as PUBLIC, but weirdly. If you're trying to prevent them from being exposed if someone installed the library, we can certainly do that.

These two are probably what you're looking for?

$<BUILD_INTERFACE: --directory-- >
$<INSTALL_INTERFACE: --directory-- >

So for ICUCommon, I think it might look something like this:

target_include_directories(ICUCommon
  PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
    $<BUILD_INTERFACE:unicode>
    include/)

@etcwilde
Copy link

etcwilde commented May 3, 2024

I see one of your questions on the email notification, but not in the GitHub UI, so I'll answer it here.

These are the shared settings across different targets. Should I repeat the same settings in each target?

CMake scoping rules are kind of strange to get used to at first. The directory structure defines scopes. Things set add_compile_definition, add_compile_options, etc... are applied to all targets defined in CMake files under add_subdirectory calls made after the add_compile_definitions/add_compile_options call. It's a scripting language too though, so the order of the add_compile_definitions and add_subdirectory calls does matter.

Copy link

@etcwilde etcwilde left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking much better. Thank you for making those changes. Still a few small comments, but this should work.

I think we can wait on the install story for now and add one later if necessary.

##
##===----------------------------------------------------------------------===##

cmake_minimum_required(VERSION 3.24)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this for CI? It might be worth trying this build across a few versions of CMake and doing something like this

cmake_minimum_required(VERSION 3.24...3.29)

That way we pick up CMP0156 and CMP0157. We definitely want these two if folks have a new enough CMake since it will fix the linker warning about duplicate libraries and will result in faster incremental builds due to the better Swift build graphs in CMake 3.29.

At the very least, between the cmake_minimum_required and project invocations, can you enable those policies? Something like this:

cmake_minimum_required(VERSION 3.24)

if(POLICY CMP0156)
    # Deduplicate linked libraries where appropriate
    cmake_policy(SET CMP0156 NEW)
endif()

if(POLICY CMP0157)
    # New Swift build model: improved incremental build performance and LSP support
    cmake_policy(SET CMP0157 NEW)
endif()

project(SwiftFoundationICU
    LANGUAGES CXX Swift)
...

CMakeLists.txt Outdated
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_Swift_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/swift)

option(BUILD_SHARED_LIBS ON)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't need this bit. CMake knows about BUILD_SHARED_LIBS already and will implicitly set any add_library that doesn't specify SHARED or STATIC with the selection there.

https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that this is trying to build as shared libraries by default.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, then it's fine to leave this line. Don't need 30 - 34 though. Those are handled implicitly by add_library if the library type isn't specified.

##===----------------------------------------------------------------------===##

file(GLOB_RECURSE _FoundationICUSources "FoundationICU/*.swift")
add_library(FoundationICU ${LIB_TYPE} ${_FoundationICUSources})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't need ${LIB_TYPE} here. BUILD_SHARED_LIBS will do that for you.

${CMAKE_CURRENT_BINARY_DIR}/SwiftFoundationICUConfig.cmake)

get_property(SWIFT_FOUNDATION_ICU_EXPORTS GLOBAL PROPERTY SWIFT_FOUNDATION_ICU_EXPORTS)
export(TARGETS ${SWIFT_FOUNDATION_ICU_EXPORTS}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I think we can leave the install story up in the air for the time being. If we need to figure it out, we can come up with how that should work later.


add_subdirectory(icuSources)
add_subdirectory(swift)
add_subdirectory(cmake/modules)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eh, we can leave it for now.

Copy link

@etcwilde etcwilde left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, missed a few things. Just based on the comment made above that we only want to expose FoundationICU, we should explicitly specify that the other libraries are STATIC so that BUILD_SHARED_LIBS doesn't turn them into dynamic libraries. That said, if BUILD_SHARED_LIBS is FALSE, we'll still need the static archives available for linking, but like I say, we can defer on the install story for now.

##===----------------------------------------------------------------------===##

file(GLOB_RECURSE _ICUCommonSources "*.cpp")
add_library(ICUCommon ${_ICUCommonSources})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make this STATIC

##===----------------------------------------------------------------------===##

file(GLOB_RECURSE _ICUI18NSources "*.cpp")
add_library(ICUI18N ${_ICUI18NSources})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make this STATIC

##===----------------------------------------------------------------------===##

file(GLOB_RECURSE _ICUIOSources "*.cpp")
add_library(ICUIO ${_ICUIOSources})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make this STATIC

##===----------------------------------------------------------------------===##

file(GLOB_RECURSE _ICUStubDataSources "*.cpp")
add_library(ICUStubData ${_ICUStubDataSources})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make this STATIC

$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/unicode/>
include/)

target_link_libraries(ICUCommon PRIVATE stdc++)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be necessary. From chatting with @compnerd, we might want to enable C++ interop so that the linker-driver is clang++ instead of just clang so that it implicitly links the C++ standard library.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, if we enable the c++ interop, we should get the c++ driver as the linker driver, ensuring that we get the c++ runtime linked properly. Additionally, users can just specify -Xclang-linker -stdlib=... to get the desired c++ runtime.

Copy link

@etcwilde etcwilde Jun 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, Swift doesn't use clang++ as the linker driver when C++-interop is enabled, just clang (except in Swift 5.9):
https://github.com/apple/swift-driver/blob/b0300a86653daf63466a47841983539b47bf7cde/Sources/SwiftDriver/Jobs/GenericUnixToolchain%2BLinkerSupport.swift#L62-L97

@iCharlesHu iCharlesHu force-pushed the charles/cmake-support branch 2 times, most recently from 745992d to 9852007 Compare May 31, 2024 21:01
@iCharlesHu iCharlesHu force-pushed the charles/cmake-support branch 2 times, most recently from 1f40e48 to 67312f3 Compare June 7, 2024 22:51
@jmschonfeld jmschonfeld requested a review from etcwilde June 7, 2024 22:55
Copy link
Contributor

@jmschonfeld jmschonfeld left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM now but since I helped a lot with this I'll leave it to the others for final approval 🙂

PUBLIC
include/)

target_link_libraries(_FoundationICU PRIVATE stdc++)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an odd one since we have to guess what flavor of C++ folks are using since the Swift driver decided to not help us out and tell us what it's using (actually, I think part of the problem is that it doesn't know what flavor of C++ we're using either leaving it as an exercise to the user to pass all the right flags).
I know this isn't going to work for Android (uses libcxx) nor for Windows (which can use the Windows C++ runtime, but I think libcxx might also work there too so it could go either way). I think it should be customizable at distribution time since the person putting together the SDK will likely have the best chance of knowing which C++ runtime they're building the SDK with. At least until the Swift compiler learns how to specify what C++ runtime you're working with.

In theory, we could do something like this to provide reasonable defaults:

if(Linux)
set(SwiftFoundation_CXX_RUNTIME_default stdc++)
elseif(Android)
set(SwiftFoundation_CXX_RUNTIME_default c++)
elseif(WIN32)
set(SwiftFoundation_CXX_RUNTIME_default WhateverWindowsUses)
endif()
set(SwiftFoundation_CXX_RUNTIME "${SwiftFoundation_CXX_RUNTIME_default}" CACHE STRING
    "C++ runtime that SwiftFoundation is built against")

@compnerd, do you have a better idea?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not leave it to the user? If we set the linker language to C++, the C++ driver will be used. The user can add in -stdlib=libc++ or -stdlib=libstdc++ if they are using clang. If they are using cl, that will default to the MS STL, as would clang-cl.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is related to using c++ interop at link time. The one thing that we should be careful of is if that gets serialized into the module.

Copy link
Contributor Author

@iCharlesHu iCharlesHu Jun 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It turns out we don't actually need it... I deleted it.

@iCharlesHu
Copy link
Contributor Author

Addressed @etcwilde 's comments.

add_library(_FoundationICU)

target_include_directories(_FoundationICU
PRIVATE

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're adding each subdirectory as an include directory with target_include_directories(_FoundationICU PRIVATE .), we shouldn't need any of these PRIVATE include directories here either.

Copy link

@etcwilde etcwilde left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, I think this looks good enough structurally.
One thing I noticed is that it looks like stubdata is not part of any target.
Is that intentional?

@jmschonfeld
Copy link
Contributor

One thing I noticed is that it looks like stubdata is not part of any target.
Is that intentional?

Yes that's intentional - the stub data is part of the icu project and part of the sources that get copied over into this repo, but it is not used because we provide data that is built directly into the binary. The stub data is only necessary to build a version of ICU without any data files. We can probably remove that in a future PR

@iCharlesHu iCharlesHu force-pushed the charles/cmake-support branch from 2dea4ba to 3122441 Compare June 14, 2024 16:51
@iCharlesHu iCharlesHu merged commit 2ba3887 into swiftlang:main Jun 14, 2024
@iCharlesHu iCharlesHu deleted the charles/cmake-support branch June 14, 2024 16:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants