# pylint: disable=g-bad-file-header # Copyright 2018 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 writing crosstool files in Skylark""" # All possible C++ compile actions COMPILE_ACTIONS = [ "c-compile", "c++-compile", "c++-header-parsing", "c++-module-compile", "c++-module-codegen", "assemble", "preprocess-assemble", "clif-match", "linkstamp-compile", "cc-flags-make-variable", ] # All possible C++ link actions LINK_ACTIONS = [ "c++-link-executable", "c++-link-dynamic-library", "c++-link-nodeps-dynamic-library", ] # All possible C++ archive actions ARCHIVE_ACTIONS = [ "c++-link-static-library", ] # All remaining actions used by C++ rules that are configured in the CROSSTOOL OTHER_ACTIONS = [ "strip", ] def action_config(action_name, tool_path): """Emit action_config message. Examples: action_config("c-compile", "/usr/bin/gcc") -> action_config { config_name: 'c-compile' action_name: 'c-compile' tool { tool_path: '/usr/bin/gcc' } } Args: action_name: name of the action tool_path: absolute or CROSSTOOL-relative path to the tool Returns: a string to be placed into the CROSSTOOL """ if action_name == None or action_name == "": fail("action_name must be present") if tool_path == None or tool_path == "": fail("tool_path must be present") return """ action_config {{ config_name: '{action_name}' action_name: '{action_name}' tool {{ tool_path: '{tool_path}' }} }}""".format(action_name = action_name, tool_path = tool_path) def feature(name, flag_sets, enabled = True, provides = None): """Emit feature message. Examples: feature("fully_static_link", flag_sets, enabled = False) -> feature { name: 'fully_static_link' enabled = false } Args: name: name of the feature flag_sets: a collection of flag_set messages enabled: whether this feature is turned on by default provides: a symbol this feature provides, used to implement mutually incompatible features Returns: a string to be placed into the CROSSTOOL """ if name == None or name == "": fail("feature name must be present") return """ feature {{ name: '{name}' enabled: {enabled}{provides}{flag_sets} }}""".format( provides = ("\n provides: '%s'" % provides if provides != None else ""), name = name, enabled = _to_proto_value(enabled), flag_sets = "".join(flag_sets), ) def simple_feature( name, actions, flags, enabled = True, provides = None, expand_if_all_available = [], iterate_over = None): """Sugar for emitting simple feature message. Examples: simple_feature("foo", ['c-compile'], flags("-foo")) -> feature { name: 'foo' flag_set { action: 'c-compile' flag_group { flag: '-foo' } } } Args: name: name of the feature actions: for which actions should flags be emitted flags: a collection of flag messages enabled: whether this feature is turned on by default provides: a symbol this feature provides, used to implement mutually incompatible features expand_if_all_available: specify which build variables need to be present for this group to be expanded iterate_over: expand this flag_group for every item in the build variable Returns: a string to be placed into the CROSSTOOL """ if len(flags) == 0: return feature(name, []) else: return feature( name, [flag_set( actions, [flag_group( [flag(f) for f in flags], iterate_over = iterate_over, expand_if_all_available = expand_if_all_available, )], )], enabled = enabled, provides = provides, ) def flag_set(actions, flag_groups): """Emit flag_set message. Examples: flag_set(['c-compile'], flag_groups) -> flag_set { action: 'c-compile' } Args: actions: for which actions should flags be emitted flag_groups: a collection of flag_group messages Returns: a string to be placed into the CROSSTOOL """ if actions == None or len(actions) == 0: fail("empty actions list is not allowed for flag_set") if flag_groups == None or len(flag_groups) == 0: fail("empty flag_groups list is not allowed for flag_set") actions_string = "" for action in actions: actions_string += "\n action: '%s'" % action return """ flag_set {{{actions}{flag_groups} }}""".format(actions = actions_string, flag_groups = "".join(flag_groups)) def flag_group( content, expand_if_all_available = [], expand_if_none_available = [], expand_if_true = [], expand_if_false = [], expand_if_equal = [], iterate_over = None): """Emit flag_group message. Examples: flag_group(flags("-foo %{output_file}"), expand_if_all_available="output_file") -> flag_group { expand_if_all_available: "output_file" flag: "-foo %{output_file}" } Args: content: a collection of flag messages or a collection of flag_group messages expand_if_all_available: specify which build variables need to be present for this group to be expanded expand_if_none_available: specify which build variables need to be missing for this group to be expanded expand_if_true: specify which build variables need to be truthy for this group to be expanded expand_if_false: specify which build variables need to be falsey for this group to be expanded expand_if_equal: [[var1, value1], [var2, value2]...] specify what values should specific build variables have for this group to be expanded iterate_over: expand this flag_group for every item in the build variable Returns: a string to be placed into the CROSSTOOL """ if content == None or len(content) == 0: fail("flag_group without flags is not allowed") conditions = "" for var in expand_if_all_available: conditions += "\n expand_if_all_available: '%s'" % var for var in expand_if_none_available: conditions += "\n expand_if_none_available: '%s'" % var for var in expand_if_true: conditions += "\n expand_if_true: '%s'" % var for var in expand_if_false: conditions += "\n expand_if_false: '%s'" % var for var in expand_if_equal: conditions += "\n expand_if_equal { variable: '%s' value: '%s' }" % (var[0], var[1]) return """ flag_group {{{conditions}{iterate_over}{content} }}""".format( content = "".join(content), iterate_over = ("\n iterate_over: '%s'" % iterate_over if iterate_over != None else ""), conditions = conditions, ) def flag(flag): """Emit flag field. Examples: flag("-foo") -> flag: '-foo' Args: flag: value to be emitted to the command line Returns: a string to be placed into the CROSSTOOL """ return "\n flag: '%s'" % flag def flags(*flags): """Sugar for emitting sequence of flag fields. Examples: flags("-foo", "-bar") -> flag: '-foo' flag: '-bar' Args: *flags: values to be emitted to the command line Returns: a string to be placed into the CROSSTOOL """ return [flag(f) for f in flags] def _to_proto_value(boolean): if boolean: return "true" else: return "false"