Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add source_output_file_map_aspect and DataStore symlink logic #33

Merged
merged 10 commits into from
Nov 4, 2022
11 changes: 10 additions & 1 deletion BazelExtensions/xchammerconfig.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,22 @@ def target_config(
def project_config(
paths, # [String]?
build_bazel_platform_options = None, # [String: [String]]?
bazel_build_service_config = None, # [String: XCHammerTargetConfig]?
generate_transitive_xcode_targets = None, # Bool
generate_xcode_schemes = None, # Bool
xcconfig_overrides = None): # : [String: String]?
return struct(paths = paths, buildBazelPlatformOptions = build_bazel_platform_options, generateTransitiveXcodeTargets = generate_transitive_xcode_targets, generateXcodeSchemes = generate_xcode_schemes, xcconfigOverrides = xcconfig_overrides)
return struct(paths = paths, buildBazelPlatformOptions = build_bazel_platform_options, bazelBuildServiceConfig = bazel_build_service_config, generateTransitiveXcodeTargets = generate_transitive_xcode_targets, generateXcodeSchemes = generate_xcode_schemes, xcconfigOverrides = xcconfig_overrides)

def xchammer_config(
targets, # [String]
projects, # [String: XCHammerProjectConfig]
target_config = None): # [String: XCHammerTargetConfig]?
return struct(targets = targets, targetConfig = target_config, projects = projects)

def bazel_build_service_config(
bep_path = None, # String?
indexing_enabled = False, # Bool
index_store_path = None, # String?
indexing_data_dir = None, # String?
progress_bar_enabled = False): # Bool
return struct(bepPath = bep_path, indexingEnabled = indexing_enabled, indexStorePath = index_store_path, indexingDataDir = indexing_data_dir, progressBarEnabled = progress_bar_enabled)
73 changes: 72 additions & 1 deletion BazelExtensions/xcode_configuration_provider.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,79 @@ def _install_action(ctx, infos, itarget):
)
return [output]

SourceOutputFileMapInfo = provider(
doc = "...",
fields = {
"mapping": "Dictionary where keys are source file paths and values are at the respective .o file under bazel-out",
},
)

def _source_output_file_map(target, ctx):
"""
Maps source code files to respective `.o` object file under bazel-out. Output group is used for indexing in xcbuildkit.
"""
source_output_file_map = ctx.actions.declare_file("{}_source_output_file_map.json".format(target.label.name))

mapping = {}
objc_srcs = []
objc_objects = []

# List of source files to be mapped to output files
if hasattr(ctx.rule.attr, "srcs"):
objc_srcs = [
f
for source_file in ctx.rule.attr.srcs
for f in source_file.files.to_list()
# Handling objc only for now
if f.path.endswith(".m")
# TODO: handle swift
]

# Get compilation outputs if present
if OutputGroupInfo in target:
if hasattr(target[OutputGroupInfo], "compilation_outputs"):
objc_objects.extend(target[OutputGroupInfo].compilation_outputs.to_list())

# Map source to output file
if len(objc_srcs):
if len(objc_srcs) != len(objc_objects):
fail("[ERROR] Unexpected number of object files")
for src in objc_srcs:
basename_without_ext = src.basename.replace(".%s" % src.extension, "")
obj = [o for o in objc_objects if "%s.o" % basename_without_ext == o.basename]
if len(obj) != 1:
fail("Failed to find single object file for source %s. Found: %s" % (src, obj))

obj = obj[0]
mapping["/{}".format(src.path)] = obj.path

# Collect info from deps
deps = getattr(ctx.rule.attr, "deps", [])
transitive_jsons = []
for dep in deps:
# Collect mappings from deps
if SourceOutputFileMapInfo in dep:
for k, v in dep[SourceOutputFileMapInfo].mapping.items():
mapping[k] = v
# Collect generated JSON files from deps
if OutputGroupInfo in dep:
if hasattr(dep[OutputGroupInfo], "source_output_file_map"):
transitive_jsons.extend(dep[OutputGroupInfo].source_output_file_map.to_list())

# Writes JSON
ctx.actions.write(source_output_file_map, json.encode(mapping))

