Skip to content

Commit 6d12d53

Browse files
authored
fix: set C/C++ language standards per target (#1217)
This PR refactors the creation of C, C++, ObjC, and ObjC++ targets. For each C-like target in a Swift package, we now create a separate `xxx_library` target based upon the type of the source file (e.g. C, C++, ObjC, ObjC++, and assembly). These child libraries are added to a parent library that represents the Swift package target. The goal of this work was to support language standard flags in the Bazel targets generated by `rules_swift_package_manager`. If a C language standard is specified in the Swift package, it is applied to the C and ObjC targets. If a C++ language standard is specified, it is applied to the C++ and the ObjC++ targets. > [!NOTE] > The configuration for `rules_xcodeproj` in the Firebase example had to be updated to use the sandbox. Without this change, we were seeing duplicate definition errors. If you see these errors in your build, you may need to do the same. Closes #1079 .
1 parent 107b221 commit 6d12d53

File tree

12 files changed

+576
-672
lines changed

12 files changed

+576
-672
lines changed

examples/firebase_example/.bazelrc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,16 @@ build --cxxopt='-std=gnu++14'
2020
# Firebase SPM support requires `-ObjC` linker option.
2121
# https://github.com/firebase/firebase-ios-sdk/blob/master/SwiftPackageManager.md#requirements
2222
build --linkopt='-ObjC'
23+
24+
# Use the sandbox for rules_xcodeproj-specific builds. rules_xcodeproj removes
25+
# the sandboxed spawn strategy (--spawn_strategy=remote,worker,local) by
26+
# default to speed up builds. However, to support setting C/C++ language
27+
# standard flags per target
28+
# (https://github.com/cgrindel/rules_swift_package_manager/issues/1079), we
29+
# need to generate multiple {cc,objc}_library targets based upon the type of
30+
# source files (e.g. C, C++, ObjC, ObjC++). When builds occur outside of the
31+
# sandbox, we can experience "error: duplicate interface definition for class
32+
# 'XXX'" errors. Until we can address the underlying error, clients that use
33+
# rules_swift_package_manager with rules_xcodeproj may need to use sandbox
34+
# builds.
35+
common:rules_xcodeproj --spawn_strategy=remote,worker,sandboxed,local

examples/google_maps_example/MODULE.bazel.lock

Lines changed: 0 additions & 529 deletions
This file was deleted.

examples/interesting_deps/.bazelrc

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,3 @@ import %workspace%/../../ci.bazelrc
66

77
# Try to import a local.rc file; typically, written by CI
88
try-import %workspace%/../../local.bazelrc
9-
10-
# Required by geos
11-
build --cxxopt='-std=c++11'

examples/resources_example/Tests/MyAppTests/MyAppTests.swift

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,26 @@ class MyAppTests: XCTestCase {
1111
XCTAssertNotNil(actual)
1212
}
1313

14-
1514
func test_CoolStuff_bundleName() {
1615
let bundle = Bundle.bundle(named: "package-with-resources_CoolUI")
1716
XCTAssertNotNil(bundle)
1817
}
19-
18+
2019
func test_AppLovinSDKResources() throws {
2120
let url = ALResourceManager.resourceBundleURL
2221
XCTAssertNotNil(url)
2322
}
24-
23+
2524
func test_IterableDataModel() throws {
2625
// Log Iterable messages to an array
2726
class LogDelegate: IterableLogDelegate {
2827
var messages: [String] = []
29-
30-
func log(level: LogLevel, message: String) {
28+
29+
func log(level _: LogLevel, message: String) {
3130
messages.append(message)
3231
}
3332
}
34-
33+
3534
let logDelegate = LogDelegate()
3635
IterableLogUtil.sharedInstance = IterableLogUtil(
3736
dateProvider: SystemDateProvider(),
@@ -41,14 +40,14 @@ class MyAppTests: XCTestCase {
4140
// Create the persistence container from the bundled CoreData model
4241
let container: PersistentContainer? = PersistentContainer.initialize()
4342
XCTAssertNotNil(container)
44-
43+
4544
// Assert that the persistence container was successfully created
4645
let lastMessage = try XCTUnwrap(logDelegate.messages.last)
4746
XCTAssert(
4847
lastMessage.contains("Successfully loaded persistent store at:"),
4948
"Expected success log message. Found: \(logDelegate.messages.last ?? "nil")"
5049
)
51-
50+
5251
IterableLogUtil.sharedInstance = nil
5352
}
5453
}
@@ -60,18 +59,18 @@ extension Foundation.Bundle {
6059
let candidates = [
6160
// Bundle should be present here when the package is linked into an App.
6261
Bundle.main.resourceURL,
63-
62+
6463
// Bundle should be present here when the package is linked into a framework.
6564
Bundle(for: BundleFinder.self).resourceURL,
66-
65+
6766
// For command-line tools.
6867
Bundle.main.bundleURL,
69-
68+
7069
// Bundle should be present here when running previews from a different package (this is the path to "…/Debug-iphonesimulator/").
7170
Bundle(for: BundleFinder.self).resourceURL?.deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent(),
7271
Bundle(for: BundleFinder.self).resourceURL?.deletingLastPathComponent().deletingLastPathComponent(),
7372
]
74-
73+
7574
for candidate in candidates {
7675
let bundlePath = candidate?.appendingPathComponent(bundleName + ".bundle")
7776
if let bundle = bundlePath.flatMap(Bundle.init(url:)) {

swiftpkg/internal/clang_files.bzl

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,30 @@ _PUBLIC_HDR_DIRNAMES = ["include", "public"]
1313
# https://bazel.build/reference/be/c-cpp#cc_library.srcs
1414
_HEADER_EXTS = [".h", ".hh", ".hpp", ".hxx", ".inl", ".H"]
1515

16+
# C source extensions
17+
_C_SRC_EXTS = [".c"]
18+
19+
# C++ source extensions
20+
_CXX_SRC_EXTS = [".cc", ".cpp"]
21+
22+
# Objective-C source extensions
23+
_OBJC_SRC_EXTS = [".m"]
24+
25+
_OBJCXX_SRC_EXTS = [".mm"]
26+
27+
# Assembly source extensions
28+
_ASSEMBLY_SRC_EXTS = [".S"]
29+
30+
# Sources that should be included when constructing other cc_xxx targets
31+
_OTHER_SRC_EXTS = [".so", ".o", ".inc"]
32+
1633
# Acceptable sources clang and objc:
1734
# https://bazel.build/reference/be/c-cpp#cc_library.srcs
1835
# https://bazel.build/reference/be/objective-c#objc_library.srcs
1936
# NOTE: From examples found so far, .inc files tend to include source, not
2037
# header declarations.
21-
_SRC_EXTS = [".c", ".cc", ".cpp", ".S", ".so", ".o", ".m", ".mm", ".inc"]
38+
_SRC_EXTS = _C_SRC_EXTS + _CXX_SRC_EXTS + _OBJC_SRC_EXTS + _OBJCXX_SRC_EXTS + \
39+
_ASSEMBLY_SRC_EXTS + _OTHER_SRC_EXTS
2240

2341
def _is_hdr(path):
2442
_root, ext = paths.split_extension(path)
@@ -323,6 +341,36 @@ def _collect_files(
323341
textual_hdrs = sorted(textual_hdrs),
324342
)
325343

344+
def _organize_srcs(srcs):
345+
c_srcs = []
346+
cxx_srcs = []
347+
objc_srcs = []
348+
objcxx_srcs = []
349+
assembly_srcs = []
350+
other_srcs = []
351+
for src in srcs:
352+
_root, ext = paths.split_extension(src)
353+
if ext in _C_SRC_EXTS:
354+
c_srcs.append(src)
355+
elif ext in _CXX_SRC_EXTS:
356+
cxx_srcs.append(src)
357+
elif ext in _OBJC_SRC_EXTS:
358+
objc_srcs.append(src)
359+
elif ext in _OBJCXX_SRC_EXTS:
360+
objcxx_srcs.append(src)
361+
elif ext in _ASSEMBLY_SRC_EXTS:
362+
assembly_srcs.append(src)
363+
else:
364+
other_srcs.append(src)
365+
return struct(
366+
c_srcs = c_srcs,
367+
cxx_srcs = cxx_srcs,
368+
objc_srcs = objc_srcs,
369+
objcxx_srcs = objcxx_srcs,
370+
assembly_srcs = assembly_srcs,
371+
other_srcs = other_srcs,
372+
)
373+
326374
clang_files = struct(
327375
collect_files = _collect_files,
328376
find_magical_public_hdr_dir = _find_magical_public_hdr_dir,
@@ -331,6 +379,7 @@ clang_files = struct(
331379
is_include_hdr = _is_include_hdr,
332380
is_public_modulemap = _is_public_modulemap,
333381
is_under_path = _is_under_path,
382+
organize_srcs = _organize_srcs,
334383
reduce_paths = _reduce_paths,
335384
relativize = _relativize,
336385
)

swiftpkg/internal/objc_resource_bundle_accessor.bzl

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,12 @@ Generate an ObjC header file with an SPM-specific `SWIFTPM_MODULE_BUNDLE` macro.
3131
)
3232

3333
def _objc_resource_bundle_accessor_impl_impl(ctx):
34-
accessor_impl = ctx.actions.declare_file("{}_ObjcResourceBundleAccessor.m".format(
35-
ctx.label.name,
36-
))
34+
accessor_impl = ctx.actions.declare_file(
35+
"{label}_ObjcResourceBundleAccessor.{ext}".format(
36+
ext = ctx.attr.extension,
37+
label = ctx.label.name,
38+
),
39+
)
3740
ctx.actions.expand_template(
3841
template = ctx.file._impl_template,
3942
output = accessor_impl,
@@ -52,6 +55,10 @@ objc_resource_bundle_accessor_impl = rule(
5255
mandatory = True,
5356
doc = "The name of the resource bundle.",
5457
),
58+
"extension": attr.string(
59+
default = "m",
60+
doc = "The extension for the accessor implementation file.",
61+
),
5562
"module_name": attr.string(
5663
mandatory = True,
5764
doc = "The name of the module.",

swiftpkg/internal/pkginfo_targets.bzl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ _modulemap_suffix = "_modulemap"
77
_resource_bundle_suffix = "_resource_bundle"
88
_objc_resource_bundle_accessor_hdr_suffix = "_objc_resource_bundle_accessor_hdr"
99
_objc_resource_bundle_accessor_impl_suffix = "_objc_resource_bundle_accessor_impl"
10+
_objc_resource_bundle_accessor_library_suffix = "_objc_resource_bundle_accessor_library"
1011
_resource_bundle_accessor_suffix = "_resource_bundle_accessor"
1112
_resource_bundle_infoplist_suffix = "_resource_bundle_infoplist"
1213
_swift_hint_suffix = "_swift_hint"
@@ -196,6 +197,17 @@ def _objc_resource_bundle_accessor_impl_label_name(target_name):
196197
"""
197198
return target_name + _objc_resource_bundle_accessor_impl_suffix
198199

200+
def _objc_resource_bundle_accessor_library_label_name(target_name):
201+
"""Returns the name of the related `objc_library` target.
202+
203+
Args:
204+
target_name: The publicly advertised name for the Swift target.
205+
206+
Returns:
207+
The name of the `objc_library` as a `string`.
208+
"""
209+
return target_name + _objc_resource_bundle_accessor_library_suffix
210+
199211
def _resource_bundle_accessor_label_name(target_name):
200212
"""Returns the name of the related `resource_bundle_accessor` target.
201213
@@ -275,6 +287,7 @@ def make_pkginfo_targets(bazel_labels):
275287
modulemap_label_name = _modulemap_label_name,
276288
objc_resource_bundle_accessor_hdr_label_name = _objc_resource_bundle_accessor_hdr_label_name,
277289
objc_resource_bundle_accessor_impl_label_name = _objc_resource_bundle_accessor_impl_label_name,
290+
objc_resource_bundle_accessor_library_label_name = _objc_resource_bundle_accessor_library_label_name,
278291
resource_bundle_accessor_label_name = _resource_bundle_accessor_label_name,
279292
resource_bundle_infoplist_label_name = _resource_bundle_infoplist_label_name,
280293
resource_bundle_label_name = _resource_bundle_label_name,

swiftpkg/internal/pkginfos.bzl

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,8 @@ def _new_from_parsed_json(
624624
targets = targets,
625625
url = url,
626626
version = version,
627+
c_language_standard = dump_manifest.get("cLanguageStandard"),
628+
cxx_language_standard = dump_manifest.get("cxxLanguageStandard"),
627629
)
628630

629631
# MARK: - Swift Package
@@ -638,7 +640,9 @@ def _new(
638640
products = [],
639641
targets = [],
640642
url = None,
641-
version = None):
643+
version = None,
644+
c_language_standard = None,
645+
cxx_language_standard = None):
642646
"""Returns a `struct` representing information about a Swift package.
643647
644648
Args:
@@ -657,6 +661,10 @@ def _new(
657661
`pkginfos.new_target()`.
658662
url: Optional. The url of the package (`string`).
659663
version: Optional. The semantic version of the package (`string`).
664+
c_language_standard: Optional. The c language standard (e.g. `c99`,
665+
`gnu99`, `c11`).
666+
cxx_language_standard: Optional. The c++ language standard (e.g.
667+
`c++11`, `c++20`).
660668
661669
Returns:
662670
A `struct` representing information about a Swift package.
@@ -672,6 +680,8 @@ def _new(
672680
targets = targets,
673681
url = url,
674682
version = version,
683+
c_language_standard = c_language_standard,
684+
cxx_language_standard = cxx_language_standard,
675685
)
676686

677687
# MARK: - Platform
@@ -1165,6 +1175,8 @@ def _new_clang_src_info_from_sources(
11651175
srcs = sets.to_list(srcs_set)
11661176
explicit_srcs = sets.to_list(explicit_srcs_set)
11671177

1178+
# GH1290: Can I remove explicit_srcs? I believe that it is obsolete.
1179+
11681180
return _new_clang_src_info(
11691181
srcs = srcs,
11701182
explicit_srcs = explicit_srcs,
@@ -1184,7 +1196,7 @@ def _new_clang_src_info(
11841196
private_includes = [],
11851197
modulemap_path = None):
11861198
return struct(
1187-
srcs = srcs,
1199+
organized_srcs = clang_files.organize_srcs(srcs),
11881200
explicit_srcs = explicit_srcs,
11891201
hdrs = hdrs,
11901202
textual_hdrs = textual_hdrs,

swiftpkg/internal/starlark_codegen.bzl

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,30 @@ def _to_starlark(val):
9797
fail("Failed to finish processing starlark for value: {}".format(val))
9898

9999
def _process_complex_types(out):
100+
def _fail_with_orig_out(msg):
101+
def _pad_str(width, value):
102+
value = str(value)
103+
val_len = len(value)
104+
if val_len >= width:
105+
return value
106+
padding = width - val_len
107+
return " " * padding + value
108+
109+
# buildifier: disable=print
110+
print("*** DEBUG _process_complex_types out: ")
111+
112+
# Not sure why buildifier is complaining about idx not being initialized.
113+
# buildifier: disable=uninitialized
114+
for idx, item in enumerate(out):
115+
# buildifier: disable=print
116+
# buildifier: disable=uninitialized
117+
print("*** DEBUG", _pad_str(3, idx), ":", _pad_str(7, type(item)), ":", item)
118+
fail(msg)
119+
100120
finished = True
101121
new_out = []
102-
for v in out:
122+
123+
for idx, v in enumerate(out):
103124
v_type = type(v)
104125

105126
# Check for a with_indent struct and get its indent value and process
@@ -127,10 +148,24 @@ def _process_complex_types(out):
127148
elif v_type == "struct":
128149
to_starlark_fn = getattr(v, "to_starlark_parts", None)
129150
if to_starlark_fn == None:
130-
fail("Starlark code gen received a struct without a to_starlark_parts function.", v)
151+
_fail_with_orig_out(
152+
"""\
153+
Starlark code gen received a struct without a to_starlark_parts function. \
154+
idx: {idx}, v: {v}\
155+
""".format(
156+
idx = idx,
157+
v = v,
158+
),
159+
)
131160
new_out.extend(to_starlark_fn(v, current_indent))
132161
else:
133-
fail("Starlark code gen received an unsupported type.", v_type, v)
162+
_fail_with_orig_out("""\
163+
Starlark code gen received an unsupported type. idx: {idx}, v_type: {v_type}, v: {v}\
164+
""".format(
165+
idx = idx,
166+
v_type = v_type,
167+
v = v,
168+
))
134169

135170
return new_out, finished
136171

0 commit comments

Comments
 (0)