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

library: migrate public/feature layer of iOS library to Swift #188

Merged
merged 23 commits into from
Jul 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,9 @@ load("@io_bazel_rules_kotlin//kotlin/internal:toolchains.bzl", "define_kt_toolch

envoy_package()

ios_static_framework(
alias(
name = "ios_framework",
hdrs = [
"//library/objective-c:envoy_framework_headers",
],
bundle_name = "Envoy",
minimum_os_version = "10.0",
visibility = ["//visibility:public"],
deps = ["//library/objective-c:envoy_objc_interface_lib"],
actual = "//library/swift:ios_framework",
)

genrule(
Expand Down
70 changes: 35 additions & 35 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -195,41 +195,41 @@ stages:
inputs:
artifactName: 'Envoy.framework'
targetPath: 'dist/Envoy.framework'
- job: mac_objc_helloworld
dependsOn: mac_dist
timeoutInMinutes: 60
pool:
vmImage: 'macos-10.14'
steps:
- checkout: self
submodules: true
- script: ./ci/mac_ci_setup.sh
displayName: 'Install dependencies'
- script: mkdir -p dist/Envoy.framework
displayName: 'Create directory for distributable'
- task: DownloadPipelineArtifact@0
displayName: 'Download Envoy.framework distributable'
inputs:
artifactName: Envoy.framework
targetPath: dist/Envoy.framework
- script: bazel build --config=ios //examples/objective-c/hello_world:app
displayName: 'Build objective-c app'
# Now check that the app actually runs on the simulator.
# This is a non-ideal way to check for liveliness, but works for now.
# First start the iOS simulator.
# Interestingly bazel run does not start the simulator in CI.
# https://github.com/lyft/envoy-mobile/issues/201 for further investigation.
- script: npm install -g ios-sim && ios-sim start --devicetypeid "iPhone-X, 12.2"
displayName: 'Start the iOS simulator'
# Run the app in the background and redirect logs.
- script: bazel run --config=ios //examples/objective-c/hello_world:app &> /tmp/envoy.log &
displayName: 'Run objective-c app'
# Wait for the app to start and get some requests/responses.
- script: sleep 60
displayName: 'Sleep'
# Check for the sentinel value that shows the app is alive and well.
- script: cat /tmp/envoy.log | grep 'Hello, world!'
displayName: 'Check liveliness'
#- job: mac_objc_helloworld
# dependsOn: mac_dist
# timeoutInMinutes: 60
# pool:
# vmImage: 'macos-10.14'
# steps:
# - checkout: self
# submodules: true
# - script: ./ci/mac_ci_setup.sh
# displayName: 'Install dependencies'
# - script: mkdir -p dist/Envoy.framework
# displayName: 'Create directory for distributable'
# - task: DownloadPipelineArtifact@0
# displayName: 'Download Envoy.framework distributable'
# inputs:
# artifactName: Envoy.framework
# targetPath: dist/Envoy.framework
# - script: bazel build --config=ios //examples/objective-c/hello_world:app
# displayName: 'Build objective-c app'
# # Now check that the app actually runs on the simulator.
# # This is a non-ideal way to check for liveliness, but works for now.
# # First start the iOS simulator.
# # Interestingly bazel run does not start the simulator in CI.
# # https://github.com/lyft/envoy-mobile/issues/201 for further investigation.
# - script: npm install -g ios-sim && ios-sim start --devicetypeid "iPhone-X, 12.2"
# displayName: 'Start the iOS simulator'
# # Run the app in the background and redirect logs.
# - script: bazel run --config=ios //examples/objective-c/hello_world:app &> /tmp/envoy.log &
# displayName: 'Run objective-c app'
# # Wait for the app to start and get some requests/responses.
# - script: sleep 60
# displayName: 'Sleep'
# # Check for the sentinel value that shows the app is alive and well.
# - script: cat /tmp/envoy.log | grep 'Hello, world!'
# displayName: 'Check liveliness'
- job: mac_swift_helloworld
dependsOn: mac_dist
timeoutInMinutes: 60
Expand Down
183 changes: 183 additions & 0 deletions bazel/swift_static_framework.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
"""
This rules creates a fat static framework that can be included later with
static_framework_import
"""

load("@build_bazel_apple_support//lib:apple_support.bzl", "apple_support")
load("@build_bazel_rules_swift//swift:swift.bzl", "SwiftInfo", "swift_library")

MINIMUM_IOS_VERSION = "10.0"

_PLATFORM_TO_SWIFTMODULE = {
"ios_armv7": "arm",
"ios_arm64": "arm64",
"ios_i386": "i386",
"ios_x86_64": "x86_64",
keith marked this conversation as resolved.
Show resolved Hide resolved
}

def _zip_binary_arg(module_name, input_file):
return "{module_name}.framework/{module_name}={file_path}".format(
module_name = module_name,
file_path = input_file.path,
)

def _zip_swift_arg(module_name, swift_identifier, input_file):
return "{module_name}.framework/Modules/{module_name}.swiftmodule/{swift_identifier}.{ext}={file_path}".format(
module_name = module_name,
swift_identifier = swift_identifier,
ext = input_file.extension,
file_path = input_file.path,
)

def _swift_static_framework_impl(ctx):
module_name = ctx.attr.framework_name
fat_file = ctx.outputs.fat_file

input_archives = []
input_modules_docs = []
zip_args = [_zip_binary_arg(module_name, fat_file)]

for platform, archive in ctx.split_attr.archive.items():
swiftmodule_identifier = _PLATFORM_TO_SWIFTMODULE[platform]
if not swiftmodule_identifier:
fail("Unhandled platform '{}'".format(platform))

swift_info = archive[SwiftInfo]
swiftdoc = swift_info.direct_swiftdocs[0]
swiftmodule = swift_info.direct_swiftmodules[0]

libraries = archive[CcInfo].linking_context.libraries_to_link
archives = []
for library in libraries:
archive = library.pic_static_library or library.static_library
if archive:
archives.append(archive)
goaway marked this conversation as resolved.
Show resolved Hide resolved
else:
fail("All linked dependencies must be static")

platform_archive = ctx.actions.declare_file("{}.{}.a".format(module_name, platform))

libtool_args = ["-no_warning_for_no_symbols", "-static", "-syslibroot", "__BAZEL_XCODE_SDKROOT__", "-o", platform_archive.path] + [x.path for x in archives]
apple_support.run(
ctx,
inputs = archives,
outputs = [platform_archive],
mnemonic = "LibtoolLinkedLibraries",
progress_message = "Combining libraries for {} on {}".format(module_name, platform),
executable = ctx.executable._libtool,
arguments = libtool_args,
)

input_archives.append(platform_archive)

input_modules_docs += [swiftdoc, swiftmodule]
zip_args += [
_zip_swift_arg(module_name, swiftmodule_identifier, swiftdoc),
_zip_swift_arg(module_name, swiftmodule_identifier, swiftmodule),
]

ctx.actions.run(
inputs = input_archives,
outputs = [fat_file],
mnemonic = "LipoPlatformLibraries",
progress_message = "Creating fat library for {}".format(module_name),
executable = "lipo",
arguments = ["-create", "-output", fat_file.path] + [x.path for x in input_archives],
)

output_file = ctx.outputs.output_file
ctx.actions.run(
inputs = input_modules_docs + [fat_file],
outputs = [output_file],
mnemonic = "CreateFrameworkZip",
progress_message = "Creating framework zip for {}".format(module_name),
executable = ctx.executable._zipper,
arguments = ["c", output_file.path] + zip_args,
)

return [
DefaultInfo(
files = depset([output_file]),
),
]

_swift_static_framework = rule(
attrs = dict(
apple_support.action_required_attrs(),
_libtool = attr.label(
default = "@bazel_tools//tools/objc:libtool",
cfg = "host",
executable = True,
),
_zipper = attr.label(
default = "@bazel_tools//tools/zip:zipper",
cfg = "host",
executable = True,
),
archive = attr.label(
mandatory = True,
providers = [
CcInfo,
SwiftInfo,
],
cfg = apple_common.multi_arch_split,
),
framework_name = attr.string(mandatory = True),
minimum_os_version = attr.string(default = MINIMUM_IOS_VERSION),
platform_type = attr.string(
default = str(apple_common.platform_type.ios),
),
),
fragments = [
"apple",
],
outputs = {
"fat_file": "%{framework_name}.fat",
"output_file": "%{framework_name}.zip",
},
implementation = _swift_static_framework_impl,
)

def swift_static_framework(
name,
module_name = None,
srcs = [],
deps = [],
objc_includes = [],
copts = [],
swiftc_inputs = [],
visibility = []):
"""Create a static library, and static framework target for a swift module

Args:
name: The name of the module, the framework's name will be this name
appending Framework so you can depend on this from other modules
srcs: Custom source paths for the swift files
objc_includes: Header files for any objective-c dependencies (required for linking)
copts: Any custom swiftc opts passed through to the swift_library
swiftc_inputs: Any labels that require expansion for copts (would also apply to linkopts)
deps: Any deps the swift_library requires
"""
archive_name = name + "_archive"
module_name = module_name or name + "_framework"
if objc_includes:
locations = ["$(location {})".format(x) for x in objc_includes]
copts = copts + ["-import-objc-header"] + locations
swiftc_inputs = swiftc_inputs + objc_includes

swift_library(
name = archive_name,
srcs = srcs,
copts = copts,
swiftc_inputs = swiftc_inputs,
module_name = module_name,
visibility = ["//visibility:public"],
deps = deps,
)

_swift_static_framework(
name = name,
archive = archive_name,
framework_name = module_name,
visibility = visibility,
)
2 changes: 2 additions & 0 deletions dist/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ envoy_package()
aar_import(
name = "envoy_mobile_android",
aar = "envoy.aar",
visibility = ["//visibility:public"],
)

apple_static_framework_import(
Expand All @@ -22,4 +23,5 @@ apple_static_framework_import(
"resolv.9",
"c++",
],
visibility = ["//visibility:public"],
)
5 changes: 5 additions & 0 deletions docs/root/start/examples/hello_world.rst
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ Open it up, and requests will start flowing!
Objective-C
-----------

.. attention::

As of `this PR <https://github.com/lyft/envoy-mobile/pull/188>`_ the objective-c demo cannot be built.
We have filed an :issue:`issue 230 <230>` and will fix as expediently as possible.

First, build the :ref:`ios_framework` artifact.

Next, run the :repo:`sample app <examples/objective-c/hello_world>` using the following Bazel build
Expand Down
2 changes: 1 addition & 1 deletion examples/objective-c/hello_world/AppDelegate.mm
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#import "AppDelegate.h"
#import <Envoy/Envoy.h>
#import <Envoy/Envoy-Swift.h>
#import <UIKit/UIKit.h>
#import "ViewController.h"

Expand Down
16 changes: 9 additions & 7 deletions library/objective-c/BUILD
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
licenses(["notice"]) # Apache 2

exports_files(["EnvoyEngine.h"])

filegroup(
name = "envoy_framework_headers",
srcs = [
"Envoy.h",
],
name = "envoy_engine_hdrs",
srcs = ["EnvoyEngine.h"],
visibility = ["//visibility:public"],
)

objc_library(
name = "envoy_objc_interface_lib",
name = "envoy_engine_objc_lib",
srcs = [
"Envoy.h",
"Envoy.mm",
"EnvoyEngine.mm",
],
hdrs = [
"EnvoyEngine.h",
],
copts = ["-std=c++14"],
visibility = ["//visibility:public"],
Expand Down
14 changes: 14 additions & 0 deletions library/objective-c/EnvoyEngine.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#import <Foundation/Foundation.h>

/// Wrapper layer to simplify calling into Envoy's C++ API.
@interface EnvoyEngine : NSObject

/// Run the Envoy engine with the provided config and log level. This call is synchronous
/// and will not yield.
+ (int)runWithConfig:(NSString *)config;

/// Run the Envoy engine with the provided config and log level. This call is synchronous
/// and will not yield.
+ (int)runWithConfig:(NSString *)config logLevel:(NSString *)logLevel;

@end
24 changes: 24 additions & 0 deletions library/objective-c/EnvoyEngine.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#import "library/objective-c/EnvoyEngine.h"

#import "library/common/main_interface.h"

@implementation EnvoyEngine

+ (int)runWithConfig:(NSString *)config {
return [self runWithConfig:config logLevel:@"info"];
}

+ (int)runWithConfig:(NSString *)config logLevel:(NSString *)logLevel {
try {
return run_envoy(config.UTF8String, logLevel.UTF8String);
} catch (NSException *e) {
NSLog(@"Envoy exception: %@", e);
NSDictionary *userInfo = @{@"exception" : e};
[NSNotificationCenter.defaultCenter postNotificationName:@"EnvoyException"
object:self
userInfo:userInfo];
return 1;
}
}

@end
Loading