Add project settings to help caching

Adds the following new project settings:
- compilation mode (dbg or opt)
- target config (i.e. ios/macos/tvos/watchos <cpu>)

You'll want to set these project settings for the expected
configuration for your project. e.g.

- ios_x86_64 for iOS 64-bit simulator builds
- ios_arm64 for iOS 64-bit device builds
- macos_x86_64 for macOS projects

- dbg compilation mode for Debug builds
- opt compilation mode for Release builds

PiperOrigin-RevId: 204796814
diff --git a/src/Tulsi.xcodeproj/project.pbxproj b/src/Tulsi.xcodeproj/project.pbxproj
index 8f59a39..9a18e8a 100644
--- a/src/Tulsi.xcodeproj/project.pbxproj
+++ b/src/Tulsi.xcodeproj/project.pbxproj
@@ -118,7 +118,6 @@
 		54EC201820D1A8270050AF12 /* TulsiApplicationSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54EC201720D1A8270050AF12 /* TulsiApplicationSupport.swift */; };
 		54EDD24520D9BC27001A1B35 /* BuildSettingsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54EDD24420D9BC26001A1B35 /* BuildSettingsTests.swift */; };
 		54EF320A1F3E0804009E9C7F /* bazel_build_events.py in Resources */ = {isa = PBXBuildFile; fileRef = 54EF32091F3E0804009E9C7F /* bazel_build_events.py */; };
-		774F6E9720A2400E00572B76 /* bazel_build_flags.py in Resources */ = {isa = PBXBuildFile; fileRef = 774F6E9620A2400E00572B76 /* bazel_build_flags.py */; };
 		8B0F78C81BE5BC7E00357561 /* ConfigEditorSourceFilterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B0F78C71BE5BC7E00357561 /* ConfigEditorSourceFilterViewController.swift */; };
 		8B29E2D01BF9386200680E11 /* TulsiProjectDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B29E2CF1BF9386200680E11 /* TulsiProjectDocument.swift */; };
 		8B8F559B1BE3ECDC0095AF7F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B8F559A1BE3ECDC0095AF7F /* AppDelegate.swift */; };
@@ -305,7 +304,6 @@
 		54EC201720D1A8270050AF12 /* TulsiApplicationSupport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TulsiApplicationSupport.swift; sourceTree = "<group>"; };
 		54EDD24420D9BC26001A1B35 /* BuildSettingsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BuildSettingsTests.swift; sourceTree = "<group>"; };
 		54EF32091F3E0804009E9C7F /* bazel_build_events.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = bazel_build_events.py; sourceTree = "<group>"; };
-		774F6E9620A2400E00572B76 /* bazel_build_flags.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; name = bazel_build_flags.py; path = TulsiGenerator/Scripts/bazel_build_flags.py; sourceTree = "<group>"; };
 		8B0F78C71BE5BC7E00357561 /* ConfigEditorSourceFilterViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigEditorSourceFilterViewController.swift; sourceTree = "<group>"; };
 		8B29E2CF1BF9386200680E11 /* TulsiProjectDocument.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TulsiProjectDocument.swift; sourceTree = "<group>"; };
 		8B8F1B111BF6BDD50008013B /* BuildLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BuildLabel.swift; sourceTree = "<group>"; };
@@ -557,7 +555,6 @@
 		8B8F558E1BE3ECDC0095AF7F = {
 			isa = PBXGroup;
 			children = (
-				774F6E9620A2400E00572B76 /* bazel_build_flags.py */,
 				E1D770E520523E780026802A /* bazel_cache_reader.xcodeproj */,
 				8B8F55981BE3ECDC0095AF7F /* Products */,
 				8B8F55991BE3ECDC0095AF7F /* Tulsi */,
@@ -778,7 +775,6 @@
 				E1C0186D2051B65D000580CC /* clean_symbol_cache.py in Resources */,
 				2D9DB34A1E5DECA40021EAF4 /* iOSXCTRunner.entitlements in Resources */,
 				3D329D0E1C4831EF00DFBD0F /* bazel_build.py in Resources */,
-				774F6E9720A2400E00572B76 /* bazel_build_flags.py in Resources */,
 				D33C204F1EC108CC00867450 /* tulsi_logging.py in Resources */,
 				54EF320A1F3E0804009E9C7F /* bazel_build_events.py in Resources */,
 				D3F78C681F391E9700AE0571 /* bazel_options.py in Resources */,
