Setup for improvements to Bazel caching
- Add new TulsiOptionValueType: stringEnum for an enum-style option
- No longer set the sdk_version flags as they aren't needed
- No longer use the tulsigen- symlink; instead tell Bazel to not
create/modify any symlinks.
- Only set the --xcode_version flag in Xcode if the version does
not match the xcode_version used when the generation was run.
PiperOrigin-RevId: 204795989
diff --git a/src/Tulsi/OptionsEditorController.swift b/src/Tulsi/OptionsEditorController.swift
index 937c3cb..6010c96 100644
--- a/src/Tulsi/OptionsEditorController.swift
+++ b/src/Tulsi/OptionsEditorController.swift
@@ -249,7 +249,12 @@
case .bool:
newNode = OptionsEditorBooleanNode(key: key, option: option, model: model, target: target)
case .string:
- newNode = OptionsEditorStringNode(key: key, option: option, model: model, target: target)
+ fallthrough
+ case .stringEnum:
+ newNode = OptionsEditorConstrainedStringNode(key: key,
+ option: option,
+ model: model,
+ target: target)
}
if let (group, displayName, description) = optionSet?.groupInfoForOptionKey(key) {
@@ -363,6 +368,8 @@
editable: editable)
}
+ case .stringEnum:
+ fallthrough
case .bool:
let identifier: String
if explicit {
diff --git a/src/Tulsi/OptionsEditorNode.swift b/src/Tulsi/OptionsEditorNode.swift
index d16f14e..59e7356 100644
--- a/src/Tulsi/OptionsEditorNode.swift
+++ b/src/Tulsi/OptionsEditorNode.swift
@@ -345,6 +345,20 @@
}
}
+/// An editor node that provides multiple string options.
+class OptionsEditorConstrainedStringNode: OptionsEditorStringNode {
+
+ override var supportsMultilineEditor: Bool {
+ return false
+ }
+
+ override var multiSelectItems: [String] {
+ if case .stringEnum(let values) = valueType {
+ return values
+ }
+ return []
+ }
+}
/// An editor node that provides multiple boolean options and maps between display strings and
/// serialization strings.
diff --git a/src/TulsiGenerator/Bazel/tulsi/tulsi_aspects.bzl b/src/TulsiGenerator/Bazel/tulsi/tulsi_aspects.bzl
index 7802138..e726cfb 100644
--- a/src/TulsiGenerator/Bazel/tulsi/tulsi_aspects.bzl
+++ b/src/TulsiGenerator/Bazel/tulsi/tulsi_aspects.bzl
@@ -478,6 +478,10 @@
return (platform_type, minimum_os_version)
return (platform_type, _minimum_os_for_platform(ctx, platform_type))
+def _get_xcode_version(ctx):
+ """Returns the current Xcode version as a string."""
+ return str(ctx.attr._tulsi_xcode_config[apple_common.XcodeVersionConfig].xcode_version())
+
def _get_platform_type(ctx):
"""Return the current apple_common.platform_type as a string."""
current_platform = (_get_opt_attr(ctx, "rule.attr.platform_type") or
@@ -665,7 +669,11 @@
if watch_app:
extensions.append(watch_app)
- # Collect bundle related information.
+ # Record the Xcode version used for all targets, although it will only be used by bazel_build.py
+ # for targets that are buildable in the xcodeproj.
+ xcode_version = _get_xcode_version(ctx)
+
+ # Collect bundle related information and Xcode version only for runnable targets.
if AppleBundleInfo in target:
apple_bundle_provider = target[AppleBundleInfo]
@@ -677,13 +685,17 @@
infoplist = apple_bundle_provider.infoplist if IosExtensionBundleInfo in target else None
else:
bundle_name = None
-
- # For macos_command_line_application, which does not have a AppleBundleInfo
- # provider but does have a bundle_id attribute for use in the Info.plist.
- bundle_id = _get_opt_attr(rule_attr, "bundle_id")
product_type = None
infoplist = None
+ # For macos_command_line_application, which does not have a
+ # AppleBundleInfo provider but does have a bundle_id attribute for use
+ # in the Info.plist.
+ if target_kind == "macos_command_line_application":
+ bundle_id = _get_opt_attr(rule_attr, "bundle_id")
+ else:
+ bundle_id = None
+
# Collect Swift related attributes.
swift_info = None
if SwiftInfo in target:
@@ -752,6 +764,7 @@
infoplist = infoplist.basename if infoplist else None,
platform_type = platform_type,
product_type = product_type,
+ xcode_version = xcode_version,
)
# Create an action to write out this target's info.
diff --git a/src/TulsiGenerator/BazelAspectInfoExtractor.swift b/src/TulsiGenerator/BazelAspectInfoExtractor.swift
index 6a8e015..bb459df 100644
--- a/src/TulsiGenerator/BazelAspectInfoExtractor.swift
+++ b/src/TulsiGenerator/BazelAspectInfoExtractor.swift
@@ -24,12 +24,6 @@
case parsingFailed(String)
}
- /// Prefix to be used by Bazel for the output of the Tulsi aspect.
- private static let SymlinkPrefix = "tulsigen-"
- /// Suffixes used by Bazel when creating output symlinks.
- private static let BazelOutputSymlinks = [
- "bin", "genfiles", "out", "testlogs"].map({ SymlinkPrefix + $0 })
-
/// The location of the bazel binary.
var bazelURL: URL
/// The location of the Bazel workspace to be examined.
@@ -72,6 +66,7 @@
guard !targets.isEmpty else {
return RuleEntryMap()
}
+
return try extractRuleEntriesUsingBEP(targets,
startupOptions: startupOptions,
buildOptions: buildOptions,
@@ -176,25 +171,37 @@
var arguments = startupOptions
arguments.append(contentsOf: [
"build",
- "-c",
- "dbg", // The aspect is run in debug mode to match the default Xcode build configuration.
- "--symlink_prefix", // Generate artifacts without overwriting the normal build symlinks.
- BazelAspectInfoExtractor.SymlinkPrefix,
+ // The aspect is run in debug mode to match the default Xcode build configuration.
+ // This does indeed affect Bazel analysis caching.
+ "--compilation_mode=dbg",
+ "--symlink_prefix=/", // Generate artifacts without overwriting the normal build symlinks.
+ // The following flags control Bazel console output and should not affect Bazel analysis
+ // caching.
"--announce_rc", // Print the RC files used by this operation.
- "--nocheck_visibility", // Don't do package visibility enforcement during aspect runs.
"--show_result=0", // Don't bother printing the build results.
"--noshow_loading_progress", // Don't show Bazel's loading progress.
"--noshow_progress", // Don't show Bazel's build progress.
- "--override_repository=tulsi=\(aspectWorkspacePath)",
- "--aspects",
- "@tulsi//tulsi:tulsi_aspects.bzl%\(aspect)",
- "--output_groups=tulsi-info,-_,-default", // Build only the aspect artifacts.
- "--tool_tag=tulsi_v\(tulsiVersion):generator", // Add a tag for tracking.
+ // The following flags are used by Tulsi to identify itself and read build information from
+ // Bazel. They should not affect Bazel analysis caching.
+ "--tool_tag=tulsi_v\(tulsiVersion):generator", // Add a tag for tracking.
"--build_event_json_file=\(self.buildEventsFilePath)",
"--noexperimental_build_event_json_file_path_conversion",
// Don't replace test_suites with their tests. This allows the Aspect to discover the
// structure of test_suites instead of just the tests they resolve to.
"--noexpand_test_suites",
+ // The following flags WILL affect Bazel analysis caching.
+ // Keep this consistent with bazel_build.py.
+ "--nocheck_visibility", // Don't do package visibility enforcement during aspect runs.
+ "--override_repository=tulsi=\(aspectWorkspacePath)",
+ "--aspects",
+ "@tulsi//tulsi:tulsi_aspects.bzl%\(aspect)",
+ "--output_groups=tulsi-info,-_,-default", // Build only the aspect artifacts.
+ ])
+ // Extra flags added by bazel_build.py.
+ arguments.append(contentsOf: [
+ "--features=debug_prefix_map_pwd_is_dot",
+ "--define=apple.add_debugger_entitlement=1",
+ "--define=apple.propagate_embedded_extra_outputs=1",
])
arguments.append(contentsOf: projectGenerationOptions)
arguments.append(contentsOf: buildOptions)
@@ -213,37 +220,12 @@
completionInfo.commandlineString,
completionInfo.terminationStatus,
stderr)
-
- self.removeGeneratedSymlinks()
terminationHandler(completionInfo.process, debugInfo)
}
return process
}
- private func removeGeneratedSymlinks() {
- let fileManager = FileManager.default
- for outputSymlink in BazelAspectInfoExtractor.BazelOutputSymlinks {
-
- let symlinkURL = workspaceRootURL.appendingPathComponent(outputSymlink, isDirectory: true)
- do {
- let attributes = try fileManager.attributesOfItem(atPath: symlinkURL.path)
- guard let type = attributes[FileAttributeKey.type] as? String, type == FileAttributeType.typeSymbolicLink.rawValue else {
- continue
- }
- } catch {
- // Any exceptions are expected to indicate that the file does not exist.
- continue
- }
-
- do {
- try fileManager.removeItem(at: symlinkURL)
- } catch let e as NSError {
- localizedMessageLogger.infoMessage("Failed to remove symlink at \(symlinkURL). \(e)")
- }
- }
- }
-
/// Builds a list of RuleEntry instances using the data in the given set of .tulsiinfo files.
private func extractRuleEntriesFromArtifacts(_ files: Set<String>,
progressNotifier: ProgressNotifier? = nil) -> RuleEntryMap {
@@ -347,6 +329,7 @@
let productType = dict["product_type"] as? String
let platformType = dict["platform_type"] as? String
+ let xcodeVersion = dict["xcode_version"] as? String
let targetProductType: PBXTarget.ProductType?
@@ -403,7 +386,8 @@
swiftToolchain: swiftToolchain,
swiftTransitiveModules: swiftTransitiveModules,
objCModuleMaps: objCModuleMaps,
- extensionType: extensionType)
+ extensionType: extensionType,
+ xcodeVersion: xcodeVersion)
progressNotifier?.incrementValue()
return ruleEntry
}
diff --git a/src/TulsiGenerator/PBXTargetGenerator.swift b/src/TulsiGenerator/PBXTargetGenerator.swift
index a1d9199..38ec877 100644
--- a/src/TulsiGenerator/PBXTargetGenerator.swift
+++ b/src/TulsiGenerator/PBXTargetGenerator.swift
@@ -1526,6 +1526,13 @@
let swiftDependency = entry.attributes[.has_swift_dependency] as? Bool ?? false
buildSettings["TULSI_SWIFT_DEPENDENCY"] = swiftDependency ? "YES" : "NO"
+ // bazel_build.py uses this to determine if it needs to pass the --xcode_version flag, as the
+ // flag can have implications for caching even if the user's active Xcode version is the same
+ // as the flag.
+ if let xcodeVersion = entry.xcodeVersion {
+ buildSettings["TULSI_XCODE_VERSION"] = xcodeVersion
+ }
+
// Disable Xcode's attempts at generating dSYM bundles as it conflicts with the operation of the
// special test runner build configurations (which have associated sources but don't actually
// compile anything).
diff --git a/src/TulsiGenerator/RuleEntry.swift b/src/TulsiGenerator/RuleEntry.swift
index 6a98a84..9bbaca4 100644
--- a/src/TulsiGenerator/RuleEntry.swift
+++ b/src/TulsiGenerator/RuleEntry.swift
@@ -261,6 +261,9 @@
/// The NSExtensionPointIdentifier of the extension associated with this rule, if any.
public let extensionType: String?
+ /// Xcode version used during the aspect run. Only set for bundled and runnable targets.
+ public let xcodeVersion: String?
+
/// Returns the set of non-versioned artifacts that are not source files.
public var normalNonSourceArtifacts: [BazelFileInfo] {
var artifacts = [BazelFileInfo]()
@@ -336,7 +339,8 @@
swiftToolchain: String? = nil,
swiftTransitiveModules: [BazelFileInfo] = [],
objCModuleMaps: [BazelFileInfo] = [],
- extensionType: String? = nil) {
+ extensionType: String? = nil,
+ xcodeVersion: String? = nil) {
var checkedAttributes = [Attribute: AnyObject]()
for (key, value) in attributes {
@@ -386,6 +390,7 @@
self.swiftLanguageVersion = swiftLanguageVersion
self.swiftToolchain = swiftToolchain
self.swiftTransitiveModules = swiftTransitiveModules
+ self.xcodeVersion = xcodeVersion
// Swift targets may have a generated Objective-C module map for their Swift generated header.
// Unfortunately, this breaks Xcode's indexing (it doesn't really make sense to ask SourceKit
@@ -432,7 +437,8 @@
swiftToolchain: String? = nil,
swiftTransitiveModules: [BazelFileInfo] = [],
objCModuleMaps: [BazelFileInfo] = [],
- extensionType: String? = nil) {
+ extensionType: String? = nil,
+ xcodeVersion: String? = nil) {
self.init(label: BuildLabel(label),
type: type,
attributes: attributes,
@@ -458,7 +464,8 @@
swiftToolchain: swiftToolchain,
swiftTransitiveModules: swiftTransitiveModules,
objCModuleMaps: objCModuleMaps,
- extensionType: extensionType)
+ extensionType: extensionType,
+ xcodeVersion: xcodeVersion)
}
// MARK: Private methods
diff --git a/src/TulsiGenerator/Scripts/bazel_build.py b/src/TulsiGenerator/Scripts/bazel_build.py
index c544bd7..1b77015 100755
--- a/src/TulsiGenerator/Scripts/bazel_build.py
+++ b/src/TulsiGenerator/Scripts/bazel_build.py
@@ -206,6 +206,7 @@
_OptionsParser.ALL_CONFIGS: [
'--verbose_failures',
'--announce_rc',
+ '--bes_outerr_buffer_size=0', # Don't buffer Bazel output.
],
'Debug': [
@@ -300,23 +301,8 @@
options = self._GetOptions(self.build_options, config)
version_string = self._GetXcodeVersionString()
- if version_string:
+ if version_string and self._NeedsXcodeVersionFlag(version_string):
self._AddDefaultOption(options, '--xcode_version', version_string)
-
- if self.sdk_version:
- if self.platform_name.startswith('watch'):
- self._AddDefaultOption(options,
- '--watchos_sdk_version',
- self.sdk_version)
- elif self.platform_name.startswith('iphone'):
- self._AddDefaultOption(options, '--ios_sdk_version', self.sdk_version)
- elif self.platform_name.startswith('macos'):
- self._AddDefaultOption(options, '--macos_sdk_version', self.sdk_version)
- elif self.platform_name.startswith('appletv'):
- self._AddDefaultOption(options, '--tvos_sdk_version', self.sdk_version)
- else:
- self._WarnUnknownPlatform()
- self._AddDefaultOption(options, '--ios_sdk_version', self.sdk_version)
return options
@staticmethod
@@ -475,6 +461,32 @@
fix_version_string = '.%d' % fix_version
return '%d.%d%s' % (major_version, minor_version, fix_version_string)
+ @staticmethod
+ def _NeedsXcodeVersionFlag(xcode_version):
+ """Returns True if the --xcode_version flag should be used for building.
+
+ The flag should be used if the active Xcode version was not the same one
+ used during project generation.
+
+ 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 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]
+
+ return xcode_version != tulsi_xcode_version
+
class BazelBuildBridge(object):
"""Handles invoking Bazel and unpacking generated binaries."""
@@ -775,18 +787,25 @@
os.path.join(os.path.dirname(__file__), '..', 'Bazel'))
bazel_command.extend([
+ # The following flags are used by Tulsi to identify itself and read
+ # build information from Bazel. They shold not affect Bazel anaylsis
+ # caching.
+ '--tool_tag=tulsi_v%s:bazel_build' % self.tulsi_version,
'--build_event_json_file=%s' % self.build_events_file_path,
'--noexperimental_build_event_json_file_path_conversion',
- '--bes_outerr_buffer_size=0', # Don't buffer Bazel output at all.
- '--aspects', '@tulsi//tulsi:tulsi_aspects.bzl%tulsi_outputs_aspect',
- '--override_repository=tulsi=%s' % tulsi_package_dir,
- '--tool_tag=tulsi_v%s:bazel_build' % self.tulsi_version])
+ '--aspects', '@tulsi//tulsi:tulsi_aspects.bzl%tulsi_outputs_aspect'])
if self.is_test and self.gen_runfiles:
bazel_command.append('--output_groups=+tulsi-outputs')
else:
bazel_command.append('--output_groups=tulsi-outputs,default')
+ bazel_command.extend([
+ # The following flags WILL affect Bazel analysis caching.
+ # Keep this consistent with BazelAspectInfoExtractor.swift.
+ '--nocheck_visibility', # Don't do package visibility enforcement.
+ '--override_repository=tulsi=%s' % tulsi_package_dir])
+
if self.generate_dsym:
bazel_command.append('--apple_generate_dsym')
diff --git a/src/TulsiGenerator/TulsiOption.swift b/src/TulsiGenerator/TulsiOption.swift
index 0005c1c..03b12a3 100644
--- a/src/TulsiGenerator/TulsiOption.swift
+++ b/src/TulsiGenerator/TulsiOption.swift
@@ -28,8 +28,18 @@
public static let InheritKeyword = "$(inherited)"
/// The valid value types for this option.
- public enum ValueType {
+ public enum ValueType: Equatable {
case bool, string
+ case stringEnum([String])
+
+ public static func ==(lhs: ValueType, rhs: ValueType) -> Bool {
+ switch (lhs, rhs) {
+ case (.bool, .bool): return true
+ case (.string, .string): return true
+ case (.stringEnum(let a), .stringEnum(let b)): return a == b
+ default: return false
+ }
+ }
}
/// How this option is intended to be used.
@@ -158,13 +168,19 @@
}
public func sanitizeValue(_ value: String?) -> String? {
- if valueType == .bool {
- if value != TulsiOption.BooleanTrueValue {
- return TulsiOption.BooleanFalseValue
- }
- return value
+ switch (valueType) {
+ case .bool:
+ if value != TulsiOption.BooleanTrueValue {
+ return TulsiOption.BooleanFalseValue
+ }
+ return value
+ case .string:
+ return value?.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
+ case .stringEnum(let values):
+ guard let curValue = value else { return defaultValue }
+ guard values.contains(curValue) else { return defaultValue }
+ return curValue
}
- return value?.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
}
// Generates a serialized form of this option's user-defined values or nil if the value is
@@ -184,13 +200,19 @@
func deserialize(_ serialized: PersistenceType) {
if let value = serialized[TulsiOption.ProjectValueKey] as? String {
- projectValue = value
+ projectValue = sanitizeValue(value)
} else {
projectValue = nil
}
if let values = serialized[TulsiOption.TargetValuesKey] as? [String: String] {
- targetValues = values
+ var validValues = [String: String]()
+ for (key, value) in values {
+ if let sanitized = sanitizeValue(value) {
+ validValues[key] = sanitized
+ }
+ }
+ targetValues = validValues
} else if optionType.contains(.TargetSpecializable) {
self.targetValues = [String: String]()
} else {
diff --git a/src/TulsiGenerator/TulsiOptionSet.swift b/src/TulsiGenerator/TulsiOptionSet.swift
index 2686e02..665c0c4 100644
--- a/src/TulsiGenerator/TulsiOptionSet.swift
+++ b/src/TulsiGenerator/TulsiOptionSet.swift
@@ -274,6 +274,16 @@
addOption(optionKey, valueType: .string, optionType: optionType, defaultValue: defaultValue)
}
+ func addStringEnumOption(_ optionKey: TulsiOptionKey,
+ _ optionType: TulsiOption.OptionType,
+ _ defaultValue: String,
+ _ values: [String]) {
+ assert(values.contains(defaultValue), "Invalid enum for \(optionKey.rawValue): " +
+ "defaultValue of \"\(defaultValue)\" is not present in enum values: \(values).")
+ addOption(optionKey, valueType: .stringEnum(values),
+ optionType: optionType, defaultValue: defaultValue)
+ }
+
addBoolOption(.ALWAYS_SEARCH_USER_PATHS, .BuildSetting, false)
addBoolOption(.BazelContinueBuildingAfterError, .Generic, false)
addStringOption(.BazelBuildOptionsProjectGenerationOnly, .Generic)
diff --git a/src/TulsiGenerator/XcodeProjectGenerator.swift b/src/TulsiGenerator/XcodeProjectGenerator.swift
index e03f8ca..9327796 100644
--- a/src/TulsiGenerator/XcodeProjectGenerator.swift
+++ b/src/TulsiGenerator/XcodeProjectGenerator.swift
@@ -111,10 +111,6 @@
/// write a stub value that will be the same regardless of the execution environment.
var redactWorkspaceSymlink = false
- /// Exposed for testing. Suppresses creating folders for artifacts that are expected to be
- /// generated by Bazel.
- var suppressGeneratedArtifactFolderCreation = false
-
/// Exposed for testing. Do not modify user defaults.
var suppressModifyingUserDefaults = false
@@ -181,7 +177,6 @@
context: config.projectName)
defer { localizedMessageLogger.logProfilingEnd(generateProfilingToken) }
try validateXcodeProjectPath(outputFolderURL)
- try resolveConfigReferences()
let mainGroup = pbxTargetGeneratorType.mainGroupForOutputFolder(outputFolderURL,
workspaceRootURL: workspaceRootURL)
@@ -331,9 +326,9 @@
}
}
- /// Invokes Bazel to load any missing information in the config file.
- private func resolveConfigReferences() throws {
- let ruleEntryMap = try loadRuleEntryMap()
+ /// Validates that the aspect output contains all targets listed in the config file and that
+ /// there are no ambiguous top-level targets.
+ private func validateConfigReferences(_ ruleEntryMap: RuleEntryMap) throws {
let unresolvedLabels = config.buildTargetLabels.filter {
!ruleEntryMap.hasAnyRuleEntry(withBuildLabel: $0)
}
@@ -380,6 +375,8 @@
}
let ruleEntryMap = try loadRuleEntryMap()
+ try validateConfigReferences(ruleEntryMap)
+
var expandedTargetLabels = Set<BuildLabel>()
var testSuiteRules = [BuildLabel: RuleEntry]()
func expandTargetLabels<T: Sequence>(_ labels: T) where T.Iterator.Element == BuildLabel {
@@ -1310,7 +1307,12 @@
let errorInfo: String?
do {
+ // Only over-write if needed.
if fileManager.fileExists(atPath: targetURL.path) {
+ guard !fileManager.contentsEqual(atPath: sourceURL.path, andPath: targetURL.path) else {
+ print("Not overwriting \(targetURL.path) as its contents haven't changed.")
+ continue;
+ }
try fileManager.removeItem(at: targetURL)
}
try fileManager.copyItem(at: sourceURL, to: targetURL)
diff --git a/src/TulsiGeneratorIntegrationTests/AspectTests.swift b/src/TulsiGeneratorIntegrationTests/AspectTests.swift
index 24d206d..c7477b6 100644
--- a/src/TulsiGeneratorIntegrationTests/AspectTests.swift
+++ b/src/TulsiGeneratorIntegrationTests/AspectTests.swift
@@ -28,6 +28,13 @@
localizedMessageLogger: localizedMessageLogger)
}
+ // Utility function to fetch RuleEntries for the given labels.
+ func extractRuleEntriesForLabels(_ labels: [BuildLabel]) throws -> RuleEntryMap {
+ return try aspectInfoExtractor.extractRuleEntriesForLabels(labels,
+ startupOptions: bazelStartupOptions,
+ buildOptions: bazelBuildOptions)
+ }
+
func testSimple() throws {
installBUILDFile("Simple", intoSubdirectory: "tulsi_test")
makeTestXCDataModel("SimpleDataModelsTestv1", inSubdirectory: "tulsi_test/SimpleTest.xcdatamodeld")
@@ -114,10 +121,8 @@
func testExceptionThrown() {
installBUILDFile("SimpleBad", intoSubdirectory: "tulsi_test")
do {
- let _ = try aspectInfoExtractor.extractRuleEntriesForLabels([BuildLabel("//tulsi_test:Application"),
- BuildLabel("//tulsi_test:XCTest")],
- startupOptions: bazelStartupOptions,
- buildOptions: bazelBuildOptions)
+ let _ = try extractRuleEntriesForLabels([BuildLabel("//tulsi_test:Application"),
+ BuildLabel("//tulsi_test:XCTest")])
} catch BazelAspectInfoExtractor.ExtractorError.buildFailed {
// Expected failure on malformed BUILD file.
XCTAssert(aspectInfoExtractor.hasQueuedInfoMessages)
@@ -141,9 +146,7 @@
withContent: ["NSExtension": ["NSExtensionPointIdentifier": "com.apple.extension-foo"]],
inSubdirectory: "(tulsi_test/TodayExtension")
- let ruleEntryMap = try aspectInfoExtractor.extractRuleEntriesForLabels([BuildLabel("//tulsi_test:XCTest")],
- startupOptions: bazelStartupOptions,
- buildOptions: bazelBuildOptions)
+ let ruleEntryMap = try extractRuleEntriesForLabels([BuildLabel("//tulsi_test:XCTest")])
let checker = InfoChecker(ruleEntryMap: ruleEntryMap)
@@ -326,9 +329,7 @@
inSubdirectory: "tulsi_test/TodayExtension")
XCTAssertNotNil(url)
- let ruleEntryMap = try aspectInfoExtractor.extractRuleEntriesForLabels([BuildLabel("//tulsi_test:XCTest")],
- startupOptions: bazelStartupOptions,
- buildOptions: bazelBuildOptions)
+ let ruleEntryMap = try extractRuleEntriesForLabels([BuildLabel("//tulsi_test:XCTest")])
let checker = InfoChecker(ruleEntryMap: ruleEntryMap)
@@ -368,9 +369,7 @@
func testWatch() throws {
installBUILDFile("Watch", intoSubdirectory: "tulsi_test")
- let ruleEntryMap = try aspectInfoExtractor.extractRuleEntriesForLabels([BuildLabel("//tulsi_test:Application")],
- startupOptions: bazelStartupOptions,
- buildOptions: bazelBuildOptions)
+ let ruleEntryMap = try extractRuleEntriesForLabels([BuildLabel("//tulsi_test:Application")])
let checker = InfoChecker(ruleEntryMap: ruleEntryMap)
@@ -403,11 +402,7 @@
func testSwift() throws {
installBUILDFile("Swift", intoSubdirectory: "tulsi_test")
- let labels = [BuildLabel("//tulsi_test:Application")]
- let ruleEntryMap =
- try aspectInfoExtractor.extractRuleEntriesForLabels(labels,
- startupOptions: bazelStartupOptions,
- buildOptions: bazelBuildOptions)
+ let ruleEntryMap = try extractRuleEntriesForLabels([BuildLabel("//tulsi_test:Application")])
let checker = InfoChecker(ruleEntryMap: ruleEntryMap)
@@ -475,10 +470,17 @@
fromResourceDirectory: "TestSuite/Three")
}
+ // Utility function to fetch RuleEntries for the given labels.
+ func extractRuleEntriesForLabels(_ labels: [BuildLabel]) throws -> RuleEntryMap {
+ return try aspectInfoExtractor.extractRuleEntriesForLabels(
+ labels,
+ startupOptions: bazelStartupOptions,
+ buildOptions: bazelBuildOptions)
+ }
+
+
func testTestSuite_ExplicitXCTests() throws {
- let ruleEntryMap = try aspectInfoExtractor.extractRuleEntriesForLabels([BuildLabel("//\(testDir):explicit_XCTests")],
- startupOptions: bazelStartupOptions,
- buildOptions: bazelBuildOptions)
+ let ruleEntryMap = try extractRuleEntriesForLabels([BuildLabel("//\(testDir):explicit_XCTests")])
let checker = InfoChecker(ruleEntryMap: ruleEntryMap)
checker.assertThat("//\(testDir):explicit_XCTests")
@@ -495,14 +497,10 @@
.hasTestHost("//\(testDir):TestApplication")
checker.assertThat("//\(testDir)/Three:XCTest")
.hasTestHost("//\(testDir):TestApplication")
-
-
}
func testTestSuite_TaggedTests() throws {
- let ruleEntryMap = try aspectInfoExtractor.extractRuleEntriesForLabels([BuildLabel("//\(testDir):local_tagged_tests")],
- startupOptions: bazelStartupOptions,
- buildOptions: bazelBuildOptions)
+ let ruleEntryMap = try extractRuleEntriesForLabels([BuildLabel("//\(testDir):local_tagged_tests")])
let checker = InfoChecker(ruleEntryMap: ruleEntryMap)
checker.assertThat("//\(testDir):local_tagged_tests")
diff --git a/src/TulsiGeneratorIntegrationTests/EndToEndIntegrationTestCase.swift b/src/TulsiGeneratorIntegrationTests/EndToEndIntegrationTestCase.swift
index 55eadd5..21619e9 100644
--- a/src/TulsiGeneratorIntegrationTests/EndToEndIntegrationTestCase.swift
+++ b/src/TulsiGeneratorIntegrationTests/EndToEndIntegrationTestCase.swift
@@ -132,9 +132,6 @@
// Bazel built-in preprocessor defines are suppressed in order to prevent any
// environment-dependent variables from mismatching the golden data.
projectGenerator.xcodeProjectGenerator.suppressCompilerDefines = true
- // Output directory generation is suppressed in order to prevent having to whitelist diffs of
- // empty directories.
- projectGenerator.xcodeProjectGenerator.suppressGeneratedArtifactFolderCreation = true
// Don't modify any user defaults.
projectGenerator.xcodeProjectGenerator.suppressModifyingUserDefaults = true
// The username is forced to a known value.
diff --git a/src/TulsiGeneratorTests/TulsiOptionSetTests.swift b/src/TulsiGeneratorTests/TulsiOptionSetTests.swift
index d039e2d..da78d97 100644
--- a/src/TulsiGeneratorTests/TulsiOptionSetTests.swift
+++ b/src/TulsiGeneratorTests/TulsiOptionSetTests.swift
@@ -49,10 +49,8 @@
func testPerUserOptionsAreOmitted() {
let optionSet = TulsiOptionSet()
- var i = 0
for (_, option) in optionSet.options {
- option.projectValue = String(i)
- i += 10
+ option.projectValue = option.defaultValue
}
var dict = [String: Any]()
optionSet.saveShareableOptionsIntoDictionary(&dict)
@@ -64,6 +62,27 @@
}
}
+ func testValueSanitization() {
+ let optionSet = TulsiOptionSet()
+ let optionKey = TulsiOptionKey.ALWAYS_SEARCH_USER_PATHS
+ optionSet[optionKey].projectValue = "invalid"
+
+ var dict = [String: AnyObject]()
+ optionSet.saveAllOptionsIntoDictionary(&dict)
+
+ let optionsDict = TulsiOptionSet.getOptionsFromContainerDictionary(dict) ?? [:]
+ let deserializedSet = TulsiOptionSet(fromDictionary: optionsDict)
+
+ for (key, option) in optionSet.options {
+ if key == optionKey {
+ XCTAssertNotEqual(deserializedSet[key], option)
+ XCTAssertEqual(deserializedSet[key].projectValue, "NO")
+ } else {
+ XCTAssertEqual(deserializedSet[key], option)
+ }
+ }
+ }
+
func testOnlyPerUserOptionsArePersisted() {
let optionSet = TulsiOptionSet()
var i = 0