Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

go/cgo: support compiling C, C++, ObjC, and Fortran sources #17001

Merged
merged 2 commits into from
Sep 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion src/python/pants/backend/go/target_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,23 @@ class GoModTarget(TargetGenerator):

class GoPackageSourcesField(MultipleSourcesField):
default = ("*.go", "*.s")
expected_file_extensions = (".go", ".s")
expected_file_extensions = (
".go",
".s",
".c",
".h",
".hh",
".hpp",
".hxx",
".cc",
".cpp",
".cxx",
".m",
".f",
".F",
".for",
".f90",
)
ban_subdirectories = True
help = generate_multiple_sources_field_help_message(
"Example: `sources=['example.go', '*_test.go', '!test_ignore.go']`"
Expand Down
26 changes: 25 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 @@ -53,6 +53,10 @@ def __init__(
coverage_config: GoCoverageConfig | None = None,
cgo_file_names: tuple[str, ...] = (),
cgo_flags: CGoCompilerFlags | None = None,
c_files: tuple[str, ...] = (),
cxx_files: tuple[str, ...] = (),
objc_files: tuple[str, ...] = (),
fortran_files: tuple[str, ...] = (),
) -> None:
"""Build a package and its dependencies as `__pkg__.a` files.

Expand All @@ -73,6 +77,10 @@ def __init__(
self.coverage_config = coverage_config
self.cgo_file_names = cgo_file_names
self.cgo_flags = cgo_flags
self.c_files = c_files
self.cxx_files = cxx_files
self.objc_files = objc_files
self.fortran_files = fortran_files
self._hashcode = hash(
(
self.import_path,
Expand All @@ -88,6 +96,10 @@ def __init__(
self.coverage_config,
self.cgo_file_names,
self.cgo_flags,
self.c_files,
self.cxx_files,
self.objc_files,
self.fortran_files,
)
)

Expand All @@ -108,7 +120,11 @@ def __repr__(self) -> str:
f"embed_config={self.embed_config}, "
f"coverage_config={self.coverage_config}, "
f"cgo_file_names={self.cgo_file_names}, "
f"cgo_flags={self.cgo_flags}"
f"cgo_flags={self.cgo_flags}, "
f"c_files={self.c_files}, "
f"cxx_files={self.cxx_files}, "
f"objc_files={self.objc_files}, "
f"fortran_files={self.fortran_files}"
")"
)

Expand All @@ -132,6 +148,10 @@ def __eq__(self, other):
and self.coverage_config == other.coverage_config
and self.cgo_file_names == other.cgo_file_names
and self.cgo_flags == other.cgo_flags
and self.c_files == other.c_files
and self.cxx_files == other.cxx_files
and self.objc_files == other.objc_files
and self.fortran_files == other.fortran_files
# TODO: Use a recursive memoized __eq__ if this ever shows up in profiles.
and self.direct_dependencies == other.direct_dependencies
)
Expand Down Expand Up @@ -340,6 +360,10 @@ async def build_go_package(
dir_path=request.dir_path,
cgo_files=cgo_files,
cgo_flags=request.cgo_flags,
c_files=request.c_files,
cxx_files=request.cxx_files,
objc_files=request.objc_files,
fortran_files=request.fortran_files,
),
)
assert cgo_compile_result is not None
Expand Down
13 changes: 12 additions & 1 deletion src/python/pants/backend/go/util_rules/build_pkg_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,10 @@ async def setup_build_go_package_target_request(
s_file_names = _first_party_pkg_analysis.s_files
cgo_file_names = _first_party_pkg_analysis.cgo_files
cgo_flags = _first_party_pkg_analysis.cgo_flags
c_files = _first_party_pkg_analysis.c_files
cxx_files = _first_party_pkg_analysis.cxx_files
objc_files = _first_party_pkg_analysis.m_files
fortran_files = _first_party_pkg_analysis.f_files

elif target.has_field(GoThirdPartyPackageDependenciesField):
import_path = target[GoImportPathField].value
Expand All @@ -199,7 +203,10 @@ async def setup_build_go_package_target_request(
embed_config = _third_party_pkg_info.embed_config
cgo_file_names = _third_party_pkg_info.cgo_files
cgo_flags = _third_party_pkg_info.cgo_flags

c_files = _third_party_pkg_info.c_files
cxx_files = _third_party_pkg_info.cxx_files
objc_files = _third_party_pkg_info.m_files
fortran_files = _third_party_pkg_info.f_files
else:
raise AssertionError(
f"Unknown how to build `{target.alias}` target at address {request.address} with Go. "
Expand Down Expand Up @@ -236,6 +243,10 @@ async def setup_build_go_package_target_request(
s_file_names=s_file_names,
cgo_file_names=cgo_file_names,
cgo_flags=cgo_flags,
c_files=c_files,
cxx_files=cxx_files,
objc_files=objc_files,
fortran_files=fortran_files,
minimum_go_version=minimum_go_version,
direct_dependencies=tuple(direct_dependencies),
for_tests=request.for_tests,
Expand Down
32 changes: 18 additions & 14 deletions src/python/pants/backend/go/util_rules/cgo.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,10 @@ class CGoCompileRequest(EngineAwareParameter):
dir_path: str
cgo_files: tuple[str, ...]
cgo_flags: CGoCompilerFlags
c_files: tuple[str, ...] = ()
cxx_files: tuple[str, ...] = ()
m_files: tuple[str, ...] = ()
f_files: tuple[str, ...] = ()
objc_files: tuple[str, ...] = ()
fortran_files: tuple[str, ...] = ()

def debug_hint(self) -> str | None:
return self.import_path
Expand Down Expand Up @@ -649,13 +650,13 @@ async def cgo_compile_request(
)

# If we are compiling Objective-C code, then we need to link against libobjc
if request.m_files:
if request.objc_files:
flags = dataclasses.replace(flags, ldflags=flags.ldflags + ("-lobjc",))

# Likewise for Fortran, except there are many Fortran compilers.
# Support gfortran out of the box and let others pass the correct link options
# via CGO_LDFLAGS
if request.f_files and "gfortran" in golang_subsystem.cgo_fortran_binary_name:
if request.fortran_files and "gfortran" in golang_subsystem.cgo_fortran_binary_name:
flags = dataclasses.replace(flags, ldflags=flags.ldflags + ("-lgfortran",))

# TODO(#16838): Add MSan (memory sanitizer) option.
Expand All @@ -681,12 +682,15 @@ async def cgo_compile_request(
)

go_files: list[str] = [os.path.join(obj_dir_path, "_cgo_gotypes.go")]
c_files: list[str] = ["_cgo_export.c"]
c_files: list[str] = [
os.path.join(obj_dir_path, "_cgo_export.c"),
*(os.path.join(dir_path, c_file) for c_file in request.c_files),
]
for cgo_file in request.cgo_files:
cgo_file_path = PurePath(cgo_file)
stem = cgo_file_path.stem
go_files.append(os.path.join(obj_dir_path, f"{stem}.cgo1.go"))
c_files.append(f"{stem}.cgo2.c")
c_files.append(os.path.join(obj_dir_path, f"{stem}.cgo2.c"))

# Note: If Pants ever supports building the Go stdlib, then certain options would need to be inserted here
# for building certain `runtime` modules.
Expand Down Expand Up @@ -748,7 +752,7 @@ async def cgo_compile_request(
binary_name=golang_subsystem.cgo_gcc_binary_name,
input_digest=cgo_result.output_digest,
work_dir=obj_dir_path,
src_file=os.path.join(obj_dir_path, c_file),
src_file=c_file,
flags=cflags,
obj_file=ofile,
description=f"Compile cgo C source: {c_file}",
Expand All @@ -760,7 +764,7 @@ async def cgo_compile_request(

# C++ files
cxxflags = [*flags.cppflags, *flags.cxxflags]
for cxx_file in request.cxx_files:
for cxx_file in (os.path.join(dir_path, cxx_file) for cxx_file in request.cxx_files):
ofile = os.path.join(obj_dir_path, "_x{:03}.o".format(oseq))
oseq = oseq + 1
out_obj_files.append(ofile)
Expand All @@ -778,7 +782,7 @@ async def cgo_compile_request(
compile_process_gets.append(Get(ProcessResult, Process, compile_process))

# Objective-C files
for m_file in request.m_files:
for objc_file in (os.path.join(dir_path, objc_file) for objc_file in request.objc_files):
ofile = os.path.join(obj_dir_path, "_x{:03}.o".format(oseq))
oseq = oseq + 1
out_obj_files.append(ofile)
Expand All @@ -787,16 +791,16 @@ async def cgo_compile_request(
binary_name=golang_subsystem.cgo_gcc_binary_name,
input_digest=cgo_result.output_digest,
work_dir=obj_dir_path,
src_file=os.path.join(obj_dir_path, m_file),
src_file=os.path.join(obj_dir_path, objc_file),
flags=cflags,
obj_file=ofile,
description=f"Compile cgo Objective-C source: {m_file}",
description=f"Compile cgo Objective-C source: {objc_file}",
golang_subsystem=golang_subsystem,
)
compile_process_gets.append(Get(ProcessResult, Process, compile_process))

fflags = [*flags.cppflags, *flags.fflags]
for f_file in request.f_files:
for fortran_file in (os.path.join(fortran_file) for fortran_file in request.fortran_files):
ofile = os.path.join(obj_dir_path, "_x{:03}.o".format(oseq))
oseq = oseq + 1
out_obj_files.append(ofile)
Expand All @@ -805,10 +809,10 @@ async def cgo_compile_request(
binary_name=golang_subsystem.cgo_fortran_binary_name,
input_digest=cgo_result.output_digest,
work_dir=obj_dir_path,
src_file=os.path.join(obj_dir_path, f_file),
src_file=os.path.join(obj_dir_path, fortran_file),
flags=fflags,
obj_file=ofile,
description=f"Compile cgo Fortran source: {f_file}",
description=f"Compile cgo Fortran source: {fortran_file}",
golang_subsystem=golang_subsystem,
)
compile_process_gets.append(Get(ProcessResult, Process, compile_process))
Expand Down
2 changes: 0 additions & 2 deletions src/python/pants/backend/go/util_rules/cgo_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,6 @@ def test_cgo_compile(rule_runner: RuleRunner) -> None:
pkg_name=analysis.name,
digest=pkg_digest.digest,
dir_path=analysis.dir_path,
m_files=(),
f_files=(),
cgo_files=analysis.cgo_files,
cgo_flags=analysis.cgo_flags,
)
Expand Down
10 changes: 10 additions & 0 deletions src/python/pants/backend/go/util_rules/first_party_pkg.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ class FirstPartyPkgAnalysis:

cgo_flags: CGoCompilerFlags

c_files: tuple[str, ...]
cxx_files: tuple[str, ...]
m_files: tuple[str, ...]
h_files: tuple[str, ...]
f_files: tuple[str, ...]
s_files: tuple[str, ...]

minimum_go_version: str | None
Expand Down Expand Up @@ -173,6 +178,11 @@ def from_process_result(
ldflags=tuple(metadata.get("CgoLDFLAGS", [])),
pkg_config=tuple(metadata.get("CgoPkgConfig", [])),
),
c_files=tuple(metadata.get("CFiles", [])),
cxx_files=tuple(metadata.get("CXXFiles", [])),
m_files=tuple(metadata.get("MFiles", [])),
h_files=tuple(metadata.get("HFiles", [])),
f_files=tuple(metadata.get("FFiles", [])),
s_files=tuple(metadata.get("SFiles", [])),
minimum_go_version=minimum_go_version,
embed_patterns=tuple(metadata.get("EmbedPatterns", [])),
Expand Down
23 changes: 17 additions & 6 deletions src/python/pants/backend/go/util_rules/third_party_pkg.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,16 @@ class ThirdPartyPkgAnalysis:
# tests directly on a third-party package.
imports: tuple[str, ...]
go_files: tuple[str, ...]
s_files: tuple[str, ...]
cgo_files: tuple[str, ...]
cgo_flags: CGoCompilerFlags

c_files: tuple[str, ...]
cxx_files: tuple[str, ...]
m_files: tuple[str, ...]
h_files: tuple[str, ...]
f_files: tuple[str, ...]
s_files: tuple[str, ...]

minimum_go_version: str | None

embed_patterns: tuple[str, ...]
Expand Down Expand Up @@ -408,11 +414,6 @@ async def analyze_go_third_party_package(

for key in (
"CompiledGoFiles",
"CFiles",
"CXXFiles",
"MFiles",
"HFiles",
"FFiles",
"SwigFiles",
"SwigCXXFiles",
"SysoFiles",
Expand Down Expand Up @@ -445,6 +446,11 @@ async def analyze_go_third_party_package(
dir_path=request.package_path,
imports=tuple(request.pkg_json.get("Imports", ())),
go_files=tuple(request.pkg_json.get("GoFiles", ())),
c_files=tuple(request.pkg_json.get("CFiles", ())),
cxx_files=tuple(request.pkg_json.get("CXXFiles", ())),
m_files=tuple(request.pkg_json.get("MFiles", ())),
h_files=tuple(request.pkg_json.get("HFiles", ())),
f_files=tuple(request.pkg_json.get("FFiles", ())),
s_files=tuple(request.pkg_json.get("SFiles", ())),
cgo_files=tuple(request.pkg_json.get("CgoFiles", ())),
minimum_go_version=request.minimum_go_version,
Expand Down Expand Up @@ -606,6 +612,11 @@ def maybe_raise_or_create_error_or_create_failed_pkg_info(
digest=EMPTY_DIGEST,
imports=(),
go_files=(),
c_files=(),
cxx_files=(),
h_files=(),
m_files=(),
f_files=(),
s_files=(),
minimum_go_version=None,
embed_patterns=(),
Expand Down
Loading