diff --git a/src/Tulsi/TulsiGeneratorConfigDocument.swift b/src/Tulsi/TulsiGeneratorConfigDocument.swift
index 2f78247..0bece77 100644
--- a/src/Tulsi/TulsiGeneratorConfigDocument.swift
+++ b/src/Tulsi/TulsiGeneratorConfigDocument.swift
@@ -469,12 +469,14 @@
       do {
         let startupOptions = optionSet[.BazelBuildStartupOptionsDebug]
         let buildOptions = optionSet[.BazelBuildOptionsDebug]
-        let projectGenBuildOptions = optionSet[.BazelBuildOptionsProjectGenerationOnly]
+        let compilationModeOption = optionSet[.ProjectGenerationCompilationMode]
+        let platformConfigOption = optionSet[.ProjectGenerationPlatformConfiguration]
         let prioritizeSwiftOption = optionSet[.ProjectPrioritizesSwift]
         ruleEntryMap = try self.infoExtractor.ruleEntriesForLabels(selectedLabels,
                                                                    startupOptions: startupOptions,
                                                                    buildOptions: buildOptions,
-                                                                   projectGenBuildOptions: projectGenBuildOptions,
+                                                                   compilationModeOption: compilationModeOption,
+                                                                   platformConfigOption: platformConfigOption,
                                                                    prioritizeSwiftOption: prioritizeSwiftOption,
                                                                    features: self.enabledFeatures(options: optionSet))
       } catch TulsiProjectInfoExtractor.ExtractorError.ruleEntriesFailed(let info) {
@@ -800,7 +802,8 @@
     let ruleEntryMap = try infoExtractor.ruleEntriesForLabels(concreteBuildTargetLabels,
                                                               startupOptions: options[.BazelBuildStartupOptionsDebug],
                                                               buildOptions: options[.BazelBuildOptionsDebug],
-                                                              projectGenBuildOptions: options[.BazelBuildOptionsProjectGenerationOnly],
+                                                              compilationModeOption: options[.ProjectGenerationCompilationMode],
+                                                              platformConfigOption: options[.ProjectGenerationPlatformConfiguration],
                                                               prioritizeSwiftOption: options[.ProjectPrioritizesSwift],
                                                               features: enabledFeatures(options: options))
     var unresolvedLabels = Set<BuildLabel>()
diff --git a/src/TulsiGenerator/BazelAspectInfoExtractor.swift b/src/TulsiGenerator/BazelAspectInfoExtractor.swift
index d253ab2..ada235f 100644
--- a/src/TulsiGenerator/BazelAspectInfoExtractor.swift
+++ b/src/TulsiGenerator/BazelAspectInfoExtractor.swift
@@ -66,8 +66,9 @@
   func extractRuleEntriesForLabels(_ targets: [BuildLabel],
                                    startupOptions: [String] = [],
                                    buildOptions: [String] = [],
-                                   projectGenerationOptions: [String] = [],
-                                   prioritizeSwift: Bool = false,
+                                   compilationMode: String? = nil,
+                                   platformConfig: String? = nil,
+                                   prioritizeSwift: Bool? = nil,
                                    features: Set<BazelSettingFeature> = []) throws -> RuleEntryMap {
     guard !targets.isEmpty else {
       return RuleEntryMap()
@@ -76,7 +77,8 @@
     return try extractRuleEntriesUsingBEP(targets,
                                           startupOptions: startupOptions,
                                           buildOptions: buildOptions,
-                                          projectGenerationOptions: projectGenerationOptions,
+                                          compilationMode: compilationMode,
+                                          platformConfig: platformConfig,
                                           prioritizeSwift: prioritizeSwift,
                                           features: features)
   }
@@ -86,8 +88,9 @@
   private func extractRuleEntriesUsingBEP(_ targets: [BuildLabel],
                                           startupOptions: [String],
                                           buildOptions: [String],
-                                          projectGenerationOptions: [String],
-                                          prioritizeSwift: Bool,
+                                          compilationMode: String?,
+                                          platformConfig: String?,
+                                          prioritizeSwift: Bool?,
                                           features: Set<BazelSettingFeature>) throws -> RuleEntryMap {
     localizedMessageLogger.infoMessage("Build Events JSON file at \"\(buildEventsFilePath)\"")
 
@@ -106,7 +109,8 @@
                                                aspect: "tulsi_sources_aspect",
                                                startupOptions: startupOptions,
                                                buildOptions: buildOptions,
-                                               projectGenerationOptions: projectGenerationOptions,
+                                               compilationMode: compilationMode,
+                                               platformConfig: platformConfig,
                                                prioritizeSwift: prioritizeSwift,
                                                features: features,
                                                progressNotifier: progressNotifier) {
@@ -164,8 +168,9 @@
                                             aspect: String,
                                             startupOptions: [String] = [],
                                             buildOptions: [String] = [],
-                                            projectGenerationOptions: [String] = [],
-                                            prioritizeSwift: Bool,
+                                            compilationMode: String?,
+                                            platformConfig: String?,
+                                            prioritizeSwift: Bool?,
                                             features: Set<BazelSettingFeature>,
                                             progressNotifier: ProgressNotifier? = nil,
                                             terminationHandler: @escaping CompletionHandler) -> Process? {
@@ -182,8 +187,19 @@
 
     let tulsiVersion = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "UNKNOWN"
 
-    let tulsiFlags = bazelSettingsProvider.tulsiFlags(hasSwift: prioritizeSwift,
-                                                      features: features).getFlags()
+    let hasSwift = prioritizeSwift ?? false
+    let isDbg = (compilationMode ?? "dbg") == "dbg"
+
+    let config: PlatformConfiguration
+    if let identifier = platformConfig,
+       let parsedConfig = PlatformConfiguration(identifier: identifier) {
+      config = parsedConfig
+    } else {
+      config = PlatformConfiguration.defaultConfiguration
+    }
+
+    let tulsiFlags = bazelSettingsProvider.tulsiFlags(hasSwift: hasSwift,
+                                                      features: features).getFlags(forDebug: isDbg)
     var arguments = startupOptions
     arguments.append(contentsOf: tulsiFlags.startup)
     arguments.append("build")
@@ -196,8 +212,8 @@
         "--noshow_progress",  // Don't show Bazel's build progress.
         "--symlink_prefix=/",  // Generate artifacts without overwriting the normal build symlinks.
     ])
-    arguments.append(contentsOf: projectGenerationOptions)
     arguments.append(contentsOf: buildOptions)
+    arguments.append(contentsOf: config.bazelFlags)
     arguments.append(contentsOf: tulsiFlags.build)
     arguments.append(contentsOf: [
         // The following flags are used by Tulsi to identify itself and read build information from
diff --git a/src/TulsiGenerator/BazelBuildSettings.swift b/src/TulsiGenerator/BazelBuildSettings.swift
index 47e7d21..87f54b5 100644
--- a/src/TulsiGenerator/BazelBuildSettings.swift
+++ b/src/TulsiGenerator/BazelBuildSettings.swift
@@ -26,10 +26,29 @@
   func toPython(_ indentation: String) -> String {
     guard !isEmpty else { return "{}" }
 
+    let nestedIndentation = "\(indentation)\(PythonSettings.doubleIndent)"
     var script = "{\n"
     for (key, value) in self {
       script += """
-\(indentation)\(PythonSettings.doubleIndent)'\(key)': \(value.toPython("")),
+\(nestedIndentation)\(key.toPython(nestedIndentation)): \(value.toPython(nestedIndentation)),
+
+"""
+    }
+    script += "\(indentation)}"
+    return script
+  }
+}
+
+// TODO(tulsi-team): When we update to Swift 4.2, delete this in favor of conditional conformances.
+extension Dictionary where Key == String, Value == [String] {
+  func toPython(_ indentation: String) -> String {
+    guard !isEmpty else { return "{}" }
+
+    let nestedIndentation = "\(indentation)\(PythonSettings.doubleIndent)"
+    var script = "{\n"
+    for (key, value) in self {
+      script += """
+\(nestedIndentation)\(key.toPython(nestedIndentation)): \(value.toPython(nestedIndentation)),
 
 """
     }
@@ -179,6 +198,9 @@
   public let bazel: String
   public let bazelExecRoot: String
 
+  public let defaultPlatformConfigIdentifier: String
+  public let platformConfigurationFlags: [String: [String]]
+
   public let tulsiCacheAffectingFlagsSet: BazelFlagsSet
   public let tulsiCacheSafeFlagSet: BazelFlagsSet
 
@@ -194,8 +216,16 @@
   public let projDefaultFlagSet: BazelFlagsSet
   public let projTargetFlagSets: [String: BazelFlagsSet]
 
+  public static var platformConfigurationFlagsMap: [String: [String]] {
+    return PlatformConfiguration.allValidConfigurations.reduce(into: [String: [String]]()) { (dict, config) in
+      dict[config.identifier] = config.bazelFlags
+    }
+  }
+
   public init(bazel: String,
               bazelExecRoot: String,
+              defaultPlatformConfigIdentifier: String,
+              platformConfigurationFlags: [String: [String]]?,
               swiftTargets: Set<String>,
               tulsiCacheAffectingFlagsSet: BazelFlagsSet,
               tulsiCacheSafeFlagSet: BazelFlagsSet,
@@ -207,6 +237,8 @@
               projTargetFlagSets: [String: BazelFlagsSet]) {
     self.bazel = bazel
     self.bazelExecRoot = bazelExecRoot
+    self.defaultPlatformConfigIdentifier = defaultPlatformConfigIdentifier
+    self.platformConfigurationFlags = platformConfigurationFlags ?? BazelBuildSettings.platformConfigurationFlagsMap
     self.swiftTargets = swiftTargets
     self.tulsiCacheAffectingFlagsSet = tulsiCacheAffectingFlagsSet
     self.tulsiCacheSafeFlagSet = tulsiCacheSafeFlagSet
@@ -224,6 +256,8 @@
 BazelBuildSettings(
 \(nestedIndentation)\(bazel.toPython(nestedIndentation)),
 \(nestedIndentation)\(bazelExecRoot.toPython(nestedIndentation)),
+\(nestedIndentation)\(defaultPlatformConfigIdentifier.toPython(nestedIndentation)),
+\(nestedIndentation)\(platformConfigurationFlags.toPython(nestedIndentation)),
 \(nestedIndentation)\(swiftTargets.toPython(nestedIndentation)),
 \(nestedIndentation)\(tulsiCacheAffectingFlagsSet.toPython(nestedIndentation)),
 \(nestedIndentation)\(tulsiCacheSafeFlagSet.toPython(nestedIndentation)),
diff --git a/src/TulsiGenerator/BazelBuildSettingsFeatures.swift b/src/TulsiGenerator/BazelBuildSettingsFeatures.swift
index 7b405f0..2ae07ba 100644
--- a/src/TulsiGenerator/BazelBuildSettingsFeatures.swift
+++ b/src/TulsiGenerator/BazelBuildSettingsFeatures.swift
@@ -12,6 +12,38 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+/// Returns the configuration flags required to build a Bazel target and all of its dependencies
+/// with the specified PlatformType and AppleCPU.
+///
+///  - Every platform has the --apple_platform_type flag set
+///  - macOS and iOS use the base --cpu flag, while tvOS and watchOS use the  --tvos_cpus and
+///    --watchos_cpus respectively
+///  - iOS also sets the --watchos_cpus flag (as it can contain a watchOS app embedded)
+extension PlatformConfiguration {
+  public var bazelFlags: [String] {
+    let cpuStr = cpu.rawValue
+    var flags = ["--apple_platform_type=\(platform.bazelPlatform)"]
+
+    switch platform {
+    case .ios:
+      fallthrough
+    case .macos:
+      flags.append("--cpu=\(platform.bazelCPUPlatform)_\(cpuStr)")
+    case .tvos:
+      fallthrough
+    case .watchos:
+      flags.append("--\(platform.bazelCPUPlatform)_cpus=\(cpuStr)")
+    }
+
+    if case .ios = platform {
+      let watchCPU: CPU = cpu.isARM ? .armv7k : .i386
+      flags.append("--\(PlatformType.watchos.bazelCPUPlatform)_cpus=\(watchCPU.rawValue)")
+    }
+
+    return flags
+  }
+}
+
 public class BazelBuildSettingsFeatures {
   public static func enabledFeatures(
       options: TulsiOptionSet,
diff --git a/src/TulsiGenerator/BazelSettingsProvider.swift b/src/TulsiGenerator/BazelSettingsProvider.swift
index 2bb3d61..fcfdac0 100644
--- a/src/TulsiGenerator/BazelSettingsProvider.swift
+++ b/src/TulsiGenerator/BazelSettingsProvider.swift
@@ -244,8 +244,18 @@
     let swiftFeatures = featureNames(features, hasSwift: true)
     let nonSwiftFeatures = featureNames(features, hasSwift: false)
 
+    let defaultConfig: PlatformConfiguration
+    if let identifier = options[.ProjectGenerationPlatformConfiguration].commonValue,
+       let parsedConfig = PlatformConfiguration(identifier: identifier) {
+      defaultConfig = parsedConfig
+    } else {
+      defaultConfig = PlatformConfiguration.defaultConfiguration
+    }
+
     return BazelBuildSettings(bazel: bazel,
                               bazelExecRoot: bazelExecRoot,
+                              defaultPlatformConfigIdentifier: defaultConfig.identifier,
+                              platformConfigurationFlags: nil,
                               swiftTargets: swiftTargets,
                               tulsiCacheAffectingFlagsSet: BazelFlagsSet(common: universalFlags) + nonCacheableFlags,
                               tulsiCacheSafeFlagSet: cacheableFlags + optionsBasedFlags(options),
diff --git a/src/TulsiGenerator/BazelWorkspaceInfoExtractor.swift b/src/TulsiGenerator/BazelWorkspaceInfoExtractor.swift
index 29623a3..feabb33 100644
--- a/src/TulsiGenerator/BazelWorkspaceInfoExtractor.swift
+++ b/src/TulsiGenerator/BazelWorkspaceInfoExtractor.swift
@@ -89,7 +89,8 @@
   func ruleEntriesForLabels(_ labels: [BuildLabel],
                             startupOptions: TulsiOption,
                             buildOptions: TulsiOption,
-                            projectGenBuildOptions: TulsiOption,
+                            compilationModeOption: TulsiOption,
+                            platformConfigOption: TulsiOption,
                             prioritizeSwiftOption: TulsiOption,
                             features: Set<BazelSettingFeature>) throws -> RuleEntryMap {
     func isLabelMissing(_ label: BuildLabel) -> Bool {
@@ -106,16 +107,17 @@
 
     let startupOptions = splitOptionString(startupOptions.commonValue)
     let buildOptions = splitOptionString(buildOptions.commonValue)
-    let projectGenerationOptions = splitOptionString(projectGenBuildOptions.commonValue)
-
-    let prioritizeSwift = prioritizeSwiftOption.commonValueAsBool ?? false
+    let compilationMode = compilationModeOption.commonValue
+    let platformConfig = platformConfigOption.commonValue
+    let prioritizeSwift = prioritizeSwiftOption.commonValueAsBool
 
     do {
       let ruleEntryMap =
         try aspectExtractor.extractRuleEntriesForLabels(labels,
                                                         startupOptions: startupOptions,
                                                         buildOptions: buildOptions,
-                                                        projectGenerationOptions: projectGenerationOptions,
+                                                        compilationMode: compilationMode,
+                                                        platformConfig: platformConfig,
                                                         prioritizeSwift: prioritizeSwift,
                                                         features: features)
       ruleEntryCache = RuleEntryMap(ruleEntryMap)
diff --git a/src/TulsiGenerator/BazelWorkspaceInfoExtractorProtocol.swift b/src/TulsiGenerator/BazelWorkspaceInfoExtractorProtocol.swift
index 04233ab..1c789f6 100644
--- a/src/TulsiGenerator/BazelWorkspaceInfoExtractorProtocol.swift
+++ b/src/TulsiGenerator/BazelWorkspaceInfoExtractorProtocol.swift
@@ -29,7 +29,8 @@
   func ruleEntriesForLabels(_ labels: [BuildLabel],
                             startupOptions: TulsiOption,
                             buildOptions: TulsiOption,
-                            projectGenBuildOptions: TulsiOption,
+                            compilationModeOption: TulsiOption,
+                            platformConfigOption: TulsiOption,
                             prioritizeSwiftOption: TulsiOption,
                             features: Set<BazelSettingFeature>) throws -> RuleEntryMap
 
diff --git a/src/TulsiGenerator/DeploymentTarget.swift b/src/TulsiGenerator/DeploymentTarget.swift
index 77c10fa..5720762 100644
--- a/src/TulsiGenerator/DeploymentTarget.swift
+++ b/src/TulsiGenerator/DeploymentTarget.swift
@@ -14,6 +14,74 @@
 
 import Foundation
 
+/// Valid CPU types (for rules_apple Bazel targets).
+public enum CPU: String {
+  case i386
+  case x86_64
+  case armv7
+  case armv7k
+  case arm64
+
+  public static let allCases: [CPU] = [.i386, .x86_64, .armv7, .armv7k, .arm64]
+
+  var isARM: Bool {
+    switch self {
+    case .i386: return false
+    case .x86_64: return false
+    case .armv7: return true
+    case .armv7k: return true
+    case .arm64: return true
+    }
+  }
+
+  var watchCPU: CPU {
+    return isARM ? .i386 : .armv7k
+  }
+}
+
+/// Represents a (PlatformType, AppleCPU) pair.
+public struct PlatformConfiguration {
+
+  public let platform: PlatformType
+  public let cpu: CPU
+
+  /// Default to iOS 64-bit simulator.
+  public static let defaultConfiguration = PlatformConfiguration(platform: .ios, cpu: .x86_64)
+
+  /// Returns all valid PlatformConfiguration identifiers.
+  public static var allValidConfigurations: [PlatformConfiguration] {
+    var platforms = [PlatformConfiguration]()
+    for platformType in PlatformType.allCases {
+      for cpu in platformType.validCPUs {
+        platforms.append(PlatformConfiguration(platform: platformType, cpu: cpu))
+      }
+    }
+    return platforms
+  }
+
+  public init(platform: PlatformType, cpu: CPU) {
+    self.platform = platform
+    self.cpu = cpu
+  }
+
+  /// Initialize based on an identifier; will only succeed if the identifier is present in
+  /// PlatformConfiguration.allPlatformCPUIdentifiers (which checks for combination validity).
+  public init?(identifier: String) {
+    for validConfiguration in PlatformConfiguration.allValidConfigurations {
+      if validConfiguration.identifier == identifier {
+        self.platform = validConfiguration.platform
+        self.cpu = validConfiguration.cpu
+        return
+      }
+    }
+    return nil
+  }
+
+  /// Human readable identifier for this (PlatformType, CPU) pair.
+  var identifier: String {
+    return "\(platform.bazelPlatform)_\(cpu.rawValue)"
+  }
+}
 
 /// Valid Apple Platform Types.
 /// See https://docs.bazel.build/versions/master/skylark/lib/apple_common.html#platform_type
@@ -23,6 +91,28 @@
   case tvos
   case watchos
 
+  public static let allCases: [PlatformType] = [.ios, .macos, .tvos, .watchos]
+
+  var validCPUs: Set<CPU> {
+    switch self {
+    case .ios: return [.i386, .x86_64, .armv7, .arm64]
+    case .macos: return  [.x86_64]
+    case .tvos: return [.x86_64, .arm64]
+    case .watchos: return [.i386, .armv7k]
+    }
+  }
+
+  var bazelCPUPlatform: String {
+    switch self {
+    case .macos: return "darwin"
+    default: return bazelPlatform
+    }
+  }
+
+  var bazelPlatform: String {
+    return rawValue
+  }
+
   var buildSettingsDeploymentTarget: String {
     switch self {
     case .ios: return "IPHONEOS_DEPLOYMENT_TARGET"
diff --git a/src/TulsiGenerator/Scripts/bazel_build.py b/src/TulsiGenerator/Scripts/bazel_build.py
index d3c1beb..b8bc396 100755
--- a/src/TulsiGenerator/Scripts/bazel_build.py
+++ b/src/TulsiGenerator/Scripts/bazel_build.py
@@ -34,7 +34,6 @@
 
 from apfs_clone_copy import CopyOnWrite
 import bazel_build_events
-from bazel_build_flags import bazel_build_flags
 import bazel_build_settings
 import bazel_options
 from bootstrap_lldbinit import BootstrapLLDBInit
@@ -210,13 +209,18 @@
     elif self.platform_name.startswith('iphone'):
       config_platform = 'ios'
     elif self.platform_name.startswith('macos'):
-      config_platform = 'darwin'
+      config_platform = 'macos'
     elif self.platform_name.startswith('appletv'):
       config_platform = 'tvos'
     else:
       self._WarnUnknownPlatform()
       config_platform = 'ios'
-    self.common_build_options.extend(bazel_build_flags(config_platform, arch))
+    self.bazel_build_config = '{}_{}'.format(config_platform, arch)
+    if self.bazel_build_config not in build_settings.platformConfigFlags:
+      _PrintXcodeError('Unknown active compilation target of "{}". '
+                       'Please report a Tulsi bug.'
+                       .format(self.bazel_build_config))
+      sys.exit(1)
 
     self.verbose = 0
     self.bazel_bin_path = 'bazel-bin'
@@ -255,7 +259,8 @@
     is_debug = config == 'Debug'
     return self.build_settings.flags_for_target(
         self.targets[0],
-        is_debug)
+        is_debug,
+        self.bazel_build_config)
 
   def GetEnabledFeatures(self):
     """Returns a list of enabled Bazel features for the active target."""
diff --git a/src/TulsiGenerator/Scripts/bazel_build_flags.py b/src/TulsiGenerator/Scripts/bazel_build_flags.py
deleted file mode 100644
index e98a8bb..0000000
--- a/src/TulsiGenerator/Scripts/bazel_build_flags.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright 2016 The Tulsi Authors. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""Bazel flags for architecture / platform combinations.
-
-Every platform has the --apple_platform_type flag set (macOS uses 'darwin'
-instead of 'macos').
-
-macOS and iOS use the base --cpu flag, while tvOS and watchOS use --tvos_cpus
-and --watchos_cpus respectively.
-
-For iOS apps, the --watchos_cpus flag is also set separately.
-"""
-
-
-def bazel_build_flags(config_platform, arch):
-  """Returns an array of command line flags for bazel."""
-  if config_platform == 'darwin':
-    options = ['--apple_platform_type=macos']
-  else:
-    options = ['--apple_platform_type=%s' % config_platform]
-  if config_platform in ['ios', 'darwin']:
-    options.append('--cpu=%s_%s' % (config_platform, arch))
-  else:
-    options.append('--%scpus=%s' % (config_platform, arch))
-  # Set watchos_cpus for bundled watch apps.
-  if config_platform == 'ios':
-    if arch.startswith('arm'):
-      options.append('--watchos_cpus=armv7k')
-    else:
-      options.append('--watchos_cpus=i386')
-
-  return options
diff --git a/src/TulsiGenerator/Scripts/bazel_build_settings.py.template b/src/TulsiGenerator/Scripts/bazel_build_settings.py.template
index 7536d0b..fc9185b 100644
--- a/src/TulsiGenerator/Scripts/bazel_build_settings.py.template
+++ b/src/TulsiGenerator/Scripts/bazel_build_settings.py.template
@@ -60,13 +60,17 @@
 class BazelBuildSettings(object):
   """Represents a Tulsi project's Bazel settings."""
 
-  def __init__(self, bazel, bazelExecRoot, swiftTargets,
+  def __init__(self, bazel, bazelExecRoot,
+               defaultPlatformConfigId, platformConfigFlags,
+               swiftTargets,
                cacheAffecting, cacheSafe,
                swiftOnly, nonSwiftOnly,
                swiftFeatures, nonSwiftFeatures,
                projDefault, projTargetMap):
     self.bazel = bazel
     self.bazelExecRoot = bazelExecRoot
+    self.defaultPlatformConfigId = defaultPlatformConfigId
+    self.platformConfigFlags = platformConfigFlags
     self.swiftTargets = swiftTargets
     self.cacheAffecting = cacheAffecting
     self.cacheSafe = cacheSafe
@@ -87,7 +91,8 @@
 
     return self.swiftFeatures if is_swift else self.nonSwiftFeatures
 
-  def flags_for_target(self, target, is_debug, is_swift_override=None):
+  def flags_for_target(self, target, is_debug,
+                       config, is_swift_override=None):
     """Returns (bazel, startup flags, build flags) for the given target."""
 
     target = _StandardizeTargetLabel(target)
@@ -100,6 +105,7 @@
       is_swift = is_swift_override
     lang = self.swiftOnly if is_swift else self.nonSwiftOnly
 
+    config_flags = self.platformConfigFlags[config]
     cache_affecting = self.cacheAffecting.flags(is_debug)
     cache_safe = self.cacheSafe.flags(is_debug)
     target = target_flag_set.flags(is_debug)
@@ -113,6 +119,7 @@
 
     buildFlags = []
     buildFlags.extend(target.build)
+    buildFlags.extend(config_flags)
     buildFlags.extend(cache_safe.build)
     buildFlags.extend(cache_affecting.build)
     buildFlags.extend(lang.build)
diff --git a/src/TulsiGenerator/Scripts/user_build.py b/src/TulsiGenerator/Scripts/user_build.py
index 244f7f4..01b201c 100755
--- a/src/TulsiGenerator/Scripts/user_build.py
+++ b/src/TulsiGenerator/Scripts/user_build.py
@@ -39,15 +39,13 @@
   """Creates a Bazel command for targets with the specified settings."""
   target = _BuildSettingsTargetForTargets(targets)
   bazel, startup, flags = build_settings.flags_for_target(
-      target, not release, is_swift_override=force_swift)
+      target, not release, config, is_swift_override=force_swift)
   bazel_action = 'test' if test else 'build'
 
   command = [bazel]
   command.extend(startup)
   command.append(bazel_action)
   command.extend(flags)
-  if config:
-    command.append('--config=%s' % config)
   if xcode_version:
     command.append('--xcode_version=%s' % xcode_version)
   command.extend(targets)
@@ -72,6 +70,11 @@
   if not BUILD_SETTINGS:
     _FatalError('Unable to fetch build settings. Please report a Tulsi bug.')
 
+  default_config = BUILD_SETTINGS.defaultPlatformConfigId
+  config_options = BUILD_SETTINGS.platformConfigFlags
+  config_help = (
+      'Bazel apple config (used for flags). Default: {}').format(default_config)
+
   parser = argparse.ArgumentParser(description='Invoke a Bazel build or test '
                                                'with the same flags as Tulsi.')
   parser.add_argument('--test', dest='test', action='store_true', default=False)
@@ -80,7 +83,8 @@
   parser.add_argument('--noprint_cmd', dest='print_cmd', action='store_false',
                       default=True)
   parser.add_argument('--norun', dest='run', action='store_false', default=True)
-  parser.add_argument('--config', help='Bazel --config flag.')
+  parser.add_argument('--config', help=config_help, default=default_config,
+                      choices=config_options)
   parser.add_argument('--xcode_version', help='Bazel --xcode_version flag.')
   parser.add_argument('--force_swift', dest='swift', action='store_true',
                       default=None, help='Forcibly treat the given targets '
diff --git a/src/TulsiGenerator/TulsiOptionSet.swift b/src/TulsiGenerator/TulsiOptionSet.swift
index 9be92b9..c921570 100644
--- a/src/TulsiGenerator/TulsiOptionSet.swift
+++ b/src/TulsiGenerator/TulsiOptionSet.swift
@@ -43,9 +43,11 @@
       // Include all .bzl files related to the build in the generated Xcodeproj.
       IncludeBuildSources,
 
-      // Include the following build options only during project generation; can override config
-      // from default (simulator) to device.
-      BazelBuildOptionsProjectGenerationOnly,
+      // Compilation mode to use during project generation.
+      ProjectGenerationCompilationMode,
+
+      // Platform configuration to use during project generation.
+      ProjectGenerationPlatformConfiguration,
 
       // Improve auto-completion for include/import statements.
       ImprovedImportAutocompletionFix,
@@ -289,7 +291,6 @@
 
     addBoolOption(.ALWAYS_SEARCH_USER_PATHS, .BuildSetting, false)
     addBoolOption(.BazelContinueBuildingAfterError, .Generic, false)
-    addStringOption(.BazelBuildOptionsProjectGenerationOnly, .Generic)
     addStringOption(.BazelBuildOptionsDebug, [.TargetSpecializable, .SupportsInheritKeyword])
     addStringOption(.BazelBuildOptionsRelease, [.TargetSpecializable, .SupportsInheritKeyword])
     addStringOption(.BazelBuildStartupOptionsDebug, [.TargetSpecializable, .SupportsInheritKeyword])
@@ -300,6 +301,12 @@
     addBoolOption(.GenerateRunfiles, .Generic, false)
     addBoolOption(.ProjectPrioritizesSwift, .Generic, false)
 
+    let defaultIdentifier = PlatformConfiguration.defaultConfiguration.identifier
+    let platformCPUIdentifiers = PlatformConfiguration.allValidConfigurations.map { $0.identifier }
+    addStringEnumOption(.ProjectGenerationPlatformConfiguration, .Generic,
+                        defaultIdentifier, platformCPUIdentifiers)
+    addStringEnumOption(.ProjectGenerationCompilationMode, .Generic, "dbg", ["dbg", "opt"])
+
     addStringOption(.CommandlineArguments, [.TargetSpecializable, .SupportsInheritKeyword])
     addStringOption(.EnvironmentVariables, [.TargetSpecializable, .SupportsInheritKeyword])
 
diff --git a/src/TulsiGenerator/TulsiProjectInfoExtractor.swift b/src/TulsiGenerator/TulsiProjectInfoExtractor.swift
index 8373e25..e0b90a6 100644
--- a/src/TulsiGenerator/TulsiProjectInfoExtractor.swift
+++ b/src/TulsiGenerator/TulsiProjectInfoExtractor.swift
@@ -55,13 +55,15 @@
   public func ruleEntriesForInfos(_ infos: [RuleInfo],
                                   startupOptions: TulsiOption,
                                   buildOptions: TulsiOption,
-                                  projectGenBuildOptions: TulsiOption,
+                                  compilationModeOption: TulsiOption,
+                                  platformConfigOption: TulsiOption,
                                   prioritizeSwiftOption: TulsiOption,
                                   features: Set<BazelSettingFeature>) throws -> RuleEntryMap {
     return try ruleEntriesForLabels(infos.map({ $0.label }),
                                     startupOptions: startupOptions,
                                     buildOptions: buildOptions,
-                                    projectGenBuildOptions: projectGenBuildOptions,
+                                    compilationModeOption: compilationModeOption,
+                                    platformConfigOption: platformConfigOption,
                                     prioritizeSwiftOption: prioritizeSwiftOption,
                                     features: features)
   }
@@ -69,14 +71,16 @@
   public func ruleEntriesForLabels(_ labels: [BuildLabel],
                                    startupOptions: TulsiOption,
                                    buildOptions: TulsiOption,
-                                   projectGenBuildOptions: TulsiOption,
+                                   compilationModeOption: TulsiOption,
+                                   platformConfigOption: TulsiOption,
                                    prioritizeSwiftOption: TulsiOption,
                                    features: Set<BazelSettingFeature>) throws -> RuleEntryMap {
     do {
       return try workspaceInfoExtractor.ruleEntriesForLabels(labels,
                                                              startupOptions: startupOptions,
                                                              buildOptions: buildOptions,
-                                                             projectGenBuildOptions: projectGenBuildOptions,
+                                                             compilationModeOption: compilationModeOption,
+                                                             platformConfigOption: platformConfigOption,
                                                              prioritizeSwiftOption: prioritizeSwiftOption,
                                                              features: features)
     } catch BazelWorkspaceInfoExtractorError.aspectExtractorFailed(let info) {
diff --git a/src/TulsiGenerator/TulsiXcodeProjectGenerator.swift b/src/TulsiGenerator/TulsiXcodeProjectGenerator.swift
index 7616035..7edaf37 100644
--- a/src/TulsiGenerator/TulsiXcodeProjectGenerator.swift
+++ b/src/TulsiGenerator/TulsiXcodeProjectGenerator.swift
@@ -50,7 +50,6 @@
         buildScript: bundle.url(forResource: "bazel_build", withExtension: "py")!,
         cleanScript: bundle.url(forResource: "bazel_clean", withExtension: "sh")!,
         extraBuildScripts: [bundle.url(forResource: "tulsi_logging", withExtension: "py")!,
-                            bundle.url(forResource: "bazel_build_flags", withExtension: "py")!,
                             bundle.url(forResource: "bazel_options", withExtension: "py")!,
                             bundle.url(forResource: "apfs_clone_copy", withExtension: "py")!,
                             bundle.url(forResource: "bazel_build_events", withExtension: "py")!,
diff --git a/src/TulsiGenerator/XcodeProjectGenerator.swift b/src/TulsiGenerator/XcodeProjectGenerator.swift
index 4fc6b88..fed887e 100644
--- a/src/TulsiGenerator/XcodeProjectGenerator.swift
+++ b/src/TulsiGenerator/XcodeProjectGenerator.swift
@@ -579,7 +579,8 @@
       return try workspaceInfoExtractor.ruleEntriesForLabels(config.buildTargetLabels,
                                                              startupOptions: config.options[.BazelBuildStartupOptionsDebug],
                                                              buildOptions: config.options[.BazelBuildOptionsDebug],
-                                                             projectGenBuildOptions: config.options[.BazelBuildOptionsProjectGenerationOnly],
+                                                             compilationModeOption: config.options[.ProjectGenerationCompilationMode],
+                                                             platformConfigOption: config.options[.ProjectGenerationPlatformConfiguration],
                                                              prioritizeSwiftOption: config.options[.ProjectPrioritizesSwift],
                                                              features: features)
     } catch BazelWorkspaceInfoExtractorError.aspectExtractorFailed(let info) {
@@ -986,7 +987,7 @@
       return
     }
     let script = scriptTemplate.replacingOccurrences(of: "# <template>",
-                                                     with: "BUILD_SETTINGS = \(bazelBuildSettings.toPython(""))")
+                                                     with: "# Generated by Tulsi. DO NOT EDIT.\nBUILD_SETTINGS = \(bazelBuildSettings.toPython(""))")
 
     var errorInfo: String? = nil
     do {
diff --git a/src/TulsiGenerator/en.lproj/Options.strings b/src/TulsiGenerator/en.lproj/Options.strings
index 2d2ecef..ccd857b 100644
--- a/src/TulsiGenerator/en.lproj/Options.strings
+++ b/src/TulsiGenerator/en.lproj/Options.strings
@@ -14,8 +14,11 @@
 "BazelBuildOptionsFastbuild" = "Fastbuild";
 "BazelBuildOptionsRelease" = "Release";
 
-"BazelBuildOptionsProjectGenerationOnly" = "'build' options ONLY for project generation";
-"BazelBuildOptionsProjectGenerationOnly_DESC" = "You can override the --config used for the aspect here (i.e. to --config=ios_arm64).";
+"ProjectGenerationCompilationMode" = "Bazel --compilation_mode for project generation";
+"ProjectGenerationCompilationMode_DESC" = "Modify this to opt if you frequently build Release builds in your generated project instead of Debug builds.";
+
+"ProjectGenerationPlatformConfiguration" = "Bazel Apple platform and cpu for project generation";
+"ProjectGenerationPlatformConfiguration_DESC" = "Modify this to the expected configuration for your project. Setting this incorrectly won't break your builds but it will potentially make them slower and may potentially alter the generated project if your project depends on Bazel `select`s.";
 
 "BazelBuildStartupOptions" = "'build' startup options";
 "BazelBuildStartupOptions_DESC" = "Startup options for bazel 'build' invocations.";
diff --git a/src/TulsiGeneratorTests/BuildSettingsTests.swift b/src/TulsiGeneratorTests/BuildSettingsTests.swift
index 1d342e3..a7c4218 100644
--- a/src/TulsiGeneratorTests/BuildSettingsTests.swift
+++ b/src/TulsiGeneratorTests/BuildSettingsTests.swift
@@ -114,6 +114,12 @@
     func testBazelBuildSettingsPythonable() {
       let bazel = "/path/to/bazel"
       let bazelExecRoot = "__MOCK_EXEC_ROOT__"
+      let defaultIdentifier = "fake_config"
+      let platformConfigurationFlags = [
+        "fake_config": ["a", "b"],
+        "another_one": ["--x", "-c"],
+      ]
+
       let swiftTargets: Set<String> = [
           "//dir/swiftTarget:swiftTarget",
           "//dir/nested/depOnswift:depOnswift"
@@ -132,6 +138,8 @@
       let nonSwiftFeatures = [BazelSettingFeature.DirectDebugPrefixMap("", "").stringValue]
       let settings = BazelBuildSettings(bazel: bazel,
                                         bazelExecRoot: bazelExecRoot,
+                                        defaultPlatformConfigIdentifier: defaultIdentifier,
+                                        platformConfigurationFlags: platformConfigurationFlags,
                                         swiftTargets: swiftTargets,
                                         tulsiCacheAffectingFlagsSet: cacheAffecting,
                                         tulsiCacheSafeFlagSet: cacheSafe,
@@ -145,6 +153,8 @@
 BazelBuildSettings(
     '\(bazel)',
     '\(bazelExecRoot)',
+    '\(defaultIdentifier)',
+    \(platformConfigurationFlags.toPython("    ")),
     \(swiftTargets.toPython("    ")),
     \(cacheAffecting.toPython("    ")),
     \(cacheSafe.toPython("    ")),
diff --git a/src/TulsiGeneratorTests/MockWorkspaceInfoExtractor.swift b/src/TulsiGeneratorTests/MockWorkspaceInfoExtractor.swift
index fe5d25b..2313041 100644
--- a/src/TulsiGeneratorTests/MockWorkspaceInfoExtractor.swift
+++ b/src/TulsiGeneratorTests/MockWorkspaceInfoExtractor.swift
@@ -27,6 +27,8 @@
                      buildRuleEntries: Set<RuleEntry>) -> BazelBuildSettings {
     return BazelBuildSettings(bazel: bazel,
                               bazelExecRoot: bazelExecRoot,
+                              defaultPlatformConfigIdentifier: "",
+                              platformConfigurationFlags: nil,
                               swiftTargets: [],
                               tulsiCacheAffectingFlagsSet: BazelFlagsSet(),
                               tulsiCacheSafeFlagSet: BazelFlagsSet(),
@@ -60,7 +62,8 @@
   func ruleEntriesForLabels(_ labels: [BuildLabel],
                             startupOptions: TulsiOption,
                             buildOptions: TulsiOption,
-                            projectGenBuildOptions: TulsiOption,
+                            compilationModeOption: TulsiOption,
+                            platformConfigOption: TulsiOption,
                             prioritizeSwiftOption: TulsiOption,
                             features: Set<BazelSettingFeature>) throws -> RuleEntryMap {
     invalidLabels.removeAll(keepingCapacity: true)
diff --git a/src/TulsiGeneratorTests/TulsiOptionSetTests.swift b/src/TulsiGeneratorTests/TulsiOptionSetTests.swift
index da78d97..bd3ef81 100644
--- a/src/TulsiGeneratorTests/TulsiOptionSetTests.swift
+++ b/src/TulsiGeneratorTests/TulsiOptionSetTests.swift
@@ -64,8 +64,10 @@
 
   func testValueSanitization() {
     let optionSet = TulsiOptionSet()
-    let optionKey = TulsiOptionKey.ALWAYS_SEARCH_USER_PATHS
-    optionSet[optionKey].projectValue = "invalid"
+    let boolOptionKey = TulsiOptionKey.ALWAYS_SEARCH_USER_PATHS
+    optionSet[boolOptionKey].projectValue = "invalid"
+    let compilationModeKey = TulsiOptionKey.ProjectGenerationCompilationMode
+    optionSet[compilationModeKey].projectValue = "also not valid."
 
     var dict = [String: AnyObject]()
     optionSet.saveAllOptionsIntoDictionary(&dict)
@@ -74,9 +76,12 @@
     let deserializedSet = TulsiOptionSet(fromDictionary: optionsDict)
 
     for (key, option) in optionSet.options {
-      if key == optionKey {
+      if key == boolOptionKey {
         XCTAssertNotEqual(deserializedSet[key], option)
         XCTAssertEqual(deserializedSet[key].projectValue, "NO")
+      } else if key == compilationModeKey {
+        XCTAssertNotEqual(deserializedSet[key], option)
+        XCTAssertEqual(deserializedSet[key].projectValue, "dbg")
       } else {
         XCTAssertEqual(deserializedSet[key], option)
       }