facebook / buck

A fast build system that encourages the creation of small, reusable modules over a variety of platforms and languages.
https://buck.build
Apache License 2.0
8.56k stars 1.16k forks source link

How can I build SBTUITestTunnel for a Swift app using Buck? #2441

Open sfarbota opened 4 years ago

sfarbota commented 4 years ago

I am trying to use Buck to include the SBTUITestTunnel library in a couple of iOS apps I am working on. This library extends the standard Xcode UI-testing framework to offer additional features such as monitoring network calls. While SBTUITestTunnel is written mostly in Objective-C, The apps I hope to include this library in are written in Swift and use Buck as their build tool. I'd also like to be able to use both Buck and Xcode to execute the tests once I have them written.

Unfortunately, the SBTUITestTunnel Installation/Setup/Usage docs do not seem to provide any guidance on building them with Buck. The repo does include CocoaPods config files, which I have tried to convert to a BUCK file. However, I've been unable to get that working properly after several attempts. I've even tried grabbing the prebuilt .frameworks from the build output of the provided SBTUITestTunnel Example App, but still no luck.

Here are the attempts I've made so far at creating a working BUCK file:

Attempt 1 - Building as Libraries from Source:

Code:

apple_library(
  name = 'SBTUITestTunnelCommon',
  visibility = ['PUBLIC'],
  swift_version = '5',
  exported_headers = glob([
    'Pod/Common/**/*.h',
  ]),
  srcs = glob([
    'Pod/Common/**/*.swift',
    'Pod/Common/**/*.m',
  ]),
  deps = [
    '//Path/GCDWebServer:GCDWebServer',
  ]
)

apple_library(
  name = 'SBTUITestTunnelClient',
  visibility = ['PUBLIC'],
  swift_version = '5',
  exported_headers = glob([
    'Pod/Client/**/*.h',
  ]),
  srcs = glob([
    'Pod/Client/**/*.swift',
    'Pod/Client/**/*.m',
  ]),
  deps = [
    '//Path/GCDWebServer:GCDWebServer',
    '//Path/SBTUITestTunnel:SBTUITestTunnelCommon',
  ]
)

apple_library(
  name = 'SBTUITestTunnelServer',
  visibility = ['PUBLIC'],
  swift_version = '5',
  exported_headers = glob([
    'Pod/Server/**/*.h',
  ]),
  srcs = glob([
    'Pod/Server/**/*.swift',
    'Pod/Server/**/*.m',
  ]),
  deps = [
    '//Path/GCDWebServer:GCDWebServer',
    '//Path/SBTUITestTunnel:SBTUITestTunnelCommon',
  ]
)

Result:

Buck build completes successfully, but running tests in Buck and trying to build in Xcode both fail due with errors like this one (when trying to run @import SBTUITestTunnelCommon; from a file in the SBTUITestTunnelServer class):

Module 'SBTUITestTunnelCommon' not found

Attempt 2 - Building as Frameworks from Source:

Code:

sbt_ui_test_tunnel_common_bundle_name = 'SBTUITestTunnelCommon'
sbt_ui_test_tunnel_common_binary_name = sbt_ui_test_tunnel_common_bundle_name + 'Binary'
sbt_ui_test_tunnel_common_module_name = sbt_ui_test_tunnel_common_bundle_name
sbt_ui_test_tunnel_common_product_name = sbt_ui_test_tunnel_common_bundle_name
# Copied `Info.plist` from the Example app to this Pod folder:
sbt_ui_test_tunnel_common_info_plist = 'Pod/Common/' + sbt_ui_test_tunnel_common_bundle_name + '-Info.plist'

sbt_ui_test_tunnel_client_bundle_name = 'SBTUITestTunnelClient'
sbt_ui_test_tunnel_client_binary_name = sbt_ui_test_tunnel_client_bundle_name + 'Binary'
sbt_ui_test_tunnel_client_module_name = sbt_ui_test_tunnel_client_bundle_name
sbt_ui_test_tunnel_client_product_name = sbt_ui_test_tunnel_client_bundle_name
# Copied `Info.plist` from the Example app to this Pod folder:
sbt_ui_test_tunnel_client_info_plist = 'Pod/Client/' + sbt_ui_test_tunnel_client_bundle_name + '-Info.plist'

sbt_ui_test_tunnel_server_bundle_name = 'SBTUITestTunnelServer'
sbt_ui_test_tunnel_server_binary_name = sbt_ui_test_tunnel_server_bundle_name + 'Binary'
sbt_ui_test_tunnel_server_module_name = sbt_ui_test_tunnel_server_bundle_name
sbt_ui_test_tunnel_server_product_name = sbt_ui_test_tunnel_server_bundle_name
# Copied `Info.plist` from the Example app to this Pod folder:
sbt_ui_test_tunnel_server_info_plist = 'Pod/Server/' + sbt_ui_test_tunnel_server_bundle_name + '-Info.plist'

apple_binary(
  name = sbt_ui_test_tunnel_common_binary_name,
  visibility = ['PUBLIC'],
  module_name = sbt_ui_test_tunnel_common_module_name,
  swift_version = '5',
  linker_flags = [
    '-ObjC', # Have tried with and without this
  ],
  link_style = 'shared',
  exported_headers = glob([
    'Pod/Common/**/*.h',
  ]),
  srcs = glob([
    'Pod/Common/**/*.swift',
    'Pod/Common/**/*.m',
  ]),
  deps = [
    '//Path/GCDWebServer:GCDWebServer',
  ]
)

apple_bundle(
  name = sbt_ui_test_tunnel_common_bundle_name,
  visibility = ['PUBLIC'],
  product_name = sbt_ui_test_tunnel_common_product_name,
  binary = ':' + sbt_ui_test_tunnel_common_binary_name,
  extension = 'framework',
  xcode_product_type = 'com.apple.product-type.framework',
  info_plist = sbt_ui_test_tunnel_common_info_plist,
)

