Skip to content

Commit

Permalink
go: add assembler_flags field for adding arbitrary extra assembler …
Browse files Browse the repository at this point in the history
…flags (#17731)

Add `assembler_flags` field for adding arbitrary extra assembler flags when assembling Go-format assembly files.

Note: These flags will not be added to gcc/clang-format assembly that is assembled packages using Cgo.

This field can be specified on several different target types:

- On `go_mod` targets, the assembler flags are used when building any package involving the module including both first-party (i.e., `go_package` targets) and third-party dependencies.

- On `go_binary` targets, the assembler flags are used when building any packages comprising that binary including third-party dependencies. These assembler flags will be added after any assembler flags added by any `assembler_flags` field set on the applicable `go_mod` target.

- On `go_package` targets, the assembler flags are used only for building that specific package and not for any other package. These assembler flags will be added after any assembler flags added by any `assembler_flags` field set on the applicable `go_mod` target or applicable `go_binary` target.

Run `go doc cmd/asm` to see the flags supported by `go tool asm`.

Fixes #17441.
  • Loading branch information
tdyas authored Dec 8, 2022
1 parent a23c3e0 commit 427d308
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 1 deletion.
29 changes: 29 additions & 0 deletions src/python/pants/backend/go/target_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,32 @@ class GoTestAddressSanitizerEnabledField(GoRaceDetectorEnabledField):
)


class GoAssemblerFlagsField(StringSequenceField):
alias = "assembler_flags"
help = softwrap(
"""
Extra flags to pass to the Go assembler (i.e., `go tool asm`) when assembling Go-format assembly code.
Note: These flags will not be added to gcc/clang-format assembly that is assembled in packages using Cgo.
This field can be specified on several different target types:
- On `go_mod` targets, the assembler flags are used when building any package involving the module
including both first-party (i.e., `go_package` targets) and third-party dependencies.
- On `go_binary` targets, the assembler flags are used when building any packages comprising that binary
including third-party dependencies. These assembler flags will be added after any assembler flags
added by any `assembler_flags` field set on the applicable `go_mod` target.
- On `go_package` targets, the assembler flags are used only for building that specific package and not
for any other package. These assembler flags will be added after any assembler flags added by any
`assembler_flags` field set on the applicable `go_mod` target or applicable `go_binary` target.
Run `go doc cmd/asm` to see the flags supported by `go tool asm`.
"""
)


class GoCompilerFlagsField(StringSequenceField):
alias = "compiler_flags"
help = softwrap(
Expand Down Expand Up @@ -290,6 +316,7 @@ class GoModTarget(TargetGenerator):
GoRaceDetectorEnabledField,
GoMemorySanitizerEnabledField,
GoAddressSanitizerEnabledField,
GoAssemblerFlagsField,
GoCompilerFlagsField,
GoLinkerFlagsField,
)
Expand Down Expand Up @@ -372,6 +399,7 @@ class GoPackageTarget(Target):
GoTestRaceDetectorEnabledField,
GoTestMemorySanitizerEnabledField,
GoTestAddressSanitizerEnabledField,
GoAssemblerFlagsField,
GoCompilerFlagsField,
SkipGoTestsField,
)
Expand Down Expand Up @@ -420,6 +448,7 @@ class GoBinaryTarget(Target):
GoRaceDetectorEnabledField,
GoMemorySanitizerEnabledField,
GoAddressSanitizerEnabledField,
GoAssemblerFlagsField,
GoCompilerFlagsField,
GoLinkerFlagsField,
RestartableField,
Expand Down
2 changes: 2 additions & 0 deletions src/python/pants/backend/go/util_rules/assembly.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class AssembleGoAssemblyFilesRequest:
dir_path: str
asm_header_path: str | None
import_path: str
extra_assembler_flags: tuple[str, ...]


