blob: e3af7c501e685f2719c5943023ea3705f7dcf015 [file] [log] [blame]
# 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
<flags_sets>
}
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'
<flag_groups>
}
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"