Skip to content
This repository has been archived by the owner on Apr 3, 2023. It is now read-only.

Commit

Permalink
Make sure that executable targets are named after the product, not th…
Browse files Browse the repository at this point in the history
…e target. (#78)

Make sure that executable targets are named after the product, not the target.
  • Loading branch information
cgrindel authored Sep 18, 2021
1 parent 09888ca commit cc306c8
Show file tree
Hide file tree
Showing 18 changed files with 331 additions and 35 deletions.
38 changes: 38 additions & 0 deletions doc/package_descriptions_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,3 +285,41 @@ Returns all of the targets that are a transitive dependency for the specified pr
| <a id="package_descriptions.transitive_dependencies-product_refs"></a>product_refs | A <code>list</code> of reference <code>string</code> values as created by <code>references.create_product_ref()</code>. | none |


<a id="#package_descriptions.is_executable_product"></a>

## package_descriptions.is_executable_product

<pre>
package_descriptions.is_executable_product(<a href="#package_descriptions.is_executable_product-product">product</a>)
</pre>

Returns a boolean indicating whether the specified product dictionary is an executable product.

**PARAMETERS**


| Name | Description | Default Value |
| :------------- | :------------- | :------------- |
| <a id="package_descriptions.is_executable_product-product"></a>product | A <code>dict</code> representing a product from package description JSON. | none |


<a id="#package_descriptions.get_product"></a>

## package_descriptions.get_product

<pre>
package_descriptions.get_product(<a href="#package_descriptions.get_product-pkg_desc">pkg_desc</a>, <a href="#package_descriptions.get_product-product_name">product_name</a>, <a href="#package_descriptions.get_product-fail_if_not_found">fail_if_not_found</a>)
</pre>

Returns the product with the specified product name.

**PARAMETERS**


| Name | Description | Default Value |
| :------------- | :------------- | :------------- |
| <a id="package_descriptions.get_product-pkg_desc"></a>pkg_desc | A <code>dict</code> representing a package description. | none |
| <a id="package_descriptions.get_product-product_name"></a>product_name | The product name as a <code>string</code>. | none |
| <a id="package_descriptions.get_product-fail_if_not_found"></a>fail_if_not_found | <p align="center"> - </p> | <code>True</code> |


3 changes: 2 additions & 1 deletion doc/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ On this page:
## SPMPackageInfo

<pre>
SPMPackageInfo(<a href="#SPMPackageInfo-name">name</a>, <a href="#SPMPackageInfo-swift_modules">swift_modules</a>, <a href="#SPMPackageInfo-clang_modules">clang_modules</a>, <a href="#SPMPackageInfo-system_library_modules">system_library_modules</a>)
SPMPackageInfo(<a href="#SPMPackageInfo-name">name</a>, <a href="#SPMPackageInfo-swift_binaries">swift_binaries</a>, <a href="#SPMPackageInfo-swift_modules">swift_modules</a>, <a href="#SPMPackageInfo-clang_modules">clang_modules</a>, <a href="#SPMPackageInfo-system_library_modules">system_library_modules</a>)
</pre>

Describes the information about an SPM package.
Expand All @@ -27,6 +27,7 @@ Describes the information about an SPM package.
| Name | Description |
| :------------- | :------------- |
| <a id="SPMPackageInfo-name"></a>name | Name of the Swift package. |
| <a id="SPMPackageInfo-swift_binaries"></a>swift_binaries | A <code>list</code> of values returned from <code>providers.swift_binary</code>. |
| <a id="SPMPackageInfo-swift_modules"></a>swift_modules | A <code>list</code> of values returned from <code>providers.swift_module</code>. |
| <a id="SPMPackageInfo-clang_modules"></a>clang_modules | A <code>list</code> of values returned from <code>providers.clang_module</code>. |
| <a id="SPMPackageInfo-system_library_modules"></a>system_library_modules | <code>List</code> of values returned from <code>providers.system_library_module</code>. |
Expand Down
20 changes: 20 additions & 0 deletions doc/providers_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,26 @@ Creates a value describing a copy operation.
| <a id="providers.copy_info-dest"></a>dest | The destination file. | none |


<a id="#providers.swift_binary"></a>

## providers.swift_binary

<pre>
providers.swift_binary(<a href="#providers.swift_binary-name">name</a>, <a href="#providers.swift_binary-executable">executable</a>, <a href="#providers.swift_binary-all_outputs">all_outputs</a>)
</pre>

Creates a value representing a Swift binary that is built from a package.

**PARAMETERS**


| Name | Description | Default Value |
| :------------- | :------------- | :------------- |
| <a id="providers.swift_binary-name"></a>name | Name of the Swift binary. | none |
| <a id="providers.swift_binary-executable"></a>executable | The executable. | <code>None</code> |
| <a id="providers.swift_binary-all_outputs"></a>all_outputs | All of the output files that are declared for the module. | <code>[]</code> |


<a id="#providers.swift_module"></a>

## providers.swift_module
Expand Down
19 changes: 19 additions & 0 deletions doc/references_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,22 @@ Returns a boolean indicating whether the reference string is a target reference.
| <a id="references.is_target_ref-for_pkg"></a>for_pkg | Optional. A package name as a <code>string</code> value to include in the check. | <code>None</code> |


<a id="#references.is_product_ref"></a>

## references.is_product_ref

<pre>
references.is_product_ref(<a href="#references.is_product_ref-ref_str">ref_str</a>, <a href="#references.is_product_ref-for_pkg">for_pkg</a>)
</pre>

Returns a boolean indicating whether the reference string is a product reference.

**PARAMETERS**


| Name | Description | Default Value |
| :------------- | :------------- | :------------- |
| <a id="references.is_product_ref-ref_str"></a>ref_str | A valid reference <code>string</code>. | none |
| <a id="references.is_product_ref-for_pkg"></a>for_pkg | Optional. A package name as a <code>string</code> value to include in the check. | <code>None</code> |


15 changes: 15 additions & 0 deletions examples/simple_with_binary/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,18 @@ sh_test(
],
deps = ["@bazel_tools//tools/bash/runfiles"],
)

