-
Notifications
You must be signed in to change notification settings - Fork 102
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Export a few utility functions from
@builtins
to rules_cc
PiperOrigin-RevId: 670979420 Change-Id: I37ce050f747a0dbc5ab0039fd32a84d7be7e1ed4
- Loading branch information
1 parent
a778282
commit 391170f
Showing
2 changed files
with
209 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,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) |