Skip to content

Commit

Permalink
feat: support resources defined in SPM targets (#245)
Browse files Browse the repository at this point in the history
- When an SPM target has `resources`, `swift_bazel` generates a resource
bundle, an `Info.plist` for the resource bundle, and a `Bundle.module`
accessor.
- Introduce `resource_bundle_accessor` rule to generate the Bundle
accessor code for Swift.
- Introduce `resource_bundle_infoplist` rule to generate the resource
bundle's `Info.plist`.
- Add `phone_number_kit` example.
- Move modulemap rule declaration code to helper functions.

Closes #175.
Closes #215.
  • Loading branch information
cgrindel authored Feb 28, 2023
1 parent a04bc99 commit 3081a64
Show file tree
Hide file tree
Showing 24 changed files with 753 additions and 50 deletions.
4 changes: 2 additions & 2 deletions .bazelrc
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# To update these lines, execute
# `bazel run @contrib_rules_bazel_integration_test//tools:update_deleted_packages`
build --deleted_packages=examples/firebase_example,examples/firebase_example/abtesting,examples/firebase_example/abtesting/SharedApp,examples/firebase_example/analytics/AnalyticsExample,examples/firebase_example/appdistribution,examples/firebase_example/appdistribution/AppDistributionExample,examples/firebase_example/appdistribution/AppDistributionTests,examples/http_archive_ext_deps,examples/http_archive_ext_deps/Sources/MyDequeModule,examples/http_archive_ext_deps/Sources/PrintStuff,examples/http_archive_ext_deps/Tests/MyDequeModuleTests,examples/http_archive_ext_deps/third_party,examples/interesting_deps,examples/ios_sim,examples/ios_sim/Sources/Foo,examples/ios_sim/Tests/FooTests,examples/objc_code,examples/pkg_manifest_minimal,examples/pkg_manifest_minimal/Sources/MyExecutable,examples/pkg_manifest_minimal/Sources/MyLibrary,examples/pkg_manifest_minimal/Tests/MyLibraryTests,examples/pkg_manifest_minimal/third_party,examples/vapor_example,examples/vapor_example/Sources/App,examples/vapor_example/Sources/Run,examples/vapor_example/Tests/AppTests,examples/vapor_example/swift,examples/xcmetrics_example
query --deleted_packages=examples/firebase_example,examples/firebase_example/abtesting,examples/firebase_example/abtesting/SharedApp,examples/firebase_example/analytics/AnalyticsExample,examples/firebase_example/appdistribution,examples/firebase_example/appdistribution/AppDistributionExample,examples/firebase_example/appdistribution/AppDistributionTests,examples/http_archive_ext_deps,examples/http_archive_ext_deps/Sources/MyDequeModule,examples/http_archive_ext_deps/Sources/PrintStuff,examples/http_archive_ext_deps/Tests/MyDequeModuleTests,examples/http_archive_ext_deps/third_party,examples/interesting_deps,examples/ios_sim,examples/ios_sim/Sources/Foo,examples/ios_sim/Tests/FooTests,examples/objc_code,examples/pkg_manifest_minimal,examples/pkg_manifest_minimal/Sources/MyExecutable,examples/pkg_manifest_minimal/Sources/MyLibrary,examples/pkg_manifest_minimal/Tests/MyLibraryTests,examples/pkg_manifest_minimal/third_party,examples/vapor_example,examples/vapor_example/Sources/App,examples/vapor_example/Sources/Run,examples/vapor_example/Tests/AppTests,examples/vapor_example/swift,examples/xcmetrics_example
build --deleted_packages=examples/firebase_example,examples/firebase_example/abtesting,examples/firebase_example/abtesting/SharedApp,examples/firebase_example/analytics/AnalyticsExample,examples/firebase_example/appdistribution,examples/firebase_example/appdistribution/AppDistributionExample,examples/firebase_example/appdistribution/AppDistributionTests,examples/http_archive_ext_deps,examples/http_archive_ext_deps/Sources/MyDequeModule,examples/http_archive_ext_deps/Sources/PrintStuff,examples/http_archive_ext_deps/Tests/MyDequeModuleTests,examples/http_archive_ext_deps/third_party,examples/interesting_deps,examples/ios_sim,examples/ios_sim/Sources/Foo,examples/ios_sim/Tests/FooTests,examples/objc_code,examples/phone_number_kit,examples/phone_number_kit/Tests/PhoneNumberKitTests,examples/pkg_manifest_minimal,examples/pkg_manifest_minimal/Sources/MyExecutable,examples/pkg_manifest_minimal/Sources/MyLibrary,examples/pkg_manifest_minimal/Tests/MyLibraryTests,examples/pkg_manifest_minimal/third_party,examples/vapor_example,examples/vapor_example/Sources/App,examples/vapor_example/Sources/Run,examples/vapor_example/Tests/AppTests,examples/vapor_example/swift,examples/xcmetrics_example
query --deleted_packages=examples/firebase_example,examples/firebase_example/abtesting,examples/firebase_example/abtesting/SharedApp,examples/firebase_example/analytics/AnalyticsExample,examples/firebase_example/appdistribution,examples/firebase_example/appdistribution/AppDistributionExample,examples/firebase_example/appdistribution/AppDistributionTests,examples/http_archive_ext_deps,examples/http_archive_ext_deps/Sources/MyDequeModule,examples/http_archive_ext_deps/Sources/PrintStuff,examples/http_archive_ext_deps/Tests/MyDequeModuleTests,examples/http_archive_ext_deps/third_party,examples/interesting_deps,examples/ios_sim,examples/ios_sim/Sources/Foo,examples/ios_sim/Tests/FooTests,examples/objc_code,examples/phone_number_kit,examples/phone_number_kit/Tests/PhoneNumberKitTests,examples/pkg_manifest_minimal,examples/pkg_manifest_minimal/Sources/MyExecutable,examples/pkg_manifest_minimal/Sources/MyLibrary,examples/pkg_manifest_minimal/Tests/MyLibraryTests,examples/pkg_manifest_minimal/third_party,examples/vapor_example,examples/vapor_example/Sources/App,examples/vapor_example/Sources/Run,examples/vapor_example/Tests/AppTests,examples/vapor_example/swift,examples/xcmetrics_example

# Import Shared settings
import %workspace%/shared.bazelrc
Expand Down
1 change: 1 addition & 0 deletions examples/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ _macos_single_bazel_version_test_examples = [
"interesting_deps",
"ios_sim",
"objc_code",
"phone_number_kit",
"xcmetrics_example",
]

Expand Down
42 changes: 42 additions & 0 deletions examples/phone_number_kit/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
load("@bazel_gazelle//:def.bzl", "gazelle", "gazelle_binary")
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
load("@cgrindel_bazel_starlib//bzltidy:defs.bzl", "tidy")
load("@cgrindel_swift_bazel//swiftpkg:defs.bzl", "swift_update_packages")

tidy(
name = "tidy",
targets = [
":swift_update_pkgs",
":update_build_files",
],
)

# MARK: - Gazelle

# Ignore the Swift build folder
# gazelle:exclude .build

gazelle_binary(
name = "gazelle_bin",
languages = [
"@bazel_skylib_gazelle_plugin//bzl",
"@cgrindel_swift_bazel//gazelle",
],
)

gazelle(
name = "update_build_files",
gazelle = ":gazelle_bin",
)

swift_update_packages(
name = "swift_update_pkgs",
gazelle = ":gazelle_bin",
)

bzl_library(
name = "swift_deps",
srcs = ["swift_deps.bzl"],
visibility = ["//visibility:public"],
deps = ["@cgrindel_swift_bazel//swiftpkg:defs"],
)
14 changes: 14 additions & 0 deletions examples/phone_number_kit/Package.resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"pins" : [
{
"identity" : "phonenumberkit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/marmelroy/PhoneNumberKit",
"state" : {
"revision" : "434a7432cceca19829bc6e34bdcfc0b0ee4c6801",
"version" : "3.5.7"
}
}
],
"version" : 2
}
10 changes: 10 additions & 0 deletions examples/phone_number_kit/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// swift-tools-version: 5.7