# Make sure that the binary is referenced. Otherwise, Bazel won't build it.
alias(
name = "swiftformat",
actual = "@swift_utils//SwiftFormat:swiftformat",
)

sh_test(
name = "swiftformat_test",
srcs = ["swiftformat_test.sh"],
data = [
":swiftformat",
],
deps = ["@bazel_tools//tools/bash/runfiles"],
)
7 changes: 7 additions & 0 deletions examples/simple_with_binary/WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ spm_repositories(
from_version = "0.0.0",
products = ["swiftlint"],
),
spm_pkg(
"https://github.com/nicklockwood/SwiftFormat.git",
from_version = "0.0.0",
products = [
"swiftformat",
],
),
],
platforms = [
".macOS(.v10_12)",
Expand Down
17 changes: 17 additions & 0 deletions examples/simple_with_binary/swiftformat_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env bash

# --- begin runfiles.bash initialization v2 ---
# Copy-pasted from the Bazel Bash runfiles library v2.
set -uo pipefail; f=bazel_tools/tools/bash/runfiles/runfiles.bash
source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \
source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \
source "$0.runfiles/$f" 2>/dev/null || \
source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \
source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \
{ echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e
# --- end runfiles.bash initialization v2 ---

swiftformat="$(rlocation "swift_utils/SwiftFormat/swiftformat")"

# Make sure that we can execute swiftformat.
"${swiftformat}" --version
15 changes: 15 additions & 0 deletions spm/internal/package_descriptions.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,18 @@ def _get_package_description(repository_ctx, working_directory = ""):

# MARK: - Product Functions

def _is_executable_product(product):
"""Returns a boolean indicating whether the specified product dictionary is an executable product.
Args:
product: A `dict` representing a product from package description
JSON.
Returns:
A `bool` indicating whether the product is an executable.
"""
return "executable" in product["type"]

def _is_library_product(product):
"""Returns a boolean indicating whether the specified product dictionary is a library product.
Expand Down Expand Up @@ -525,6 +537,9 @@ package_descriptions = struct(
dependency_repository_name = _dependency_repository_name,
# Transitive Dependency Functions
transitive_dependencies = _transitive_dependencies,
# Product Functions
is_executable_product = _is_executable_product,
get_product = _get_product,
# Constants
root_pkg_name = "_root",
)
19 changes: 19 additions & 0 deletions spm/internal/providers.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,30 @@ SPMPackageInfo = provider(
doc = "Describes the information about an SPM package.",
fields = {
"name": "Name of the Swift package.",
"swift_binaries": "A `list` of values returned from `providers.swift_binary`.",
"swift_modules": "A `list` of values returned from `providers.swift_module`.",
"clang_modules": "A `list` of values returned from `providers.clang_module`.",
"system_library_modules": "`List` of values returned from `providers.system_library_module`.",
},
)

def _create_swift_binary(name, executable = None, all_outputs = []):
"""Creates a value representing a Swift binary that is built from a package.
Args:
name: Name of the Swift binary.
executable: The executable.
all_outputs: All of the output files that are declared for the module.
Returns:
A struct which provides info about a Swift binary built by SPM.
"""
return struct(
name = name,
executable = executable,
all_outputs = all_outputs,
)

def _create_swift_module(
module_name,
o_files = [],
Expand Down Expand Up @@ -149,6 +167,7 @@ def _create_copy_info(src, dest):
providers = struct(
clang_module = _create_clang_module,
copy_info = _create_copy_info,
swift_binary = _create_swift_binary,
swift_module = _create_swift_module,
system_library_module = _create_system_library_module,
)
18 changes: 18 additions & 0 deletions spm/internal/references.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,23 @@ def _is_target_ref(ref_str, for_pkg = None):
starts_with_parts.extend([for_pkg, "/"])
return ref_str.startswith("".join(starts_with_parts))

def _is_product_ref(ref_str, for_pkg = None):
"""Returns a boolean indicating whether the reference string is a product reference.
Args:
ref_str: A valid reference `string`.
for_pkg: Optional. A package name as a `string` value to include in
the check.
Returns:
A `bool` value indicating whether the reference string is a product
reference.
"""
starts_with_parts = [reference_types.product, ":"]
if for_pkg != None:
starts_with_parts.extend([for_pkg, "/"])
return ref_str.startswith("".join(starts_with_parts))

# MARK: - Namespace

reference_types = struct(
Expand All @@ -99,4 +116,5 @@ references = struct(
create_target_ref = _create_target_ref,
create_product_ref = _create_product_ref,
is_target_ref = _is_target_ref,
is_product_ref = _is_product_ref,
)
47 changes: 32 additions & 15 deletions spm/internal/spm_package.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,11 @@ def _declare_swift_library_target_files(ctx, target, build_config_path):
all_outputs = all_build_outs,
)

def _declare_swift_executable_target_files(ctx, target, build_config_path):
target_name = target["name"]
module_name = target["name"]

executable = ctx.actions.declare_file("%s/%s" % (build_config_path, target_name))

return providers.swift_module(
module_name = module_name,
def _declare_swift_executable_product_files(ctx, product, build_config_path):
product_name = product["name"]
executable = ctx.actions.declare_file("%s/%s" % (build_config_path, product_name))
return providers.swift_binary(
name = product_name,
executable = executable,
all_outputs = [executable],
)
Expand Down Expand Up @@ -158,6 +155,7 @@ def _gather_package_build_info(
build_config_path,
clang_custom_infos_dict,
pkg_desc,
product_refs,
target_refs):
"""Gathers build information for a Swift package.
Expand All @@ -168,6 +166,7 @@ def _gather_package_build_info(
for this package and the values are a `list`
of public headers.
pkg_desc: A `dict` representing the package descprtion JSON.
product_refs: A `list` of product references (`string`) for this package.
target_refs: A `list` of target references (`string`) that are
dependencies for the package.
Expand All @@ -176,11 +175,30 @@ def _gather_package_build_info(
the build information for the Swift package.
"""
build_outs = []
swift_binaries = []
swift_modules = []
clang_modules = []
system_library_modules = []
pkg_name = pkg_desc["name"]

# Collect the executable products
exec_products = []
for product_ref in product_refs:
ref_type, pkg_name, product_name = refs.split(product_ref)
product = pds.get_product(pkg_desc, product_name)
if pds.is_executable_product(product):
exec_products.append(product)

# Declare the outputs for all of the Swift binaries
for product in exec_products:
swift_binary_info = _declare_swift_executable_product_files(
ctx,
product,
build_config_path,
)
swift_binaries.append(swift_binary_info)
build_outs.extend(swift_binary_info.all_outputs)

# Declare outputs for the targets that will be used
for target_ref in target_refs:
ref_type, pname, target_name = refs.split(target_ref)
Expand All @@ -196,13 +214,9 @@ def _gather_package_build_info(
swift_modules.append(swift_module_info)
build_outs.extend(swift_module_info.all_outputs)
elif pds.is_executable_target(target):
swift_module_info = _declare_swift_executable_target_files(
ctx,
target,
build_config_path,
)
swift_modules.append(swift_module_info)
build_outs.extend(swift_module_info.all_outputs)
# Executable targets are declared by product, not by SPM target.
pass

else:
fail("Unrecognized Swift target type. %s" % (target))

Expand Down Expand Up @@ -230,6 +244,7 @@ def _gather_package_build_info(

pkg_info = SPMPackageInfo(
name = pkg_name,
swift_binaries = swift_binaries,
swift_modules = swift_modules,
clang_modules = clang_modules,
system_library_modules = system_library_modules,
Expand Down Expand Up @@ -478,11 +493,13 @@ def _spm_package_impl(ctx):
continue
pkg_desc = pkg_descs_dict[pkg_name]
clang_custom_infos_dict = pkg_clang_custom_infos_dict.get(pkg_name, default = {})
product_refs = [pr for pr in declared_product_refs if refs.is_product_ref(pr, for_pkg = pkg_name)]
pkg_build_infos_dict[pkg_name] = _gather_package_build_info(
ctx,
build_config_path,
clang_custom_infos_dict,
pkg_desc,
product_refs,
target_refs,
)

Expand Down
3 changes: 3 additions & 0 deletions spm/internal/spm_package_info_utils.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ def _get_module_info(pkg_info, module_name):
module a `struct` value as created by `providers.clang_module()` is
returned.
"""
for binary in pkg_info.swift_binaries:
if binary.name == module_name:
return binary
for module in pkg_info.swift_modules:
if module.module_name == module_name:
return module
Expand Down
Loading

0 comments on commit cc306c8

Please sign in to comment.