| # 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.""" |
| |
| load("//cc:find_cc_toolchain.bzl", "CC_TOOLCHAIN_TYPE") |
| load(":cc_common.bzl", "cc_common") |
| load(":visibility.bzl", "INTERNAL_VISIBILITY") |
| |
| visibility(INTERNAL_VISIBILITY) |
| |
| # LINT.IfChange(linker_mode) |
| linker_mode = struct( |
| LINKING_DYNAMIC = "dynamic_linking_mode", |
| LINKING_STATIC = "static_linking_mode", |
| ) |
| # LINT.ThenChange(https://github.com/bazelbuild/bazel/blob/master/src/main/starlark/builtins_bzl/common/cc/cc_helper.bzl:linker_mode) |
| |
| # LINT.IfChange(forked_exports) |
| def _get_static_mode_params_for_dynamic_library_libraries(libs): |
| linker_inputs = [] |
| for lib in libs.to_list(): |
| if lib.pic_static_library: |
| linker_inputs.append(lib.pic_static_library) |
| elif lib.static_library: |
| linker_inputs.append(lib.static_library) |
| elif lib.interface_library: |
| linker_inputs.append(lib.interface_library) |
| else: |
| linker_inputs.append(lib.dynamic_library) |
| return linker_inputs |
| |
| def _create_strip_action(ctx, cc_toolchain, cpp_config, input, output, feature_configuration): |
| if cc_common.is_enabled(feature_configuration = feature_configuration, feature_name = "no_stripping"): |
| ctx.actions.symlink( |
| output = output, |
| target_file = input, |
| progress_message = "Symlinking original binary as stripped binary", |
| ) |
| return |
| |
| if not cc_common.action_is_enabled(feature_configuration = feature_configuration, action_name = "strip"): |
| fail("Expected action_config for 'strip' to be configured.") |
| |
| variables = cc_common.create_compile_variables( |
| cc_toolchain = cc_toolchain, |
| feature_configuration = feature_configuration, |
| output_file = output.path, |
| input_file = input.path, |
| strip_opts = cpp_config.strip_opts(), |
| ) |
| command_line = cc_common.get_memory_inefficient_command_line( |
| feature_configuration = feature_configuration, |
| action_name = "strip", |
| variables = variables, |
| ) |
| env = cc_common.get_environment_variables( |
| feature_configuration = feature_configuration, |
| action_name = "strip", |
| variables = variables, |
| ) |
| execution_info = {} |
| for execution_requirement in cc_common.get_tool_requirement_for_action(feature_configuration = feature_configuration, action_name = "strip"): |
| execution_info[execution_requirement] = "" |
| ctx.actions.run( |
| inputs = depset( |
| direct = [input], |
| transitive = [cc_toolchain._strip_files], |
| ), |
| outputs = [output], |
| use_default_shell_env = True, |
| env = env, |
| executable = cc_common.get_tool_for_action(feature_configuration = feature_configuration, action_name = "strip"), |
| toolchain = CC_TOOLCHAIN_TYPE, |
| execution_requirements = execution_info, |
| progress_message = "Stripping {} for {}".format(output.short_path, ctx.label), |
| mnemonic = "CcStrip", |
| arguments = command_line, |
| ) |
| |
| 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)) |
| |
| def _should_use_pic(ctx, cc_toolchain, feature_configuration): |
| """Whether to use pic files |
| |
| Args: |
| ctx: (RuleContext) |
| cc_toolchain: (CcToolchainInfo) |
| feature_configuration: (FeatureConfiguration) |
| |
| Returns: |
| (bool) |
| """ |
| return ctx.fragments.cpp.force_pic() or ( |
| cc_toolchain.needs_pic_for_dynamic_libraries(feature_configuration = feature_configuration) and ( |
| ctx.var["COMPILATION_MODE"] != "opt" or |
| cc_common.is_enabled(feature_configuration = feature_configuration, feature_name = "prefer_pic_for_opt_binaries") |
| ) |
| ) |
| |
| cc_helper = struct( |
| create_strip_action = _create_strip_action, |
| get_expanded_env = _get_expanded_env, |
| get_static_mode_params_for_dynamic_library_libraries = _get_static_mode_params_for_dynamic_library_libraries, |
| should_use_pic = _should_use_pic, |
| tokenize = _tokenize, |
| ) |
| # LINT.ThenChange(https://github.com/bazelbuild/bazel/blob/master/src/main/starlark/builtins_bzl/common/cc/cc_helper.bzl:forked_exports) |