Skip to content
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
2 changes: 2 additions & 0 deletions docs/notes/2.33.x.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ Fixed a traversal bug that silently dropped prebuilt object files (`.syso`) of t

The Go standard library is now pre-compiled with a single `go install std` build action per (Go toolchain, cgo configuration) pair, and packages link against those archives, instead of compiling each standard library package (and its assembly and symabis steps) as separate build actions. This significantly speeds up cold builds (numbers in the PR). Pants automatically falls back to compiling the standard library from source for build configurations that change archive content — the race detector and the other sanitizers, code coverage, and custom compiler or assembler flags — and on Go toolchains older than 1.20. Set the new advanced option `[golang].use_prebuilt_stdlib_archives = false` to always compile the standard library from source.

The Go backend now supports CGo directives `nocallback` and `noescape`, which were introduced by Go 1.24.

### Plugin API changes

`FrozenOrderedSet` is now backed by a native Rust implementation and is built in `__new__` rather than `__init__`. Subclasses that customized the set's contents by overriding `__init__` (for example, to transform the input iterable before constructing) must move that logic to `__new__`, since the set is already built and immutable by the time `__init__` runs.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,11 @@ func saveCgo(filename string, pkg *Package, cg *ast.CommentGroup, buildContext *
continue
}

// #cgo (nocallback|noescape) <function name>
if fields := strings.Fields(line); len(fields) == 3 && (fields[1] == "nocallback" || fields[1] == "noescape") {
Comment thread
tdyas marked this conversation as resolved.
continue
}

// Split at colon.
line, argstr, ok := stringsCut(strings.TrimSpace(line[4:]), ":")
if !ok {
Expand Down
95 changes: 94 additions & 1 deletion src/python/pants/backend/go/util_rules/cgo_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@
from pants.core.goals.package import BuiltPackage
from pants.core.target_types import ResourceTarget
from pants.core.util_rules import source_files
from pants.engine.environment import EnvironmentName
from pants.engine.internals.native_engine import EMPTY_DIGEST
from pants.engine.process import Process, ProcessResult
from pants.engine.process import FallibleProcessResult, Process, ProcessResult
from pants.testutil.rule_runner import QueryRule, RuleRunner
from pants.testutil.skip_utils import skip_if_linux_arm64

Expand Down Expand Up @@ -68,6 +69,13 @@ def rule_runner() -> RuleRunner:
QueryRule(FallibleFirstPartyPkgDigest, [FirstPartyPkgDigestRequest]),
QueryRule(CGoCompileResult, [CGoCompileRequest]),
QueryRule(ProcessResult, (Process,)),
QueryRule(
FallibleProcessResult,
(
EnvironmentName,
Process,
),
),
],
target_types=[
GoModTarget,
Expand Down Expand Up @@ -879,3 +887,88 @@ def test_cgo_with_embedded_static_library(rule_runner: RuleRunner) -> None:

tgt = rule_runner.get_target(Address("", target_name="bin"))
rule_runner.request(BuiltPackage, [GoBinaryFieldSet.create(tgt)])


def test_cgo_nocallback_noescape_directives(rule_runner: RuleRunner) -> None:
rule_runner.write_files(
{
"BUILD": dedent(
"""\
go_mod(name="mod")
go_package(name="pkg")
go_binary(name="bin")
"""
),
"go.mod": "module example.pantsbuild.org/cgo_nocallback_noescape_test\n",
"main.go": dedent(
"""\
package main

// void goCallback();
// static inline void triggerViolation() {
// goCallback();
// }
// #cgo nocallback triggerViolation
import "C"
import "fmt"

//export goCallback
func goCallback() {
fmt.Println("[Go] Inside goCallback!")
}

func main() {
fmt.Println("[Go] Calling triggerViolation...")
C.triggerViolation()
fmt.Println("[Go] Should panic before this line due to violation of nocallback")
}
"""
),
}
)

tgt = rule_runner.get_target(Address("", target_name="pkg"))
maybe_analysis = rule_runner.request(
FallibleFirstPartyPkgAnalysis,
[FirstPartyPkgAnalysisRequest(tgt.address, build_opts=GoBuildOptions())],
)
assert maybe_analysis.analysis is not None
analysis = maybe_analysis.analysis
assert analysis.cgo_files == ("main.go",)

maybe_digest = rule_runner.request(
FallibleFirstPartyPkgDigest,
[FirstPartyPkgDigestRequest(tgt.address, build_opts=GoBuildOptions())],
)
assert maybe_digest.pkg_digest is not None
pkg_digest = maybe_digest.pkg_digest

cgo_request = CGoCompileRequest(
import_path=analysis.import_path,
pkg_name=analysis.name,
digest=pkg_digest.digest,
build_opts=GoBuildOptions(),
dir_path=analysis.dir_path,
cgo_files=analysis.cgo_files,
cgo_flags=analysis.cgo_flags,
)
cgo_compile_result = rule_runner.request(CGoCompileResult, [cgo_request])
assert cgo_compile_result.digest != EMPTY_DIGEST

tgt = rule_runner.get_target(Address("", target_name="bin"))
pkg = rule_runner.request(BuiltPackage, [GoBinaryFieldSet.create(tgt)])
result = rule_runner.request(
FallibleProcessResult,
[
Process(
argv=["./bin"],
input_digest=pkg.digest,
description="Run cgo binary",
)
],
)
assert result.exit_code != 0
assert (
"panic: runtime: function marked with #cgo nocallback called back into Go"
in result.stderr.decode()
)
Loading