Add an aspect to collect build outputs.
* WORKSPACE file and the "tulsi" package is now copied into the
generated Xcode project. This ensures that the aspect can be used on
every bazel_build.py invocation.
* The new aspect is disabled by default and is used for development
purposes.
--
PiperOrigin-RevId: 150321088
MOS_MIGRATED_REVID=150321088
diff --git a/src/TulsiGenerator/Bazel/tulsi/tulsi_aspects.bzl b/src/TulsiGenerator/Bazel/tulsi/tulsi_aspects.bzl
index c76079a..f683c03 100644
--- a/src/TulsiGenerator/Bazel/tulsi/tulsi_aspects.bzl
+++ b/src/TulsiGenerator/Bazel/tulsi/tulsi_aspects.bzl
@@ -589,9 +589,37 @@
transitive_attributes=transitive_attributes,
)
+def _tulsi_outputs_aspect(target, ctx):
+ """Collects outputs of each build invocation."""
+
+ # TODO(b/35322727): Move apple_watch2_extension into _IPA_GENERATING_RULES
+ # when dynamic outputs is the default strategy and it does need to be
+ # special-cased above.
+ if ctx.rule.kind not in _IPA_GENERATING_RULES + ['apple_watch2_extension']:
+ return
+
+ # An IPA output is guaranteed to exist for rules in _IPA_GENERATING_RULES
+ ipa_output = [x.path for x in target.files if x.path.endswith('.ipa')][0]
+ info = _struct_omitting_none(ipa=ipa_output)
+
+ output = ctx.new_file(target.label.name + '.tulsiouts')
+ ctx.file_action(output, info.to_json())
+
+ return struct(
+ output_groups={
+ 'tulsi-outputs': [output],
+ },
+ )
+
tulsi_sources_aspect = aspect(
implementation=_tulsi_sources_aspect,
- attr_aspects=_TULSI_COMPILE_DEPS,
fragments=['apple', 'cpp', 'objc'],
)
+
+
+# This aspect does not propagate past the top-level target because we only need
+# the IPA, which is at top level.
+tulsi_outputs_aspect = aspect(
+ implementation=_tulsi_outputs_aspect,
+)
diff --git a/src/TulsiGenerator/Scripts/bazel_build.py b/src/TulsiGenerator/Scripts/bazel_build.py
index c200723..d15456c 100755
--- a/src/TulsiGenerator/Scripts/bazel_build.py
+++ b/src/TulsiGenerator/Scripts/bazel_build.py
@@ -31,6 +31,9 @@
import time
import zipfile
+# TOOD(b/35322727): Remove when this is the default behavior.
+USE_DYNAMIC_OUTPUTS = False
+
def _PrintXcodeWarning(msg):
sys.stdout.write(':: warning: %s\n' % msg)
@@ -635,6 +638,19 @@
bazel_command.append('build')
bazel_command.extend(options.GetBuildOptions(configuration))
+ if USE_DYNAMIC_OUTPUTS:
+ # Do not follow symlinks on __file__ in case this script is linked during
+ # development.
+ tulsi_package_dir = os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', 'Bazel'))
+ package_path = '%%workspace%%:%s' % tulsi_package_dir
+
+ bazel_command.extend([
+ '--experimental_show_artifacts',
+ '--output_groups=tulsi-outputs,default',
+ '--aspects', '/tulsi/tulsi_aspects.bzl%tulsi_outputs_aspect',
+ '--package_path=%s' % package_path])
+
if self.code_coverage_enabled:
self._PrintVerbose('Enabling code coverage information.')
bazel_command.extend([
diff --git a/src/TulsiGenerator/TulsiXcodeProjectGenerator.swift b/src/TulsiGenerator/TulsiXcodeProjectGenerator.swift
index 7dd9862..4569163 100644
--- a/src/TulsiGenerator/TulsiXcodeProjectGenerator.swift
+++ b/src/TulsiGenerator/TulsiXcodeProjectGenerator.swift
@@ -56,7 +56,9 @@
stubInfoPlist: bundle.url(forResource: "StubInfoPlist", withExtension: "plist")!,
stubIOSAppExInfoPlist: bundle.url(forResource: "StubIOSAppExtensionInfoPlist", withExtension: "plist")!,
stubWatchOS2InfoPlist: bundle.url(forResource: "StubWatchOS2InfoPlist", withExtension: "plist")!,
- stubWatchOS2AppExInfoPlist: bundle.url(forResource: "StubWatchOS2AppExtensionInfoPlist", withExtension: "plist")!)
+ stubWatchOS2AppExInfoPlist: bundle.url(forResource: "StubWatchOS2AppExtensionInfoPlist", withExtension: "plist")!,
+ workspaceFile: bundle.url(forResource: "WORKSPACE", withExtension: nil)!,
+ packageFiles: bundle.urls(forResourcesWithExtension: nil, subdirectory: "tulsi")!)
// Note: A new extractor is created on each generate in order to allow users to modify their
// BUILD files (or add new files to glob's) and regenerate without restarting Tulsi.
diff --git a/src/TulsiGenerator/XcodeProjectGenerator.swift b/src/TulsiGenerator/XcodeProjectGenerator.swift
index 36d93a5..7b3fdb9 100644
--- a/src/TulsiGenerator/XcodeProjectGenerator.swift
+++ b/src/TulsiGenerator/XcodeProjectGenerator.swift
@@ -36,18 +36,33 @@
let stubIOSAppExInfoPlist: URL // Stub Info.plist (needed for app extension targets).
let stubWatchOS2InfoPlist: URL // Stub Info.plist (needed for watchOS2 app targets).
let stubWatchOS2AppExInfoPlist: URL // Stub Info.plist (needed for watchOS2 appex targets).
+
+ // In order to load tulsi_aspects, Tulsi constructs a Bazel repository inside of the generated
+ // Xcode project. Its structure looks like this:
+ // ├── Bazel
+ // │ ├── WORKSPACE
+ // │ └── tulsi
+ // │ ├── file1
+ // │ └── ...
+ // These two items define the content of this repository, including the WORKSPACE file and the
+ // "tulsi" package.
+ let bazelWorkspaceFile: URL // Stub WORKSPACE file.
+ let tulsiPackageFiles: [URL] // Files to copy into the "tulsi" package.
}
/// Path relative to PROJECT_FILE_PATH in which Tulsi generated files (scripts, artifacts, etc...)
/// should be placed.
private static let TulsiArtifactDirectory = ".tulsi"
static let ScriptDirectorySubpath = "\(TulsiArtifactDirectory)/Scripts"
+ static let BazelDirectorySubpath = "\(TulsiArtifactDirectory)/Bazel"
+ static let TulsiPackageName = "tulsi"
static let UtilDirectorySubpath = "\(TulsiArtifactDirectory)/Utils"
static let ConfigDirectorySubpath = "\(TulsiArtifactDirectory)/Configs"
static let ProjectResourcesDirectorySubpath = "\(TulsiArtifactDirectory)/Resources"
static let ManifestFileSubpath = "\(TulsiArtifactDirectory)/generatorManifest.json"
private static let BuildScript = "bazel_build.py"
private static let CleanScript = "bazel_clean.sh"
+ private static let WorkspaceFile = "WORKSPACE"
private static let PostProcessorUtil = "post_processor"
private static let UIRunnerEntitlements = "XCTRunner.entitlements"
private static let StubInfoPlistFilename = "StubInfoPlist.plist"
@@ -179,6 +194,7 @@
projectURL: projectURL,
projectBundleName: projectBundleName)
installTulsiScripts(projectURL)
+ installTulsiBazelPackage(projectURL)
installUtilities(projectURL)
installGeneratorConfig(projectURL)
installGeneratedProjectResources(projectURL)
@@ -723,6 +739,30 @@
(resourceURLs.cleanScript, XcodeProjectGenerator.CleanScript),
],
toDirectory: scriptDirectoryURL)
+
+ localizedMessageLogger.logProfilingEnd(profilingToken)
+ }
+ }
+
+ private func installTulsiBazelPackage(_ projectURL: URL) {
+
+ let bazelWorkspaceURL = projectURL.appendingPathComponent(XcodeProjectGenerator.BazelDirectorySubpath,
+ isDirectory: true)
+ let bazelPackageURL = bazelWorkspaceURL.appendingPathComponent(XcodeProjectGenerator.TulsiPackageName,
+ isDirectory: true)
+
+ if createDirectory(bazelPackageURL) {
+ let profilingToken = localizedMessageLogger.startProfiling("installing_package",
+ context: config.projectName)
+ let progressNotifier = ProgressNotifier(name: InstallingScripts, maxValue: 1)
+ defer { progressNotifier.incrementValue() }
+ localizedMessageLogger.infoMessage("Installing Bazel integration package")
+
+ installFiles([(resourceURLs.bazelWorkspaceFile, XcodeProjectGenerator.WorkspaceFile)],
+ toDirectory: bazelWorkspaceURL)
+ installFiles(resourceURLs.tulsiPackageFiles.map { ($0, $0.lastPathComponent) },
+ toDirectory: bazelPackageURL)
+
localizedMessageLogger.logProfilingEnd(profilingToken)
}
}
diff --git a/src/TulsiGeneratorTests/XcodeProjectGeneratorTests.swift b/src/TulsiGeneratorTests/XcodeProjectGeneratorTests.swift
index a4e76c7..3b58504 100644
--- a/src/TulsiGeneratorTests/XcodeProjectGeneratorTests.swift
+++ b/src/TulsiGeneratorTests/XcodeProjectGeneratorTests.swift
@@ -41,7 +41,9 @@
stubInfoPlist: URL(fileURLWithPath: "/generatedProjectResources/StubInfoPlist.plist"),
stubIOSAppExInfoPlist: URL(fileURLWithPath: "/generatedProjectResources/stubIOSAppExInfoPlist.plist"),
stubWatchOS2InfoPlist: URL(fileURLWithPath: "/generatedProjectResources/StubWatchOS2InfoPlist.plist"),
- stubWatchOS2AppExInfoPlist: URL(fileURLWithPath: "/generatedProjectResources/StubWatchOS2AppExInfoPlist.plist"))
+ stubWatchOS2AppExInfoPlist: URL(fileURLWithPath: "/generatedProjectResources/StubWatchOS2AppExInfoPlist.plist"),
+ workspaceFile: URL(fileURLWithPath: "/WORKSPACE"),
+ packageFiles: [URL(fileURLWithPath: "/tulsi/tulsi_aspects.bzl")])
var config: TulsiGeneratorConfig! = nil
var mockLocalizedMessageLogger: MockLocalizedMessageLogger! = nil
@@ -228,6 +230,12 @@
let resources = projectURL.appendingPathComponent(".tulsi/Resources")
mockFileManager.allowedDirectoryCreates.insert(resources.path)
+ let tulsiBazelRoot = projectURL.appendingPathComponent(".tulsi/Bazel")
+ mockFileManager.allowedDirectoryCreates.insert(tulsiBazelRoot.path)
+
+ let tulsiBazelPackage = projectURL.appendingPathComponent(".tulsi/Bazel/tulsi")
+ mockFileManager.allowedDirectoryCreates.insert(tulsiBazelPackage.path)
+
mockExtractor.labelToRuleEntry = ruleEntries
generator = XcodeProjectGenerator(workspaceRootURL: workspaceRoot,