• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2017 The TensorFlow Authors. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#    http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Utilities for defining TensorFlow Bazel dependencies."""
16
17_SINGLE_URL_WHITELIST = depset([
18    "arm_compiler",
19])
20
21def _is_windows(ctx):
22    return ctx.os.name.lower().find("windows") != -1
23
24def _wrap_bash_cmd(ctx, cmd):
25    if _is_windows(ctx):
26        bazel_sh = _get_env_var(ctx, "BAZEL_SH")
27        if not bazel_sh:
28            fail("BAZEL_SH environment variable is not set")
29        cmd = [bazel_sh, "-l", "-c", " ".join(["\"%s\"" % s for s in cmd])]
30    return cmd
31
32def _get_env_var(ctx, name):
33    if name in ctx.os.environ:
34        return ctx.os.environ[name]
35    else:
36        return None
37
38# Checks if we should use the system lib instead of the bundled one
39def _use_system_lib(ctx, name):
40    syslibenv = _get_env_var(ctx, "TF_SYSTEM_LIBS")
41    if syslibenv:
42        for n in syslibenv.strip().split(","):
43            if n.strip() == name:
44                return True
45    return False
46
47# Executes specified command with arguments and calls 'fail' if it exited with
48# non-zero code
49def _execute_and_check_ret_code(repo_ctx, cmd_and_args):
50    result = repo_ctx.execute(cmd_and_args, timeout = 60)
51    if result.return_code != 0:
52        fail(("Non-zero return code({1}) when executing '{0}':\n" + "Stdout: {2}\n" +
53              "Stderr: {3}").format(
54            " ".join([str(x) for x in cmd_and_args]),
55            result.return_code,
56            result.stdout,
57            result.stderr,
58        ))
59
60def _repos_are_siblings():
61    return Label("@foo//bar").workspace_root.startswith("../")
62
63# Apply a patch_file to the repository root directory
64# Runs 'patch -p1' on both Windows and Unix.
65def _apply_patch(ctx, patch_file):
66    patch_command = ["patch", "-p1", "-d", ctx.path("."), "-i", ctx.path(patch_file)]
67    cmd = _wrap_bash_cmd(ctx, patch_command)
68    _execute_and_check_ret_code(ctx, cmd)
69
70def _apply_delete(ctx, paths):
71    for path in paths:
72        if path.startswith("/"):
73            fail("refusing to rm -rf path starting with '/': " + path)
74        if ".." in path:
75            fail("refusing to rm -rf path containing '..': " + path)
76    cmd = _wrap_bash_cmd(ctx, ["rm", "-rf"] + [ctx.path(path) for path in paths])
77    _execute_and_check_ret_code(ctx, cmd)
78
79def _tf_http_archive(ctx):
80    if ("mirror.tensorflow.org" not in ctx.attr.urls[0] and
81        (len(ctx.attr.urls) < 2 and
82         ctx.attr.name not in _SINGLE_URL_WHITELIST.to_list())):
83        fail("tf_http_archive(urls) must have redundant URLs. The " +
84             "mirror.tensorflow.org URL must be present and it must come first. " +
85             "Even if you don't have permission to mirror the file, please " +
86             "put the correctly formatted mirror URL there anyway, because " +
87             "someone will come along shortly thereafter and mirror the file.")
88
89    use_syslib = _use_system_lib(ctx, ctx.attr.name)
90
91    # Work around the bazel bug that redownloads the whole library.
92    # Remove this after https://github.com/bazelbuild/bazel/issues/10515 is fixed.
93    if ctx.attr.additional_build_files:
94        for internal_src in ctx.attr.additional_build_files:
95            _ = ctx.path(Label(internal_src))
96
97    # End of workaround.
98
99    if not use_syslib:
100        ctx.download_and_extract(
101            ctx.attr.urls,
102            "",
103            ctx.attr.sha256,
104            ctx.attr.type,
105            ctx.attr.strip_prefix,
106        )
107        if ctx.attr.delete:
108            _apply_delete(ctx, ctx.attr.delete)
109        if ctx.attr.patch_file != None:
110            _apply_patch(ctx, ctx.attr.patch_file)
111
112    if use_syslib and ctx.attr.system_build_file != None:
113        # Use BUILD.bazel to avoid conflict with third party projects with
114        # BUILD or build (directory) underneath.
115        ctx.template("BUILD.bazel", ctx.attr.system_build_file, {
116            "%prefix%": ".." if _repos_are_siblings() else "external",
117        }, False)
118
119    elif ctx.attr.build_file != None:
120        # Use BUILD.bazel to avoid conflict with third party projects with
121        # BUILD or build (directory) underneath.
122        ctx.template("BUILD.bazel", ctx.attr.build_file, {
123            "%prefix%": ".." if _repos_are_siblings() else "external",
124        }, False)
125
126    if use_syslib:
127        for internal_src, external_dest in ctx.attr.system_link_files.items():
128            ctx.symlink(Label(internal_src), ctx.path(external_dest))
129
130    if ctx.attr.additional_build_files:
131        for internal_src, external_dest in ctx.attr.additional_build_files.items():
132            ctx.symlink(Label(internal_src), ctx.path(external_dest))
133
134tf_http_archive = repository_rule(
135    attrs = {
136        "sha256": attr.string(mandatory = True),
137        "urls": attr.string_list(
138            mandatory = True,
139            allow_empty = False,
140        ),
141        "strip_prefix": attr.string(),
142        "type": attr.string(),
143        "delete": attr.string_list(),
144        "patch_file": attr.label(),
145        "build_file": attr.label(),
146        "system_build_file": attr.label(),
147        "system_link_files": attr.string_dict(),
148        "additional_build_files": attr.string_dict(),
149    },
150    environ = [
151        "TF_SYSTEM_LIBS",
152    ],
153    implementation = _tf_http_archive,
154)
155
156"""Downloads and creates Bazel repos for dependencies.
157
158This is a swappable replacement for both http_archive() and
159new_http_archive() that offers some additional features. It also helps
160ensure best practices are followed.
161"""
162
163def _third_party_http_archive(ctx):
164    if ("mirror.tensorflow.org" not in ctx.attr.urls[0] and
165        (len(ctx.attr.urls) < 2 and
166         ctx.attr.name not in _SINGLE_URL_WHITELIST.to_list())):
167        fail("tf_http_archive(urls) must have redundant URLs. The " +
168             "mirror.tensorflow.org URL must be present and it must come first. " +
169             "Even if you don't have permission to mirror the file, please " +
170             "put the correctly formatted mirror URL there anyway, because " +
171             "someone will come along shortly thereafter and mirror the file.")
172
173    use_syslib = _use_system_lib(ctx, ctx.attr.name)
174
175    # Use "BUILD.bazel" to avoid conflict with third party projects that contain a
176    # file or directory called "BUILD"
177    buildfile_path = ctx.path("BUILD.bazel")
178
179    if use_syslib:
180        if ctx.attr.system_build_file == None:
181            fail("Bazel was configured with TF_SYSTEM_LIBS to use a system " +
182                 "library for %s, but no system build file for %s was configured. " +
183                 "Please add a system_build_file attribute to the repository rule" +
184                 "for %s." % (ctx.attr.name, ctx.attr.name, ctx.attr.name))
185        ctx.symlink(Label(ctx.attr.system_build_file), buildfile_path)
186
187    else:
188        ctx.download_and_extract(
189            ctx.attr.urls,
190            "",
191            ctx.attr.sha256,
192            ctx.attr.type,
193            ctx.attr.strip_prefix,
194        )
195        if ctx.attr.delete:
196            _apply_delete(ctx, ctx.attr.delete)
197        if ctx.attr.patch_file != None:
198            _apply_patch(ctx, ctx.attr.patch_file)
199        ctx.symlink(Label(ctx.attr.build_file), buildfile_path)
200
201    link_dict = {}
202    if use_syslib:
203        link_dict.update(ctx.attr.system_link_files)
204
205    for internal_src, external_dest in ctx.attr.link_files.items():
206        # if syslib and link exists in both, use the system one
207        if external_dest not in link_dict.values():
208            link_dict[internal_src] = external_dest
209
210    for internal_src, external_dest in link_dict.items():
211        ctx.symlink(Label(internal_src), ctx.path(external_dest))
212
213# Downloads and creates Bazel repos for dependencies.
214#
215# This is an upgrade for tf_http_archive that works with go/tfbr-thirdparty.
216#
217# For link_files, specify each dict entry as:
218# "//path/to/source:file": "localfile"
219third_party_http_archive = repository_rule(
220    attrs = {
221        "sha256": attr.string(mandatory = True),
222        "urls": attr.string_list(
223            mandatory = True,
224            allow_empty = False,
225        ),
226        "strip_prefix": attr.string(),
227        "type": attr.string(),
228        "delete": attr.string_list(),
229        "build_file": attr.string(mandatory = True),
230        "system_build_file": attr.string(mandatory = False),
231        "patch_file": attr.label(),
232        "link_files": attr.string_dict(),
233        "system_link_files": attr.string_dict(),
234    },
235    environ = [
236        "TF_SYSTEM_LIBS",
237    ],
238    implementation = _third_party_http_archive,
239)
240