diff --git a/.adms/bazel/adms.mirror.cfg b/.adms/bazel/adms.mirror.cfg index ea63b5e49c0e..d12436b44fc7 100644 --- a/.adms/bazel/adms.mirror.cfg +++ b/.adms/bazel/adms.mirror.cfg @@ -33,6 +33,7 @@ allow gnupg.org allow lua.org allow mirrors.edge.kernel.org allow mirrors.kernel.org +allow repo.msys2.org allow sourceware.org allow sqlite.org diff --git a/.bazelrc b/.bazelrc index 55c064271177..e007449c2190 100644 --- a/.bazelrc +++ b/.bazelrc @@ -1,102 +1,102 @@ -# Do not edit this file without a review from @DataDog/agent-build - -# ADMS config ---------------------------------------------------------------------------------------------------------- -# Ensure access to DataDog internal artifact repositories in CI -import %workspace%/.adms/adms.bazelrc - -# Use --config=adms to use adms for upstream dependency caching. -# TOOD: rename dd-internal in the .adms tree to adms-internal. That -# will be easier to understand. -common:adms --config=dd-internal - -# Startup options ------------------------------------------------------------------------------------------------------ -startup --max_idle_secs=28800 # Keep the server alive for at most 8 hours of inactivity - -# Common options ------------------------------------------------------------------------------------------------------- -common --@rules_python//python/config_settings:bootstrap_impl=script # https://github.com/bazel-contrib/rules_python/blob/main/BZLMOD_SUPPORT.md -common --check_direct_dependencies=error # Escalate any bypassed `bazel_dep` to a resolution failure -common --enable_platform_specific_config # Supported OS identifiers are linux, macos, windows, freebsd, and openbsd -common --experimental_disk_cache_gc_max_size=30G # Cap applied whenever --disk_cache is also set, no-op otherwise. Override in user.bazelrc (50G good, 100G ideal) -common --experimental_proto_descriptor_sets_include_source_info # Preserve comments in generated pb.go -common --experimental_strict_repo_env # Do not leak uncontrolled environment variables into repository rules -common --experimental_ui_max_stdouterr_bytes=1073741819 # why? -common --http_timeout_scaling=3.0 # At least one attempt reaches 30s (3,6,12,24,30,30,30,30) instead of only 10s (1,2,4,8,10,10,10,10) -common --repo_env=DEPLOY_AGENT # Keep in sync with env_vars in MODULE.bazel -common --repo_env=FORCED_PACKAGE_COMPRESSION_LEVEL # Keep in sync with env_vars in MODULE.bazel -common --repo_env=GOCACHE # https://pkg.go.dev/cmd/go#hdr-Build_and_test_caching -common --repo_env=GOMODCACHE # https://wiki.archlinux.org/title/XDG_Base_Directory#Partial -common --repo_env=PACKAGE_VERSION # Keep in sync with env_vars in MODULE.bazel -common --repo_env=SIGN_MAC # Keep in sync with env_vars in MODULE.bazel -common --repo_env=XDG_CACHE_HOME # https://wiki.archlinux.org/title/XDG_Base_Directory -common --skip_incompatible_explicit_targets # Let target_compatible_with skip rather than fail -common --test_output=errors # Print test errors to console output instead of only capturing them in buried test.log -common --verbose_failures - -# Lint config (static code analyzers) ---------------------------------------------------------------------------------- -# Go -TODO(agent-build) -# Python -TODO(agent-build) -# Rust -common:lint --aspects=@rules_rust//rust:defs.bzl%rust_clippy_aspect --output_groups=+clippy_checks -common:lint --aspects=@rules_rust//rust:defs.bzl%rustfmt_aspect --output_groups=+rustfmt_checks - -# Linux config --------------------------------------------------------------------------------------------------------- -common:linux --credential_helper=buildbarn-edge-cache.buildbarn.local-cluster.local-dc.fabric.dog=%workspace%/bazel/tools/credential-helper -common:linux --strategy=sandboxed - -# macOS config --------------------------------------------------------------------------------------------------------- -common:macos --credential_helper=buildbarn-edge-cache.buildbarn.local-cluster.local-dc.fabric.dog=%workspace%/bazel/tools/credential-helper -common:macos --features=-macos_default_link_flags # https://github.com/bazelbuild/bazel/issues/23312 -common:macos --macos_minimum_os=12.0 # Keep in sync with https://docs.datadoghq.com/agent/supported_platforms/?tab=macos -common:macos --strategy=sandboxed - -# Windows config ------------------------------------------------------------------------------------------------------- -common:windows --credential_helper=buildbarn-edge-cache.buildbarn.local-cluster.local-dc.fabric.dog=%workspace%/bazel/tools/credential-helper.bat -common:windows --strategy=standalone # Valid values are: [dynamic_worker, standalone, dynamic, remote, worker, local] -# rules_python 1.9.0 transitions enable_runfiles to `true` for every py_ target on Windows. Pre-setting it here makes -# that transition a no-op, so Bazel deduplicates python_win and avoids 2 concurrent MSBuild racing on shared resources. -common:windows --enable_runfiles -common:windows --repo_env=BAZEL_SH=C:/tools/msys64/usr/bin/bash.exe # for https://github.com/bazelbuild/bazel/pull/26927 -common:windows --repo_env=PIP_CACHE_DIR # https://pip.pypa.io/en/stable/topics/caching/#default-paths -common:windows --repo_env=SYSTEMDRIVE # needed by vswhere to locate the VS installer instance database -common:windows --repo_env=SYSTEMROOT # used by COM to load system DLLs, needed by vswhere -common:windows --repo_env=USERPROFILE # used by MSYS2 bash to emulate HOME, needed by git to fetch repositories -common:windows --repo_env=VSTUDIO_ROOT # visual_studio(path_variable) in MODULE.bazel -common:windows --shell_executable=C:/tools/msys64/usr/bin/bash.exe - -# Force the x86_64-pc-windows-gnu Rust toolchain (compact name for rust_windows_gnu_x86_64) -# to take priority over the default MSVC toolchain, since the CI cc_toolchain is MinGW/GCC. -common:windows --extra_toolchains=@rust_toolchains//:rw-2070622084 - -# Remote cache config -------------------------------------------------------------------------------------------------- -# datadog-agent virtually isolates caching instance from its parent (which is remote-caching). -# If entry isn't found in datadog-agent, it will be searched in remote-caching. -common:cache --remote_cache=grpcs://buildbarn-frontend-datadog-agent.us1.ddbuild.io:443 -common:cache --remote_instance_name=ci/datadog-agent -common:cache --remote_local_fallback # best-effort on transient connection errors (no such host) -common:cache --incompatible_remote_local_fallback_for_remote_cache # works only if --remote_local_fallback is also set -common:cache --remote_retries=1 -common:cache --remote_timeout=60 - -# CI config ------------------------------------------------------------------------------------------------------------ -common:ci --config=adms -common:ci --config=cache -common:ci --config=lint -# Opt-in override: only Linux CI runs in k8s and can reach the in-cluster edge cache. -# tools/bazel adds `--config=ci-edge-cache` when `uname -s = Linux`. -common:ci-edge-cache --remote_cache=grpc://buildbarn-edge-cache.buildbarn.local-cluster.local-dc.fabric.dog:443 -common:ci --noexperimental_convenience_symlinks # not CI-suitable: "These symlinks are only for the user's convenience" - -# Project/Language configs -------------------------------------------------------------------------------------- -import %workspace%/bazel/configs/rust.bazelrc - -# Global release config ------------------------------------------------------------------------------------------------ -# This should aggregate all the release configs for all the languages with enabled optimizations, -# stripping, etc. It does not strictly mean that this build is the one to be released as a product. -# It just selects all the flags we should use on product builds. For example, we need to build with -# optimization and compress packages in CI so that we can run performance and size gates, even -# though we will never release that instance of the package to customers. -common:release --config=rust-release -common:release --//:release - -# Local development options -------------------------------------------------------------------------------------------- -try-import %workspace%/user.bazelrc +# Do not edit this file without a review from @DataDog/agent-build + +# ADMS config ---------------------------------------------------------------------------------------------------------- +# Ensure access to DataDog internal artifact repositories in CI +import %workspace%/.adms/adms.bazelrc + +# Use --config=adms to use adms for upstream dependency caching. +# TOOD: rename dd-internal in the .adms tree to adms-internal. That +# will be easier to understand. +common:adms --config=dd-internal + +# Startup options ------------------------------------------------------------------------------------------------------ +startup --max_idle_secs=28800 # Keep the server alive for at most 8 hours of inactivity + +# Common options ------------------------------------------------------------------------------------------------------- +common --@rules_python//python/config_settings:bootstrap_impl=script # https://github.com/bazel-contrib/rules_python/blob/main/BZLMOD_SUPPORT.md +common --check_direct_dependencies=error # Escalate any bypassed `bazel_dep` to a resolution failure +common --enable_platform_specific_config # Supported OS identifiers are linux, macos, windows, freebsd, and openbsd +common --experimental_disk_cache_gc_max_size=30G # Cap applied whenever --disk_cache is also set, no-op otherwise. Override in user.bazelrc (50G good, 100G ideal) +common --experimental_proto_descriptor_sets_include_source_info # Preserve comments in generated pb.go +common --experimental_strict_repo_env # Do not leak uncontrolled environment variables into repository rules +common --experimental_ui_max_stdouterr_bytes=1073741819 # why? +common --http_timeout_scaling=3.0 # At least one attempt reaches 30s (3,6,12,24,30,30,30,30) instead of only 10s (1,2,4,8,10,10,10,10) +common --repo_env=DEPLOY_AGENT # Keep in sync with env_vars in MODULE.bazel +common --repo_env=FORCED_PACKAGE_COMPRESSION_LEVEL # Keep in sync with env_vars in MODULE.bazel +common --repo_env=GOCACHE # https://pkg.go.dev/cmd/go#hdr-Build_and_test_caching +common --repo_env=GOMODCACHE # https://wiki.archlinux.org/title/XDG_Base_Directory#Partial +common --repo_env=PACKAGE_VERSION # Keep in sync with env_vars in MODULE.bazel +common --repo_env=SIGN_MAC # Keep in sync with env_vars in MODULE.bazel +common --repo_env=XDG_CACHE_HOME # https://wiki.archlinux.org/title/XDG_Base_Directory +common --skip_incompatible_explicit_targets # Let target_compatible_with skip rather than fail +common --test_output=errors # Print test errors to console output instead of only capturing them in buried test.log +common --verbose_failures + +# Lint config (static code analyzers) ---------------------------------------------------------------------------------- +# Go -TODO(agent-build) +# Python -TODO(agent-build) +# Rust +common:lint --aspects=@rules_rust//rust:defs.bzl%rust_clippy_aspect --output_groups=+clippy_checks +common:lint --aspects=@rules_rust//rust:defs.bzl%rustfmt_aspect --output_groups=+rustfmt_checks + +# Linux config --------------------------------------------------------------------------------------------------------- +common:linux --credential_helper=buildbarn-edge-cache.buildbarn.local-cluster.local-dc.fabric.dog=%workspace%/bazel/tools/credential-helper +common:linux --strategy=sandboxed + +# macOS config --------------------------------------------------------------------------------------------------------- +common:macos --credential_helper=buildbarn-edge-cache.buildbarn.local-cluster.local-dc.fabric.dog=%workspace%/bazel/tools/credential-helper +common:macos --features=-macos_default_link_flags # https://github.com/bazelbuild/bazel/issues/23312 +common:macos --macos_minimum_os=12.0 # Keep in sync with https://docs.datadoghq.com/agent/supported_platforms/?tab=macos +common:macos --strategy=sandboxed + +# Windows config ------------------------------------------------------------------------------------------------------- +common:windows --credential_helper=buildbarn-edge-cache.buildbarn.local-cluster.local-dc.fabric.dog=%workspace%/bazel/tools/credential-helper.bat +common:windows --strategy=standalone # Valid values are: [dynamic_worker, standalone, dynamic, remote, worker, local] +# rules_python 1.9.0 transitions enable_runfiles to `true` for every py_ target on Windows. Pre-setting it here makes +# that transition a no-op, so Bazel deduplicates python_win and avoids 2 concurrent MSBuild racing on shared resources. +common:windows --enable_runfiles +common:windows --repo_env=PIP_CACHE_DIR # https://pip.pypa.io/en/stable/topics/caching/#default-paths +common:windows --repo_env=SYSTEMDRIVE # needed by vswhere to locate the VS installer instance database +common:windows --repo_env=SYSTEMROOT # used by COM to load system DLLs, needed by vswhere +common:windows --repo_env=USERPROFILE # used by MSYS2 bash to emulate HOME, needed by git to fetch repositories +common:windows --repo_env=VSTUDIO_ROOT # visual_studio(path_variable) in MODULE.bazel +# Hermetic bash discovery for ctx.actions.run_shell / genrule / rules_foreign_cc. +common:windows --shell_executable=bazel/toolchains/msys2/bash_shim.exe + +# Force the x86_64-pc-windows-gnu Rust toolchain (compact name for rust_windows_gnu_x86_64) +# to take priority over the default MSVC toolchain, since the CI cc_toolchain is MinGW/GCC. +common:windows --extra_toolchains=@rust_toolchains//:rw-2070622084 + +# Remote cache config -------------------------------------------------------------------------------------------------- +# datadog-agent virtually isolates caching instance from its parent (which is remote-caching). +# If entry isn't found in datadog-agent, it will be searched in remote-caching. +common:cache --remote_cache=grpcs://buildbarn-frontend-datadog-agent.us1.ddbuild.io:443 +common:cache --remote_instance_name=ci/datadog-agent +common:cache --remote_local_fallback # best-effort on transient connection errors (no such host) +common:cache --incompatible_remote_local_fallback_for_remote_cache # works only if --remote_local_fallback is also set +common:cache --remote_retries=1 +common:cache --remote_timeout=60 + +# CI config ------------------------------------------------------------------------------------------------------------ +common:ci --config=adms +common:ci --config=cache +common:ci --config=lint +# Opt-in override: only Linux CI runs in k8s and can reach the in-cluster edge cache. +# tools/bazel adds `--config=ci-edge-cache` when `uname -s = Linux`. +common:ci-edge-cache --remote_cache=grpc://buildbarn-edge-cache.buildbarn.local-cluster.local-dc.fabric.dog:443 +common:ci --noexperimental_convenience_symlinks # not CI-suitable: "These symlinks are only for the user's convenience" + +# Project/Language configs -------------------------------------------------------------------------------------- +import %workspace%/bazel/configs/rust.bazelrc + +# Global release config ------------------------------------------------------------------------------------------------ +# This should aggregate all the release configs for all the languages with enabled optimizations, +# stripping, etc. It does not strictly mean that this build is the one to be released as a product. +# It just selects all the flags we should use on product builds. For example, we need to build with +# optimization and compress packages in CI so that we can run performance and size gates, even +# though we will never release that instance of the package to customers. +common:release --config=rust-release +common:release --//:release + +# Local development options -------------------------------------------------------------------------------------------- +try-import %workspace%/user.bazelrc diff --git a/.gitattributes b/.gitattributes index d49c2be22c42..e0cf8a0c8728 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,65 +1,66 @@ -# Set batch file line endings to CRLF so that they can be executed on Windows -*.bat text eol=crlf -*.cmd text eol=crlf -*.bin binary - -# Set go source line endings to LF on all platforms so gofmt can be used -*.go text=auto eol=lf -# Same for gopatch (does not handle CRLF line endings) -*.gopatch text eol=lf -# Same for go workspace files (root and testdata) -*go.work text eol=lf -go.sum -diff -merge linguist-generated=true -*.pb.go -diff -merge -*.pb.go linguist-generated=true -*.pb.gw.go -diff -merge -*.pb.gw.go linguist-generated=true -*_easyjson.go -diff -merge -*_easyjson.go linguist-generated=true -pkg/config/schema/*.yaml text eol=lf -pkg/config/schema/compressed/*.zstd binary -diff -merge linguist-generated=true -pkg/security/probe/constantfetch/btfhub/constants.json -diff -merge linguist-generated=true -pkg/security/seclwin/** -diff -merge linguist-generated=true -# CWS doc JSON is generated as LF on Linux; force LF on Windows too -# so Bazel's byte-exact diff_test doesn't trip on core.autocrlf-rewritten CRLF. -docs/cloud-workload-security/** text eol=lf -# Fixtures should have LF line endings because they are checked against OCI packages built on Linux -pkg/fleet/installer/fixtures/** text=auto eol=lf - -# Fix `git diff` when running on the below file formats. -# Our windows build image uses MinGit which tries to use the astextplain diff algorithm (https://git-scm.com/docs/gitattributes#_setting_the_internal_diff_algorithm). -# The astextplain binary is not embedded in the docker image making the git diff command fail when one of the below file formats is in the diff. -# The error is: -# ``` -# error: cannot spawn astexplain: No such files or directory -# fatal: unable to read files diff -# ``` -# We're overriding the MinGit default gitattributes config to avoid using astextplain on the file formats below. -# The MinGit's gitconfig file still have the problematic config though it should not use it anymore: -# ``` -# [diff "astextplain"] -# textconv = astextplain -# ``` - -*.doc diff -*.DOC diff -*.docx diff -*.DOCX diff -*.docm diff -*.DOCM diff -*.dot diff -*.DOT diff -*.dotx diff -*.DOTX diff -*.dotm diff -*.DOTM diff -*.pdf diff -*.PDF diff -*.rtf diff -*.RTF diff -*.ods diff -*.ODS diff -*.odf diff -*.ODF diff -*.odt diff -*.ODT diff +# Set batch file line endings to CRLF so that they can be executed on Windows +*.bat text eol=crlf +*.cmd text eol=crlf +*.bin binary +*.exe binary + +# Set go source line endings to LF on all platforms so gofmt can be used +*.go text=auto eol=lf +# Same for gopatch (does not handle CRLF line endings) +*.gopatch text eol=lf +# Same for go workspace files (root and testdata) +*go.work text eol=lf +go.sum -diff -merge linguist-generated=true +*.pb.go -diff -merge +*.pb.go linguist-generated=true +*.pb.gw.go -diff -merge +*.pb.gw.go linguist-generated=true +*_easyjson.go -diff -merge +*_easyjson.go linguist-generated=true +pkg/config/schema/*.yaml text eol=lf +pkg/config/schema/compressed/*.zstd binary -diff -merge linguist-generated=true +pkg/security/probe/constantfetch/btfhub/constants.json -diff -merge linguist-generated=true +pkg/security/seclwin/** -diff -merge linguist-generated=true +# CWS doc JSON is generated as LF on Linux; force LF on Windows too +# so Bazel's byte-exact diff_test doesn't trip on core.autocrlf-rewritten CRLF. +docs/cloud-workload-security/** text eol=lf +# Fixtures should have LF line endings because they are checked against OCI packages built on Linux +pkg/fleet/installer/fixtures/** text=auto eol=lf + +# Fix `git diff` when running on the below file formats. +# Our windows build image uses MinGit which tries to use the astextplain diff algorithm (https://git-scm.com/docs/gitattributes#_setting_the_internal_diff_algorithm). +# The astextplain binary is not embedded in the docker image making the git diff command fail when one of the below file formats is in the diff. +# The error is: +# ``` +# error: cannot spawn astexplain: No such files or directory +# fatal: unable to read files diff +# ``` +# We're overriding the MinGit default gitattributes config to avoid using astextplain on the file formats below. +# The MinGit's gitconfig file still have the problematic config though it should not use it anymore: +# ``` +# [diff "astextplain"] +# textconv = astextplain +# ``` + +*.doc diff +*.DOC diff +*.docx diff +*.DOCX diff +*.docm diff +*.DOCM diff +*.dot diff +*.DOT diff +*.dotx diff +*.DOTX diff +*.dotm diff +*.DOTM diff +*.pdf diff +*.PDF diff +*.rtf diff +*.RTF diff +*.ods diff +*.ODS diff +*.odf diff +*.ODF diff +*.odt diff +*.ODT diff diff --git a/.gitignore b/.gitignore index dce69cf88371..4070904101b9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,256 +1,258 @@ -# dogstatsd image temporary binaries -Dockerfiles/dogstatsd/alpine/static/ - -# folders -vendor/ -.vendor-new/ -bin/ -/dev/ -/site/ -__pycache__ -.pytest_cache -venv*/ -target - -**/*.tmp -**/debug.test -# files -.DS_Store -# *.cov could be removed in OCT 2023, replaced by **/coverage.out -*.cov -**/coverage.out -# Python coverage -.coverage -*.pyc -*.swp -*.exe -*.syso -*.log -/main - -# Build System Protocol (Bazel <-> JetBrains IDEs) -/.bazelbsp/ -# In-workpace cache -/.cache/ -# Bazel convenience symlinks to output folders -/bazel-* -# User specific bazelrc file -/user.bazelrc - -# file generated by Cluster Agent image build -Dockerfiles/cluster-agent/nosys-seccomp - -# local development environment control files -.env -.envrc -# I explicitely include this file because it could be ignored globally in custom configurations -!.python-version - -# specific pre-commit hooks -.pre-commit-config-*.yaml - -# Utility tools -devagent -deva - -# go-generated files -datadog.yaml -system-probe.yaml -security-agent.yaml -dogstatsd.yaml -cloudfoundry.yaml -apm-inject.yaml -Dockerfiles/cluster-agent/datadog-cluster.yaml -Dockerfiles/cluster-agent/dist -Dockerfiles/cluster-agent/security-agent-policies -pkg/status/template.go - -# JetBrains IDE project files: GoLand, IntelliJ IDEA, PyCharm, etc. -/*.iml -/.idea/ -/.ijwb/ - -# Ignore debs from the root of the project. -datadog-agent*_amd64.deb - -# Ignore pem created during the tests -*.pem - -pkg/process/config/testdata/secret - -auth_token -/test/e2e/scripts/setup-instance/instance-id.json -/test/e2e/scripts/setup-instance/specification.json -/test/e2e/scripts/setup-instance/spot-instance-request.json -/test/e2e/scripts/setup-instance/spot-request-id.json -/test/e2e/scripts/setup-instance/id_rsa -/test/e2e/scripts/setup-instance/id_rsa.pub -/test/e2e/scripts/setup-instance/ignition.json -/test/e2e/containers/fake_datadog/venv/ -/e2e-output -/comp/logs-library/pipeline/registry.json - -# local copy of device signing cert -platform.pk8 - -# trace agent windows artifacts -pkg/trace/info/git_version.go - -# process agent test artifacts -pkg/process/config/logs - -# sd-agent -/pkg/discovery/module/rust/sd-agent - -# sysprobe artifacts -**/.ninja_log -**/.ninja_deps -*.ninja -compile_commands.json -pkg/ebpf/bytecode/build/**/*.d -pkg/ebpf/kernelbugs/c/*.d -pkg/ebpf/kernelbugs/c/*.o -pkg/ebpf/kernelbugs/c/detect-seccomp-bug -pkg/security/tests/syscall_tester/**/*.d - -# dsd artifacts -cmd/dogstatsd/windows_resources/dogstatsd-msg.rc -cmd/dogstatsd/windows_resources/*.bin -dogstatsd-msg.h - -# omnibus files -omnibus/.tarball-version -omnibus/files/sources -omnibus/resources/agent/msi/cal/packages/ - -# serverless artifact -cmd/serverless/serverless - -#visual studio files -*.aps -tools/windows/install-help/cal/packages/ -./[Dd]ebug/ -[Rr]elease/ - -# ebpf object files -pkg/ebpf/bytecode/build/ -*.bc - -# CGo generated object files -**/_obj/*.o -**/_cgo_*.o - -# windows container build output -build.out/ - -# windows resource files -pkg/util/winutil/messagestrings/*.h -pkg/util/winutil/messagestrings/*.rc -pkg/util/winutil/messagestrings/*.bin - -# dev VM -.vagrant -Vagrantfile -packer.json -*.box -devenv/iso -devenv/output-virtualbox-iso/ -devenv/packer_cache -test_output*.json -module_test_output.json -e2e_test_output.json -e2e_test_output*.json.*.part -junit-out-AgentFlavor.base-*.xml - -# doxygen doc & error log -rtloader/doc -rtloader/doxygen/errors.log - -# integrations-core when checked out for unit tests -integrations-core/ - -# netlink message dump test files -pkg/network/netlink/testdata/message_dump* - -# serverless -.layers -.extension - -# Emacs -*~ -.dir-locals.el - -# Vim -.vimrc - -# etags -/TAGS - -# cscope -cscope.out - -tools/windows/DatadogAgentInstaller/.vs/ -tools/windows/DatadogAgentInstaller/obj/ -tools/windows/DatadogAgentInstaller/packages/ -tools/windows/DatadogAgentInstaller/WixSetup/cabcache/ - -*.wixobj -*.obj -*.g.wxs -*.DotSettings.user - -# e2e test intake generated binary -test/fakeintake/build/* - - -# Allow Single Machine Performance material to ignore excludes -!test/regression/** -!test/workload-checks/** - -kmt-deps/ -test/new-e2e/system-probe/test-json-review/test-json-review -test/new-e2e/system-probe/test-runner/test-runner -test/new-e2e/start-microvms - -# trace-agent generates install.json files during tests -pkg/serverless/trace/**/install.json - -# Files used for eBPF complexity analysis -ebpf-calculator - -# File generated by job creating flake finder pipeline -flake-finder-gitlab-ci.yml - - -# go.work.sum is always changing, we should not need it since we tidy each module individually -go.work.sum - - -#### Cursor related files - -# Personal rules -**/.cursor/rules/personal/ - -# CLAUDE override file for personal use -CLAUDE_PERSONAL.md - -# Claude local settings and overrides -**/.claude/settings.local.json -**/CLAUDE.local.md - -# Claude Code auto-jira work tracking (local only, never commit) -AUTO_JIRA.md -.claude/skills/auto-jira/SKILL.notes.md - -# Claude Code worktrees (local development, not for repo) -.claude/worktrees/ - -# Zed config -.zed - -# Devcontainer -.devcontainer/ -!.devcontainer/datadog/default -.python-nix-home -.venv-nix -flake.lock +# dogstatsd image temporary binaries +Dockerfiles/dogstatsd/alpine/static/ + +# folders +vendor/ +.vendor-new/ +bin/ +/dev/ +/site/ +__pycache__ +.pytest_cache +venv*/ +target + +**/*.tmp +**/debug.test +# files +.DS_Store +# *.cov could be removed in OCT 2023, replaced by **/coverage.out +*.cov +**/coverage.out +# Python coverage +.coverage +*.pyc +*.swp +*.exe +# Checked-in MSYS2 bash shim used by the Windows Bazel toolchain +!bazel/toolchains/msys2/bash_shim.exe +*.syso +*.log +/main + +# Build System Protocol (Bazel <-> JetBrains IDEs) +/.bazelbsp/ +# In-workpace cache +/.cache/ +# Bazel convenience symlinks to output folders +/bazel-* +# User specific bazelrc file +/user.bazelrc + +# file generated by Cluster Agent image build +Dockerfiles/cluster-agent/nosys-seccomp + +# local development environment control files +.env +.envrc +# I explicitely include this file because it could be ignored globally in custom configurations +!.python-version + +# specific pre-commit hooks +.pre-commit-config-*.yaml + +# Utility tools +devagent +deva + +# go-generated files +datadog.yaml +system-probe.yaml +security-agent.yaml +dogstatsd.yaml +cloudfoundry.yaml +apm-inject.yaml +Dockerfiles/cluster-agent/datadog-cluster.yaml +Dockerfiles/cluster-agent/dist +Dockerfiles/cluster-agent/security-agent-policies +pkg/status/template.go + +# JetBrains IDE project files: GoLand, IntelliJ IDEA, PyCharm, etc. +/*.iml +/.idea/ +/.ijwb/ + +# Ignore debs from the root of the project. +datadog-agent*_amd64.deb + +# Ignore pem created during the tests +*.pem + +pkg/process/config/testdata/secret + +auth_token +/test/e2e/scripts/setup-instance/instance-id.json +/test/e2e/scripts/setup-instance/specification.json +/test/e2e/scripts/setup-instance/spot-instance-request.json +/test/e2e/scripts/setup-instance/spot-request-id.json +/test/e2e/scripts/setup-instance/id_rsa +/test/e2e/scripts/setup-instance/id_rsa.pub +/test/e2e/scripts/setup-instance/ignition.json +/test/e2e/containers/fake_datadog/venv/ +/e2e-output +/comp/logs-library/pipeline/registry.json + +# local copy of device signing cert +platform.pk8 + +# trace agent windows artifacts +pkg/trace/info/git_version.go + +# process agent test artifacts +pkg/process/config/logs + +# sd-agent +/pkg/discovery/module/rust/sd-agent + +# sysprobe artifacts +**/.ninja_log +**/.ninja_deps +*.ninja +compile_commands.json +pkg/ebpf/bytecode/build/**/*.d +pkg/ebpf/kernelbugs/c/*.d +pkg/ebpf/kernelbugs/c/*.o +pkg/ebpf/kernelbugs/c/detect-seccomp-bug +pkg/security/tests/syscall_tester/**/*.d + +# dsd artifacts +cmd/dogstatsd/windows_resources/dogstatsd-msg.rc +cmd/dogstatsd/windows_resources/*.bin +dogstatsd-msg.h + +# omnibus files +omnibus/.tarball-version +omnibus/files/sources +omnibus/resources/agent/msi/cal/packages/ + +# serverless artifact +cmd/serverless/serverless + +#visual studio files +*.aps +tools/windows/install-help/cal/packages/ +./[Dd]ebug/ +[Rr]elease/ + +# ebpf object files +pkg/ebpf/bytecode/build/ +*.bc + +# CGo generated object files +**/_obj/*.o +**/_cgo_*.o + +# windows container build output +build.out/ + +# windows resource files +pkg/util/winutil/messagestrings/*.h +pkg/util/winutil/messagestrings/*.rc +pkg/util/winutil/messagestrings/*.bin + +# dev VM +.vagrant +Vagrantfile +packer.json +*.box +devenv/iso +devenv/output-virtualbox-iso/ +devenv/packer_cache +test_output*.json +module_test_output.json +e2e_test_output.json +e2e_test_output*.json.*.part +junit-out-AgentFlavor.base-*.xml + +# doxygen doc & error log +rtloader/doc +rtloader/doxygen/errors.log + +# integrations-core when checked out for unit tests +integrations-core/ + +# netlink message dump test files +pkg/network/netlink/testdata/message_dump* + +# serverless +.layers +.extension + +# Emacs +*~ +.dir-locals.el + +# Vim +.vimrc + +# etags +/TAGS + +# cscope +cscope.out + +tools/windows/DatadogAgentInstaller/.vs/ +tools/windows/DatadogAgentInstaller/obj/ +tools/windows/DatadogAgentInstaller/packages/ +tools/windows/DatadogAgentInstaller/WixSetup/cabcache/ + +*.wixobj +*.obj +*.g.wxs +*.DotSettings.user + +# e2e test intake generated binary +test/fakeintake/build/* + + +# Allow Single Machine Performance material to ignore excludes +!test/regression/** +!test/workload-checks/** + +kmt-deps/ +test/new-e2e/system-probe/test-json-review/test-json-review +test/new-e2e/system-probe/test-runner/test-runner +test/new-e2e/start-microvms + +# trace-agent generates install.json files during tests +pkg/serverless/trace/**/install.json + +# Files used for eBPF complexity analysis +ebpf-calculator + +# File generated by job creating flake finder pipeline +flake-finder-gitlab-ci.yml + + +# go.work.sum is always changing, we should not need it since we tidy each module individually +go.work.sum + + +#### Cursor related files + +# Personal rules +**/.cursor/rules/personal/ + +# CLAUDE override file for personal use +CLAUDE_PERSONAL.md + +# Claude local settings and overrides +**/.claude/settings.local.json +**/CLAUDE.local.md + +# Claude Code auto-jira work tracking (local only, never commit) +AUTO_JIRA.md +.claude/skills/auto-jira/SKILL.notes.md + +# Claude Code worktrees (local development, not for repo) +.claude/worktrees/ + +# Zed config +.zed + +# Devcontainer +.devcontainer/ +!.devcontainer/datadog/default +.python-nix-home +.venv-nix +flake.lock diff --git a/MODULE.bazel b/MODULE.bazel index e38c02825d4a..08f555cef4c6 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -93,25 +93,25 @@ git_override( remote = "https://github.com/bazelbuild/rules_pkg.git", ) -# Temporary until rules_rust > 0.69.0 ships with Windows GNU ABI support (PR #3940). -# The CI runner uses MinGW as its cc_toolchain; rules_rust 0.69.0 defaults to -# x86_64-pc-windows-msvc whose linker flags are incompatible with MinGW's g++. +# Pinned past the 0.69.0 release to pick up the Windows-GNU ABI support from +# bazelbuild/rules_rust#3940 (lib name resolution, stdlib link flags, PDB +# emission split by abi). Required because our Windows cc_toolchain is MinGW +# (@winlibs_mingw64), and the 0.69.0 stable release still hard-codes MSVC +# behavior for `target_os == "windows"`. git_override( module_name = "rules_rust", - commit = "82506df3f31240f97194b2e9453022125eaa07f4", # main as of April 21, 2026 + commit = "f31db8b6f124dd5eebdd0ed8de4daf20d4d9685f", # main as of April 27, 2026 (includes #3940 + #3990) patch_strip = 1, patches = [ - # Stops rustc_compile_action from leaking default_shell_env into - # CrateInfo.rustc_env, which otherwise clobbers cc_toolchain link_env - # for rust_test(crate = ...). Upstream issue pending. - "//bazel/patches:rules_rust-crateinfo-env-leak.patch", + # Derives `-Cdlltool=/dlltool.exe` automatically + "//bazel/patches:rules_rust-derive-dlltool-from-linker.patch", ], remote = "https://github.com/bazelbuild/rules_rust.git", ) git_override( module_name = "rules_rust_prost", - commit = "82506df3f31240f97194b2e9453022125eaa07f4", + commit = "f31db8b6f124dd5eebdd0ed8de4daf20d4d9685f", remote = "https://github.com/bazelbuild/rules_rust.git", strip_prefix = "extensions/prost", ) @@ -282,11 +282,114 @@ llvm.toolchain( llvm_version = "19.1.7", # https://github.com/bazel-contrib/toolchains_llvm/blob/master/toolchain/internal/llvm_distributions.bzl ) +# Hermetic MinGW-w64 toolchain sourced from a pinned WinLibs distribution. +# Variant: GCC 14.2.0 + MinGW-w64 12.0.0, POSIX threads, MSVCRT runtime, +# no LLVM bundle. URL, sha256 and gcc_version must move together when bumping. +winlibs_mingw_repository = use_repo_rule( + "//bazel/toolchains/mingw:winlibs.bzl", + "winlibs_mingw_repository", +) + +winlibs_mingw_repository( + name = "winlibs_mingw64", + gcc_version = "14.2.0", + sha256 = "ff475e985a98c5f3785129baf7460db14fee27708bce35f2833db5009507f1b9", + url = "https://github.com/brechtsanders/winlibs_mingw/releases/download/14.2.0posix-19.1.7-12.0.0-msvcrt-r3/winlibs-x86_64-posix-seh-gcc-14.2.0-mingw-w64msvcrt-12.0.0-r3.zip", +) + +# Hermetic MSYS2 sh_toolchain sourced from a pinned MSYS2 base archive. +# Replaces the previous --shell_executable=C:/tools/msys64/usr/bin/bash.exe +# pointer in .bazelrc so Windows shell actions no longer depend on a system +# MSYS2 install. Contains the `base` package only: bash + coreutils + sed + +# gawk + grep + tar + xz + msys-2.0.dll runtime. No make/perl/autotools yet; +# rules_foreign_cc workloads (openssl, krb5, ...) still need those overlaid +# in a follow-up. +msys2_base_repository = use_repo_rule( + "//bazel/toolchains/msys2:msys2.bzl", + "msys2_base_repository", +) + +msys2_base_repository( + name = "msys2_base", + # Curated overlay of pacman packages from https://repo.msys2.org/msys/x86_64/. + # Together they satisfy rules_foreign_cc's preinstalled_{make,m4,autoconf, + # automake,pkgconfig}_toolchain lookups (see deps/repos.MODULE.bazel) plus + # the perl interpreter that openssl's Configure script needs, and the + # libiconv import lib that any glib/gettext-style configure scripts probe + # for. Pinned URL+SHA256 for supply-chain integrity; bumping a package is + # a single-line change here. + overlay_packages = { + "make": [ + "https://repo.msys2.org/msys/x86_64/make-4.4.1-2-x86_64.pkg.tar.zst", + "2408af61717dae87b00c855b132769a125c708907fc94a46bb16dae076113e5c", + ], + "m4": [ + "https://repo.msys2.org/msys/x86_64/m4-1.4.21-1-x86_64.pkg.tar.zst", + "b561e8871c7599502d9fae7196ee965b3fed3d7af09154eb8d2f5e4d9e5e5106", + ], + "libtool": [ + "https://repo.msys2.org/msys/x86_64/libtool-2.5.4-4-x86_64.pkg.tar.zst", + "c8e812a9a32dd86c53d6e9bd5ec94977a5fc66d2ce3e691d92196aa06f616775", + ], + # pkgconf ships pkg-config.exe as a co-installed copy of pkgconf.exe + # (drop-in CLI compatible). Preferred over the legacy pkg-config + # package, which is no longer in the MSYS2 repo. + "pkgconf": [ + "https://repo.msys2.org/msys/x86_64/pkgconf-2.5.1-1-x86_64.pkg.tar.zst", + "38efd4928ac0cb06e9bd558baa533fc923c0592115bf9bd44970250f3ae2cb7e", + ], + # MSYS2 splits autoconf/automake into a version-selecting wrapper + # (autoconf, automake, aclocal, ...) and per-series implementations. + # We ship one current series of each — bump these two together. + "autoconf-wrapper": [ + "https://repo.msys2.org/msys/x86_64/autoconf-wrapper-20260320-1-any.pkg.tar.zst", + "84c1f93d4450b3bf1ef567110b51c15ffb3f13670d656cc48fea70a01371b20c", + ], + "autoconf2.72": [ + "https://repo.msys2.org/msys/x86_64/autoconf2.72-2.72-4-any.pkg.tar.zst", + "65b3a37edf8bb4221d38db910ce1acc1ed879c7bcba33faa058066d06d35e7b0", + ], + "automake-wrapper": [ + "https://repo.msys2.org/msys/x86_64/automake-wrapper-20260320-1-any.pkg.tar.zst", + "693817c288c81bb9f94ac92e5b6f914402735b80bb33e326ccddd7c89cc6a719", + ], + "automake1.18": [ + "https://repo.msys2.org/msys/x86_64/automake1.18-1.18.1-1-any.pkg.tar.zst", + "c045d9eddf900dbacae06d9e0e19e250cc32d849def7d06b833253f85e3fc941", + ], + # `-devel` ships iconv.h + libiconv.dll.a needed by glib/gettext/krb5 + # configure scripts; the runtime msys-iconv-2.dll is already in base. + "libiconv-devel": [ + "https://repo.msys2.org/msys/x86_64/libiconv-devel-1.19-1-x86_64.pkg.tar.zst", + "b4836f02199a2ae02d5a836d5a6dc4a174a055da79c30e256635e638b52e2c5d", + ], + # perl is openssl's `Configure` runtime; libcrypt + libxcrypt are + # perl's link-time crypto deps (older + newer ABI both present so we + # don't have to track which one MSYS2's perl build linked against). + "perl": [ + "https://repo.msys2.org/msys/x86_64/perl-5.42.2-1-x86_64.pkg.tar.zst", + "7c96ef91b7395c12cfbd967ebf7daa8d3f950d1c0be23bb1dc2a635c95a07c01", + ], + "libcrypt": [ + "https://repo.msys2.org/msys/x86_64/libcrypt-2.1-5-x86_64.pkg.tar.zst", + "3e7822d392eed281cd6019b80078ab201eefa7c83fff2d5bab0491b01088f674", + ], + "libxcrypt": [ + "https://repo.msys2.org/msys/x86_64/libxcrypt-4.5.2-1-x86_64.pkg.tar.zst", + "e63db0aa2b708359e25f61f506e2d2c92c315febf5f88cc9f386e4ec0bdf4fa5", + ], + }, + sha256 = "7a3e5c0a728ebefd24dca6da179fe569dc98d6c7d20ce80981d8dc33fb15f7fc", + url = "https://repo.msys2.org/distrib/x86_64/msys2-base-x86_64-20260322.tar.zst", + version = "20260322", +) + # TODO{agent-build}: Find a way to register platform-specific toolchains dynamically register_toolchains( "@gcc_toolchain_x86_64//:cc_toolchain", "@gcc_toolchain_aarch64//:cc_toolchain", - "//bazel/toolchains/mingw:mingw_cc_toolchain", + "@winlibs_mingw64//:mingw_cc_toolchain", + "@msys2_base//:sh_toolchain", "@llvm_toolchain//:all", # last to avoid taking precedence over GCC toolchains ) @@ -334,21 +437,10 @@ rust = use_extension("@rules_rust//rust:extensions.bzl", "rust") # Register a GNU-ABI Rust toolchain for Windows so that rustc generates # GNU-style linker flags (-lkernel32, -o, etc.) compatible with the MinGW -# cc_toolchain used on the Windows CI runner. Processed before the default -# toolchains, so Bazel's toolchain resolution picks it over the auto-registered -# x86_64-pc-windows-msvc toolchain. rust.repository_set( name = "rust_windows_gnu_x86_64", edition = "2024", exec_triple = "x86_64-pc-windows-gnu", - extra_exec_rustc_flags = [ - "-Clinker=C:/tools/msys64/mingw64/bin/gcc.exe", - "-Cdlltool=C:/tools/msys64/mingw64/bin/dlltool.exe", - ], - extra_rustc_flags = [ - "-Clinker=C:/tools/msys64/mingw64/bin/gcc.exe", - "-Cdlltool=C:/tools/msys64/mingw64/bin/dlltool.exe", - ], target_compatible_with = [ "@platforms//os:windows", "@platforms//cpu:x86_64", diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 3f62f09956fe..b3a3ed9613d3 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -818,8 +818,8 @@ }, "@@rules_rust+//crate_universe/private:internal_extensions.bzl%cu_nr": { "general": { - "bzlTransitiveDigest": "PGSiiAcZuyl14IppjuydWg9+28ItT5UwsBN+MBQyohs=", - "usagesDigest": "qhmy4zrnFb6knNQRx2XBCVfHObom0vnflm+sibd7SA0=", + "bzlTransitiveDigest": "SuqYwxlOUUzTNbmo6As6hEPbrnbUdDVxAsQUXk5f9RQ=", + "usagesDigest": "w6DeRbiDSXRVPZPJF6BTEEe4fMh1OiL8grDVrOA0M98=", "recordedInputs": [ "REPO_MAPPING:bazel_features+,bazel_features_globals bazel_features++version_extension+bazel_features_globals", "REPO_MAPPING:bazel_features+,bazel_features_version bazel_features++version_extension+bazel_features_version", diff --git a/bazel/patches/rules_rust-crateinfo-env-leak.patch b/bazel/patches/rules_rust-crateinfo-env-leak.patch deleted file mode 100644 index 1e8129375763..000000000000 --- a/bazel/patches/rules_rust-crateinfo-env-leak.patch +++ /dev/null @@ -1,34 +0,0 @@ -Do not leak ctx.configuration.default_shell_env into CrateInfo.rustc_env. - -At the tail of rustc_compile_action, CrateInfo is re-created with -`rustc_env = env`. But `env` was built as `dict(default_shell_env) + -env_from_args`, so it carries the host's default_shell_env (PATH, LIB, -INCLUDE, ...) on top of the rustc-specific env. Every downstream consumer -that inherits crate_info.rustc_env (notably rust_test(crate = ...)) then -picks up the host env, and at its own compile time the inherited values -clobber cc_toolchain's link_env -- because construct_arguments applies -link_env first (line ~1183) and crate_info.rustc_env after. - -Persisting only env_from_args keeps CrateInfo honest: Bazel re-injects -default_shell_env at action execution time anyway, so there is no reason -to bake it into provider state. - -Upstream issue: https://github.com/bazelbuild/rules_rust/issues/TBD - -diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl -index f43ddaba67abb9b27f840b7d984c4cafd3147c29..dc30e62545a6778315c30237a3895a63911babb9 100644 ---- a/rust/private/rustc.bzl -+++ b/rust/private/rustc.bzl -@@ -1797,8 +1797,11 @@ def rustc_compile_action( - ) - - if crate_info_dict != None: -+ # Persist only rustc-specific env; `env` above also carries default_shell_env -+ # which must not leak through CrateInfo (it would clobber cc_toolchain -+ # link_env in downstream rust_test(crate = ...)). - crate_info_dict.update({ -- "rustc_env": env, -+ "rustc_env": env_from_args, - }) - crate_info = rust_common.create_crate_info( - deps = depset(deps), diff --git a/bazel/patches/rules_rust-derive-dlltool-from-linker.patch b/bazel/patches/rules_rust-derive-dlltool-from-linker.patch new file mode 100644 index 000000000000..ee04cfec0238 --- /dev/null +++ b/bazel/patches/rules_rust-derive-dlltool-from-linker.patch @@ -0,0 +1,35 @@ +diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl +index c8f6ecc9..c7319735 100644 +--- a/rust/private/rustc.bzl ++++ b/rust/private/rustc.bzl +@@ -1158,6 +1158,30 @@ def construct_arguments( + # If linker_type is not explicitly set, infer from which linker is actually being used + ld_is_direct_driver = False + ++ # rustc on Windows-GNU shells out to dlltool to synthesize import ++ # libraries -- both for #[link(... kind = "raw-dylib")] declarations ++ # in *any* crate (rlib included; windows-sys 0.61+ is the common case) ++ # and for the *.dll.a companion that cdylib crates emit alongside ++ # their .dll. cc_toolchain has no action / tool slot for dlltool ++ # because gcc-as-linker invokes it internally -- but rustc drives the ++ # linker directly, so it needs an explicit pointer. ++ # ++ # Inject the derived path here, *outside* the link-emit gate below, so ++ # rlib compiles also see it. Every MinGW distribution (winlibs, MSYS2 ++ # mingw64, llvm-mingw) ships dlltool.exe next to the linker, so reuse ++ # the cc_toolchain's CPP_LINK_EXECUTABLE_ACTION_NAME tool path and ++ # swap the basename. User-supplied `-Cdlltool=` via extra_rustc_flags ++ # is appended later and still wins because rustc honors the last ++ # `-C=` for a given key. ++ if cc_toolchain and toolchain.target_os == "windows" and toolchain.target_abi != "msvc": ++ dlltool_linker = cc_common.get_tool_for_action( ++ feature_configuration = feature_configuration, ++ action_name = CPP_LINK_EXECUTABLE_ACTION_NAME, ++ ) ++ dlltool_sep = max(dlltool_linker.rfind("/"), dlltool_linker.rfind("\\")) ++ if dlltool_sep >= 0: ++ rustc_flags.add(dlltool_linker[:dlltool_sep] + "/dlltool.exe", format = "--codegen=dlltool=%s") ++ + # Link! + if ("link" in emit and crate_info.type not in ["rlib", "lib"]) or add_flags_for_binary: + # Rust's built-in linker can handle linking wasm files. We don't want to attempt to use the cc diff --git a/bazel/rules/windows_resources.bzl b/bazel/rules/windows_resources.bzl index 744aa23603d6..6d8beb013ad7 100644 --- a/bazel/rules/windows_resources.bzl +++ b/bazel/rules/windows_resources.bzl @@ -13,7 +13,9 @@ load("@rules_cc//cc:action_names.bzl", "C_COMPILE_ACTION_NAME") load("@rules_cc//cc:defs.bzl", "cc_common") load("@rules_cc//cc:find_cc_toolchain.bzl", "CC_TOOLCHAIN_ATTRS", "find_cc_toolchain", "use_cc_toolchain") load("//bazel/rules:version_info.bzl", "agent_version_defines") -load("//bazel/toolchains/mingw:paths.bzl", "MINGW_PATH") + +_WINDRES = "@winlibs_mingw64//:windres" +_WINDMC = "@winlibs_mingw64//:windmc" def _cc_env(ctx): """Returns (env, cc_toolchain) from the resolved CC toolchain.""" @@ -48,7 +50,7 @@ def _win_messagetable_impl(ctx): windmc_args.add(src) ctx.actions.run( - executable = MINGW_PATH + "/bin/windmc", + executable = ctx.executable._windmc, arguments = [windmc_args], inputs = [src], outputs = [rc_out, h_out, bin_out], @@ -67,7 +69,7 @@ def _win_messagetable_impl(ctx): windres_args.add("-o", syso_out) ctx.actions.run( - executable = MINGW_PATH + "/bin/windres", + executable = ctx.executable._windres, arguments = [windres_args], env = env, inputs = depset([rc_out, bin_out], transitive = [cc_toolchain.all_files]), @@ -83,6 +85,8 @@ _win_messagetable = rule( doc = "Compiles a .mc message file into a .syso resource and .h header via windmc + windres.", attrs = { "src": attr.label(mandatory = True, allow_single_file = [".mc"]), + "_windmc": attr.label(default = _WINDMC, executable = True, cfg = "exec", allow_single_file = True), + "_windres": attr.label(default = _WINDRES, executable = True, cfg = "exec", allow_single_file = True), } | CC_TOOLCHAIN_ATTRS, toolchains = use_cc_toolchain(), fragments = ["cpp"], @@ -129,7 +133,7 @@ def _win_resource_impl(ctx): windres_args.add("-o", syso_out) ctx.actions.run( - executable = MINGW_PATH + "/bin/windres", + executable = ctx.executable._windres, arguments = [windres_args], env = env, inputs = depset([src] + ctx.files.deps, transitive = [cc_toolchain.all_files]), @@ -147,6 +151,7 @@ _win_resource = rule( "src": attr.label(mandatory = True, allow_single_file = [".rc"]), "deps": attr.label_list(allow_files = True), "defines": attr.string_dict(), + "_windres": attr.label(default = _WINDRES, executable = True, cfg = "exec", allow_single_file = True), } | CC_TOOLCHAIN_ATTRS, toolchains = use_cc_toolchain(), fragments = ["cpp"], diff --git a/bazel/toolchains/mingw/BUILD.bazel b/bazel/toolchains/mingw/BUILD.bazel index 92ea1f55f483..83739850964f 100644 --- a/bazel/toolchains/mingw/BUILD.bazel +++ b/bazel/toolchains/mingw/BUILD.bazel @@ -1,50 +1,3 @@ -load("@rules_cc//cc:defs.bzl", "cc_toolchain") -load("//bazel/toolchains/mingw:paths.bzl", "MINGW_PATH", "MSYS2_PATH") -load("//bazel/toolchains/mingw:toolchain.bzl", "mingw_cc_toolchain_config") - -mingw_cc_toolchain_config( - name = "mingw-cc-toolchain-config", - GCC_VERSION = "14.2.0", - MINGW_PATH = MINGW_PATH, - MSYS2_PATH = MSYS2_PATH, -) - -filegroup( - name = "toolchain_files", -) - -# Define our cc_toolchain -# (https://bazel.build/reference/be/c-cpp#cc_toolchain). -# The cc_toolchain rule is pre-defined by the C++ rule owners. It uses these -# parameters to construct a ToolchainInfo provider, as required by Bazel's -# platform/toolchain APIs. -cc_toolchain( - name = "mingw_cc_toolchain_definition", - all_files = ":toolchain_files", - compiler_files = ":toolchain_files", - dwp_files = ":toolchain_files", - linker_files = ":toolchain_files", - objcopy_files = ":toolchain_files", - strip_files = ":toolchain_files", - toolchain_config = ":mingw-cc-toolchain-config", -) - -# Bazel's platform/toolchain APIs require this wrapper around the actual -# toolchain defined above. It serves two purposes: declare which -# constraint_values it supports (which can be matched to appropriate platforms) -# and tell Bazel what language this toolchain is for. -# -# So when you're building a cc_binary, Bazel has all the info it needs to give -# that cc_binary the right toolchain: it knows cc_binary requires a "C++-type -# toolchain" (this is encoded in the cc_binary rule definition) and needs to -# use a toolchain that matches whatever you set --platforms to at the command -# line. -toolchain( - name = "mingw_cc_toolchain", - target_compatible_with = [ - "@platforms//os:windows", - "@platforms//cpu:x86_64", - ], - toolchain = ":mingw_cc_toolchain_definition", - toolchain_type = "@bazel_tools//tools/cpp:toolchain_type", -) +# MinGW cc_toolchain lives in @winlibs_mingw64//: (see winlibs.BUILD.bazel). +# This file exists only to mark the package; the toolchain config rule is +# loaded from //bazel/toolchains/mingw:toolchain.bzl by the WinLibs repo. diff --git a/bazel/toolchains/mingw/paths.bzl b/bazel/toolchains/mingw/paths.bzl deleted file mode 100644 index a53c3e8c80d2..000000000000 --- a/bazel/toolchains/mingw/paths.bzl +++ /dev/null @@ -1,4 +0,0 @@ -"""MinGW/MSYS2 installation paths shared across toolchain and resource rules.""" - -MSYS2_PATH = "C:/tools/msys64" -MINGW_PATH = MSYS2_PATH + "/mingw64" diff --git a/bazel/toolchains/mingw/toolchain.bzl b/bazel/toolchains/mingw/toolchain.bzl index 7b8a803caea4..ede60f1c88db 100644 --- a/bazel/toolchains/mingw/toolchain.bzl +++ b/bazel/toolchains/mingw/toolchain.bzl @@ -1,9 +1,18 @@ +"""Hermetic MinGW-w64 cc_toolchain_config. + +All tool paths are relative to the package containing the cc_toolchain target +(see @winlibs_mingw64//:winlibs.BUILD.bazel), so this rule has no host-path +attrs and works in sandboxed / remote-exec contexts. + +WinLibs gcc.exe locates its sibling tools (as, ld, cc1, ...) via relative path +lookup against argv[0], which is why no PATH env_entry is needed once the full +WinLibs tree is staged as the cc_toolchain's `all_files`. +""" + load( "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl", "action_config", "artifact_name_pattern", - "env_entry", - "env_set", "feature", "flag_group", "flag_set", @@ -13,40 +22,27 @@ load( load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES") load("@rules_cc//cc:defs.bzl", "CcToolchainConfigInfo", "cc_common") -def _impl(ctx): - tools = [ - "ar", - "cpp", - "gcc", - "gcov", - "ld", - "nm", - "objdump", - "strip", - ] - - gpp_tool = tool( - path = ctx.attr.MINGW_PATH + "/bin/g++", - ) - - gcc_tool = tool( - path = ctx.attr.MINGW_PATH + "/bin/gcc", - ) - - # For assembly files, GCC can act as the assembler (preprocesses and assembles .S files) - as_tool = tool( - path = ctx.attr.MINGW_PATH + "/bin/as", - ) - - # For creating static libraries (.a files) - ar_tool = tool( - path = ctx.attr.MINGW_PATH + "/bin/ar", - ) +_TOOL_NAMES = [ + "ar", + "cpp", + "gcc", + "gcov", + "ld", + "nm", + "objdump", + "strip", +] - tool_paths = [] +def _impl(ctx): + gpp_tool = tool(path = "bin/g++.exe") + gcc_tool = tool(path = "bin/gcc.exe") + as_tool = tool(path = "bin/as.exe") + ar_tool = tool(path = "bin/ar.exe") - for mingw_tool in tools: - tool_paths.append(tool_path(name = mingw_tool, path = "{}/bin/{}".format(ctx.attr.MINGW_PATH, mingw_tool))) + tool_paths = [ + tool_path(name = name, path = "bin/{}.exe".format(name)) + for name in _TOOL_NAMES + ] default_feature = feature( name = "default_env_feature", @@ -68,25 +64,8 @@ def _impl(ctx): ], ), ], - env_sets = [ - env_set( - actions = [ - ACTION_NAMES.c_compile, - ACTION_NAMES.cpp_compile, - ACTION_NAMES.assemble, - ACTION_NAMES.preprocess_assemble, - ACTION_NAMES.cpp_link_executable, - ACTION_NAMES.cpp_link_dynamic_library, - ACTION_NAMES.cpp_link_static_library, - ], - env_entries = [ - env_entry("PATH", "{}/usr/bin;{}/bin".format(ctx.attr.MSYS2_PATH, ctx.attr.MINGW_PATH)), - ], - ), - ], ) - # Feature for archiver flags (ar) archiver_flags_feature = feature( name = "archiver_flags", enabled = True, @@ -96,9 +75,8 @@ def _impl(ctx): flag_groups = [ flag_group( flags = ["rcsD", "%{output_execpath}"], - # This is needed to make rules_foreign_cc work - # as it cannot find output_execpath. This way we - # don't expand this flag group if variable is not available. + # Needed for rules_foreign_cc which doesn't expose + # output_execpath; skip the group when absent. expand_if_available = "output_execpath", ), flag_group( @@ -125,37 +103,32 @@ def _impl(ctx): target_libc = "nothing", cc_target_os = "windows", compiler = "mingw-gcc", - abi_version = "gcc-" + ctx.attr.GCC_VERSION, + abi_version = "gcc-" + ctx.attr.gcc_version, abi_libc_version = "nothing", action_configs = [ - # C compilation - use gcc action_config( action_name = ACTION_NAMES.c_compile, enabled = True, tools = [gcc_tool], ), - # C++ compilation - use g++ action_config( action_name = ACTION_NAMES.cpp_compile, enabled = True, tools = [gpp_tool], ), - # Assembly actions - # preprocess_assemble: for .S files (assembly with C preprocessor) + # .S files (assembly with C preprocessor) go through gcc. action_config( action_name = ACTION_NAMES.preprocess_assemble, enabled = True, - tools = [gcc_tool], # GCC can preprocess and assemble .S files + tools = [gcc_tool], ), - # assemble: for .s files (pure assembly, no preprocessing) + # .s files (pure assembly) go straight to as. action_config( action_name = ACTION_NAMES.assemble, enabled = True, - tools = [as_tool], # Use 'as' directly for pure assembly + tools = [as_tool], ), - # Linking actions (shared between C and C++) - # For C projects, gcc is typically used; for C++ projects, g++ is used - # We'll use g++ for linking to handle both cases properly + # g++ for linking covers both C and C++ link cases. action_config( action_name = ACTION_NAMES.cpp_link_executable, enabled = True, @@ -166,7 +139,6 @@ def _impl(ctx): enabled = True, tools = [gpp_tool], ), - # Static library archiving - use ar (archiver) action_config( action_name = ACTION_NAMES.cpp_link_static_library, enabled = True, @@ -174,12 +146,17 @@ def _impl(ctx): ), ], tool_paths = tool_paths, + # Paths are relative to the cc_toolchain's package (the root of + # @winlibs_mingw64//:), so rules_cc resolves them via crosstool_top + # and never constructs a Label("@winlibs_mingw64//...") from inside + # @@rules_cc+'s repo_mapping. Using %package(@winlibs_mingw64//)% + # here trips rules_cc 0.2.18 on Bazel 9 with an invalid-Label error. cxx_builtin_include_directories = [ - ctx.attr.MINGW_PATH + "/include", - ctx.attr.MINGW_PATH + "/lib/gcc/x86_64-w64-mingw32/" + ctx.attr.GCC_VERSION + "/include-fixed", - ctx.attr.MINGW_PATH + "/lib/gcc/x86_64-w64-mingw32/" + ctx.attr.GCC_VERSION + "/include", - ctx.attr.MINGW_PATH + "/lib/gcc/x86_64-w64-mingw32/" + ctx.attr.GCC_VERSION + "/install-tools/include", - ctx.attr.MINGW_PATH + "/x86_64-w64-mingw32/include", + "include", + "lib/gcc/x86_64-w64-mingw32/" + ctx.attr.gcc_version + "/include-fixed", + "lib/gcc/x86_64-w64-mingw32/" + ctx.attr.gcc_version + "/include", + "lib/gcc/x86_64-w64-mingw32/" + ctx.attr.gcc_version + "/install-tools/include", + "x86_64-w64-mingw32/include", ], artifact_name_patterns = [ artifact_name_pattern( @@ -204,9 +181,7 @@ def _impl(ctx): mingw_cc_toolchain_config = rule( implementation = _impl, attrs = { - "MSYS2_PATH": attr.string(mandatory = True), - "MINGW_PATH": attr.string(mandatory = True), - "GCC_VERSION": attr.string(mandatory = True), + "gcc_version": attr.string(mandatory = True), }, provides = [CcToolchainConfigInfo], ) diff --git a/bazel/toolchains/mingw/winlibs.BUILD.bazel b/bazel/toolchains/mingw/winlibs.BUILD.bazel new file mode 100644 index 000000000000..2217eb1f7272 --- /dev/null +++ b/bazel/toolchains/mingw/winlibs.BUILD.bazel @@ -0,0 +1,80 @@ +"""BUILD template instantiated by winlibs_mingw_repository. + +%GCC_VERSION% is substituted at fetch time so the URL pin in MODULE.bazel +stays the single source of truth for which WinLibs zip we use. + +After strip_prefix the layout is `bin/`, `include/`, `lib/`, +`x86_64-w64-mingw32/` at the repo root. +""" + +load("@//bazel/toolchains/mingw:toolchain.bzl", "mingw_cc_toolchain_config") +load("@rules_cc//cc:defs.bzl", "cc_toolchain") + +package(default_visibility = ["//visibility:public"]) + +GCC_VERSION = "%GCC_VERSION%" + +filegroup( + name = "all", + srcs = glob( + ["**"], + exclude = [ + "BUILD.bazel", + "WORKSPACE", + "WORKSPACE.bazel", + ], + ), +) + +[ + filegroup( + name = tool_name, + srcs = ["bin/" + tool_name + ".exe"], + ) + for tool_name in [ + "ar", + "as", + "cpp", + "dlltool", + "g++", + "gcc", + "gcov", + "ld", + "nm", + "objdump", + "strip", + "windmc", + "windres", + ] +] + +filegroup( + name = "make", + srcs = ["bin/mingw32-make.exe"], +) + +mingw_cc_toolchain_config( + name = "mingw-cc-toolchain-config", + gcc_version = GCC_VERSION, +) + +cc_toolchain( + name = "mingw_cc_toolchain_definition", + all_files = ":all", + compiler_files = ":all", + dwp_files = ":all", + linker_files = ":all", + objcopy_files = ":all", + strip_files = ":all", + toolchain_config = ":mingw-cc-toolchain-config", +) + +toolchain( + name = "mingw_cc_toolchain", + target_compatible_with = [ + "@platforms//os:windows", + "@platforms//cpu:x86_64", + ], + toolchain = ":mingw_cc_toolchain_definition", + toolchain_type = "@bazel_tools//tools/cpp:toolchain_type", +) diff --git a/bazel/toolchains/mingw/winlibs.bzl b/bazel/toolchains/mingw/winlibs.bzl new file mode 100644 index 000000000000..50bee4a68024 --- /dev/null +++ b/bazel/toolchains/mingw/winlibs.bzl @@ -0,0 +1,48 @@ +"""Repository rule that materializes a hermetic MinGW-w64 toolchain from a +pinned WinLibs distribution. + +Pins URL, SHA256 and GCC version together in MODULE.bazel so bumping the zip +and the include-dir layout it implies stays a single atomic change. +""" + +def _winlibs_mingw_repository_impl(ctx): + ctx.download_and_extract( + url = ctx.attr.url, + sha256 = ctx.attr.sha256, + stripPrefix = ctx.attr.strip_prefix, + ) + ctx.template( + "BUILD.bazel", + ctx.attr._build_file_template, + substitutions = { + "%GCC_VERSION%": ctx.attr.gcc_version, + }, + executable = False, + ) + +winlibs_mingw_repository = repository_rule( + implementation = _winlibs_mingw_repository_impl, + doc = "Downloads a pinned WinLibs MinGW-w64 zip and exposes it as a hermetic cc_toolchain.", + attrs = { + "url": attr.string( + mandatory = True, + doc = "Direct URL to the WinLibs .zip release asset.", + ), + "sha256": attr.string( + mandatory = True, + doc = "SHA256 of the zip, pinned for supply-chain integrity.", + ), + "strip_prefix": attr.string( + default = "mingw64", + doc = "Top-level directory stripped from the zip (WinLibs ships everything under mingw64/).", + ), + "gcc_version": attr.string( + mandatory = True, + doc = "GCC version baked into the WinLibs zip; must match the URL or builtin include dirs will be wrong.", + ), + "_build_file_template": attr.label( + default = "//bazel/toolchains/mingw:winlibs.BUILD.bazel", + allow_single_file = True, + ), + }, +) diff --git a/bazel/toolchains/msys2/BUILD.bazel b/bazel/toolchains/msys2/BUILD.bazel new file mode 100644 index 000000000000..d577736862e1 --- /dev/null +++ b/bazel/toolchains/msys2/BUILD.bazel @@ -0,0 +1,46 @@ +load("@bazel_lib//lib:write_source_files.bzl", "write_source_file") +load("@rules_cc//cc:cc_binary.bzl", "cc_binary") + +# Hermetic MSYS2 sh_toolchain lives in @msys2_base//: (see msys2.BUILD.bazel, +# loaded by the MSYS2 repository rule). This package additionally owns the +# bash_shim wired into .bazelrc as --shell_executable on Windows: the +# checked-in bash_shim.exe is the runtime artifact (Bazel reads it at startup); +# the cc_binary below is the source of truth for how to rebuild it, and the +# write_source_file target keeps the two in sync. + +exports_files(["bash_shim.exe"]) + +cc_binary( + name = "bash_shim_bin", + srcs = ["bash_shim.c"], + copts = [ + "-O2", + "-municode", + ], + linkopts = [ + "-municode", + "-static", + "-s", + # Zero out the PE timestamp so two builds of the same source produce + # byte-identical output and the diff_test below stays stable. + "-Wl,--no-insert-timestamp", + ], + target_compatible_with = [ + "@platforms//os:windows", + "@platforms//cpu:x86_64", + ], +) + +# `bazel run //bazel/toolchains/msys2:bash_shim` rebuilds bash_shim.exe in +# place. `bazel test //bazel/toolchains/msys2:bash_shim_test` (auto-generated) +# fails CI if the in-tree binary drifts from what bash_shim.c would produce. +write_source_file( + name = "bash_shim", + in_file = ":bash_shim_bin", + out_file = "bash_shim.exe", + target_compatible_with = [ + "@platforms//os:windows", + "@platforms//cpu:x86_64", + ], + visibility = ["//visibility:public"], +) diff --git a/bazel/toolchains/msys2/README.md b/bazel/toolchains/msys2/README.md new file mode 100644 index 000000000000..4d529a35fa78 --- /dev/null +++ b/bazel/toolchains/msys2/README.md @@ -0,0 +1,36 @@ +# bash_shim + +`bash_shim.exe` is Bazel's `--shell_executable` on Windows. It exists because +`cmd.exe` re-tokenises arguments when running `.bat` files and truncates +`bash -c "..."` payloads at the first newline, which silently breaks +multi-line `ctx.actions.run_shell` actions (e.g. `GoMockSourceGen`). + +The shim: +1. Reads its raw command line via `GetCommandLineW`, so embedded newlines + survive intact. +2. Prepends the hermetic `@msys2_base` and `@winlibs_mingw64` bin directories + to `PATH`. +3. Spawns `bash.exe` from `@msys2_base` with the original arguments via + `CreateProcessW` and forwards its exit code. + +## Rebuilding + +`bash_shim.exe` is checked in so Bazel can find it on startup (`.bazelrc` +wires `--shell_executable=bazel/toolchains/msys2/bash_shim.exe` +unconditionally on Windows). The canonical recipe for producing it is the +`cc_binary` in this directory, compiled with the hermetic +`@winlibs_mingw64` toolchain. + +Rebuild after editing `bash_shim.c`: + +```powershell +bazel run //bazel/toolchains/msys2:bash_shim +git add bazel/toolchains/msys2/bash_shim.exe +``` + +`bazel test //bazel/toolchains/msys2:bash_shim_test` (auto-generated by +`write_source_file`) verifies the committed `.exe` still matches what +`bash_shim.c` would produce. Wire it into the CI test suite to catch drift. + +Reproducibility relies on `-Wl,--no-insert-timestamp` to zero out the PE +header timestamp; if the diff_test flaps, that's the first thing to check. diff --git a/bazel/toolchains/msys2/bash_shim.c b/bazel/toolchains/msys2/bash_shim.c new file mode 100644 index 000000000000..2ec10e07f52c --- /dev/null +++ b/bazel/toolchains/msys2/bash_shim.c @@ -0,0 +1,104 @@ +// bash_shim: thin C launcher routed to hermetic MSYS2 bash, used as Bazel's +// --shell_executable on Windows. A .bat wrapper here truncates multi-line +// -c arguments at the first newline because cmd.exe re-tokenises argv when +// running batch files; this .exe is invoked directly via CreateProcessW and +// keeps the raw command line intact. +// +// See README.md in this directory for the build/rebuild workflow. + +#include +#include +#include +#include +#include + +#define MSYS2_REL L"..\\..\\external\\+msys2_base_repository+msys2_base" +#define MINGW_REL L"..\\..\\external\\+winlibs_mingw_repository+winlibs_mingw64" + +static int file_exists(const wchar_t *p) { + DWORD attr = GetFileAttributesW(p); + return attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY); +} + +static void die_missing(const wchar_t *what, const wchar_t *where, const wchar_t *fetch_hint) { + fwprintf(stderr, L"bash_shim: hermetic %ls not found at %ls\n", what, where); + fwprintf(stderr, L"bash_shim: run 'bazelisk fetch %ls' to materialise it\n", fetch_hint); +} + +// Skip past argv[0] in a raw command line as Windows would tokenise it. +static const wchar_t *skip_argv0(const wchar_t *cmd) { + const wchar_t *p = cmd; + if (*p == L'"') { + for (++p; *p && *p != L'"'; ++p) {} + if (*p == L'"') ++p; + } else { + while (*p && *p != L' ' && *p != L'\t') ++p; + } + while (*p == L' ' || *p == L'\t') ++p; + return p; +} + +int wmain(int argc, wchar_t **argv) { + (void)argc; (void)argv; + + wchar_t cwd[MAX_PATH]; + if (!GetCurrentDirectoryW(MAX_PATH, cwd)) { + fwprintf(stderr, L"bash_shim: GetCurrentDirectory failed: %lu\n", GetLastError()); + return 1; + } + + wchar_t bash_path[MAX_PATH], gcc_path[MAX_PATH], msys_bin[MAX_PATH], mingw_bin[MAX_PATH]; + _snwprintf(bash_path, MAX_PATH, L"%ls\\%ls\\usr\\bin\\bash.exe", cwd, MSYS2_REL); + _snwprintf(gcc_path, MAX_PATH, L"%ls\\%ls\\bin\\gcc.exe", cwd, MINGW_REL); + _snwprintf(msys_bin, MAX_PATH, L"%ls\\%ls\\usr\\bin", cwd, MSYS2_REL); + _snwprintf(mingw_bin, MAX_PATH, L"%ls\\%ls\\bin", cwd, MINGW_REL); + + if (!file_exists(bash_path)) { + die_missing(L"bash", bash_path, L"@msys2_base//..."); + return 1; + } + if (!file_exists(gcc_path)) { + die_missing(L"gcc", gcc_path, L"@winlibs_mingw64//..."); + return 1; + } + + // Prepend hermetic tool dirs to PATH so bash and the commands it execs + // resolve to the pinned binaries rather than whatever leaked in. + DWORD path_len = GetEnvironmentVariableW(L"PATH", NULL, 0); + wchar_t *old_path = (wchar_t *)calloc(path_len + 1, sizeof(wchar_t)); + if (!old_path) { fwprintf(stderr, L"bash_shim: oom\n"); return 1; } + if (path_len) GetEnvironmentVariableW(L"PATH", old_path, path_len); + size_t new_path_len = wcslen(msys_bin) + wcslen(mingw_bin) + wcslen(old_path) + 3; + wchar_t *new_path = (wchar_t *)calloc(new_path_len, sizeof(wchar_t)); + if (!new_path) { fwprintf(stderr, L"bash_shim: oom\n"); return 1; } + _snwprintf(new_path, new_path_len, L"%ls;%ls;%ls", msys_bin, mingw_bin, old_path); + SetEnvironmentVariableW(L"PATH", new_path); + free(old_path); + free(new_path); + + // Forward the raw command line so embedded newlines in -c "..." survive + // intact instead of going through argv[] reparsing. + const wchar_t *rest = skip_argv0(GetCommandLineW()); + size_t bash_len = wcslen(bash_path); + size_t rest_len = wcslen(rest); + size_t full_len = bash_len + rest_len + 4; // quotes + space + NUL + wchar_t *full_cmd = (wchar_t *)calloc(full_len, sizeof(wchar_t)); + if (!full_cmd) { fwprintf(stderr, L"bash_shim: oom\n"); return 1; } + _snwprintf(full_cmd, full_len, L"\"%ls\" %ls", bash_path, rest); + + STARTUPINFOW si = { .cb = sizeof(si) }; + PROCESS_INFORMATION pi = { 0 }; + if (!CreateProcessW(NULL, full_cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) { + fwprintf(stderr, L"bash_shim: CreateProcess failed: %lu\n", GetLastError()); + free(full_cmd); + return 1; + } + free(full_cmd); + + WaitForSingleObject(pi.hProcess, INFINITE); + DWORD exit_code = 1; + GetExitCodeProcess(pi.hProcess, &exit_code); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return (int)exit_code; +} diff --git a/bazel/toolchains/msys2/bash_shim.exe b/bazel/toolchains/msys2/bash_shim.exe new file mode 100644 index 000000000000..0d8bbbb437cc Binary files /dev/null and b/bazel/toolchains/msys2/bash_shim.exe differ diff --git a/bazel/toolchains/msys2/msys2.BUILD.bazel b/bazel/toolchains/msys2/msys2.BUILD.bazel new file mode 100644 index 000000000000..bbd10898cbce --- /dev/null +++ b/bazel/toolchains/msys2/msys2.BUILD.bazel @@ -0,0 +1,55 @@ +"""BUILD template instantiated by msys2_base_repository. + +%VERSION% and %BASH_ABSOLUTE_PATH% are substituted at fetch time so the URL +pin in MODULE.bazel stays the single source of truth for which MSYS2 archive +we use, and so the sh_toolchain points at the actual extracted bash.exe. + +After strip_prefix the layout is `usr/bin/`, `etc/`, `var/`, ... at the repo +root, matching what `msys2-base-x86_64-*.sfx.exe -y -oC:/` would produce. +""" + +load("@rules_shell//shell/toolchains:sh_toolchain.bzl", "sh_toolchain") + +package(default_visibility = ["//visibility:public"]) + +MSYS2_VERSION = "%VERSION%" + +filegroup( + name = "all", + srcs = glob( + ["**"], + exclude = [ + "BUILD.bazel", + "WORKSPACE", + "WORKSPACE.bazel", + ], + # MSYS2 archives contain symlinks (/bin -> /usr/bin etc.). Developer + # Mode on Windows handles them; without it Bazel would fail at fetch + # time, not here. + allow_empty = False, + ), +) + +# Bash plus the MSYS2 runtime DLLs it depends on. Globbing msys-*.dll captures +# future additions (msys-iconv-*.dll, msys-intl-*.dll, ...) without churn. +filegroup( + name = "bash_files", + srcs = ["usr/bin/bash.exe"] + glob(["usr/bin/msys-*.dll"]), +) + +sh_toolchain( + name = "msys2_bash", + path = "%BASH_ABSOLUTE_PATH%", + launcher = "@bazel_tools//tools/launcher", + launcher_maker = "@bazel_tools//tools/launcher:launcher_maker", +) + +toolchain( + name = "sh_toolchain", + target_compatible_with = [ + "@platforms//os:windows", + "@platforms//cpu:x86_64", + ], + toolchain = ":msys2_bash", + toolchain_type = "@bazel_tools//tools/sh:toolchain_type", +) diff --git a/bazel/toolchains/msys2/msys2.bzl b/bazel/toolchains/msys2/msys2.bzl new file mode 100644 index 000000000000..27529b175bdf --- /dev/null +++ b/bazel/toolchains/msys2/msys2.bzl @@ -0,0 +1,92 @@ +"""Repository rule that materializes a hermetic MSYS2 base environment from a +pinned MSYS2 distribution archive, plus a curated set of pacman packages +overlaid on top. + +Pins URL, SHA256 and release date together in MODULE.bazel so bumping the +archive stays a single atomic change. + +Layer 1 — `base` archive (msys2-base-*.tar.zst): + bash, coreutils, sed, gawk, grep, find, tar, gzip, xz, msys-2.0.dll runtime. + +Layer 2 — `overlay_packages` (.pkg.tar.zst from repo.msys2.org/msys/x86_64/): + Each pacman package is a tarball that ships its files under the same + `usr//` layout as the base, so extracting at the + same root merges cleanly. We use this to provide `make`, `perl`, `pkgconf`, + autotools, etc. without falling back to rules_foreign_cc's slow source + bootstrap (which on Windows ends up trying to compile glib for pkg-config). + +Skips the upstream first-run post-install (which generates /etc/passwd, +/etc/group and pacman gpg keyrings) because: + * passwd/group are auto-synthesised by the Cygwin/MSYS2 runtime on demand + since cygwin 1.7.34 (2015); + * gpg keyrings are only consumed by pacman, which we never run hermetically. + +`sh_toolchain.path` is a string attribute documented as "Absolute path to the +shell interpreter". We compute the absolute path to the extracted bash.exe at +fetch time and bake it into the generated BUILD.bazel via template +substitution — the standard pattern for hermetic-but-absolute-path toolchains +on Windows. +""" + +def _msys2_base_repository_impl(ctx): + ctx.download_and_extract( + url = ctx.attr.url, + sha256 = ctx.attr.sha256, + stripPrefix = ctx.attr.strip_prefix, + ) + + # Starlark dicts preserve insertion order, so the extract sequence is + # deterministic and matches the MODULE.bazel declaration order — useful + # when two packages own the same file (rare for MSYS2; last write wins). + for pkg_name, spec in ctx.attr.overlay_packages.items(): + if len(spec) != 2: + fail("overlay_packages[%r] must be [url, sha256], got %r" % (pkg_name, spec)) + ctx.download_and_extract( + url = spec[0], + sha256 = spec[1], + ) + + # Bazel passes this path verbatim to its action executor; forward slashes + # work for both cmd.exe and bash on Windows, native backslashes do not + # round-trip through Starlark string handling cleanly. + bash_abs_path = str(ctx.path("usr/bin/bash.exe")).replace("\\", "/") + + ctx.template( + "BUILD.bazel", + ctx.attr._build_file_template, + substitutions = { + "%VERSION%": ctx.attr.version, + "%BASH_ABSOLUTE_PATH%": bash_abs_path, + }, + executable = False, + ) + +msys2_base_repository = repository_rule( + implementation = _msys2_base_repository_impl, + doc = "Downloads a pinned MSYS2 base archive and exposes its bash as a hermetic sh_toolchain.", + attrs = { + "url": attr.string( + mandatory = True, + doc = "Direct URL to the msys2-base-x86_64-*.tar.zst release asset.", + ), + "sha256": attr.string( + mandatory = True, + doc = "SHA256 of the archive, pinned for supply-chain integrity.", + ), + "strip_prefix": attr.string( + default = "msys64", + doc = "Top-level directory stripped from the archive (MSYS2 ships everything under msys64/).", + ), + "version": attr.string( + mandatory = True, + doc = "MSYS2 release date (e.g. 20260322); must match the URL.", + ), + "overlay_packages": attr.string_list_dict( + doc = "Pacman packages overlaid on the base tree, as name -> [url, sha256].", + ), + "_build_file_template": attr.label( + default = "//bazel/toolchains/msys2:msys2.BUILD.bazel", + allow_single_file = True, + ), + }, +) diff --git a/pkg/util/executable/executable_test.go b/pkg/util/executable/executable_test.go index 312d40c2b48a..73adeabf9e91 100644 --- a/pkg/util/executable/executable_test.go +++ b/pkg/util/executable/executable_test.go @@ -17,13 +17,15 @@ import ( "github.com/stretchr/testify/require" ) -func TestResolvePath(t *testing.T) { - testProgram := "ls" +func probeProgram() string { if runtime.GOOS == "windows" { - testProgram = "dir" + return "where" } + return "ls" +} - actualPath, err := ResolvePath(testProgram) +func TestResolvePath(t *testing.T) { + actualPath, err := ResolvePath(probeProgram()) require.NoError(t, err) require.NotEmpty(t, actualPath) @@ -34,12 +36,7 @@ func TestResolvePath(t *testing.T) { } func TestResolvePathIsAbsolute(t *testing.T) { - testProgram := "ls" - if runtime.GOOS == "windows" { - testProgram = "dir" - } - - actualPath, err := ResolvePath(testProgram) + actualPath, err := ResolvePath(probeProgram()) require.NoError(t, err) absPath, err := filepath.Abs(actualPath)