import PackageDescription

let package = Package(
name: "PhoneNumberKitExample",
dependencies: [
.package(url: "https://github.com/marmelroy/PhoneNumberKit", from: "3.4.0"),
]
)
3 changes: 3 additions & 0 deletions examples/phone_number_kit/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# PhoneNumberKit Example

This example demonstrates support for resources defined in a Swift package manifest.
18 changes: 18 additions & 0 deletions examples/phone_number_kit/Tests/PhoneNumberKitTests/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
load("@build_bazel_rules_apple//apple:ios.bzl", "ios_unit_test")
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")

swift_library(
name = "PhoneNumberKitTestsLib",
testonly = True,
srcs = ["ParseTests.swift"],
module_name = "PhoneNumberKitTests",
tags = ["manual"],
deps = ["@swiftpkg_phonenumberkit//:PhoneNumberKit"],
)

ios_unit_test(
name = "PhoneNumberKitTests",
minimum_os_version = "14.0",
visibility = ["//visibility:private"],
deps = [":PhoneNumberKitTestsLib"],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@testable import PhoneNumberKit
import XCTest

class ParseTests: XCTestCase {
let phoneNumberKit = PhoneNumberKit()

func test_parse() throws {
let phoneNumber = try phoneNumberKit.parse("+33 6 89 017383")
XCTAssertNotNil(phoneNumber)
}
}
84 changes: 84 additions & 0 deletions examples/phone_number_kit/WORKSPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
workspace(name = "phone_number_kit_example")

local_repository(
name = "cgrindel_swift_bazel",
path = "../..",
)

load("@cgrindel_swift_bazel//:deps.bzl", "swift_bazel_dependencies")

swift_bazel_dependencies()

load("@cgrindel_bazel_starlib//:deps.bzl", "bazel_starlib_dependencies")

bazel_starlib_dependencies()

# MARK: - Gazelle

# gazelle:repo bazel_gazelle

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
name = "bazel_skylib_gazelle_plugin",
sha256 = "04182233284fcb6545d36b94248fe28186b4d9d574c4131d6a511d5aeb278c46",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.4.0/bazel-skylib-gazelle-plugin-1.4.0.tar.gz",
"https://github.com/bazelbuild/bazel-skylib/releases/download/1.4.0/bazel-skylib-gazelle-plugin-1.4.0.tar.gz",
],
)

