rust: Add rust_bindgen std version w/ cc defaults.

Adds the c_std and cpp_std properties to rust_bindgen, and use the
default values from cc if these are undefined.

This assumes by default that the header extension indicates whether
the header is a C or C++ header file. This default can be overridden
by setting either c_std or cpp_std.

Test: Soong tests pass, "-std=" arg included in bindgen calls
Bug: 163580541
Change-Id: I5b0d3b8eae9a54dd91d8a0aca583d7803a344f27
diff --git a/rust/bindgen.go b/rust/bindgen.go
index cafdb8b..0d89b24 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -21,6 +21,7 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	cc_config "android/soong/cc/config"
 )
 
 var (
@@ -56,7 +57,11 @@
 var _ SourceProvider = (*bindgenDecorator)(nil)
 
 type BindgenProperties struct {
-	// The wrapper header file
+	// The wrapper header file. By default this is assumed to be a C header unless the extension is ".hh" or ".hpp".
+	// This is used to specify how to interpret the header and determines which '-std' flag to use by default.
+	//
+	// If your C++ header must have some other extension, then the default behavior can be overridden by setting the
+	// cpp_std property.
 	Wrapper_src *string `android:"path,arch_variant"`
 
 	// list of bindgen-specific flags and options
@@ -81,6 +86,22 @@
 	// "my_bindgen [flags] wrapper_header.h -o [output_path] -- [clang flags]"
 	Custom_bindgen string `android:"path"`
 
+	// C standard version to use. Can be a specific version (such as "gnu11"),
+	// "experimental" (which will use draft versions like C1x when available),
+	// or the empty string (which will use the default).
+	//
+	// If this is set, the file extension will be ignored and this will be used as the std version value. Setting this
+	// to "default" will use the build system default version. This cannot be set at the same time as cpp_std.
+	C_std *string
+
+	// C++ standard version to use. Can be a specific version (such as
+	// "gnu++11"), "experimental" (which will use draft versions like C++1z when
+	// available), or the empty string (which will use the default).
+	//
+	// If this is set, the file extension will be ignored and this will be used as the std version value. Setting this
+	// to "default" will use the build system default version. This cannot be set at the same time as c_std.
+	Cpp_std *string
+
 	//TODO(b/161141999) Add support for headers from cc_library_header modules.
 }
 
@@ -90,6 +111,45 @@
 	Properties BindgenProperties
 }
 
+func (b *bindgenDecorator) getStdVersion(ctx ModuleContext, src android.Path) (string, bool) {
+	// Assume headers are C headers
+	isCpp := false
+	stdVersion := ""
+
+	switch src.Ext() {
+	case ".hpp", ".hh":
+		isCpp = true
+	}
+
+	if String(b.Properties.Cpp_std) != "" && String(b.Properties.C_std) != "" {
+		ctx.PropertyErrorf("c_std", "c_std and cpp_std cannot both be defined at the same time.")
+	}
+
+	if String(b.Properties.Cpp_std) != "" {
+		if String(b.Properties.Cpp_std) == "experimental" {
+			stdVersion = cc_config.ExperimentalCppStdVersion
+		} else if String(b.Properties.Cpp_std) == "default" {
+			stdVersion = cc_config.CppStdVersion
+		} else {
+			stdVersion = String(b.Properties.Cpp_std)
+		}
+	} else if b.Properties.C_std != nil {
+		if String(b.Properties.C_std) == "experimental" {
+			stdVersion = cc_config.ExperimentalCStdVersion
+		} else if String(b.Properties.C_std) == "default" {
+			stdVersion = cc_config.CStdVersion
+		} else {
+			stdVersion = String(b.Properties.C_std)
+		}
+	} else if isCpp {
+		stdVersion = cc_config.CppStdVersion
+	} else {
+		stdVersion = cc_config.CStdVersion
+	}
+
+	return stdVersion, isCpp
+}
+
 func (b *bindgenDecorator) GenerateSource(ctx ModuleContext, deps PathDeps) android.Path {
 	ccToolchain := ctx.RustModule().ccToolchain(ctx)
 
@@ -134,6 +194,17 @@
 		ctx.PropertyErrorf("wrapper_src", "invalid path to wrapper source")
 	}
 
+	// Add C std version flag
+	stdVersion, isCpp := b.getStdVersion(ctx, wrapperFile.Path())
+	cflags = append(cflags, "-std="+stdVersion)
+
+	// Specify the header source language to avoid ambiguity.
+	if isCpp {
+		cflags = append(cflags, "-x c++")
+	} else {
+		cflags = append(cflags, "-x c")
+	}
+
 	outputFile := android.PathForModuleOut(ctx, b.BaseSourceProvider.getStem(ctx)+".rs")
 
 	var cmd, cmdDesc string