return [
OutputGroupInfo(
source_output_file_map = depset([source_output_file_map] + transitive_jsons),
),
SourceOutputFileMapInfo(
mapping = mapping
),
]
def _xcode_build_sources_aspect_impl(itarget, ctx):
""" Install Xcode project dependencies into the source root.

This is required as by default, Bazel only installs genfiles for those
genfiles which are passed to the Bazel command line.
"""
Expand Down Expand Up @@ -192,7 +263,7 @@ def _xcode_build_sources_aspect_impl(itarget, ctx):
),
),
XcodeBuildSourceInfo(values = depset(infos), transitive=[depset(compacted_transitive_files)]),
]
] + _source_output_file_map(itarget, ctx)


# Note, that for "pure" Xcode builds we build swiftmodules with Xcode, so we
Expand Down
19 changes: 11 additions & 8 deletions BazelExtensions/xcodeproject.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ load(
"@xchammer//:BazelExtensions/xchammerconfig.bzl",
"xchammer_config",
"gen_xchammer_config",
"project_config"
"project_config",
)

load(
"@xchammer//:BazelExtensions/xcode_configuration_provider.bzl",
"XcodeProjectTargetInfo",
"XcodeConfigurationAspectInfo",
"target_config_aspect",
"xcode_build_sources_aspect",
"XcodeBuildSourceInfo",
"SourceOutputFileMapInfo",
)

non_hermetic_execution_requirements = { "no-cache": "1", "no-remote": "1", "local": "1", "no-sandbox": "1" }
Expand All @@ -41,6 +41,7 @@ def _xcode_project_impl(ctx):
# Collect Target configuration JSON from deps
# Then, merge them to a list
aggregate_target_config = {}

for dep in ctx.attr.targets:
if XcodeConfigurationAspectInfo in dep:
for info in dep[XcodeConfigurationAspectInfo].values:
Expand Down Expand Up @@ -146,12 +147,11 @@ def _xcode_project_impl(ctx):
execution_requirements = { "local": "1" }
)