apple_binary(
  name = sbt_ui_test_tunnel_client_binary_name,
  visibility = ['PUBLIC'],
  module_name = sbt_ui_test_tunnel_client_module_name,
  swift_version = '5',
  linker_flags = [
    '-ObjC', # Have tried with and without this
  ],
  link_style = 'shared',
  exported_headers = glob([
    'Pod/Client/**/*.h',
  ]),
  srcs = glob([
    'Pod/Client/**/*.swift',
    'Pod/Client/**/*.m',
  ]),
  deps = [
    '//Path/GCDWebServer:GCDWebServer',
    '//Path/SBTUITestTunnel:' + sbt_ui_test_tunnel_common_binary_name,
  ]
)

apple_bundle(
  name = sbt_ui_test_tunnel_client_bundle_name,
  visibility = ['PUBLIC'],
  product_name = sbt_ui_test_tunnel_client_product_name,
  binary = ':' + sbt_ui_test_tunnel_client_binary_name,
  extension = 'framework',
  xcode_product_type = 'com.apple.product-type.framework',
  info_plist = sbt_ui_test_tunnel_client_info_plist,
)

apple_binary(
  name = sbt_ui_test_tunnel_server_binary_name,
  visibility = ['PUBLIC'],
  module_name = sbt_ui_test_tunnel_server_module_name,
  swift_version = '5',
enter code here
  linker_flags = [
    '-ObjC', # Have tried with and without this
  ],
  link_style = 'shared',
  exported_headers = glob([
    'Pod/Server/**/*.h',
  ]),
  srcs = glob([
    'Pod/Server/**/*.swift',
    'Pod/Server/**/*.m',
  ]),
  deps = [
    '//Path/GCDWebServer:GCDWebServer',
    '//Path/SBTUITestTunnel:' + sbt_ui_test_tunnel_common_binary_name,
  ]
)

apple_bundle(
  name = sbt_ui_test_tunnel_server_bundle_name,
  visibility = ['PUBLIC'],
  product_name = sbt_ui_test_tunnel_server_product_name,
  binary = ':' + sbt_ui_test_tunnel_server_binary_name,
  extension = 'framework',
  xcode_product_type = 'com.apple.product-type.framework',
  info_plist = sbt_ui_test_tunnel_server_info_plist,
)

Result:

Buck build fails with this error message:

Command failed with exit code 1.
stderr: ld: warning: -sdk_version and -platform_version are not compatible, ignoring - 
sdk_version
Undefined symbols for architecture x86_64:
  "_main", referenced from:
     implicit entry/start for main executable
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Attempt 3 - Building as prebuilt_apple_frameworks:

Code:

sbt_ui_test_tunnel_common_framework_name = 'SBTUITestTunnelCommon'
# Copied .framework file from build output of the Example app to this Frameworks folder:
sbt_ui_test_tunnel_common_framework_path = 'Frameworks/' + sbt_ui_test_tunnel_common_framework_name + '/' + sbt_ui_test_tunnel_common_framework_name + '.framework'

sbt_ui_test_tunnel_client_framework_name = 'SBTUITestTunnelClient'
# Copied .framework file from build output of the Example app to this Frameworks folder:
sbt_ui_test_tunnel_client_framework_path = 'Frameworks/' + sbt_ui_test_tunnel_common_framework_name + '/' + sbt_ui_test_tunnel_common_framework_name + '.framework'

sbt_ui_test_tunnel_server_framework_name = 'SBTUITestTunnelServer'
# Copied .framework file from build output of the Example app to this Frameworks folder:
sbt_ui_test_tunnel_server_framework_path = 'Frameworks/' + sbt_ui_test_tunnel_common_framework_name + '/' + sbt_ui_test_tunnel_common_framework_name + '.framework'

prebuilt_apple_framework(
  name = sbt_ui_test_tunnel_common_framework_name,
  framework = sbt_ui_test_tunnel_common_framework_path,
  preferred_linkage = 'stat',
  visibility = [
    'PUBLIC'
  ],
  deps = [
    '//Path/GCDWebServer:GCDWebServer',
  ],
)

prebuilt_apple_framework(
  name = sbt_ui_test_tunnel_client_framework_name,
  framework = sbt_ui_test_tunnel_client_framework_path,
  preferred_linkage = 'shared',
  visibility = [
    'PUBLIC'
  ],
  deps = [
    '//Path/GCDWebServer:GCDWebServer',
    '//Path/SBTUITestTunnel:' + sbt_ui_test_tunnel_common_framework_name,
  ],
)

prebuilt_apple_framework(
  name = sbt_ui_test_tunnel_server_framework_name,
  framework = sbt_ui_test_tunnel_server_framework_path,
  preferred_linkage = 'shared',
  visibility = [
    'PUBLIC'
  ],
  deps = [
    '//Path/GCDWebServer:GCDWebServer',
    '//Path/SBTUITestTunnel:' + sbt_ui_test_tunnel_common_framework_name,
  ],
)

Result:

Buck build fails with this error message:

Command failed with exit code 1.
stderr: /Users/me/path/BridgingHeader.h:12:9: error: 'SBTUITestTunnelServer/SBTUITestTunnelServer.h' file not found
#import <SBTUITestTunnelServer/SBTUITestTunnelServer.h>
        ^
<unknown>:0: error: failed to import bridging header 'path/BridgingHeader.h'

Additional Notes:

Any idea what I am doing wrong? Or at least maybe some advice on which of these options is most likely the best path?

sfarbota commented 4 years ago

FYI, I've also asked the same question in a couple other places: