-
Notifications
You must be signed in to change notification settings - Fork 89
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Brentley Jones <[email protected]> Signed-off-by: Matt Pennig <[email protected]>
- Loading branch information
1 parent
1ab2f73
commit 1130d08
Showing
6 changed files
with
297 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import ArgumentParser | ||
import Foundation | ||
|
||
extension OutputGroupsCalculator { | ||
struct Arguments: ParsableArguments { | ||
@Argument( | ||
help: "Value of the 'XCODE_VERSION_ACTUAL' environment variable." | ||
) | ||
var xcodeVersionActual: Int | ||
|
||
@Argument( | ||
help: """ | ||
Value of the 'OBJROOT' build setting when 'ENABLE_PREVIEWS = NO'. | ||
""", | ||
transform: { URL(fileURLWithPath: $0, isDirectory: true) } | ||
) | ||
var nonPreviewObjRoot: URL | ||
|
||
@Argument( | ||
help: """ | ||
Value of 'nonPreviewObjRoot' when 'INDEX_ENABLE_BUILD_ARENA = NO'. | ||
""", | ||
transform: { URL(fileURLWithPath: $0, isDirectory: true) } | ||
) | ||
var baseObjRoot: URL | ||
|
||
@Argument( | ||
help: """ | ||
Path to a file that has a ctime at or after the start of the build. | ||
""", | ||
transform: { URL(fileURLWithPath: $0, isDirectory: false) } | ||
) | ||
var buildMarkerFile: URL | ||
|
||
@Argument( | ||
help: "Comma seperated list of output group prefixes.", | ||
transform: { $0.split(separator: ",").map(String.init) } | ||
) | ||
var outputGroupPrefixes: [String] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
load("@build_bazel_rules_apple//apple:apple.bzl", "apple_universal_binary") | ||
load( | ||
"@build_bazel_rules_apple//apple:macos.bzl", | ||
"macos_command_line_application", | ||
) | ||
load( | ||
"@build_bazel_rules_swift//swift:swift.bzl", | ||
"swift_binary", | ||
"swift_library", | ||
) | ||
|
||
# This target exists to keep configurations the same between the generator | ||
# and the tests, which makes the Xcode development experience better. If we used | ||
# `swift_binary` or `apple_universal_binary` in `xcodeproj`, then the | ||
# `macos_unit_test` transition (which is used to be able to set a minimum os | ||
# version on the tests) will create slightly different configurations for our | ||
# `swift_library`s. Maybe https://github.com/bazelbuild/bazel/issues/6526 will | ||
# fix that for us. | ||
macos_command_line_application( | ||
name = "calculate_output_groups", | ||
minimum_os_version = "12.0", | ||
visibility = ["//visibility:public"], | ||
deps = [":calculate_output_groups.library"], | ||
) | ||
|
||
swift_library( | ||
name = "calculate_output_groups.library", | ||
srcs = glob(["*.swift"]), | ||
module_name = "calculate_output_groups", | ||
deps = [ | ||
"//tools/lib/ToolCommon", | ||
"@com_github_apple_swift_argument_parser//:ArgumentParser", | ||
"@com_github_michaeleisel_zippyjson//:ZippyJSON", | ||
], | ||
) | ||
|
||
swift_binary( | ||
name = "calculate_output_groups_binary", | ||
deps = [":calculate_output_groups.library"], | ||
) | ||
|
||
apple_universal_binary( | ||
name = "universal_calculate_output_groups", | ||
binary = ":calculate_output_groups_binary", | ||
forced_cpus = [ | ||
"x86_64", | ||
"arm64", | ||
], | ||
minimum_os_version = "12.0", | ||
platform_type = "macos", | ||
visibility = ["//visibility:public"], | ||
) | ||
|
||
# Release | ||
|
||
filegroup( | ||
name = "release_files", | ||
srcs = [ | ||
"BUILD.release.bazel", | ||
":universal_calculate_output_groups", | ||
], | ||
tags = ["manual"], | ||
visibility = ["//:__subpackages__"], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
exports_files(["universal_calculate_output_groups"]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import ArgumentParser | ||
import Darwin | ||
import ToolCommon | ||
|
||
@main | ||
struct CalculateOutputGroups: AsyncParsableCommand { | ||
@Argument( | ||
help: "Value of the 'COLOR_DIAGNOSTICS' environment variable.", | ||
transform: { $0 == "YES" } | ||
) | ||
var colorDiagnostics: Bool | ||
|
||
@OptionGroup var arguments: OutputGroupsCalculator.Arguments | ||
|
||
func run() async throws { | ||
let logger = DefaultLogger( | ||
standardError: StderrOutputStream(), | ||
standardOutput: StdoutOutputStream(), | ||
colorize: colorDiagnostics | ||
) | ||
|
||
let calculator = OutputGroupsCalculator() | ||
|
||
do { | ||
try await calculator.calculateOutputGroups(arguments: arguments) | ||
} catch { | ||
logger.logError(error.localizedDescription) | ||
Darwin.exit(1) | ||
} | ||
} | ||
} |
126 changes: 126 additions & 0 deletions
126
tools/calculate_output_groups/OutputGroupsCalculator.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
import Foundation | ||
import ToolCommon | ||
import ZippyJSON | ||
|
||
struct OutputGroupsCalculator { | ||
func calculateOutputGroups(arguments: Arguments) async throws { | ||
let pifCache = arguments.baseObjRoot | ||
.appendingPathComponent("XCBuildData/PIFCache") | ||
let projectCache = pifCache.appendingPathComponent("project") | ||
let targetCache = pifCache.appendingPathComponent("target") | ||
|
||
let fileManager = FileManager.default | ||
|
||
guard fileManager.fileExists(atPath: projectCache.path) && | ||
fileManager.fileExists(atPath: targetCache.path) | ||
else { | ||
throw UsageError(message: """ | ||
error: PIFCache (\(pifCache)) doesn't exist. If you manually cleared Derived \ | ||
Data, you need to close and re-open the project for the PIFCache to be created \ | ||
again. Using the "Clean Build Folder" command instead (⇧ ⌘ K) won't trigger \ | ||
this error. If this error still happens after re-opening the project, please \ | ||
file a bug report here: \ | ||
https://github.com/MobileNativeFoundation/rules_xcodeproj/issues/new?template=bug.md | ||
""") | ||
} | ||
|
||
let projectURL = try Self.findProjectURL(in: projectCache) | ||
let project = try Self.decodeProject(at: projectURL) | ||
let targets = | ||
try await Self.decodeTargets(project.targets, in: targetCache) | ||
|
||
dump(targets) | ||
} | ||
|
||
static func findProjectURL(in projectCache: URL) throws -> URL { | ||
let projectPIFsEnumerator = FileManager.default.enumerator( | ||
at: projectCache, | ||
includingPropertiesForKeys: [.contentModificationDateKey], | ||
options: [ | ||
.skipsHiddenFiles, | ||
.skipsPackageDescendants, | ||
.skipsSubdirectoryDescendants, | ||
] | ||
)! | ||
|
||
var newestProjectPIF: URL? | ||
var newestProjectPIFDate = Date.distantPast | ||
for case let projectPIF as URL in projectPIFsEnumerator { | ||
guard let resourceValues = try? projectPIF.resourceValues( | ||
forKeys: [.contentModificationDateKey] | ||
), let modificationDate = resourceValues.contentModificationDate | ||
else { | ||
continue | ||
} | ||
|
||
// TODO: The modification date is in the filename, should we use | ||
// that instead? | ||
if modificationDate > newestProjectPIFDate { | ||
newestProjectPIF = projectPIF | ||
newestProjectPIFDate = modificationDate | ||
} | ||
} | ||
|
||
guard let projectPIF = newestProjectPIF else { | ||
throw UsageError(message: """ | ||
error: Couldn't find a Project PIF at "\(projectCache)". Please file a bug \ | ||
report here: https://github.com/MobileNativeFoundation/rules_xcodeproj/issues/new?template=bug.md | ||
""") | ||
} | ||
|
||
return projectPIF | ||
} | ||
|
||
static func decodeProject(at url: URL) throws -> ProjectPIF { | ||
let decoder = ZippyJSONDecoder() | ||
return try decoder.decode(ProjectPIF.self, from: Data(contentsOf: url)) | ||
} | ||
|
||
static func decodeTargets( | ||
_ targets: [String], | ||
in targetCache: URL | ||
) async throws -> [TargetPIF] { | ||
return try await withThrowingTaskGroup( | ||
of: TargetPIF.self, | ||
returning: [TargetPIF].self | ||
) { group in | ||
for target in targets { | ||
group.addTask { | ||
let url = | ||
targetCache.appendingPathComponent("\(target)-json") | ||
let decoder = ZippyJSONDecoder() | ||
return try decoder | ||
.decode(TargetPIF.self, from: Data(contentsOf: url)) | ||
} | ||
} | ||
|
||
var targetPIFs: [TargetPIF] = [] | ||
for try await target in group { | ||
targetPIFs.append(target) | ||
} | ||
|
||
return targetPIFs | ||
} | ||
} | ||
} | ||
|
||
struct ProjectPIF: Decodable { | ||
let targets: [String] | ||
} | ||
|
||
struct TargetPIF: Decodable { | ||
struct BuildConfiguration: Decodable { | ||
let name: String | ||
let buildSettings: [String: String] | ||
} | ||
|
||
let guid: String | ||
let buildConfigurations: [BuildConfiguration] | ||
} | ||
|
||
struct Target { | ||
let label: String | ||
|
||
// Maps Platform Name -> [Target ID] | ||
let targetIds: [String: [String]] | ||
} |