Skip to content

Xcode project for Carthage support#55

Merged
krzyzanowskim merged 4 commits into
krzyzanowskim:masterfrom
ilammy:carthage-support
Mar 22, 2019
Merged

Xcode project for Carthage support#55
krzyzanowskim merged 4 commits into
krzyzanowskim:masterfrom
ilammy:carthage-support

Conversation

@ilammy
Copy link
Copy Markdown
Contributor

@ilammy ilammy commented Mar 19, 2019

This adds an Xcode project to build OpenSSL frameworks. Carthage build system relies exclusively on Xcode projects so adding one adds support for building with Carthage. This is a modernized version of PR #27, and it should hopefully close the issue #31.

With this it is now possible to use OpenSSL via Carthage by putting the following line into Cartfile:

github "ilammy/OpenSSL" "carthage-support"

or, alternatively, with versions:

github "ilammy/OpenSSL" ~> 1.0.2

Then run carthage update which will download the source code and build the frameworks (or just download prebuilt binaries), and put them into Carthage directory.

Summary of changes

The changes are based off the 1.0.2.14 branch and do not use existing support scripts. Unfortunately, I could not find a way to reuse them from Xcode (but that should be the right way, probably). On the flip side, it's now possible to build framworks using xcodebuild from command-line.

Most of the changes are in Xcode XML stuff which is hard to review directly so I'll describe it here in more detail.

The project is configured as follows:

  • Two targets OpenSSL (iOS) and OpenSSL (macOS) which build Cocoa Touch and (desktop) Cocoa frameworks respectively. They support iOS 8.0+ and macOS 10.9+.

    Xcode project structure
  • Each target has a corresponding Xcode scheme that builds it. The schemes are shared which is required for Carthage to work.

    Xcode scheme setup
  • Both do not compile any new code and only combine prebuilt binaries already present in the repository.

    prebuilt target setup

Some interesting caveats about configuration:

  • Resulting frameworks are called openssl.framework for the sake of compatibility with existing header include usage like #include <openssl/evp.h> with all lowercase. This plays better with so-called 'modular includes' that are necessary to use the framework from Swift code.

    framework name configuration
  • Umbrella headers openssl.h are compiled manually, because apparently inclusion order is important for OpenSSL. This is important for Swift compiler, but mostly irrelevant to both Swift and Objective-C users.

  • In order to preserve the symbols from libssl.a and libcrypto.a we use a custom linker flag -all_load. It keeps the 'unused' symbols from being stripped out by the linker (which is the default behavior for static libraries).

    -all_load flag setup
  • Speaking of linker flags, the frameworks are explicitly not code-signed. This is expected for frameworks which should be signed only by the end-users (the application). Xcode does not make it easy (even now), but it seems I got it right...

    code signing setup

And that's probably it for the project configuration.

How to maintain this

Making releases

Carthage relies on git tags for versioning. Therefore, tagging a release for OpenSSL will also release the new version of Carthage package. Currently there are no suitable tags (1.0.2.14 does not contain Xcode project). You would probably like to make a new one (say, 1.0.2.14.1) which will serve OpenSSL 1.0.2o to Carthage users. After that when you release a new version, just tag it as usual and it will be made accessible via Carthage.

Prebuilt binaries

carthage build --archive will produce a ZIP archive that can be attached to a GitHub release. This will allow the users to avoid compiling OpenSSL themselves. Depending on the bandwidth it may be quicker to download an archive than to compile it from scratch.

Updating versions

It is also a good idea to keep the framework versions themselves in sync with the OpenSSL version. You can update them via Xcode here:

version update

Note that iOS and macOS frameworks are separate, you have to update both targets. Also note that App Store requires the bundles to have three-component versions, therefore we use slightly weird scheme 1.0.214 (= 1.0.2 + 14, where 14 is the ordinal value of 'o', patch version of OpenSSL).

When updating OpenSSL version umbrella headers may need to be updated manually. You'll need to update the Frameworks/{ios,macos}/openssl.h files so that they include all relevant headers from include-{ios,macos}/openssl. Headers from the a relevant directory should be included into the respective umbrella header.

This adds an Xcode project to build OpenSSL frameworks. Carthage build
system relies exclusively on Xcode projects so adding one adds support
for building with Carthage.

The changes are based off 1.0.2.14 branch and do not use existing
support scripts. Unfortunately, I could not find a way to reuse them
(but that should be the right way). On the flip side, it's now possible
to build framworks using "xcodebuild" from command-line.

The project is configured as follows:

  - Two targets "OpenSSL (iOS)" and "OpenSSL (macOS)" which build
    Cocoa Touch and (desktop) Cocoa frameworks respectively. They
    support iOS 8.0+ and macOS 10.9+.

  - Each target has a corresponding Xcode scheme that builds it.
    The schemes are *shared* which is required for Carthage to work.

  - Both do not compile any new code and only combine prebuilt
    binaries already present in the repository.