load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies")
load("@bazel_skylib_gazelle_plugin//:workspace.bzl", "bazel_skylib_gazelle_plugin_workspace")
load("@cgrindel_swift_bazel//:go_deps.bzl", "swift_bazel_go_dependencies")
load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies")

# Declare Go dependencies before calling go_rules_dependencies.
swift_bazel_go_dependencies()

bazel_skylib_gazelle_plugin_workspace()

go_rules_dependencies()

go_register_toolchains(version = "1.19.5")

gazelle_dependencies()

# MARK: - Swift Toolchain

http_archive(
name = "build_bazel_rules_swift",
sha256 = "32f95dbe6a88eb298aaa790f05065434f32a662c65ec0a6aabdaf6881e4f169f",
url = "https://github.com/bazelbuild/rules_swift/releases/download/1.5.0/rules_swift.1.5.0.tar.gz",
)

http_archive(
name = "build_bazel_rules_apple",
sha256 = "43737f28a578d8d8d7ab7df2fb80225a6b23b9af9655fcdc66ae38eb2abcf2ed",
url = "https://github.com/bazelbuild/rules_apple/releases/download/2.0.0/rules_apple.2.0.0.tar.gz",
)

load(
"@build_bazel_rules_apple//apple:repositories.bzl",
"apple_rules_dependencies",
)

apple_rules_dependencies()

load(
"@build_bazel_rules_swift//swift:repositories.bzl",
"swift_rules_dependencies",
)
load("//:swift_deps.bzl", "swift_dependencies")

# gazelle:repository_macro swift_deps.bzl%swift_dependencies
swift_dependencies()

swift_rules_dependencies()

load(
"@build_bazel_rules_swift//swift:extras.bzl",
"swift_rules_extra_dependencies",
)

swift_rules_extra_dependencies()
18 changes: 18 additions & 0 deletions examples/phone_number_kit/do_test
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env bash

set -o errexit -o nounset -o pipefail

script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" > /dev/null && pwd)"

assert_match() {
local pattern=${1}
local actual="${2}"
local err_msg="Expected to match. pattern: ${pattern}, actual: ${actual}"
[[ "${actual}" =~ ${pattern} ]] || (echo >&2 "${err_msg}" && exit 1)
}

# Generate Swift external deps and update build files
bazel run //:tidy

# Ensure that it builds and tests pass
bazel test //...
6 changes: 6 additions & 0 deletions examples/phone_number_kit/set_up_clean_test
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env bash

set -o errexit -o nounset -o pipefail

# NOTE: This is a placeholder. The phone_number_kit example does not support a clean
# test because some of the targets have been marked as 'manual'.
10 changes: 10 additions & 0 deletions examples/phone_number_kit/swift_deps.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
load("@cgrindel_swift_bazel//swiftpkg:defs.bzl", "swift_package")

