Add bp2build support for cpp_std.

This converts cpp_std and gnu_extensions into a -std copt, if cpp_std is
specified or gnu_extensions is false if cpp_std is not specified.

I chose to go with this copts approach because the tradeoff is a much
simpler setting than adding a new attr(s) everywhere that uses features
to set the flag.

This approach limits the number of user-configurable knobs (since users
would then be able to set std in _both_ copts and the new attr). But it
does rely on the user copt overriding the toolchain's default gnu++17
version, which can mean a `-std` flag showing up twice in the action.

Fixes: b/202462232
Test: b build //system/libziparchive:libziparchive
Change-Id: I81dad029059461739b91f318d662e089edb46b84
diff --git a/cc/bp2build.go b/cc/bp2build.go
index ebba9d1..ec15b26 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -277,7 +277,24 @@
 					srcs.SetSelectValue(axis, config, srcsList)
 				}
 
-				archVariantCopts := parseCommandLineFlags(baseCompilerProps.Cflags)
+				var archVariantCopts []string
+				if axis == bazel.NoConfigAxis {
+					// If cpp_std is not specified, don't generate it in the
+					// BUILD file. For readability purposes, cpp_std and gnu_extensions are
+					// combined into a single -std=<version> copt, except in the
+					// default case where cpp_std is nil and gnu_extensions is true or unspecified,
+					// then the toolchain's default "gnu++17" will be used.
+					if baseCompilerProps.Cpp_std != nil {
+						// TODO(b/202491296): Handle C_std.
+						// These transformations are shared with compiler.go.
+						cppStdVal := parseCppStd(baseCompilerProps.Cpp_std)
+						_, cppStdVal = maybeReplaceGnuToC(baseCompilerProps.Gnu_extensions, "", cppStdVal)
+						archVariantCopts = append(archVariantCopts, "-std="+cppStdVal)
+					} else if baseCompilerProps.Gnu_extensions != nil && !*baseCompilerProps.Gnu_extensions {
+						archVariantCopts = append(archVariantCopts, "-std=c++17")
+					}
+				}
+				archVariantCopts = append(archVariantCopts, parseCommandLineFlags(baseCompilerProps.Cflags)...)
 				archVariantAsflags := parseCommandLineFlags(baseCompilerProps.Asflags)
 
 				localIncludeDirs := baseCompilerProps.Local_include_dirs
diff --git a/cc/compiler.go b/cc/compiler.go
index b535e7f..4f96712 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -305,6 +305,25 @@
 	getNamedMapForConfig(ctx.Config(), key).Store(module, true)
 }
 
+func maybeReplaceGnuToC(gnuExtensions *bool, cStd string, cppStd string) (string, string) {
+	if gnuExtensions != nil && *gnuExtensions == false {
+		cStd = gnuToCReplacer.Replace(cStd)
+		cppStd = gnuToCReplacer.Replace(cppStd)
+	}
+	return cStd, cppStd
+}
+
+func parseCppStd(cppStdPtr *string) string {
+	cppStd := String(cppStdPtr)
+	switch cppStd {
+	case "":
+		cppStd = config.CppStdVersion
+	case "experimental":
+		cppStd = config.ExperimentalCppStdVersion
+	}
+	return cppStd
+}
+
 // Create a Flags struct that collects the compile flags from global values,
 // per-target values, module type values, and per-module Blueprints properties
 func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags, deps PathDeps) Flags {
@@ -484,18 +503,9 @@
 		cStd = String(compiler.Properties.C_std)
 	}
 
-	cppStd := String(compiler.Properties.Cpp_std)
-	switch String(compiler.Properties.Cpp_std) {
-	case "":
-		cppStd = config.CppStdVersion
-	case "experimental":
-		cppStd = config.ExperimentalCppStdVersion
-	}
+	cppStd := parseCppStd(compiler.Properties.Cpp_std)
 
-	if compiler.Properties.Gnu_extensions != nil && *compiler.Properties.Gnu_extensions == false {
-		cStd = gnuToCReplacer.Replace(cStd)
-		cppStd = gnuToCReplacer.Replace(cppStd)
-	}
+	cStd, cppStd = maybeReplaceGnuToC(compiler.Properties.Gnu_extensions, cStd, cppStd)
 
 	flags.Local.ConlyFlags = append([]string{"-std=" + cStd}, flags.Local.ConlyFlags...)
 	flags.Local.CppFlags = append([]string{"-std=" + cppStd}, flags.Local.CppFlags...)