Skip to content

Commit

Permalink
Build Hermes for iOS (facebook#332)
Browse files Browse the repository at this point in the history
Summary:
This PR allows building and running Hermes on iOS devices. It extends the prior work done by alloy that enabled Hermes to run on MacOS.

In a nutshell, the Hermes podspec was extended to include a dedicated framework for iOS devices. That framework itself is a universal framework that includes binaries for different iOS architectures and an iPhone simulator.

Every target is built into `build_<name>`, e.g. `build_iphoneos` and `build_iphonesimulator`. This convention (and helpers that I have created) allows for quick and easy extension to support iPad as well (and probably should happen as a part of this or next PR).

Here's dump of `destroot` folder to give you an idea how frameworks are located:
```
destroot/Library/Frameworks
├── iphoneos
│   ├── hermes.framework
│   └── hermes.framework.dSYM
├── iphonesimulator
│   ├── hermes.framework
│   └── hermes.framework.dSYM
└── macosx
    ├── hermes.framework
    └── hermes.framework.dSYM
```

Things work locally and the test mobile application prints "Hello World" just fine.

I have started an appropriate discussion regarding them around the lines where the todos are located.

As a follow-up,

## Follow-up

- [ ] a PR to React Native to allow using Hermes as a default engine
- [ ] facebook#332 (comment)

## Todo

- [x] facebook#332 (comment)

Pull Request resolved: facebook#332

Test Plan:
1. Clone the project
2. `cd test/ApplePlatformsIntegrationTestApp/` && `pod install`
3. Open `ApplePlatformsIntegrationTestApp.xcworkspace`
4. Choose `ApplePlatformsIntegrationTestMobileApp` target
5. Run on a device or a simulator

Reviewed By: willholen

Differential Revision: D23320554

Pulled By: mhorowitz

fbshipit-source-id: 806c98257904df426a76b441954061a261f97e86
  • Loading branch information
grabbou authored and facebook-github-bot committed Sep 9, 2020
1 parent 0918353 commit 09be792
Show file tree
Hide file tree
Showing 25 changed files with 1,169 additions and 1,247 deletions.
105 changes: 64 additions & 41 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,35 @@ workflows:
- android
- linux
- macos
- build-macos-runtime
- test-apple-runtime
- apple-runtime:
requires:
- test-apple-runtime
- windows
- npm:
requires:
- android
- linux
- macos
- build-macos-runtime
- apple-runtime
- windows
- test-linux
- test-macos
- test-macos-runtime-build-and-cocoapods-integration
- test-e2e:
requires:
- npm

# Default settings for Apple jobs (apple-runtime, test-apple-runtime)
apple_defaults: &apple_defaults
macos:
xcode: "10.3.0"
working_directory: ~/hermes
environment:
- TERM: dumb
# Homebrew currently breaks while updating:
# https://discuss.circleci.com/t/brew-install-fails-while-updating/32992
- HOMEBREW_NO_AUTO_UPDATE: 1

jobs:
android:
docker:
Expand Down Expand Up @@ -228,41 +241,71 @@ jobs:
paths:
- .

build-macos-runtime:
macos:
xcode: "10.3.0"
environment:
- HERMES_WS_DIR: /tmp/hermes
- TERM: dumb
# Homebrew currently breaks while updating:
# https://discuss.circleci.com/t/brew-install-fails-while-updating/32992
- HOMEBREW_NO_AUTO_UPDATE: 1
# This job builds Hermes runtime for Apple (Mac, iPhone) and tests it.
# On success, build is persisted into workspace to make other jobs
# run faster.
test-apple-runtime:
<<: *apple_defaults
steps:
- checkout
- restore_cache:
key: v1-repo-{{ .Environment.CIRCLE_SHA1 }}
- run:
name: Install dependencies
command: |
brew install cmake ninja
- run:
name: Build the framework
command: |
pod install
working_directory: test/ApplePlatformsIntegrationTestApp
- run:
name: Test MacOS application
command: |
xcodebuild test \
-workspace ApplePlatformsIntegrationTests.xcworkspace \
-configuration Debug \
-destination 'platform=macOS' \
-scheme ApplePlatformsIntegrationMacTests
working_directory: test/ApplePlatformsIntegrationTestApp
- run:
name: Test iPhone application
command: |
xcodebuild test \
-workspace ApplePlatformsIntegrationTests.xcworkspace \
-configuration Debug \
-destination 'platform=iOS Simulator,name=iPhone X' \
-scheme ApplePlatformsIntegrationMobileTests
working_directory: test/ApplePlatformsIntegrationTestApp
- save_cache:
key: v1-repo-{{ .Environment.CIRCLE_SHA1 }}
paths:
- ~/hermes

apple-runtime:
<<: *apple_defaults
steps:
- restore_cache:
key: v1-repo-{{ .Environment.CIRCLE_SHA1 }}
- run:
name: Set up workspace
command: mkdir -p /tmp/hermes/output
- run:
name: Install dependencies
command: |
mkdir -p "$HERMES_WS_DIR" "$HERMES_WS_DIR/output" "$HERMES_WS_DIR/package-root"
ln -sf "$PWD" "$HERMES_WS_DIR/hermes"
brew install cmake ninja
- run:
name: Build Hermes for macOS as CocoaPods pod
name: Package the framework
command: |
cd "$HERMES_WS_DIR"
hermes/utils/build/configure.py --distribute --cmake-flags="$(ruby -rcocoapods-core -e 'load %{hermes/hermes.podspec}; puts HermesHelper.cmake_configuration') -DCMAKE_INSTALL_PREFIX:PATH=../destroot"
cmake --build ./build_release --target hermes-runtime-darwin-cocoapods-release
cmake --build ./build_iphoneos --target hermes-runtime-darwin-cocoapods-release
- run:
name: Copy artifacts
command: |
cd "$HERMES_WS_DIR"
cp "build_release/github"/hermes-runtime-darwin-*.tar.gz "output"
cp "build_iphoneos/github"/hermes-runtime-darwin-*.tar.gz /tmp/hermes/output
- run:
name: Checksum artifacts
command: |
cd "$HERMES_WS_DIR/output"
cd /tmp/hermes/output
for file in *
do
shasum -a 256 "$file" > "$file.sha256"
Expand Down Expand Up @@ -303,26 +346,6 @@ jobs:
cmake --build ./build
cmake --build ./build --target check-hermes
test-macos-runtime-build-and-cocoapods-integration:
macos:
xcode: "10.3.0"
environment:
- TERM: dumb
# Homebrew currently breaks while updating:
# https://discuss.circleci.com/t/brew-install-fails-while-updating/32992
- HOMEBREW_NO_AUTO_UPDATE: 1
steps:
- checkout
- run:
name: Install dependencies
command: |
brew install cmake ninja
- run:
name: Run CocoaPods installation and test framework
command: |
cd test/ApplePlatformsIntegrationTestApp
./run.sh
windows:
executor:
name: win/default
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ buck-out

# CocoaPods testing
build
build_*
destroot
test/ApplePlatformsIntegrationTestApp/Podfile.lock
test/ApplePlatformsIntegrationTestApp/Pods
Expand Down
6 changes: 3 additions & 3 deletions API/hermes/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ if(APPLE AND HERMES_BUILD_APPLE_FRAMEWORK)
FRAMEWORK_VERSION ${PROJECT_VERSION_MAJOR}
MACOSX_FRAMEWORK_SHORT_VERSION_STRING ${PROJECT_VERSION}
MACOSX_FRAMEWORK_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_FRAMEWORK_IDENTIFIER dev.hermesengine.${CMAKE_SYSTEM_NAME}
MACOSX_FRAMEWORK_IDENTIFIER dev.hermesengine.${HERMES_APPLE_TARGET_PLATFORM}
)
# Install headers into `Headers` while keeping required directory structure
set_source_files_properties(${api_headers} PROPERTIES
Expand All @@ -125,7 +125,7 @@ install(TARGETS libhermes
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
FRAMEWORK DESTINATION Library/Frameworks
FRAMEWORK DESTINATION Library/Frameworks/${HERMES_APPLE_TARGET_PLATFORM}
)
# Install headers into `include` while keeping required directory structure
install(DIRECTORY "${PROJECT_SOURCE_DIR}/API/hermes" DESTINATION include
Expand Down Expand Up @@ -155,7 +155,7 @@ if(HERMES_BUILD_APPLE_DSYM)
)

if(HERMES_BUILD_APPLE_FRAMEWORK)
install(DIRECTORY ${DSYM_PATH} DESTINATION Library/Frameworks)
install(DIRECTORY ${DSYM_PATH} DESTINATION Library/Frameworks/${HERMES_APPLE_TARGET_PLATFORM})
else()
install(DIRECTORY ${DSYM_PATH} DESTINATION lib)
endif()
Expand Down
6 changes: 5 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ if (POLICY CMP0026)
cmake_policy(SET CMP0026 OLD)
endif()

# Has to be set before `project` as per documentation
# https://cmake.org/cmake/help/latest/variable/CMAKE_OSX_SYSROOT.html
set(CMAKE_OSX_SYSROOT ${HERMES_APPLE_TARGET_PLATFORM})

# This must be consistent with the release_version in:
# - android/build.gradle
# - npm/package.json
Expand Down Expand Up @@ -719,4 +723,4 @@ add_custom_target(
cp ${CMAKE_CURRENT_SOURCE_DIR}/hermes.podspec ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE ${HERMES_PKG_ROOT}
COMMAND
tar -C ${HERMES_PKG_ROOT}/ -czvf ${HERMES_GITHUB_DIR}/hermes-runtime-${HERMES_GITHUB_SYSTEM_NAME}-v${HERMES_RELEASE_VERSION}.tar.gz .
)
)
51 changes: 25 additions & 26 deletions hermes.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,8 @@ module HermesHelper
# BUILD_TYPE = :debug
BUILD_TYPE = :release

DEPLOYMENT_TARGET = "10.13"

def self.command_exists?(bin)
"command -v #{bin} > /dev/null 2>&1"
end

def self.cmake_configuration
"-DCMAKE_OSX_DEPLOYMENT_TARGET:STRING='#{DEPLOYMENT_TARGET}' -DHERMES_ENABLE_DEBUGGER:BOOLEAN=true -DHERMES_ENABLE_FUZZING:BOOLEAN=false -DHERMES_ENABLE_TEST_SUITE:BOOLEAN=false -DHERMES_BUILD_APPLE_FRAMEWORK:BOOLEAN=true -DHERMES_BUILD_APPLE_DSYM:BOOLEAN=true"
end

def self.configure_command
"./utils/build/configure.py #{BUILD_TYPE == :release ? "--distribute" : "--build-type=Debug"} --cmake-flags='#{HermesHelper.cmake_configuration} -DCMAKE_INSTALL_PREFIX:PATH=../destroot' build"
end
OSX_DEPLOYMENT_TARGET = "10.13"
IOS_DEPLOYMENT_TARGET = "10.0"
end

Pod::Spec.new do |spec|
Expand All @@ -31,26 +20,36 @@ Pod::Spec.new do |spec|
spec.license = { type: "MIT", file: "LICENSE" }
spec.author = "Facebook"
spec.source = { git: "https://github.com/facebook/hermes.git", tag: "v#{spec.version}" }
spec.platforms = { :osx => HermesHelper::DEPLOYMENT_TARGET }
spec.platforms = { :osx => HermesHelper::OSX_DEPLOYMENT_TARGET, :ios => HermesHelper::IOS_DEPLOYMENT_TARGET }