Some interesting caveats about configuration:

  - Resulting frameworks are called "openssl.framework" for the sake
    of compatibility with existing header include usage like
    "#include <openssl/evp.h>" with all lowercase. This plays better
    with so-called 'modular includes' that are necessary to use the
    framework from Swift code.

  - Umbrella headers "openssl.h" are compiled manually because
    apparently inclusion order is important for OpenSSL. This is
    important for Swift compiler, but mostly irrelevant to both
    Swift and Objective-C users.

  - In order to preserve the symbols from libssl.a and libcrypto.a
    we use a custom linker flag "-all_load". It keeps the 'unused'
    symbols from being removed by the linker (which is the default
    behavior)

  - Speaking of linker flags, the frameworks are explicitly *not*
    code-signed. This is expected for frameworks which should be
    signed only by the end-users (the application). Xcode does not
    make it easy (even now), but it seems I got it right...

And that's probably it for the project configuration.
@vixentael
Copy link
Copy Markdown

@krzyzanowskim Marcin please take a look :)

We are not a Carthage-gurus ourselves, but start using it recently, so we believe that Carthage-OpenSSL will help to spread it among other projects.

We've tested various combinations before submitting this PR, it works these ways:

  • usual iOS projects that use this OpenSSL-Carthage can be built and run for iPhone, can be archived for AppStore.

  • other libraries use Carthage and that depend on this OpenSSL-Carthage can be added to iOS/macOS apps, run on device, archived for AppStore.

  • tested on iOS (from iOS 10, but should work since iOS 8)

  • tested on macOS (from 10.13, but should works since 10.10)

  • tested with Swift v4+, I haven't spent time to test on previous versions

  • tested with ObjC (works 👌as always)

@krzyzanowskim
Copy link
Copy Markdown
Owner

krzyzanowskim commented Mar 20, 2019

When updating OpenSSL version umbrella headers may need to be updated manually. You'll need to update the Frameworks/{ios,macos}/openssl.h files so that they include all relevant headers from include-{ios,macos}/openssl. Headers from the a relevant directory should be included into the respective umbrella header

What's in Frameworks is overwritten by a create-framework.sh script. Since you compiled headers manually, it won't survive next update. What do you think could be done to improve script to generate the proper module header? Do you mind take care of that part?

@ilammy
Copy link
Copy Markdown
Contributor Author

ilammy commented Mar 21, 2019

@krzyzanowskim Hm... I'm not sure that it's feasible to actually derive the correct inclusion order from headers. However, I think it will be okay to automate the way I compiled the headers manually:

  • include "openssl/ssl.h" first
  • include all other headers in alphabetical order

This should work fine (at least for OpenSSL 1.0.2, I guess).

It turns out that AppStore requires bundles to have strictly three-part
semver-style versions. Replace "1.0.2.14" with "1.0.214" in the short
identifier. Note that we have to keep the patch version in there because
this is how the OS will decide on updates, so we have to keep it
different and at least somewhat ordered.
@ilammy
Copy link
Copy Markdown
Contributor Author

ilammy commented Mar 21, 2019

@krzyzanowskim, as a side note, we have found that App Store is actually quite picky about framework bundle versions. I have pushed a commit that changes the bundle version from 1.0.2.14 to 1.0.214 (three components), because that's how Apple requires it to be.

When you update to a newer OpenSSL this version has to be updated accordingly.

ilammy added 2 commits March 21, 2019 20:17
It turns out that we cannot simply take all the headers and include them
from the umbrella header. OpenSSL has some intricate interdependencies
there so we have to resort to hacks.

Instead of using "find" to locate all the headers (which returns them
in some unspecified order), make sure to include the "ssl.h" header
first and then follow it with other headers in alphabetic order.
This seems to work for the current OpenSSL version (1.0.2o).

Take care to not include the umbrella header into itself to avoid
blowing up compiler's include stack.
@ilammy
Copy link
Copy Markdown
Contributor Author

ilammy commented Mar 21, 2019

@krzyzanowskim I have updated the create-framework.sh script to produce the same header includes as in the manual files. It grabs "ssl.h" first and then appends whatever else is there in the Headers directory.

Copy link
Copy Markdown
Owner

@krzyzanowskim krzyzanowskim left a comment

Choose a reason for hiding this comment

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

Looks great thanks! I'm wondering, should we mention that in README.md as well?

@krzyzanowskim krzyzanowskim merged commit 6e0992c into krzyzanowskim:master Mar 22, 2019
@ilammy
Copy link
Copy Markdown
Contributor Author

ilammy commented Mar 22, 2019

Yeah, I guess it makes sense to drop a note about Carthage support in README.

Something like this...

Installation

CocoaPods

Latest stable version:

pod 'OpenSSL-Universal'

Latest development version:

pod 'OpenSSL-Universal', :git => 'https://github.com/krzyzanowskim/OpenSSL.git', :branch => :master

Carthage

Latest stable version:

github "krzyzanowskim/OpenSSL"

Latest development version:

github "krzyzanowskim/OpenSSL" "master"

The 'stable' version will be broken for Carthage until a new tag is made (as 1.0.2.14 tag does not contain an Xcode project).

I'm not sure how detailed that needs to be. I guess Carthage users should be able to figure out what needs to be done with their project to link against OpenSSL framework the way then need to.

@krzyzanowskim
Copy link
Copy Markdown
Owner

krzyzanowskim commented Mar 22, 2019

Anything in Installation section would be fine. It looks good. Do you want me to copy it, or will you PR?

@krzyzanowskim
Copy link
Copy Markdown
Owner

done e7e8d01

@ilammy ilammy deleted the carthage-support branch September 18, 2019 11:06
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.

3 participants