@dataclass(frozen=True)
Expand Down Expand Up @@ -160,6 +161,7 @@ def obj_output_path(s_file: str) -> str:
os.path.join(goroot.path, "pkg", "include"),
*maybe_asm_header_path_args,
*maybe_package_import_path_args,
*request.extra_assembler_flags,
"-o",
obj_output_path(s_file),
str(os.path.normpath(PurePath(".", request.dir_path, s_file))),
Expand Down
19 changes: 19 additions & 0 deletions src/python/pants/backend/go/util_rules/build_opts.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from pants.backend.go.subsystems.gotest import GoTestSubsystem
from pants.backend.go.target_types import (
GoAddressSanitizerEnabledField,
GoAssemblerFlagsField,
GoCgoEnabledField,
GoCompilerFlagsField,
GoLinkerFlagsField,
Expand Down Expand Up @@ -62,6 +63,11 @@ class GoBuildOptions:
# Note: These flags come from `go_mod` and `go_binary` targets.
linker_flags: tuple[str, ...] = ()

# Extra flags to pass to the Go assembler (i.e., `go tool asm`).
# Note: These flags come from `go_mod` and `go_binary` targets. Package-specific assembler flags
# may still come from `go_package` targets.
assembler_flags: tuple[str, ...] = ()

def __post_init__(self):
assert not (self.with_race_detector and self.with_msan)
assert not (self.with_race_detector and self.with_asan)
Expand Down Expand Up @@ -94,6 +100,7 @@ class GoBuildOptionsFieldSet(FieldSet):
asan: GoAddressSanitizerEnabledField
compiler_flags: GoCompilerFlagsField
linker_flags: GoLinkerFlagsField
assembler_flags: GoAssemblerFlagsField


@dataclass(frozen=True)
Expand Down Expand Up @@ -350,13 +357,25 @@ async def go_extract_build_options_from_target(
if target_fields is not None:
linker_flags.extend(target_fields.linker_flags.value or [])

# Extract any extra assembler flags specified on `go_mod` or `go_binary` targets.
# Note: An `assembler_flags` field specified on a `go_package` target is extracted elsewhere.
assembler_flags: list[str] = []
if go_mod_target_fields is not None:
# To avoid duplication of options, only add the module-specific assembler flags if the target for this request
# is not a `go_mod` target (i.e., because it does not conform to the field set or has a different address).
if target_fields is None or go_mod_target_fields.address != target_fields.address:
assembler_flags.extend(go_mod_target_fields.assembler_flags.value or [])
if target_fields is not None:
assembler_flags.extend(target_fields.assembler_flags.value or [])

return GoBuildOptions(
cgo_enabled=cgo_enabled,
with_race_detector=with_race_detector,
with_msan=with_msan,
with_asan=with_asan,
compiler_flags=tuple(compiler_flags),
linker_flags=tuple(linker_flags),
assembler_flags=tuple(assembler_flags),
)


Expand Down
74 changes: 74 additions & 0 deletions src/python/pants/backend/go/util_rules/build_opts_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,3 +442,77 @@ def assert_flags(address: Address, expected_value: Iterable[str]) -> None:
assert_flags(Address("mod_with_field", target_name="mod"), ["-foo"])
assert_flags(Address("mod_with_field", target_name="bin_without_field"), ["-foo"])
assert_flags(Address("mod_with_field", target_name="bin_with_field"), ["-foo", "-bar"])


def test_assembler_flags_fields(rule_runner: RuleRunner) -> None:
rule_runner.write_files(
{
"mod_with_field/BUILD": dedent(
"""\
go_mod(
name="mod",
assembler_flags=["-foo"],
)
go_package(name="pkg")
go_binary(
name="bin_without_field",
)
go_binary(
name="bin_with_field",
assembler_flags=["-bar"],
)
"""
),
"mod_with_field/go.mod": "module example.pantsbuild.org/mod_with_field\n",
"mod_with_field/main.go": dedent(
"""\
package main
func main() {}
"""
),
"mod_with_field/pkg_with_field/BUILD": dedent(
"""
go_package(
assembler_flags=["-xyzzy"],
)
"""
),
"mod_with_field/pkg_with_field/foo.go": dedent(
"""\
package pkg_with_field
"""
),
}
)

def assert_flags(address: Address, expected_value: Iterable[str]) -> None:
opts = rule_runner.request(
GoBuildOptions,
(
GoBuildOptionsFromTargetRequest(
address=address,
),
),
)
assert opts.assembler_flags == tuple(
expected_value
), f"{address}: expected `assembler_flags` to be {expected_value}"

assert_flags(Address("mod_with_field", target_name="mod"), ["-foo"])
assert_flags(Address("mod_with_field", target_name="bin_without_field"), ["-foo"])
assert_flags(Address("mod_with_field", target_name="bin_with_field"), ["-foo", "-bar"])
assert_flags(Address("mod_with_field", target_name="pkg"), ["-foo"])
assert_flags(Address("mod_with_field/pkg_with_field"), ["-foo"])

build_request = rule_runner.request(
BuildGoPackageRequest,
[
BuildGoPackageTargetRequest(
Address("mod_with_field/pkg_with_field"), build_opts=GoBuildOptions()
)
],
)
assert build_request.pkg_specific_assembler_flags == ("-xyzzy",)
10 changes: 9 additions & 1 deletion src/python/pants/backend/go/util_rules/build_pkg.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def __init__(
fortran_files: tuple[str, ...] = (),
prebuilt_object_files: tuple[str, ...] = (),
pkg_specific_compiler_flags: tuple[str, ...] = (),
pkg_specific_assembler_flags: tuple[str, ...] = (),
) -> None:
"""Build a package and its dependencies as `__pkg__.a` files.
Expand Down Expand Up @@ -105,6 +106,7 @@ def __init__(
self.fortran_files = fortran_files
self.prebuilt_object_files = prebuilt_object_files
self.pkg_specific_compiler_flags = pkg_specific_compiler_flags
self.pkg_specific_assembler_flags = pkg_specific_assembler_flags
self._hashcode = hash(
(
self.import_path,
Expand All @@ -127,6 +129,7 @@ def __init__(
self.fortran_files,
self.prebuilt_object_files,
self.pkg_specific_compiler_flags,
self.pkg_specific_assembler_flags,
)
)

Expand Down Expand Up @@ -154,7 +157,8 @@ def __repr__(self) -> str:
f"objc_files={self.objc_files}, "
f"fortran_files={self.fortran_files}, "
f"prebuilt_object_files={self.prebuilt_object_files}, "
f"pkg_specific_compiler_flags={self.pkg_specific_compiler_flags}"
f"pkg_specific_compiler_flags={self.pkg_specific_compiler_flags}, "
f"pkg_specific_assembler_flags={self.pkg_specific_assembler_flags}"
")"
)

Expand Down Expand Up @@ -185,6 +189,7 @@ def __eq__(self, other):
and self.fortran_files == other.fortran_files
and self.prebuilt_object_files == other.prebuilt_object_files
and self.pkg_specific_compiler_flags == other.pkg_specific_compiler_flags
and self.pkg_specific_assembler_flags == other.pkg_specific_assembler_flags
# TODO: Use a recursive memoized __eq__ if this ever shows up in profiles.
and self.direct_dependencies == other.direct_dependencies
)
Expand Down Expand Up @@ -628,6 +633,9 @@ async def build_go_package(
dir_path=request.dir_path,
asm_header_path=asm_header_path,
import_path=request.import_path,
extra_assembler_flags=tuple(
*request.build_opts.assembler_flags, *request.pkg_specific_assembler_flags
),
),
)
assembly_result = assembly_fallible_result.result
Expand Down
8 changes: 8 additions & 0 deletions src/python/pants/backend/go/util_rules/build_pkg_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from pants.backend.go.dependency_inference import GoModuleImportPathsMapping
from pants.backend.go.target_type_rules import GoImportPathMappingRequest
from pants.backend.go.target_types import (
GoAssemblerFlagsField,
GoCompilerFlagsField,
GoImportPathField,
GoPackageSourcesField,
Expand Down Expand Up @@ -287,6 +288,12 @@ async def setup_build_go_package_target_request(
if compiler_flags_field and compiler_flags_field.value:
pkg_specific_compiler_flags = compiler_flags_field.value

pkg_specific_assembler_flags: tuple[str, ...] = ()
if target.has_field(GoAssemblerFlagsField):
assembler_flags_field = target.get(GoAssemblerFlagsField)
if assembler_flags_field and assembler_flags_field.value:
pkg_specific_assembler_flags = assembler_flags_field.value

all_direct_dependencies = await Get(Targets, DependenciesRequest(target[Dependencies]))

first_party_dep_import_path_targets = []
Expand Down Expand Up @@ -398,6 +405,7 @@ async def setup_build_go_package_target_request(
embed_config=embed_config,
with_coverage=with_coverage,
pkg_specific_compiler_flags=tuple(pkg_specific_compiler_flags),
pkg_specific_assembler_flags=tuple(pkg_specific_assembler_flags),
)
return FallibleBuildGoPackageRequest(result, import_path)

Expand Down

0 comments on commit 427d308

Please sign in to comment.