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
78 changes: 78 additions & 0 deletions BazelExtensions/source_output_file_map_aspect.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"""
Aspect to map source to output files (i.e. .o files generated during compilation).

The mappings are generated as JSON files per target.
"""

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_aspect_impl(target, ctx):
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
thiagohmcruz marked this conversation as resolved.
Show resolved Hide resolved
]

# 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
),
]

source_output_file_map_aspect = aspect(
thiagohmcruz marked this conversation as resolved.
Show resolved Hide resolved
implementation = _source_output_file_map_aspect_impl,
attr_aspects = ["deps"],
)
5 changes: 5 additions & 0 deletions BazelExtensions/xchammerconfig.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,8 @@ def xchammer_config(
projects, # [String: XCHammerProjectConfig]
target_config = None): # [String: XCHammerTargetConfig]?
return struct(targets = targets, targetConfig = target_config, projects = projects)

def build_service_config(
enable_indexing = False, # Bool
relative_index_store_path = None): # String?
return struct(enableIndexing = enable_indexing, relativeIndexStorePath = relative_index_store_path)
34 changes: 30 additions & 4 deletions BazelExtensions/xcodeproject.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ load(
"@xchammer//:BazelExtensions/xchammerconfig.bzl",
"xchammer_config",
"gen_xchammer_config",
"project_config"
"project_config",
"build_service_config",
)

load(
Expand Down Expand Up @@ -146,7 +147,6 @@ def _xcode_project_impl(ctx):
execution_requirements = { "local": "1" }
)


_xcode_project = rule(
implementation=_xcode_project_impl,
attrs={
Expand All @@ -169,6 +169,16 @@ 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

build_service_config = (
ctx.attr.build_service_config
if ctx.attr.build_service_config
else build_service_config().to_json()
)
build_service_config = json.decode(build_service_config)
enable_indexing = build_service_config.get("enableIndexing", False)
relative_index_store_path = build_service_config.get("relativeIndexStorePath", None)

command = [
"SRCROOT=" + get_srcroot,
"ditto " + xcodeproj.path + " " + output_proj,
Expand All @@ -185,6 +195,14 @@ def _install_xcode_project_impl(ctx):
"(rm -f $SRCROOT/external && ln -sf $PWD/../../external $SRCROOT/external)",
'echo "' + output_proj + '" > ' + ctx.outputs.out.path,
]

if enable_indexing and relative_index_store_path:
command.extend([
"DD=$(xcodebuild -project " + output_proj + " -showBuildSettings 2>&1 | grep -e \"^\\s*BUILT_PRODUCTS_DIR = \" | cut -d'=' -f2 | xargs | sed -e 's/\\/Build.*//g')",
thiagohmcruz marked this conversation as resolved.
Show resolved Hide resolved
'mv $DD/Index/DataStore $DD/Index/DataStore.default || true',
"mkdir -p '$SRCROOT{}'".format(relative_index_store_path),
"ln -s $(echo $SRCROOT){} $DD/Index/DataStore".format(relative_index_store_path),
thiagohmcruz marked this conversation as resolved.
Show resolved Hide resolved
])
ctx.actions.run_shell(
inputs=ctx.attr.xcodeproj.files,
command=";".join(command),
Expand All @@ -196,7 +214,10 @@ 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),
"build_service_config": attr.string(),
},
outputs={"out": "%{name}.dummy"},
)

Expand Down Expand Up @@ -236,7 +257,11 @@ 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

# For now pop to use in '_install_xcode_project', we might end up using this in the
# _xcode_project impl later on
build_service_config = proj_args.pop("build_service_config").to_json() if "build_service_config" in proj_args else None

_xcode_project(**proj_args)

Expand All @@ -246,4 +271,5 @@ def xcode_project(**kwargs):
name=rule_name,
xcodeproj=kwargs["name"],
testonly=proj_args.get("testonly", False),
build_service_config = build_service_config,
)
8 changes: 6 additions & 2 deletions Sources/XCHammer/Generator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,9 @@ 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",
"--aspects @xchammer//:BazelExtensions/source_output_file_map_aspect.bzl%source_output_file_map_aspect",
"--output_groups=+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 +432,9 @@ 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",
"--aspects @xchammer//:BazelExtensions/source_output_file_map_aspect.bzl%source_output_file_map_aspect",
"--output_groups=+source_output_file_map",
]

let buildOptions = (targetConfig?.buildBazelOptions ?? "") + " " +
Expand Down