spec.preserve_paths = ["destroot/bin/*"].concat(HermesHelper::BUILD_TYPE == :debug ? ["**/*.{h,c,cpp}"] : [])
spec.source_files = "destroot/include/**/*.h"
spec.header_mappings_dir = "destroot/include"
spec.vendored_frameworks = "destroot/Library/Frameworks/hermes.framework"

spec.ios.vendored_frameworks = "destroot/Library/Frameworks/iphoneos/hermes.framework"
spec.osx.vendored_frameworks = "destroot/Library/Frameworks/macosx/hermes.framework"

spec.xcconfig = { "CLANG_CXX_LANGUAGE_STANDARD" => "c++14", "CLANG_CXX_LIBRARY" => "compiler-default", "GCC_PREPROCESSOR_DEFINITIONS" => "HERMES_ENABLE_DEBUGGER=1" }

spec.prepare_command = <<-EOS
if [ ! -d destroot/Library/Frameworks/hermes.framework ]; then
if #{HermesHelper.command_exists?("cmake")}; then
if #{HermesHelper.command_exists?("ninja")}; then
#{HermesHelper.configure_command} --build-system='Ninja' && cd build && ninja install/strip
else
#{HermesHelper.configure_command} --build-system='Unix Makefiles' && cd build && make install/strip
fi
else
echo >&2 'CMake is required to install Hermes, install it with: brew install cmake'
exit 1
fi
# When true, debug build will be used.
# See `build-apple-framework.sh` for details
DEBUG=#{HermesHelper::BUILD_TYPE == :debug}
# Source utilities into the scope
. ./utils/build-apple-framework.sh
# If universal framework for iOS does not exist, build one
if [ ! -d destroot/Library/Frameworks/iphoneos/hermes.framework ]; then
build_apple_framework "iphoneos" "armv7;armv7s;arm64" "#{HermesHelper::IOS_DEPLOYMENT_TARGET}"
build_apple_framework "iphonesimulator" "x86_64" "#{HermesHelper::IOS_DEPLOYMENT_TARGET}"
create_universal_framework "iphoneos" "iphonesimulator"
fi
# If MacOS framework does not exist, build one
if [ ! -d destroot/Library/Frameworks/macosx/hermes.framework ]; then
build_apple_framework "macosx" "x86_64" "#{HermesHelper::OSX_DEPLOYMENT_TARGET}"
fi
EOS
end
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
Expand All @@ -20,15 +18,5 @@
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSMainStoryboardFile</key>
<string>Main</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSSupportsAutomaticTermination</key>
<true/>
<key>NSSupportsSuddenTermination</key>
<true/>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
Loading

0 comments on commit 09be792

Please sign in to comment.