blob: 44c5e921f6c85c1f1e5c3a8b45c6e8a2996e6dad [file] [log] [blame]
# Copyright 2024 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Process GN labels for `fx build` and other tools."""
import os
from typing import Callable, List
# A dictionary of convenience Fuchsia toolchain aliases.
# Keep these in sync with the rest of the Fuchsia build definitions.
_TOOLCHAIN_ALIASES = {
"host": "//build/toolchain:host_{host_cpu}",
"default": "//build/toolchain/fuchsia:{target_cpu}",
"fuchsia": "//build/toolchain/fuchsia:{target_cpu}",
"fidl": "//build/fidl:fidling",
}
def split_gn_target(target: str) -> (str, str):
dir, colon, name = target.partition(":")
if colon == ":":
return (dir, name)
pos = target.rfind("/")
assert pos >= 0
return (target[:pos], target[pos + 1 :])
def split_gn_label(label: str) -> (str, str):
"""Split a label into a (target, toolchain) pair."""
assert label.startswith("//"), f"GN Label must start with //: {label}"
tc_pos = label.find("(", 2)
if tc_pos < 0:
assert label.find("//", 2) < 0, f"GN label cannot use // after start."
return (label, "")
assert (
label[-1] == ")"
), f"GN label missing toolchain closing paren: {label}"
assert (
label.find("//", tc_pos + 3) < 0
), f"Malformed GN label toolchain: {label}"
return (label[0:tc_pos], label[tc_pos + 1 : -1])
def qualify_gn_target_name(target: str) -> str:
"""Qualify GN target with a proper name (e.g. //foo -> //foo:foo)."""
assert target.startswith("//"), f"GN Label must start with //: {target}"
if target.find(":") >= 0:
return target
pos = target.rfind("/")
assert pos >= 1
return f"{target}:{target[pos + 1:]}"
class GnLabelQualifier(object):
def __init__(self, host_cpu: str, target_cpu: str):
self._host_cpu = host_cpu
self._target_cpu = target_cpu
alias_dict = {
"host_cpu": host_cpu,
"target_cpu": target_cpu,
}
self._aliases = {
alias: label.format(**alias_dict)
for alias, label in _TOOLCHAIN_ALIASES.items()
}
self._default_toolchain = self._aliases["default"]
self._ninja_path_to_gn_label: Callable[[str], str] = lambda x: x
def set_ninja_path_to_gn_label(self, func: Callable[[str], str]):
self._ninja_path_to_gn_label = func
def qualify_toolchain(self, toolchain: str) -> str:
if toolchain.startswith("//"):
assert (
toolchain.find("(") < 0
), f"GN toolchain cannot have toolchain suffix: {toolchain}"
return qualify_gn_target_name(toolchain)
alias = self._aliases.get(toolchain)
assert alias is not None, f"Unknown GN toolchain alias: {toolchain}"
return alias
def qualify_label(self, label: str, fallback_toolchain: str = "") -> str:
target, toolchain = split_gn_label(label)
target = qualify_gn_target_name(target)
if toolchain:
toolchain = self.qualify_toolchain(toolchain)
if toolchain == self._default_toolchain:
toolchain = ""
else:
toolchain = fallback_toolchain
if toolchain:
return f"{target}({toolchain})"
else:
return target
def label_to_build_args(self, label: str) -> List[str]:
target, toolchain = split_gn_label(label)
target_dir, target_name = split_gn_target(target)
if target_name == os.path.basename(target_dir):
target = target_dir
if toolchain == self._default_toolchain:
toolchain = ""
if not toolchain:
return [target]
for alias, alias_toolchain in self._aliases.items():
if alias_toolchain == toolchain:
return [f"--{alias}", target]
return [f"--toolchain={toolchain}", target]
def build_args_to_labels(self, args: List[str]) -> List[str]:
result = []
cur_toolchain = ""
for arg in args:
if arg.startswith("--toolchain="):
cur_toolchain = self.qualify_toolchain(
arg[len("--toolchain=") :]
)
continue
if arg.startswith("--"):
alias = arg[2:]
cur_toolchain = self._aliases.get(alias)
assert (
cur_toolchain is not None
), f"Invalid toolchain alias {arg}"
if cur_toolchain == self._default_toolchain:
cur_toolchain = ""
continue
if arg.startswith("//"):
result.append(self.qualify_label(arg, cur_toolchain))
continue
# A Ninja target path maybe? To be handled by the caller.
label = self._ninja_path_to_gn_label(arg)
if label:
result.append(label)
return result