From 7b1ba1f2a89006fbe358e97011cb1c1516435806 Mon Sep 17 00:00:00 2001 From: katre Date: Wed, 27 Sep 2017 09:53:04 -0400 Subject: [PATCH] Update rules_rust to use Bazel's toolchains feature. (#52) This allows rust targets to only depend on (and thus download) the toolchains that are used, dynamically chosen based on the current target platform. Fixes #14. --- rust/BUILD | 48 +-------- rust/repositories.bzl | 62 +++++++++++ rust/rust.bzl | 241 ++++++++++-------------------------------- rust/toolchain.bzl | 201 +++++++++++++++++++++++++++++++++++ 4 files changed, 325 insertions(+), 227 deletions(-) create mode 100644 rust/toolchain.bzl diff --git a/rust/BUILD b/rust/BUILD index 1942c9ac47..e9114f9715 100644 --- a/rust/BUILD +++ b/rust/BUILD @@ -1,45 +1,7 @@ package(default_visibility = ["//visibility:public"]) -exports_files(["rust.bzl"]) - -config_setting( - name = "darwin", - values = {"host_cpu": "darwin"}, -) - -config_setting( - name = "k8", - values = {"host_cpu": "k8"}, -) - -filegroup( - name = "rustc", - srcs = select({ - ":darwin": ["@rust_darwin_x86_64//:rustc"], - ":k8": ["@rust_linux_x86_64//:rustc"], - }), -) - -filegroup( - name = "rustc_lib", - srcs = select({ - ":darwin": ["@rust_darwin_x86_64//:rustc_lib"], - ":k8": ["@rust_linux_x86_64//:rustc_lib"], - }), -) - -filegroup( - name = "rustdoc", - srcs = select({ - ":darwin": ["@rust_darwin_x86_64//:rustdoc"], - ":k8": ["@rust_linux_x86_64//:rustdoc"], - }), -) - -filegroup( - name = "rust_lib", - srcs = select({ - ":darwin": ["@rust_darwin_x86_64//:rust_lib"], - ":k8": ["@rust_linux_x86_64//:rust_lib"], - }), -) +exports_files([ + "rust.bzl", + "toolchain.bzl", + "repositories.bzl", +]) diff --git a/rust/repositories.bzl b/rust/repositories.bzl index f7d05700e9..afd4fa785d 100644 --- a/rust/repositories.bzl +++ b/rust/repositories.bzl @@ -64,6 +64,58 @@ filegroup( ) """ +# This defines the default toolchain separately from the actual repositories, so that the remote +# repositories will only be downloaded if they are actually used. +DEFAULT_TOOLCHAINS = """ +load("@io_bazel_rules_rust//rust:toolchain.bzl", "rust_toolchain") + +toolchain( + name = "rust-linux-x86_64", + exec_compatible_with = [ + "@bazel_tools//platforms:linux", + "@bazel_tools//platforms:x86_64", + ], + target_compatible_with = [ + "@bazel_tools//platforms:linux", + "@bazel_tools//platforms:x86_64", + ], + toolchain = ":rust-linux-x86_64_impl", + toolchain_type = "@io_bazel_rules_rust//rust:toolchain", +) + +rust_toolchain( + name = "rust-linux-x86_64_impl", + rust_doc = "@rust_linux_x86_64//:rustdoc", + rust_lib = ["@rust_linux_x86_64//:rust_lib"], + rustc = "@rust_linux_x86_64//:rustc", + rustc_lib = ["@rust_linux_x86_64//:rustc_lib"], + visibility = ["//visibility:public"], +) + +toolchain( + name = "rust-darwin-x86_64", + exec_compatible_with = [ + "@bazel_tools//platforms:osx", + "@bazel_tools//platforms:x86_64", + ], + target_compatible_with = [ + "@bazel_tools//platforms:osx", + "@bazel_tools//platforms:x86_64", + ], + toolchain = ":rust-darwin-x86_64_impl", + toolchain_type = "@io_bazel_rules_rust//rust:toolchain", +) + +rust_toolchain( + name = "rust-darwin-x86_64_impl", + rust_doc = "@rust_darwin_x86_64//:rustdoc", + rust_lib = ["@rust_darwin_x86_64//:rust_lib"], + rustc = "@rust_darwin_x86_64//:rustc", + rustc_lib = ["@rust_darwin_x86_64//:rustc_lib"], + visibility = ["//visibility:public"], +) +""" + # Eventually with better toolchain hosting options we could load only one of these, not both. def rust_repositories(): native.new_http_archive( @@ -81,3 +133,13 @@ def rust_repositories(): sha256 = "38606e464b31a778ffa7d25d490a9ac53b472102bad8445b52e125f63726ac64", build_file_content = RUST_DARWIN_BUILD_FILE, ) + + native.new_local_repository( + name = "rust_default_toolchains", + path = ".", + build_file_content = DEFAULT_TOOLCHAINS) + + # Register toolchains + native.register_toolchains( + "@rust_default_toolchains//:rust-linux-x86_64", + "@rust_default_toolchains//:rust-darwin-x86_64") diff --git a/rust/rust.bzl b/rust/rust.bzl index fbf6e244fe..6974cb9ba6 100644 --- a/rust/rust.bzl +++ b/rust/rust.bzl @@ -49,6 +49,8 @@ rust_repositories() [Cargo](https://crates.io/). """ +load(":toolchain.bzl", "build_rustc_command", "build_rustdoc_command", "build_rustdoc_test_command") + RUST_FILETYPE = FileType([".rs"]) A_FILETYPE = FileType([".a"]) @@ -68,8 +70,6 @@ HTML_MD_FILETYPE = FileType([ CSS_FILETYPE = FileType([".css"]) -ZIP_PATH = "/usr/bin/zip" - def _path_parts(path): """Takes a path and returns a list of its parts with all "." elements removed. @@ -193,88 +193,8 @@ def _setup_deps(deps, name, working_dir, allow_cc_deps=False, search_flags = search_flags, link_flags = link_flags) -def _get_features_flags(features): - """ - Constructs a string containing the feature flags from the features specified - in the features attribute. - """ - features_flags = [] - for feature in features: - features_flags += ["--cfg feature=\\\"%s\\\"" % feature] - return features_flags - -def _get_dirname(short_path): - return short_path[0:short_path.rfind('/')] - -def _rust_toolchain(ctx): - return struct( - rustc_path = ctx.file._rustc.path, - rustc_lib_path = ctx.files._rustc_lib[0].dirname, - rustc_lib_short_path = _get_dirname(ctx.files._rustc_lib[0].short_path), - rust_lib_path = ctx.files._rust_lib[0].dirname, - rust_lib_short_path = _get_dirname(ctx.files._rust_lib[0].short_path), - rustdoc_path = ctx.file._rustdoc.path, - rustdoc_short_path = ctx.file._rustdoc.short_path) - -def _build_rustc_command(ctx, crate_name, crate_type, src, output_dir, - depinfo, rust_flags=[]): - """Builds the rustc command. - - Constructs the rustc command used to build the current target. - - Args: - ctx: The ctx object for the current target. - crate_type: The type of crate to build ("lib" or "bin") - src: The File object for crate root source file ("lib.rs" or "main.rs") - output_dir: The output directory for the target. - depinfo: Struct containing information about dependencies as returned by - _setup_deps - - Return: - String containing the rustc command. - """ - - # Paths to the Rust compiler and standard libraries. - toolchain = _rust_toolchain(ctx) - - # Paths to cc (for linker) and ar - cpp_fragment = ctx.fragments.cpp - cc = cpp_fragment.compiler_executable - ar = cpp_fragment.ar_executable - # Currently, the CROSSTOOL config for darwin sets ar to "libtool". Because - # rust uses ar-specific flags, use /usr/bin/ar in this case. - # TODO(dzc): This is not ideal. Remove this workaround once ar_executable - # always points to an ar binary. - ar_str = "%s" % ar - if ar_str.find("libtool", 0) != -1: - ar = "/usr/bin/ar" - - # Construct features flags - features_flags = _get_features_flags(ctx.attr.crate_features) - - return " ".join( - ["set -e;"] + - depinfo.setup_cmd + - [ - "LD_LIBRARY_PATH=%s" % toolchain.rustc_lib_path, - "DYLD_LIBRARY_PATH=%s" % toolchain.rustc_lib_path, - toolchain.rustc_path, - src.path, - "--crate-name %s" % crate_name, - "--crate-type %s" % crate_type, - "-C opt-level=3", - "--codegen ar=%s" % ar, - "--codegen linker=%s" % cc, - "--codegen link-args='%s'" % ' '.join(cpp_fragment.link_options), - "-L all=%s" % toolchain.rust_lib_path, - "--out-dir %s" % output_dir, - "--emit=dep-info,link", - ] + - features_flags + - rust_flags + - depinfo.search_flags + - depinfo.link_flags + - ctx.attr.rustc_flags) +def _find_toolchain(ctx): + return ctx.toolchains["@io_bazel_rules_rust//rust:toolchain"] def _find_crate_root_src(srcs, file_names=["lib.rs"]): """Finds the source file for the crate root.""" @@ -320,8 +240,10 @@ def _rust_library_impl(ctx): allow_cc_deps=True) # Build rustc command - cmd = _build_rustc_command( + toolchain = _find_toolchain(ctx) + cmd = build_rustc_command( ctx = ctx, + toolchain = toolchain, crate_name = ctx.label.name, crate_type = crate_type, src = lib_rs, @@ -334,10 +256,10 @@ def _rust_library_impl(ctx): ctx.files.data + depinfo.libs + depinfo.transitive_libs + - [ctx.file._rustc] + - ctx.files._rustc_lib + - ctx.files._rust_lib + - ctx.files._crosstool) + [toolchain.rustc] + + toolchain.rustc_lib + + toolchain.rust_lib + + toolchain.crosstool_files) ctx.action( inputs = compile_inputs, @@ -374,7 +296,9 @@ def _rust_binary_impl(ctx): allow_cc_deps=False) # Build rustc command. - cmd = _build_rustc_command(ctx = ctx, + toolchain = _find_toolchain(ctx) + cmd = build_rustc_command(ctx = ctx, + toolchain = toolchain, crate_name = ctx.label.name, crate_type = "bin", src = main_rs, @@ -387,10 +311,10 @@ def _rust_binary_impl(ctx): ctx.files.data + depinfo.libs + depinfo.transitive_libs + - [ctx.file._rustc] + - ctx.files._rustc_lib + - ctx.files._rust_lib + - ctx.files._crosstool) + [toolchain.rustc] + + toolchain.rustc_lib + + toolchain.rust_lib + + toolchain.crosstool_files) ctx.action( inputs = compile_inputs, @@ -438,7 +362,9 @@ def _rust_test_common(ctx, test_binary): output_dir, allow_cc_deps=True) - cmd = _build_rustc_command(ctx = ctx, + toolchain = _find_toolchain(ctx) + cmd = build_rustc_command(ctx = ctx, + toolchain = toolchain, crate_name = test_binary.basename, crate_type = target.crate_type, src = target.crate_root, @@ -449,10 +375,10 @@ def _rust_test_common(ctx, test_binary): compile_inputs = (target.srcs + depinfo.libs + depinfo.transitive_libs + - [ctx.file._rustc] + - ctx.files._rustc_lib + - ctx.files._rust_lib + - ctx.files._crosstool) + [toolchain.rustc] + + toolchain.rustc_lib + + toolchain.rust_lib + + toolchain.crosstool_files) ctx.action( inputs = compile_inputs, @@ -526,41 +452,15 @@ def _rust_doc_impl(ctx): doc_flags = _build_rustdoc_flags(ctx) # Build rustdoc command. - toolchain = _rust_toolchain(ctx) - docs_dir = rust_doc_zip.dirname + "/_rust_docs" - doc_cmd = " ".join( - ["set -e;"] + - depinfo.setup_cmd + [ - "rm -rf %s;" % docs_dir, - "mkdir %s;" % docs_dir, - "LD_LIBRARY_PATH=%s" % toolchain.rustc_lib_path, - "DYLD_LIBRARY_PATH=%s" % toolchain.rustc_lib_path, - toolchain.rustdoc_path, - lib_rs.path, - "--crate-name %s" % target.name, - "-L all=%s" % toolchain.rust_lib_path, - "-o %s" % docs_dir, - ] + - doc_flags + - depinfo.search_flags + - depinfo.link_flags + [ - "&&", - "(cd %s" % docs_dir, - "&&", - ZIP_PATH, - "-qR", - rust_doc_zip.basename, - "$(find . -type f) )", - "&&", - "mv %s/%s %s" % (docs_dir, rust_doc_zip.basename, rust_doc_zip.path), - ]) + toolchain = _find_toolchain(ctx) + doc_cmd = build_rustdoc_command(ctx, toolchain, rust_doc_zip, depinfo, lib_rs, target, doc_flags) # Rustdoc action rustdoc_inputs = (target.srcs + depinfo.libs + - [ctx.file._rustdoc] + - ctx.files._rustc_lib + - ctx.files._rust_lib) + [toolchain.rust_doc] + + toolchain.rustc_lib + + toolchain.rust_lib) ctx.action( inputs = rustdoc_inputs, @@ -595,20 +495,8 @@ def _rust_doc_test_impl(ctx): # Construct rustdoc test command, which will be written to a shell script # to be executed to run the test. - toolchain = _rust_toolchain(ctx) - doc_test_cmd = " ".join( - ["#!/bin/bash\n"] + - ["set -e\n"] + - depinfo.setup_cmd + - [ - "LD_LIBRARY_PATH=%s" % toolchain.rustc_lib_short_path, - "DYLD_LIBRARY_PATH=%s" % toolchain.rustc_lib_short_path, - toolchain.rustdoc_short_path, - "-L all=%s" % toolchain.rust_lib_short_path, - lib_rs.path, - ] + - depinfo.search_flags + - depinfo.link_flags) + toolchain = _find_toolchain(ctx) + doc_test_cmd = build_rustdoc_test_command(ctx, toolchain, depinfo, lib_rs) ctx.file_action(output = rust_doc_test, content = doc_test_cmd, @@ -617,9 +505,9 @@ def _rust_doc_test_impl(ctx): doc_test_inputs = (target.srcs + depinfo.libs + depinfo.transitive_libs + - [ctx.file._rustdoc] + - ctx.files._rustc_lib + - ctx.files._rust_lib) + [toolchain.rust_doc] + + toolchain.rustc_lib + + toolchain.rust_lib) runfiles = ctx.runfiles(files = doc_test_inputs, collect_data = True) return struct(runfiles = runfiles) @@ -639,30 +527,6 @@ _rust_common_attrs = { "rustc_flags": attr.string_list(), } -_rust_toolchain_attrs = { - "_rustc": attr.label( - default = Label("//rust:rustc"), - executable = True, - cfg = "host", - single_file = True, - ), - "_rustc_lib": attr.label( - default = Label("//rust:rustc_lib"), - ), - "_rust_lib": attr.label( - default = Label("//rust:rust_lib"), - ), - "_rustdoc": attr.label( - default = Label("//rust:rustdoc"), - executable = True, - cfg = "host", - single_file = True, - ), - "_crosstool": attr.label( - default = Label("//tools/defaults:crosstool") - ), -} - _rust_library_attrs = { "crate_type": attr.string(), } @@ -670,13 +534,14 @@ _rust_library_attrs = { rust_library = rule( _rust_library_impl, attrs = dict(_rust_common_attrs.items() + - _rust_library_attrs.items() + - _rust_toolchain_attrs.items()), - fragments = ["cpp"], + _rust_library_attrs.items()), + host_fragments = ["cpp"], outputs = { "rust_lib": "lib%{name}.rlib", }, + toolchains = ["@io_bazel_rules_rust//rust:toolchain"], ) + """Builds a Rust library crate. Args: @@ -777,10 +642,12 @@ Example: rust_binary = rule( _rust_binary_impl, - attrs = dict(_rust_common_attrs.items() + _rust_toolchain_attrs.items()), + attrs = _rust_common_attrs, executable = True, - fragments = ["cpp"], + host_fragments = ["cpp"], + toolchains = ["@io_bazel_rules_rust//rust:toolchain"], ) + """Builds a Rust binary crate. Args: @@ -900,11 +767,13 @@ Example: rust_test = rule( _rust_test_impl, - attrs = dict(_rust_common_attrs.items() + _rust_toolchain_attrs.items()), + attrs = _rust_common_attrs, executable = True, - fragments = ["cpp"], + host_fragments = ["cpp"], test = True, + toolchains = ["@io_bazel_rules_rust//rust:toolchain"], ) + """Builds a Rust test crate. Args: @@ -1062,11 +931,13 @@ Examples: rust_bench_test = rule( _rust_bench_test_impl, - attrs = dict(_rust_common_attrs.items() + _rust_toolchain_attrs.items()), + attrs = _rust_common_attrs, executable = True, - fragments = ["cpp"], + host_fragments = ["cpp"], test = True, + toolchains = ["@io_bazel_rules_rust//rust:toolchain"], ) + """Builds a Rust benchmark test. **Warning**: This rule is currently experimental. [Rust Benchmark @@ -1194,12 +1065,13 @@ _rust_doc_attrs = { rust_doc = rule( _rust_doc_impl, attrs = dict(_rust_doc_common_attrs.items() + - _rust_doc_attrs.items() + - _rust_toolchain_attrs.items()), + _rust_doc_attrs.items()), outputs = { "rust_doc_zip": "%{name}-docs.zip", }, + toolchains = ["@io_bazel_rules_rust//rust:toolchain"], ) + """Generates code documentation. Args: @@ -1253,11 +1125,12 @@ Example: rust_doc_test = rule( _rust_doc_test_impl, - attrs = dict(_rust_doc_common_attrs.items() + - _rust_toolchain_attrs.items()), + attrs = _rust_doc_common_attrs, executable = True, test = True, + toolchains = ["@io_bazel_rules_rust//rust:toolchain"], ) + """Runs Rust documentation tests. Args: diff --git a/rust/toolchain.bzl b/rust/toolchain.bzl new file mode 100644 index 0000000000..c8223a15d3 --- /dev/null +++ b/rust/toolchain.bzl @@ -0,0 +1,201 @@ +""" +Toolchain rules used by Rust. +""" + +ZIP_PATH = "/usr/bin/zip" + +# Utility methods that use the toolchain provider. +def build_rustc_command(ctx, toolchain, crate_name, crate_type, src, output_dir, + depinfo, rust_flags=[]): + """ + Constructs the rustc command used to build the current target. + """ + + # Paths to cc (for linker) and ar + cpp_fragment = ctx.host_fragments.cpp + cc = cpp_fragment.compiler_executable + ar = cpp_fragment.ar_executable + # Currently, the CROSSTOOL config for darwin sets ar to "libtool". Because + # rust uses ar-specific flags, use /usr/bin/ar in this case. + # TODO(dzc): This is not ideal. Remove this workaround once ar_executable + # always points to an ar binary. + ar_str = "%s" % ar + if ar_str.find("libtool", 0) != -1: + ar = "/usr/bin/ar" + + # Construct features flags + features_flags = _get_features_flags(ctx.attr.crate_features) + + return " ".join( + ["set -e;"] + + depinfo.setup_cmd + + [ + "LD_LIBRARY_PATH=%s" % _get_path_str(_get_dir_names(toolchain.rustc_lib)), + "DYLD_LIBRARY_PATH=%s" % _get_path_str(_get_dir_names(toolchain.rustc_lib)), + toolchain.rustc.path, + src.path, + "--crate-name %s" % crate_name, + "--crate-type %s" % crate_type, + "-C opt-level=3", + "--codegen ar=%s" % ar, + "--codegen linker=%s" % cc, + "--codegen link-args='%s'" % ' '.join(cpp_fragment.link_options), + ] + ["-L all=%s" % dir for dir in _get_dir_names(toolchain.rust_lib)] + [ + "--out-dir %s" % output_dir, + "--emit=dep-info,link", + ] + + features_flags + + rust_flags + + depinfo.search_flags + + depinfo.link_flags + + ctx.attr.rustc_flags) + +def build_rustdoc_command(ctx, toolchain, rust_doc_zip, depinfo, lib_rs, target, doc_flags): + """ + Constructs the rustdocc command used to build the current target. + """ + + docs_dir = rust_doc_zip.dirname + "/_rust_docs" + return " ".join( + ["set -e;"] + + depinfo.setup_cmd + [ + "rm -rf %s;" % docs_dir, + "mkdir %s;" % docs_dir, + "LD_LIBRARY_PATH=%s" % _get_path_str(_get_dir_names(toolchain.rustc_lib)), + "DYLD_LIBRARY_PATH=%s" % _get_path_str(_get_dir_names(toolchain.rustc_lib)), + toolchain.rust_doc.path, + lib_rs.path, + "--crate-name %s" % target.name, + ] + ["-L all=%s" % dir for dir in _get_dir_names(toolchain.rust_lib)] + [ + "-o %s" % docs_dir, + ] + + doc_flags + + depinfo.search_flags + + depinfo.link_flags + [ + "&&", + "(cd %s" % docs_dir, + "&&", + ZIP_PATH, + "-qR", + rust_doc_zip.basename, + "$(find . -type f) )", + "&&", + "mv %s/%s %s" % (docs_dir, rust_doc_zip.basename, rust_doc_zip.path), + ]) + +def build_rustdoc_test_command(ctx, toolchain, depinfo, lib_rs): + """ + Constructs the rustdocc command used to test the current target. + """ + return " ".join( + ["#!/bin/bash\n"] + + ["set -e\n"] + + depinfo.setup_cmd + + [ + "LD_LIBRARY_PATH=%s" % _get_path_str(_get_dir_names(toolchain.rustc_lib)), + "DYLD_LIBRARY_PATH=%s" % _get_path_str(_get_dir_names(toolchain.rustc_lib)), + toolchain.rust_doc.path, + ] + ["-L all=%s" % dir for dir in _get_dir_names(toolchain.rust_lib)] + [ + lib_rs.path, + ] + + depinfo.search_flags + + depinfo.link_flags) + +def _get_features_flags(features): + """ + Constructs a string containing the feature flags from the features specified + in the features attribute. + """ + features_flags = [] + for feature in features: + features_flags += ["--cfg feature=\\\"%s\\\"" % feature] + return features_flags + +def _get_dir_names(files): + dirs = {} + for f in files: + dirs[f.dirname] = None + return dirs.keys() + +def _get_path_str(dirs): + return ":".join(dirs) + +def _get_first_file(input): + if hasattr(input, "files"): + for f in input.files: + return f + return input + +def _get_files(input): + files = [] + for i in input: + if hasattr(i, "files"): + files += [f for f in i.files] + return files + +# The rust_toolchain rule definition and implementation. + +def _rust_toolchain_impl(ctx): + toolchain = platform_common.ToolchainInfo( + rustc = _get_first_file(ctx.attr.rustc), + rust_doc = _get_first_file(ctx.attr.rust_doc), + rustc_lib = _get_files(ctx.attr.rustc_lib), + rust_lib = _get_files(ctx.attr.rust_lib), + crosstool_files = ctx.files._crosstool) + return [toolchain] + +rust_toolchain = rule( + _rust_toolchain_impl, + attrs = { + "rustc": attr.label(allow_files = True), + "rust_doc": attr.label(allow_files = True), + "rustc_lib": attr.label_list(allow_files = True), + "rust_lib": attr.label_list(allow_files = True), + "_crosstool": attr.label( + default = Label("//tools/defaults:crosstool"), + ), + }, +) + +"""Declares a Rust toolchain for use. + +This is used when porting the rust_rules to a new platform. + +Args: + name: The name of the toolchain instance. + rustc: The location of the `rustc` binary. Can be a direct source or a filegroup containing one + item. + rustdoc: The location of the `rustdoc` binary. Can be a direct source or a filegroup containing + one item. + rustc_lib: The libraries used by rustc. + rust_lib: The libraries used by rustc. + +Example: + Suppose the core rust team has ported the compiler to a new target CPU, called `cpuX`. This + support can be used in Bazel by defining a new toolchain definition and declaration: + ``` + load('@io_bazel_rules_rust//rust:toolchain.bzl', 'rust_toolchain') + + toolchain( + name="rust_cpuX", + exec_compatible_with = [ + "@bazel_tools//platforms:cpuX", + ], + target_compatible_with = [ + "@bazel_tools//platforms:cpuX", + ], + toolchain = ":rust_cpuX_impl") + rust_toolchain( + name="rust_cpuX_impl", + rustc="@rust_cpuX//:rustc", + rustc_lib=["@rust_cpuX//:rustc_lib"], + rust_lib=["@rust_cpuX//:rust_lib"], + rust_doc="@rust_cpuX//:rustdoc") + ``` + + Then, either add the label of the toolchain rule to register_toolchains in the WORKSPACE, or pass + it to the "--extra_toolchains" flag for Bazel, and it will be used. + + See @io_bazel_rules_rust//rust:repositories.bzl for examples of defining the @rust_cpuX repository + with the actual binaries and libraries. +"""