_xcode_project = rule(
implementation=_xcode_project_impl,
attrs={
"targets": attr.label_list(
aspects=[tulsi_sources_aspect, target_config_aspect]
aspects=[tulsi_sources_aspect, target_config_aspect],
),
"project_name": attr.string(),
"bazel": attr.string(default="bazel"),
Expand All @@ -169,11 +169,13 @@ get_srcroot = "\"$(cat ../../DO_NOT_BUILD_HERE)/\""
def _install_xcode_project_impl(ctx):
xcodeproj = ctx.attr.xcodeproj.files.to_list()[0]
output_proj = "$SRCROOT/" + xcodeproj.basename

command = [
"SRCROOT=" + get_srcroot,
"ditto " + xcodeproj.path + " " + output_proj,
"sed -i '' \"s,__BAZEL_EXEC_ROOT__,$PWD,g\" " + output_proj + "/XCHammerAssets/bazel_build_settings.py",
"sed -i '' \"s,__BAZEL_OUTPUT_BASE__,$(dirname $(dirname $PWD)),g\" " + output_proj + "/XCHammerAssets/bazel_build_settings.py",
"sed -i '' \"s,__BAZEL_EXEC_ROOT__,$PWD,g\" " + output_proj + "/XCHammerAssets/bazel_build_service_setup.sh",
# This is kind of a hack for reference bazel relative to the source
# directory, as bazel_build_settings.py doesn't sub Xcode build
# settings.
Expand All @@ -185,6 +187,7 @@ def _install_xcode_project_impl(ctx):
"(rm -f $SRCROOT/external && ln -sf $PWD/../../external $SRCROOT/external)",
'echo "' + output_proj + '" > ' + ctx.outputs.out.path,
]

ctx.actions.run_shell(
inputs=ctx.attr.xcodeproj.files,
command=";".join(command),
Expand All @@ -196,11 +199,12 @@ def _install_xcode_project_impl(ctx):

_install_xcode_project = rule(
implementation=_install_xcode_project_impl,
attrs={"xcodeproj": attr.label(mandatory=True)},
attrs={
"xcodeproj": attr.label(mandatory=True),
},
outputs={"out": "%{name}.dummy"},
)


def xcode_project(**kwargs):
""" Generate an Xcode project

Expand All @@ -225,7 +229,6 @@ def xcode_project(**kwargs):
proj_args["project_name"] = kwargs["name"]

# Build an XCHammer config Based on inputs
targets_json = [str(t) for t in kwargs.get("targets")]
if "target_config" in proj_args:
str_dict = {}
for k in proj_args["target_config"]:
Expand All @@ -236,7 +239,7 @@ def xcode_project(**kwargs):
proj_args["target_config"] = "{}"

proj_args["name"] = rule_name + "_impl"
proj_args["project_config"] = proj_args["project_config"].to_json() if "project_config" in proj_args else None
proj_args["project_config"] = proj_args["project_config"].to_json() if "project_config" in proj_args else None

_xcode_project(**proj_args)

Expand Down
4 changes: 2 additions & 2 deletions Sources/XCHammer/Generator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ enum Generator {
let overrides = getRepositoryOverrides(genOptions: genOptions)
let bazelArgs: [String] = [
"--aspects @xchammer//:BazelExtensions/xcode_configuration_provider.bzl%pure_xcode_build_sources_aspect",
"--output_groups=xcode_project_deps"
"--output_groups=xcode_project_deps,+source_output_file_map",
] + overrides + labels.map { $0.value }

// We retry.sh the bazel command so if Xcode updates, the build still works
Expand Down Expand Up @@ -430,7 +430,7 @@ enum Generator {
] + overrides + [
// Build xcode_project_deps for targets in question.
"--aspects @xchammer//:BazelExtensions/xcode_configuration_provider.bzl%xcode_build_sources_aspect",
"--output_groups=+xcode_project_deps"
"--output_groups=+xcode_project_deps,+source_output_file_map",
]

let buildOptions = (targetConfig?.buildBazelOptions ?? "") + " " +
Expand Down
25 changes: 24 additions & 1 deletion Sources/XCHammer/XCBuildSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,12 @@ enum XCSettingCodingKey: String, CodingKey {
case sdkRoot = "SDKROOT"
case targetedDeviceFamily = "TARGETED_DEVICE_FAMILY"

// Build Service configs (xcbuildkit)
case buildServiceBEPPath = "BUILD_SERVICE_BEP_PATH"
case buildServiceIndexingEnabled = "BUILD_SERVICE_INDEXING_ENABLED"
case buildServiceIndexStorePath = "BUILD_SERVICE_INDEX_STORE_PATH"
case buildServiceIndexingDataDir = "BUILD_SERVICE_INDEXING_DATA_DIR"
case buildServiceProgressBarEnabled = "BUILD_SERVICE_PROGRESS_BAR_ENABLED"
}


Expand Down Expand Up @@ -237,6 +243,11 @@ struct XCBuildSettings: Encodable {
var targetedDeviceFamily: OrderedArray<String> = OrderedArray.empty
var isBazel: First<String> = First("NO")
var diagnosticFlags: [String] = []
var buildServiceBEPPath: First<String>?
var buildServiceIndexingEnabled: First<String>?
var buildServiceIndexStorePath: First<String>?
var buildServiceIndexingDataDir: First<String>?
var buildServiceProgressBarEnabled: First<String>?


func encode(to encoder: Encoder) throws {
Expand Down Expand Up @@ -309,6 +320,13 @@ struct XCBuildSettings: Encodable {
// XCHammer only supports Xcode projects at the root directory
try container.encode("$SOURCE_ROOT", forKey: .tulsiWR)
try container.encode(diagnosticFlags.joined(separator: " "), forKey: .diagnosticFlags)

// Build Service (xcbuildkit)
try buildServiceBEPPath.map { try container.encode($0.v, forKey: .buildServiceBEPPath) }
try buildServiceIndexingEnabled.map { try container.encode($0.v, forKey: .buildServiceIndexingEnabled) }
try buildServiceIndexStorePath.map { try container.encode($0.v, forKey: .buildServiceIndexStorePath) }
try buildServiceIndexingDataDir.map { try container.encode($0.v, forKey: .buildServiceIndexingDataDir) }
try buildServiceProgressBarEnabled.map { try container.encode($0.v, forKey: .buildServiceProgressBarEnabled) }
}
}

Expand Down Expand Up @@ -366,7 +384,12 @@ extension XCBuildSettings: Monoid {
targetedDeviceFamily: lhs.targetedDeviceFamily <>
rhs.targetedDeviceFamily,
isBazel: lhs.isBazel <> rhs.isBazel,
diagnosticFlags: lhs.diagnosticFlags <> rhs.diagnosticFlags
diagnosticFlags: lhs.diagnosticFlags <> rhs.diagnosticFlags,
buildServiceBEPPath: lhs.buildServiceBEPPath <> rhs.buildServiceBEPPath,
buildServiceIndexingEnabled: lhs.buildServiceIndexingEnabled <> rhs.buildServiceIndexingEnabled,
buildServiceIndexStorePath: lhs.buildServiceIndexStorePath <> rhs.buildServiceIndexStorePath,
buildServiceIndexingDataDir: lhs.buildServiceIndexingDataDir <> rhs.buildServiceIndexingDataDir,
buildServiceProgressBarEnabled: lhs.buildServiceProgressBarEnabled <> rhs.buildServiceProgressBarEnabled
)
}

Expand Down
13 changes: 13 additions & 0 deletions Sources/XCHammer/XCHammerConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ public struct XCHammerProjectConfig: Codable {
/// serialized. Spaces and escapes matter.
public let buildBazelPlatformOptions: [String: [String]]?

/// Allows one to configure and pass information to the Build Service
public let bazelBuildServiceConfig: BazelBuildServiceConfig?

/// Enable generation of transitive Xcode targets.
/// Defaults to `true`
/// @note this is _generally_ required for Xcode projects to build with
Expand Down Expand Up @@ -193,9 +196,19 @@ public struct XCHammerProjectConfig: Codable {
xcconfigOverrides = (try container.decodeIfPresent(
[String: String].self, forKey: .xcconfigOverrides)) ?? nil

bazelBuildServiceConfig = try container.decodeIfPresent(
BazelBuildServiceConfig.self, forKey: .bazelBuildServiceConfig)
}
}

public struct BazelBuildServiceConfig: Codable {
public let bepPath: String?
public let indexStorePath: String?
public let indexingEnabled: Bool
public let indexingDataDir: String?
public let progressBarEnabled: Bool
}

public struct XCHammerConfig: Codable {
/// Labels for all targets.
/// Transitve dependencies are converted into targets unless excluded by
Expand Down
14 changes: 12 additions & 2 deletions Sources/XCHammer/XcodeTarget.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1499,9 +1499,18 @@ public class XcodeTarget: Hashable, Equatable {
settings.pythonPath =
First("${PYTHONPATH}:$(PROJECT_FILE_PATH)/XCHammerAssets")
settings <>= getDeploymentTargetSettings()
// Build Service configs (xcbuildkit)
if let bazelBuildServiceConfig = genOptions.config.projects[genOptions.projectName]?.bazelBuildServiceConfig {
settings.buildServiceBEPPath = First(bazelBuildServiceConfig.bepPath ?? "")
settings.buildServiceIndexingEnabled = First(bazelBuildServiceConfig.indexingEnabled ? "YES" : "NO")
settings.buildServiceIndexStorePath = First(bazelBuildServiceConfig.indexStorePath ?? "")
settings.buildServiceIndexingDataDir = First(bazelBuildServiceConfig.indexingDataDir ?? "")
settings.buildServiceProgressBarEnabled = First(bazelBuildServiceConfig.progressBarEnabled ? "YES" : "NO")
}

let bazelScript = ProjectSpec.BuildScript(path: nil, script: getScriptContent(), name: "Bazel build")
let buildServiceSetupScript = ProjectSpec.BuildScript(path: nil, script: "$PROJECT_FILE_PATH/XCHammerAssets/bazel_build_service_setup.sh", name: "Build Service Setup")

let bazelScript = ProjectSpec.BuildScript(path: nil, script: getScriptContent(),
name: "Bazel build")
return ProjectSpec.Target(
name: xcTargetName,
type: PBXProductType(rawValue: productType.rawValue)!,
Expand All @@ -1510,6 +1519,7 @@ public class XcodeTarget: Hashable, Equatable {
configFiles: getXCConfigFiles(for: self),
sources: sources,
dependencies: [],
preBuildScripts: [buildServiceSetupScript],
postBuildScripts: [bazelScript]
)
}
Expand Down
2 changes: 1 addition & 1 deletion WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ xchammer_dependencies()
# https://github.com/bazelbuild/bazel/issues/1550
git_repository(
name = "xcbuildkit",
commit = "b2f0e4dd5a572b7029db3cf791d0897977f96a80",
commit = "a03d9c7c5efb961b317f5efb95cbfcf7471b683e",
remote = "https://github.com/jerrymarino/xcbuildkit.git",
)

Expand Down
Loading