def swift_dependencies():
# version: 3.5.7
swift_package(
name = "swiftpkg_phonenumberkit",
commit = "434a7432cceca19829bc6e34bdcfc0b0ee4c6801",
dependencies_index = "@//:swift_deps_index.json",
remote = "https://github.com/marmelroy/PhoneNumberKit",
)
53 changes: 53 additions & 0 deletions examples/phone_number_kit/swift_deps_index.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"direct_dep_identities": [
"phonenumberkit"
],
"modules": [
{
"name": "PhoneNumberKit",
"c99name": "PhoneNumberKit",
"src_type": "swift",
"label": "@swiftpkg_phonenumberkit//:PhoneNumberKit",
"package_identity": "phonenumberkit",
"product_memberships": [
"PhoneNumberKit",
"PhoneNumberKit-Static",
"PhoneNumberKit-Dynamic"
]
},
{
"name": "PhoneNumberKitTests",
"c99name": "PhoneNumberKitTests",
"src_type": "swift",
"label": "@swiftpkg_phonenumberkit//:PhoneNumberKitTests",
"package_identity": "phonenumberkit",
"product_memberships": []
}
],
"products": [
{
"identity": "phonenumberkit",
"name": "PhoneNumberKit",
"type": "library",
"target_labels": [
"@swiftpkg_phonenumberkit//:PhoneNumberKit"
]
},
{
"identity": "phonenumberkit",
"name": "PhoneNumberKit-Dynamic",
"type": "library",
"target_labels": [
"@swiftpkg_phonenumberkit//:PhoneNumberKit"
]
},
{
"identity": "phonenumberkit",
"name": "PhoneNumberKit-Static",
"type": "library",
"target_labels": [
"@swiftpkg_phonenumberkit//:PhoneNumberKit"
]
}
]
}
10 changes: 10 additions & 0 deletions swiftpkg/build_defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,15 @@ load(
"//swiftpkg/internal:generate_modulemap.bzl",
_generate_modulemap = "generate_modulemap",
)
load(
"//swiftpkg/internal:resource_bundle_accessor.bzl",
_resource_bundle_accessor = "resource_bundle_accessor",
)
load(
"//swiftpkg/internal:resource_bundle_infoplist.bzl",
_resource_bundle_infoplist = "resource_bundle_infoplist",
)

generate_modulemap = _generate_modulemap
resource_bundle_accessor = _resource_bundle_accessor
resource_bundle_infoplist = _resource_bundle_infoplist
17 changes: 17 additions & 0 deletions swiftpkg/internal/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ load("@cgrindel_bazel_starlib//bzlformat:defs.bzl", "bzlformat_pkg")

bzlformat_pkg(name = "bzlformat")

exports_files([
"ResourceBundleAccessor.swift.tmpl",
"resource_bundle_info.plist.tmpl",
])

# MARK: - Integration Test

filegroup(
Expand Down Expand Up @@ -265,6 +270,18 @@ bzl_library(
visibility = ["//swiftpkg:__subpackages__"],
)

bzl_library(
name = "resource_bundle_accessor",
srcs = ["resource_bundle_accessor.bzl"],
visibility = ["//swiftpkg:__subpackages__"],
)

bzl_library(
name = "resource_bundle_infoplist",
srcs = ["resource_bundle_infoplist.bzl"],
visibility = ["//swiftpkg:__subpackages__"],
)

bzl_library(
name = "starlark_codegen",
srcs = ["starlark_codegen.bzl"],
Expand Down
45 changes: 45 additions & 0 deletions swiftpkg/internal/ResourceBundleAccessor.swift.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import Foundation

// MARK: - CurrentBundleFinder

private class CurrentBundleFinder {}

extension Foundation.Bundle {

/// This is a workaround for the provided module static var that doesn't always work.
/// https://forums.swift.org/t/unable-to-find-bundle-in-package-target-tests-when-package-depends-on-another-package-containing-resources-accessed-via-bundle-module/43974/5
static var module: Bundle = {
let bundleName = "{BUNDLE_NAME}"
let localBundleName = "LocalPackages_{BUNDLE_NAME}"

let candidates = [
/* Bundle should be present here when the package is linked into an App. */
Bundle.main.resourceURL,

/* Bundle should be present here when the package is linked into a framework. */
Bundle(for: CurrentBundleFinder.self).resourceURL,

/* For command-line tools. */
Bundle.main.bundleURL,

/* Bundle should be present here when running previews from a different package (this is the path to "…/Debug-iphonesimulator/"). */
Bundle(for: CurrentBundleFinder.self).resourceURL?.deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent(),
Bundle(for: CurrentBundleFinder.self).resourceURL?.deletingLastPathComponent().deletingLastPathComponent()
]

for candidate in candidates {
let bundlePath = candidate?.appendingPathComponent(bundleName + ".bundle")
if let bundle = bundlePath.flatMap(Bundle.init(url:)) {
return bundle
}

let localBundlePath = candidate?.appendingPathComponent(localBundleName + ".bundle")
if let bundle = bundlePath.flatMap(Bundle.init(url:)) {
return bundle
}
}

fatalError("unable to find bundle")

}()
}
Loading

0 comments on commit 3081a64

Please sign in to comment.