mirror of
https://github.com/bazelbuild/rules_cc
synced 2024-11-27 20:43:26 +00:00
Export a few utility functions from @builtins
to rules_cc
PiperOrigin-RevId: 670979420 Change-Id: I37ce050f747a0dbc5ab0039fd32a84d7be7e1ed4
This commit is contained in:
parent
a778282a15
commit
391170f339
|
@ -21,6 +21,12 @@ bzl_library(
|
|||
deps = ["//cc/private/rules_impl:native_bzl"],
|
||||
)
|
||||
|
||||
bzl_library(
|
||||
name = "cc_helper_bzl",
|
||||
srcs = ["cc_helper.bzl"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "srcs",
|
||||
srcs = glob([
|
||||
|
|
203
cc/common/cc_helper.bzl
Normal file
203
cc/common/cc_helper.bzl
Normal file
|
@ -0,0 +1,203 @@
|
|||
# Copyright 2020 The Bazel Authors. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Utility functions for C++ rules."""
|
||||
|
||||
# LINT.IfChange
|
||||
|
||||
def _lookup_var(ctx, additional_vars, var):
|
||||
expanded_make_var_ctx = ctx.var.get(var)
|
||||
expanded_make_var_additional = additional_vars.get(var)
|
||||
if expanded_make_var_additional != None:
|
||||
return expanded_make_var_additional
|
||||
if expanded_make_var_ctx != None:
|
||||
return expanded_make_var_ctx
|
||||
fail("{}: {} not defined".format(ctx.label, "$(" + var + ")"))
|
||||
|
||||
def _expand_nested_variable(ctx, additional_vars, exp, execpath = True, targets = []):
|
||||
# If make variable is predefined path variable(like $(location ...))
|
||||
# we will expand it first.
|
||||
if exp.find(" ") != -1:
|
||||
if not execpath:
|
||||
if exp.startswith("location"):
|
||||
exp = exp.replace("location", "rootpath", 1)
|
||||
data_targets = []
|
||||
if ctx.attr.data != None:
|
||||
data_targets = ctx.attr.data
|
||||
|
||||
# Make sure we do not duplicate targets.
|
||||
unified_targets_set = {}
|
||||
for data_target in data_targets:
|
||||
unified_targets_set[data_target] = True
|
||||
for target in targets:
|
||||
unified_targets_set[target] = True
|
||||
return ctx.expand_location("$({})".format(exp), targets = unified_targets_set.keys())
|
||||
|
||||
# Recursively expand nested make variables, but since there is no recursion
|
||||
# in Starlark we will do it via for loop.
|
||||
unbounded_recursion = True
|
||||
|
||||
# The only way to check if the unbounded recursion is happening or not
|
||||
# is to have a look at the depth of the recursion.
|
||||
# 10 seems to be a reasonable number, since it is highly unexpected
|
||||
# to have nested make variables which are expanding more than 10 times.
|
||||
for _ in range(10):
|
||||
exp = _lookup_var(ctx, additional_vars, exp)
|
||||
if len(exp) >= 3 and exp[0] == "$" and exp[1] == "(" and exp[len(exp) - 1] == ")":
|
||||
# Try to expand once more.
|
||||
exp = exp[2:len(exp) - 1]
|
||||
continue
|
||||
unbounded_recursion = False
|
||||
break
|
||||
|
||||
if unbounded_recursion:
|
||||
fail("potentially unbounded recursion during expansion of {}".format(exp))
|
||||
return exp
|
||||
|
||||
def _expand(ctx, expression, additional_make_variable_substitutions, execpath = True, targets = []):
|
||||
idx = 0
|
||||
last_make_var_end = 0
|
||||
result = []
|
||||
n = len(expression)
|
||||
for _ in range(n):
|
||||
if idx >= n:
|
||||
break
|
||||
if expression[idx] != "$":
|
||||
idx += 1
|
||||
continue
|
||||
|
||||
idx += 1
|
||||
|
||||
# We've met $$ pattern, so $ is escaped.
|
||||
if idx < n and expression[idx] == "$":
|
||||
idx += 1
|
||||
result.append(expression[last_make_var_end:idx - 1])
|
||||
last_make_var_end = idx
|
||||
# We might have found a potential start for Make Variable.
|
||||
|
||||
elif idx < n and expression[idx] == "(":
|
||||
# Try to find the closing parentheses.
|
||||
make_var_start = idx
|
||||
make_var_end = make_var_start
|
||||
for j in range(idx + 1, n):
|
||||
if expression[j] == ")":
|
||||
make_var_end = j
|
||||
break
|
||||
|
||||
# Note we cannot go out of string's bounds here,
|
||||
# because of this check.
|
||||
# If start of the variable is different from the end,
|
||||
# we found a make variable.
|
||||
if make_var_start != make_var_end:
|
||||
# Some clarifications:
|
||||
# *****$(MAKE_VAR_1)*******$(MAKE_VAR_2)*****
|
||||
# ^ ^ ^
|
||||
# | | |
|
||||
# last_make_var_end make_var_start make_var_end
|
||||
result.append(expression[last_make_var_end:make_var_start - 1])
|
||||
make_var = expression[make_var_start + 1:make_var_end]
|
||||
exp = _expand_nested_variable(ctx, additional_make_variable_substitutions, make_var, execpath, targets)
|
||||
result.append(exp)
|
||||
|
||||
# Update indexes.
|
||||
idx = make_var_end + 1
|
||||
last_make_var_end = idx
|
||||
|
||||
# Add the last substring which would be skipped by for loop.
|
||||
if last_make_var_end < n:
|
||||
result.append(expression[last_make_var_end:n])
|
||||
|
||||
return "".join(result)
|
||||
|
||||
def _get_expanded_env(ctx, additional_make_variable_substitutions):
|
||||
if not hasattr(ctx.attr, "env"):
|
||||
fail("could not find rule attribute named: 'env'")
|
||||
expanded_env = {}
|
||||
for k in ctx.attr.env:
|
||||
expanded_env[k] = _expand(
|
||||
ctx,
|
||||
ctx.attr.env[k],
|
||||
additional_make_variable_substitutions,
|
||||
# By default, Starlark `ctx.expand_location` has `execpath` semantics.
|
||||
# For legacy attributes, e.g. `env`, we want `rootpath` semantics instead.
|
||||
execpath = False,
|
||||
)
|
||||
return expanded_env
|
||||
|
||||
# Implementation of Bourne shell tokenization.
|
||||
# Tokenizes str and appends result to the options list.
|
||||
def _tokenize(options, options_string):
|
||||
token = []
|
||||
force_token = False
|
||||
quotation = "\0"
|
||||
length = len(options_string)
|
||||
|
||||
# Since it is impossible to modify loop variable inside loop
|
||||
# in Starlark, and also there is no while loop, I have to
|
||||
# use this ugly hack.
|
||||
i = -1
|
||||
for _ in range(length):
|
||||
i += 1
|
||||
if i >= length:
|
||||
break
|
||||
c = options_string[i]
|
||||
if quotation != "\0":
|
||||
# In quotation.
|
||||
if c == quotation:
|
||||
# End quotation.
|
||||
quotation = "\0"
|
||||
elif c == "\\" and quotation == "\"":
|
||||
i += 1
|
||||
if i == length:
|
||||
fail("backslash at the end of the string: {}".format(options_string))
|
||||
c = options_string[i]
|
||||
if c != "\\" and c != "\"":
|
||||
token.append("\\")
|
||||
token.append(c)
|
||||
else:
|
||||
# Regular char, in quotation.
|
||||
token.append(c)
|
||||
else:
|
||||
# Not in quotation.
|
||||
if c == "'" or c == "\"":
|
||||
# Begin single double quotation.
|
||||
quotation = c
|
||||
force_token = True
|
||||
elif c == " " or c == "\t":
|
||||
# Space not quoted.
|
||||
if force_token or len(token) > 0:
|
||||
options.append("".join(token))
|
||||
token = []
|
||||
force_token = False
|
||||
elif c == "\\":
|
||||
# Backslash not quoted.
|
||||
i += 1
|
||||
if i == length:
|
||||
fail("backslash at the end of the string: {}".format(options_string))
|
||||
token.append(options_string[i])
|
||||
else:
|
||||
# Regular char, not quoted.
|
||||
token.append(c)
|
||||
if quotation != "\0":
|
||||
fail("unterminated quotation at the end of the string: {}".format(options_string))
|
||||
|
||||
if force_token or len(token) > 0:
|
||||
options.append("".join(token))
|
||||
|
||||
cc_helper = struct(
|
||||
get_expanded_env = _get_expanded_env,
|
||||
tokenize = _tokenize,
|
||||
)
|
||||
|
||||
# LINT.ThenChange(https://github.com/bazelbuild/bazel/blob/master/src/main/starlark/builtins_bzl/common/cc/cc_helper.bzl)
|
Loading…
Reference in a new issue