diff --git a/docs/bzlmod_extensions_overview.md b/docs/bzlmod_extensions_overview.md index ae15d0303..ade21ebe9 100755 --- a/docs/bzlmod_extensions_overview.md +++ b/docs/bzlmod_extensions_overview.md @@ -16,7 +16,7 @@ On this page:
swift_deps = use_extension("@rules_swift_package_manager//:extensions.bzl", "swift_deps") swift_deps.configure_package(name, init_submodules, patch_args, patch_cmds, patch_cmds_win, - patch_tool, patches, recursive_init_submodules) + patch_tool, patches, recursive_init_submodules, experimental_expose_build_files) swift_deps.configure_swift_package(build_path, cache_path, dependency_caching, manifest_cache, manifest_caching, security_path) swift_deps.from_package(declare_swift_deps_info, declare_swift_package, resolved, swift) @@ -43,6 +43,7 @@ Used to add or override settings for a particular Swift package. | patch_tool | The patch(1) utility to use. If this is specified, Bazel will use the specified patch tool instead of the Bazel-native patch implementation. | String | optional | `""` | | patches | A list of files that are to be applied as patches after extracting the archive. By default, it uses the Bazel-native patch implementation which doesn't support fuzz match and binary patch, but Bazel will fall back to use patch command line tool if `patch_tool` attribute is specified or there are arguments other than `-p` in `patch_args` attribute. | List of labels | optional | `[]` | | recursive_init_submodules | Whether to clone submodules recursively in the repository. | Boolean | optional | `True` | +| experimental_expose_build_files | Allows to expose internal build files required for package compilation. This option is experimental and should be used at your own risk. The structure and labels of exposed build files may change in future releases without requiring a major version bump. | Boolean | optional | `False` | diff --git a/swiftpkg/bzlmod/swift_deps.bzl b/swiftpkg/bzlmod/swift_deps.bzl index c5c83465b..eb448b161 100644 --- a/swiftpkg/bzlmod/swift_deps.bzl +++ b/swiftpkg/bzlmod/swift_deps.bzl @@ -5,7 +5,7 @@ load("//swiftpkg/internal:local_swift_package.bzl", "local_swift_package") load("//swiftpkg/internal:pkginfos.bzl", "pkginfos") load("//swiftpkg/internal:repository_utils.bzl", "repository_utils") load("//swiftpkg/internal:swift_deps_info.bzl", "swift_deps_info") -load("//swiftpkg/internal:swift_package.bzl", "PATCH_ATTRS", "swift_package") +load("//swiftpkg/internal:swift_package.bzl", "EXPERIMENTAL_ATTRS", "PATCH_ATTRS", "swift_package") load("//swiftpkg/internal:swift_package_tool.bzl", "SWIFT_PACKAGE_CONFIG_ATTRS") load("//swiftpkg/internal:swift_package_tool_repo.bzl", "swift_package_tool_repo") @@ -150,6 +150,16 @@ the Swift package to make it available.\ ) _declare_pkg_from_dependency(dep, config_pkg) + # Add all transitive dependencies to direct_dep_repo_names if `experimental_expose_build_files` flag is set. + for dep in all_deps_by_id.values(): + config_pkg = config_pkgs.get(dep.name) or config_pkgs.get( + bazel_repo_names.from_identity(dep.identity), + ) + if config_pkg and config_pkg.experimental_expose_build_files: + bazel_repo_name = bazel_repo_names.from_identity(dep.identity) + if bazel_repo_name not in direct_dep_repo_names: + direct_dep_repo_names.append(bazel_repo_name) + return direct_dep_repo_names def _declare_pkg_from_dependency(dep, config_pkg): @@ -162,6 +172,7 @@ def _declare_pkg_from_dependency(dep, config_pkg): patch_cmds_win = None patch_tool = None patches = None + experimental_expose_build_files = None if config_pkg: init_submodules = config_pkg.init_submodules recursive_init_submodules = config_pkg.recursive_init_submodules @@ -170,6 +181,7 @@ def _declare_pkg_from_dependency(dep, config_pkg): patch_cmds_win = config_pkg.patch_cmds_win patch_tool = config_pkg.patch_tool patches = config_pkg.patches + experimental_expose_build_files = config_pkg.experimental_expose_build_files pin = dep.source_control.pin swift_package( @@ -186,6 +198,7 @@ def _declare_pkg_from_dependency(dep, config_pkg): patch_cmds_win = patch_cmds_win, patch_tool = patch_tool, patches = patches, + experimental_expose_build_files = experimental_expose_build_files, ) elif dep.file_system: @@ -291,7 +304,7 @@ The identity (i.e., name in the package's manifest) for the Swift package.\ default = True, doc = "Whether to clone submodules recursively in the repository.", ), - } | PATCH_ATTRS, + } | PATCH_ATTRS | EXPERIMENTAL_ATTRS, doc = "Used to add or override settings for a particular Swift package.", ) diff --git a/swiftpkg/internal/swift_package.bzl b/swiftpkg/internal/swift_package.bzl index fb21b87af..ed2b3a595 100644 --- a/swiftpkg/internal/swift_package.bzl +++ b/swiftpkg/internal/swift_package.bzl @@ -175,8 +175,19 @@ PATCH_ATTRS = { ), } +EXPERIMENTAL_ATTRS = { + "experimental_expose_build_files": attr.bool( + default = False, + doc = "Allows to expose internal build files required for package compilation. " + + "This option is experimental and should be used at your own risk. " + + "The structure and labels of exposed build files may change in future releases " + + "without requiring a major version bump.", + ), +} + _ALL_ATTRS = dicts.add( PATCH_ATTRS, + EXPERIMENTAL_ATTRS, _GIT_ATTRS, repo_rules.env_attrs, repo_rules.swift_attrs, diff --git a/swiftpkg/internal/swiftpkg_build_files.bzl b/swiftpkg/internal/swiftpkg_build_files.bzl index 3075f1889..6e3d467b5 100644 --- a/swiftpkg/internal/swiftpkg_build_files.bzl +++ b/swiftpkg/internal/swiftpkg_build_files.bzl @@ -22,7 +22,7 @@ def _new_for_target(repository_ctx, pkg_ctx, target, artifact_infos = []): if target.module_type == module_types.clang: return _clang_target_build_file(repository_ctx, pkg_ctx, target) elif target.module_type == module_types.swift: - return _swift_target_build_file(pkg_ctx, target) + return _swift_target_build_file(repository_ctx, pkg_ctx, target) elif target.module_type == module_types.system_library: return _system_library_build_file(target) elif target.module_type == module_types.binary: @@ -32,14 +32,14 @@ def _new_for_target(repository_ctx, pkg_ctx, target, artifact_infos = []): lambda ai: ai.artifiact_type == artifact_types.xcframework, ) if xcf_artifact_info != None: - return _xcframework_import_build_file(target, xcf_artifact_info) + return _xcframework_import_build_file(repository_ctx, target, xcf_artifact_info) # GH046: Support plugins. return None # MARK: - Swift Target -def _swift_target_build_file(pkg_ctx, target): +def _swift_target_build_file(repository_ctx, pkg_ctx, target): if target.swift_src_info == None: fail("Expected a `swift_src_info`. name: ", target.name) @@ -47,7 +47,7 @@ def _swift_target_build_file(pkg_ctx, target): attrs = { "module_name": target.c99name, "srcs": pkginfo_targets.srcs(target), - "visibility": ["//:__subpackages__"], + "visibility": _build_file_visibility(repository_ctx), } def _update_attr_list(name, value): @@ -144,6 +144,7 @@ def _swift_target_build_file(pkg_ctx, target): if target.resources: swift_apple_res_bundle_info = _apple_resource_bundle_for_swift( + repository_ctx, pkg_ctx, target, ) @@ -176,7 +177,7 @@ def _swift_target_build_file(pkg_ctx, target): # Generate a modulemap for the Swift module. if attrs.get("generates_header", False): - all_build_files.append(_generate_modulemap_for_swift_target(target, deps)) + all_build_files.append(_generate_modulemap_for_swift_target(repository_ctx, target, deps)) return build_files.merge(*all_build_files) @@ -274,6 +275,7 @@ def _clang_target_build_file(repository_ctx, pkg_ctx, target): clang_apple_res_bundle_info = None if target.resources: clang_apple_res_bundle_info = _apple_resource_bundle_for_clang( + repository_ctx, pkg_ctx, target, ) @@ -363,7 +365,7 @@ def _clang_target_build_file(repository_ctx, pkg_ctx, target): attrs = { "copts": copts, "srcs": srcs, - "visibility": ["//:__subpackages__"], + "visibility": _build_file_visibility(repository_ctx), } if clang_src_info.hdrs: attrs["hdrs"] = clang_src_info.hdrs @@ -438,7 +440,7 @@ def _clang_target_build_file(repository_ctx, pkg_ctx, target): "hdrs": clang_src_info.hdrs, "module_name": target.c99name, "noop": noop_modulemap, - "visibility": ["//:__subpackages__"], + "visibility": _build_file_visibility(repository_ctx), } decls.append( build_decls.new( @@ -646,7 +648,7 @@ def _system_library_build_file(target): # MARK: - Apple xcframework Targets -def _xcframework_import_build_file(target, artifact_info): +def _xcframework_import_build_file(repository_ctx, target, artifact_info): attrs = {} if artifact_info.link_type == link_types.static: load_stmts = [apple_static_xcframework_import_load_stmt] @@ -684,7 +686,7 @@ expected: {expected}\ kind = kind, name = pkginfo_targets.bazel_label_name(target), attrs = attrs | { - "visibility": ["//:__subpackages__"], + "visibility": _build_file_visibility(repository_ctx), "xcframework_imports": glob, }, ), @@ -696,7 +698,7 @@ expected: {expected}\ # MARK: - Apple Resource Group -def _apple_resource_bundle(target, package_name, default_localization): +def _apple_resource_bundle(repository_ctx, target, package_name, default_localization): bzl_target_name = pkginfo_targets.bazel_label_name(target) bundle_label_name = pkginfo_targets.resource_bundle_label_name(bzl_target_name) bundle_name = pkginfo_targets.resource_bundle_name(package_name, target.c99name) @@ -730,7 +732,7 @@ def _apple_resource_bundle(target, package_name, default_localization): # Based upon the code in SPM, it looks like they only support unstructured resources. # https://github.com/apple/swift-package-manager/blob/main/Sources/PackageModel/Resource.swift#L25-L33 "resources": resources, - "visibility": ["//:__subpackages__"], + "visibility": _build_file_visibility(repository_ctx), }, ), ] @@ -740,8 +742,9 @@ def _apple_resource_bundle(target, package_name, default_localization): build_file = build_files.new(load_stmts = load_stmts, decls = decls), ) -def _apple_resource_bundle_for_swift(pkg_ctx, target): +def _apple_resource_bundle_for_swift(repository_ctx, pkg_ctx, target): apple_res_bundle_info = _apple_resource_bundle( + repository_ctx, target, pkg_ctx.pkg_info.name, pkg_ctx.pkg_info.default_localization, @@ -772,8 +775,9 @@ def _apple_resource_bundle_for_swift(pkg_ctx, target): ), ) -def _apple_resource_bundle_for_clang(pkg_ctx, target): +def _apple_resource_bundle_for_clang(repository_ctx, pkg_ctx, target): apple_res_bundle_info = _apple_resource_bundle( + repository_ctx, target, pkg_ctx.pkg_info.name, pkg_ctx.pkg_info.default_localization, @@ -856,7 +860,7 @@ def _collect_modulemap_deps(deps): modulemap_deps.append(mm_dep) return modulemap_deps -def _generate_modulemap_for_swift_target(target, deps): +def _generate_modulemap_for_swift_target(repository_ctx, target, deps): load_stmts = [swiftpkg_generate_modulemap_load_stmt] bzl_target_name = pkginfo_targets.bazel_label_name(target) modulemap_target_name = pkginfo_targets.modulemap_label_name(bzl_target_name) @@ -865,7 +869,7 @@ def _generate_modulemap_for_swift_target(target, deps): "deps": bzl_selects.to_starlark(modulemap_deps), "hdrs": [":{}".format(bzl_target_name)], "module_name": target.c99name, - "visibility": ["//:__subpackages__"], + "visibility": _build_file_visibility(repository_ctx), } decls = [ build_decls.new( @@ -1053,6 +1057,12 @@ def _new_for_license(pkg_info, license): decls = decls, ) +# MARK: - Build files encapsulation + +def _build_file_visibility(repository_ctx): + experimental_expose_build_files = getattr(repository_ctx.attr, "experimental_expose_build_files", False) + return ["//visibility:public"] if experimental_expose_build_files else ["//:__subpackages__"] + # MARK: - Constants and API Definition swift_location = "@build_bazel_rules_swift//swift:swift.bzl" diff --git a/swiftpkg/tests/swiftpkg_build_files_tests.bzl b/swiftpkg/tests/swiftpkg_build_files_tests.bzl index 8a656e24b..141dfb8e6 100644 --- a/swiftpkg/tests/swiftpkg_build_files_tests.bzl +++ b/swiftpkg/tests/swiftpkg_build_files_tests.bzl @@ -488,6 +488,7 @@ def _target_generation_test(ctx): struct( msg = "Swift library target", name = "RegularSwiftTargetAsLibrary", + attr = {}, exp = """\ load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") @@ -509,6 +510,7 @@ swift_library( struct( msg = "Swift regular target associated with executable product", name = "RegularTargetForExec", + attr = {}, exp = """\ load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") @@ -528,6 +530,7 @@ swift_library( struct( msg = "Swift test target", name = "RegularSwiftTargetAsLibraryTests", + attr = {}, exp = """\ load("@build_bazel_rules_swift//swift:swift.bzl", "swift_test") @@ -545,6 +548,7 @@ swift_test( struct( msg = "Swift executable target", name = "SwiftExecutableTarget", + attr = {}, exp = """\ load("@build_bazel_rules_swift//swift:swift.bzl", "swift_binary") @@ -574,6 +578,7 @@ swift_binary( struct( msg = "simple clang target", name = "ClangLibrary", + attr = {}, exp = """\ load("@build_bazel_rules_swift//swift:swift.bzl", "swift_interop_hint") @@ -620,6 +625,7 @@ swift_interop_hint( struct( msg = "Objc target", name = "ObjcLibrary", + attr = {}, exp = """\ load("@rules_swift_package_manager//swiftpkg:build_defs.bzl", "generate_modulemap") @@ -703,6 +709,7 @@ objc_library( struct( msg = "Objc target with a modulemap", name = "ObjcLibraryWithModulemap", + attr = {}, exp = """\ load("@rules_swift_package_manager//swiftpkg:build_defs.bzl", "generate_modulemap") @@ -786,6 +793,7 @@ objc_library( struct( msg = "Swift target with conditional dep", name = "SwiftLibraryWithConditionalDep", + attr = {}, exp = """\ load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") @@ -809,6 +817,7 @@ swift_library( struct( msg = "Clang target with conditional dep", name = "ClangLibraryWithConditionalDep", + attr = {}, exp = """\ load("@build_bazel_rules_swift//swift:swift.bzl", "swift_interop_hint") @@ -855,6 +864,7 @@ swift_interop_hint( struct( msg = "Swift library target with @objc directives and Objc dep", name = "SwiftForObjcTarget", + attr = {}, exp = """\ load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") load("@rules_swift_package_manager//swiftpkg:build_defs.bzl", "generate_modulemap") @@ -887,6 +897,7 @@ swift_library( struct( msg = "Swift library target with file path resource", name = "SwiftLibraryWithFilePathResource", + attr = {}, exp = """\ load("@build_bazel_rules_apple//apple:resources.bzl", "apple_resource_bundle") load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") @@ -929,6 +940,7 @@ swift_library( struct( msg = "Objc target with resources", name = "ObjcLibraryWithResources", + attr = {}, exp = """\ load("@build_bazel_rules_apple//apple:resources.bzl", "apple_resource_bundle") load("@rules_swift_package_manager//swiftpkg:build_defs.bzl", "generate_modulemap", "objc_resource_bundle_accessor_hdr", "objc_resource_bundle_accessor_impl", "resource_bundle_infoplist") @@ -1039,6 +1051,48 @@ resource_bundle_infoplist( name = "ObjcLibraryWithResources.rspm_resource_bundle_infoplist", region = "en", ) +""", + ), + struct( + msg = "Swift library target with default visibility", + name = "RegularSwiftTargetAsLibrary", + attr = { + "experimental_expose_build_files": False, + }, + exp = """\ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "RegularSwiftTargetAsLibrary.rspm", + always_include_developer_search_paths = True, + copts = ["-DSWIFT_PACKAGE"], + module_name = "RegularSwiftTargetAsLibrary", + package_name = "MyPackage", + srcs = ["Source/RegularSwiftTargetAsLibrary/RegularSwiftTargetAsLibrary.swift"], + tags = ["manual"], + visibility = ["//:__subpackages__"], +) +""", + ), + struct( + msg = "Swift library target with public visibility", + name = "RegularSwiftTargetAsLibrary", + attr = { + "experimental_expose_build_files": True, + }, + exp = """\ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "RegularSwiftTargetAsLibrary.rspm", + always_include_developer_search_paths = True, + copts = ["-DSWIFT_PACKAGE"], + module_name = "RegularSwiftTargetAsLibrary", + package_name = "MyPackage", + srcs = ["Source/RegularSwiftTargetAsLibrary/RegularSwiftTargetAsLibrary.swift"], + tags = ["manual"], + visibility = ["//visibility:public"], +) """, ), ] @@ -1061,6 +1115,7 @@ resource_bundle_infoplist( paths.normalize(paths.join(target.path, path)): result for path, result in getattr(t, "is_directory", {}).items() }, + attr = t.attr, ) actual = scg.to_starlark( swiftpkg_build_files.new_for_target(repository_ctx, _pkg_ctx, target), diff --git a/swiftpkg/tests/testutils.bzl b/swiftpkg/tests/testutils.bzl index 822a0b8ad..06998d392 100644 --- a/swiftpkg/tests/testutils.bzl +++ b/swiftpkg/tests/testutils.bzl @@ -13,7 +13,8 @@ def _new_stub_repository_ctx( find_results = {}, is_directory_results = {}, file_type_results = {}, - load_commands_results = {}): + load_commands_results = {}, + attr = {}): def read(path): return file_contents.get(path, "") @@ -56,6 +57,7 @@ def _new_stub_repository_ctx( execute = execute, attr = struct( bazel_package_name = repo_name, + **attr ), )