bazel: Build with both C++17 and C++20

Move the C++ standard version configuration from on-each-target to the
.bazelrc. The on-target settings have the highest precedence and cannot
be overridden. The .bazelrc settings have the _lowest_ precedence and
can be overridden, either on the command line or on individual targets.

Use this flexibility to build with both C++17 and C++20 in presubmit.

Bug: 268249509
Change-Id: I6399e4d95acbedc9549c2074eceb9c31c8118e00
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/128373
Commit-Queue: Ted Pudlik <tpudlik@google.com>
Reviewed-by: Wyatt Hepler <hepler@google.com>
Reviewed-by: Fernando Garcia Bermudez <fgb@google.com>
diff --git a/.bazelrc b/.bazelrc
index 21b97e7..1cbfd49 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -35,6 +35,9 @@
 # (https://github.com/bazelembedded/rules_cc_toolchain/issues/46).
 build --features=external_include_paths
 
+# By default build with C++20.
+build --cxxopt='-std=c++20'
+
 # This leaks the PATH variable into the Bazel build environment, which
 # enables the Python pw_protobuf plugins to find the Python version
 # via CIPD and pw_env_setup. This is a partial fix for pwbug/437,
diff --git a/docs/concepts/index.rst b/docs/concepts/index.rst
index af24802..a08bbf8 100644
--- a/docs/concepts/index.rst
+++ b/docs/concepts/index.rst
@@ -119,7 +119,8 @@
 :ref:`module-pw_kvs` and :ref:`module-pw_tokenizer`, work with C++14. All
 Pigweed code is compatible with C++20. Pigweed defines GN toolchains for
 building with C++14 and C++20; see :ref:`target-host` target documentation for
-more information. Bazel builds use C++20.
+more information. For Bazel, the C++ standard version can be configured using
+the `--cxxopt flag <https://bazel.build/docs/user-manual#cxxopt>`_.
 
 .. _docs-concepts-python-version:
 
diff --git a/pw_build/bazel_internal/pigweed_internal.bzl b/pw_build/bazel_internal/pigweed_internal.bzl
index e8d2cf4..b0a9ba2 100644
--- a/pw_build/bazel_internal/pigweed_internal.bzl
+++ b/pw_build/bazel_internal/pigweed_internal.bzl
@@ -39,8 +39,7 @@
     "-Wno-error=deprecated-declarations",  # [[deprecated]] attribute
 ]
 
-CPP20_COPTS = [
-    "-std=c++20",
+CPP_COPTS = [
     "-fno-rtti",
     "-Wnon-virtual-dtor",
     # Allow uses of the register keyword, which may appear in C headers.
@@ -102,7 +101,7 @@
 
     cc = dict(kwargs.items())
     cc["srcs"] = [src for src in kwargs["srcs"] if not src.endswith(".c")]
-    cc["copts"] = cc["copts"] + CPP20_COPTS
+    cc["copts"] = cc["copts"] + CPP_COPTS
 
     c_srcs = [src for src in kwargs["srcs"] if src.endswith(".c")]
 
diff --git a/pw_presubmit/py/pw_presubmit/pigweed_presubmit.py b/pw_presubmit/py/pw_presubmit/pigweed_presubmit.py
index c95804a..1df7734 100755
--- a/pw_presubmit/py/pw_presubmit/pigweed_presubmit.py
+++ b/pw_presubmit/py/pw_presubmit/pigweed_presubmit.py
@@ -469,13 +469,15 @@
         ],
     }
 
-    for platform, targets in targets_for_platform.items():
-        build.bazel(
-            ctx,
-            'build',
-            f'--platforms={platform}',
-            *targets,
-        )
+    for cxxversion in ('c++17', 'c++20'):
+        for platform, targets in targets_for_platform.items():
+            build.bazel(
+                ctx,
+                'build',
+                f'--platforms={platform}',
+                f"--cxxopt='-std={cxxversion}'",
+                *targets,
+            )
 
 
 def pw_transfer_integration_test(ctx: PresubmitContext) -> None: