Improve handling of xcode version in bazel_build.py

Bazel recently modified the format of Xcode version to be
`major.minor.fix.BUILD_VERSION`, which Tulsi needs to handle
in order to be able to specify the proper version of a beta
Xcode.

PiperOrigin-RevId: 237272263
diff --git a/src/TulsiGenerator/Scripts/bazel_build.py b/src/TulsiGenerator/Scripts/bazel_build.py
index ef7d59a..73a2008 100755
--- a/src/TulsiGenerator/Scripts/bazel_build.py
+++ b/src/TulsiGenerator/Scripts/bazel_build.py
@@ -288,9 +288,9 @@
     all_build.extend(self.common_build_options)
     all_build.extend(build)
 
-    version_string = self._GetXcodeVersionString()
-    if version_string and self._NeedsXcodeVersionFlag(version_string):
-      all_build.append('--xcode_version=%s' % version_string)
+    xcode_version_flag = self._ComputeXcodeVersionFlag()
+    if xcode_version_flag:
+      all_build.append('--xcode_version=%s' % xcode_version_flag)
 
     return bazel, start_up, all_build
 
@@ -326,6 +326,11 @@
     return (None, 0)
 
   @staticmethod
+  def _GetXcodeBuildVersionString():
+    """Returns Xcode build version from the environment as a string."""
+    return os.environ['XCODE_PRODUCT_BUILD_VERSION']
+
+  @staticmethod
   def _GetXcodeVersionString():
     """Returns Xcode version info from the environment as a string."""
     reported_version = os.environ['XCODE_VERSION_ACTUAL']
@@ -337,14 +342,11 @@
     major_version = int(match.group(1))
     minor_version = int(match.group(2))
     fix_version = int(match.group(3))
-    fix_version_string = ''
-    if fix_version:
-      fix_version_string = '.%d' % fix_version
-    return '%d.%d%s' % (major_version, minor_version, fix_version_string)
+    return '%d.%d.%d' % (major_version, minor_version, fix_version)
 
   @staticmethod
-  def _NeedsXcodeVersionFlag(xcode_version):
-    """Returns True if the --xcode_version flag should be used for building.
+  def _ComputeXcodeVersionFlag():
+    """Returns a string for the --xcode_version build flag, if any.
 
     The flag should be used if the active Xcode version was not the same one
     used during project generation.
@@ -352,21 +354,31 @@
     Note this a best-attempt only; this may not be accurate as Bazel itself
     caches the active DEVELOPER_DIR path and the user may have changed their
     installed Xcode version.
-
-    Args:
-      xcode_version: active Xcode version string.
     """
-    tulsi_xcode_version = os.environ.get('TULSI_XCODE_VERSION')
-    if not tulsi_xcode_version:
-      return True
+    xcode_version = _OptionsParser._GetXcodeVersionString()
+    build_version = _OptionsParser._GetXcodeBuildVersionString()
 
-    # xcode_version will be of the form Major.Minor(.Fix)? while
-    # TULSI_XCODE_VERSION will be of the form Major.Minor.Fix so we'll need to
-    # remove the trailing .0 if it exists.
-    if tulsi_xcode_version.endswith('.0'):
-      tulsi_xcode_version = tulsi_xcode_version[:-2]
+    if not xcode_version or not build_version:
+      return None
 
-    return xcode_version != tulsi_xcode_version
+    # Of the form Major.Minor.Fix.Build (new Bazel form) or Major.Min.Fix (old).
+    full_bazel_version = os.environ.get('TULSI_XCODE_VERSION')
+    if not full_bazel_version:  # Unexpected: Tulsi gen didn't set the flag.
+      return xcode_version
+
+    # Newer Bazel versions specify the version as Major.Minor.Fix.Build.
+    if full_bazel_version.count('.') == 3:
+      components = full_bazel_version.rsplit('.', 1)
+      bazel_xcode_version = components[0]
+      bazel_build_version = components[1]
+
+      if (xcode_version != bazel_xcode_version
+          or build_version != bazel_build_version):
+        return '{}.{}'.format(xcode_version, build_version)
+      else:
+        return None
+    else:  # Old version of Bazel. We need to use form Major.Minor.Fix.
+      return xcode_version if xcode_version != full_bazel_version else None
 
 
 class BazelBuildBridge(object):
diff --git a/src/TulsiGeneratorIntegrationTests/Resources/GoldenProjects/ComplexSingleProject.xcodeproj/project.pbxproj b/src/TulsiGeneratorIntegrationTests/Resources/GoldenProjects/ComplexSingleProject.xcodeproj/project.pbxproj
index 491c2e6..3f866b3 100644
--- a/src/TulsiGeneratorIntegrationTests/Resources/GoldenProjects/ComplexSingleProject.xcodeproj/project.pbxproj
+++ b/src/TulsiGeneratorIntegrationTests/Resources/GoldenProjects/ComplexSingleProject.xcodeproj/project.pbxproj
@@ -90,7 +90,6 @@
 		25889F7C15C7840200000000 /* NonLocalized.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = NonLocalized.strings; path = "tulsi-workspace/tulsi_e2e_complex/Application/NonLocalized.strings"; sourceTree = "<group>"; };
 		25889F7C160C3D9400000000 /* src1.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = src1.m; path = "tulsi-workspace/tulsi_e2e_complex/LibrarySources/srcs/src1.m"; sourceTree = "<group>"; };
 		25889F7C1ACE4D0400000000 /* NonARCFile.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = NonARCFile.mm; path = "tulsi-workspace/tulsi_e2e_complex/Application/non_arc_srcs/NonARCFile.mm"; sourceTree = "<group>"; };
-		25889F7C1B2B24D300000000 /* structured_resources.file2 */ = {isa = PBXFileReference; lastKnownFileType = dyn.age80q4pqqy3a; name = structured_resources.file2; path = "tulsi-workspace/tulsi_e2e_complex/Application/structured_resources.file2"; sourceTree = "<group>"; };
 		25889F7C245DC24200000000 /* lib_idx_SubLibrary_241BBB47_ios_min10.0.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; name = lib_idx_SubLibrary_241BBB47_ios_min10.0.a; path = lib_idx_SubLibrary_241BBB47_ios_min10.0.a; sourceTree = BUILT_PRODUCTS_DIR; };
 		25889F7C24DA1F1C00000000 /* _TodayExtension-Private-Info.plist */ = {isa = PBXFileReference; explicitFileType = text.plist; name = "_TodayExtension-Private-Info.plist"; path = "tulsi-workspace/_tulsi-includes/x/x/tulsi_e2e_complex/_TodayExtension-Private-Info.plist"; sourceTree = "<group>"; };
 		25889F7C2F57AD1500000000 /* today_extension_library.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = today_extension_library.m; path = "tulsi-workspace/tulsi_e2e_complex/TodayExtension/srcs/today_extension_library.m"; sourceTree = "<group>"; };
@@ -115,7 +114,6 @@
 		25889F7C8DF0400F00000000 /* en */ = {isa = PBXFileReference; lastKnownFileType = text; name = en; path = "tulsi-workspace/tulsi_e2e_complex/Application/en.lproj/EN.strings"; sourceTree = "<group>"; };
 		25889F7C8FC56C5400000000 /* XCTest.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; name = XCTest.xctest; path = XCTest.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		25889F7C99D62A3000000000 /* sub_library_with_identical_defines.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = sub_library_with_identical_defines.m; path = "tulsi-workspace/tulsi_e2e_complex/SubLibraryWithIdenticalDefines/srcs/sub_library_with_identical_defines.m"; sourceTree = "<group>"; };
-		25889F7CA1F2FE8F00000000 /* ObjCBundle.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = ObjCBundle.bundle; path = "tulsi-workspace/tulsi_e2e_complex/ObjCBundle.bundle"; sourceTree = "<group>"; };
 		25889F7CB097D0E400000000 /* input.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = input.m; path = "tulsi-workspace/tulsi_e2e_complex/SrcGenerator/srcs/input.m"; sourceTree = "<group>"; };
 		25889F7CB225790200000000 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = main.m; path = "tulsi-workspace/tulsi_e2e_complex/Application/srcs/main.m"; sourceTree = "<group>"; };
 		25889F7CB778F33400000000 /* src2.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = src2.m; path = "tulsi-workspace/tulsi_e2e_complex/LibrarySources/srcs/src2.m"; sourceTree = "<group>"; };
@@ -126,7 +124,6 @@
 		25889F7CD4FFBE6F00000000 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; name = Info.plist; path = "tulsi-workspace/tulsi_e2e_complex/Application/Info.plist"; sourceTree = "<group>"; };
 		25889F7CD685CDB600000000 /* lib_idx_Library_FAFE9183_ios_min10.0.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; name = lib_idx_Library_FAFE9183_ios_min10.0.a; path = lib_idx_Library_FAFE9183_ios_min10.0.a; sourceTree = BUILT_PRODUCTS_DIR; };
 		25889F7CE0B4DD9E00000000 /* Plist1.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; name = Plist1.plist; path = tulsi_e2e_complex/TodayExtension/Plist1.plist; sourceTree = "<group>"; };
-		25889F7CE377B97800000000 /* structured_resources.file1 */ = {isa = PBXFileReference; lastKnownFileType = dyn.age80q4pqqy2u; name = structured_resources.file1; path = "tulsi-workspace/tulsi_e2e_complex/Application/structured_resources.file1"; sourceTree = "<group>"; };
 		25889F7CE56C768000000000 /* src.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = src.mm; path = "tulsi-workspace/tulsi_e2e_complex/SubLibrary/srcs/src.mm"; sourceTree = "<group>"; };
 		25889F7CE7146E0400000000 /* src.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = src.mm; path = "tulsi-workspace/tulsi_e2e_complex/SubLibraryWithDefines/srcs/src.mm"; sourceTree = "<group>"; };
 		25889F7CEDC9AFE300000000 /* en */ = {isa = PBXFileReference; lastKnownFileType = text; name = en; path = "tulsi-workspace/tulsi_e2e_complex/Application/en.lproj/Localized.strings"; sourceTree = "<group>"; };
@@ -168,8 +165,6 @@
 				25889F7C77D27D9400000000 /* entitlements.entitlements */,
 				45D05629F82F4AD500000000 /* non_arc_srcs */,
 				45D05629CC62AD9600000000 /* srcs */,
-				25889F7CE377B97800000000 /* structured_resources.file1 */,
-				25889F7C1B2B24D300000000 /* structured_resources.file2 */,
 			);
 			name = Application;
 			sourceTree = "<group>";
@@ -251,7 +246,6 @@
 				25889F7CF35FB52600000000 /* ComplexSingle.bzl */,
 				45D0562954A7395500000000 /* Library */,
 				45D05629A302280600000000 /* LibrarySources */,
-				25889F7CA1F2FE8F00000000 /* ObjCBundle.bundle */,
 				45D0562966E9654200000000 /* ObjCFramework */,
 				45D056292699913B00000000 /* SrcGenerator */,
 				45D05629DCA3B6A800000000 /* SubLibrary */,
diff --git a/src/TulsiGeneratorIntegrationTests/Resources/GoldenProjects/WatchProject.xcodeproj/project.pbxproj b/src/TulsiGeneratorIntegrationTests/Resources/GoldenProjects/WatchProject.xcodeproj/project.pbxproj
index 633e100..c0a6be6 100644
--- a/src/TulsiGeneratorIntegrationTests/Resources/GoldenProjects/WatchProject.xcodeproj/project.pbxproj
+++ b/src/TulsiGeneratorIntegrationTests/Resources/GoldenProjects/WatchProject.xcodeproj/project.pbxproj
@@ -34,13 +34,11 @@
 		25889F7C209FA0FE00000000 /* WatchExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; name = WatchExtension.appex; path = WatchExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
 		25889F7C295E8D8E00000000 /* watch2_extension_binary.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = watch2_extension_binary.m; path = "tulsi-workspace/tulsi_e2e_watch/Watch2ExtensionBinary/srcs/watch2_extension_binary.m"; sourceTree = "<group>"; };
 		25889F7C35DA0F1D00000000 /* ext_entitlements.entitlements */ = {isa = PBXFileReference; lastKnownFileType = "com.apple.xcode.entitlements-property-list"; name = ext_entitlements.entitlements; path = "tulsi-workspace/tulsi_e2e_watch/Watch2Extension/ext_entitlements.entitlements"; sourceTree = "<group>"; };
-		25889F7C3820488800000000 /* ext_structured_resources.file */ = {isa = PBXFileReference; lastKnownFileType = dyn.age80q4pqqy; name = ext_structured_resources.file; path = "tulsi-workspace/tulsi_e2e_watch/Watch2Extension/ext_structured_resources.file"; sourceTree = "<group>"; };
 		25889F7C4DAFC77200000000 /* WatchApplication.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; name = WatchApplication.app; path = WatchApplication.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		25889F7C623F952E00000000 /* _Application-Private-Info.plist */ = {isa = PBXFileReference; explicitFileType = text.plist; name = "_Application-Private-Info.plist"; path = "tulsi-workspace/_tulsi-includes/x/x/tulsi_e2e_watch/_Application-Private-Info.plist"; sourceTree = "<group>"; };
 		25889F7C69FDDDAB00000000 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; name = Info.plist; path = "tulsi-workspace/tulsi_e2e_watch/Watch2Extension/ext_infoplists/Info.plist"; sourceTree = "<group>"; };
 		25889F7C6CA77FEE00000000 /* entitlements.entitlements */ = {isa = PBXFileReference; lastKnownFileType = "com.apple.xcode.entitlements-property-list"; name = entitlements.entitlements; path = "tulsi-workspace/tulsi_e2e_watch/Application/entitlements.entitlements"; sourceTree = "<group>"; };
 		25889F7C6EE78B0C00000000 /* lib_idx_WatchExtensionLibrary_2A5269B8_watchos_min3.0.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; name = lib_idx_WatchExtensionLibrary_2A5269B8_watchos_min3.0.a; path = lib_idx_WatchExtensionLibrary_2A5269B8_watchos_min3.0.a; sourceTree = BUILT_PRODUCTS_DIR; };
-		25889F7C8BCA626C00000000 /* structured_resources.file1 */ = {isa = PBXFileReference; lastKnownFileType = dyn.age80q4pqqy2u; name = structured_resources.file1; path = "tulsi-workspace/tulsi_e2e_watch/Application/structured_resources.file1"; sourceTree = "<group>"; };
 		25889F7C97FBB66F00000000 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; name = Info.plist; path = "tulsi-workspace/tulsi_e2e_watch/Watch2Extension/app_infoplists/Info.plist"; sourceTree = "<group>"; };
 		25889F7C9FFA61B100000000 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = main.m; path = "tulsi-workspace/tulsi_e2e_watch/Library/srcs/main.m"; sourceTree = "<group>"; };
 		25889F7CCCCE004E00000000 /* Application.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; name = Application.app; path = Application.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -75,7 +73,6 @@
 			children = (
 				25889F7C11BBAF6C00000000 /* Info.plist */,
 				25889F7C6CA77FEE00000000 /* entitlements.entitlements */,
-				25889F7C8BCA626C00000000 /* structured_resources.file1 */,
 			);
 			name = Application;
 			sourceTree = "<group>";
@@ -120,7 +117,6 @@
 				25889F7C35DA0F1D00000000 /* ext_entitlements.entitlements */,
 				45D056291F7B9D7400000000 /* ext_infoplists */,
 				25889F7C0E6614C000000000 /* ext_resources.file */,
-				25889F7C3820488800000000 /* ext_structured_resources.file */,
 			);
 			name = Watch2Extension;
 			sourceTree = "<group>";