Initial check-in.
This commit is contained in:
commit
82b3ad6e9e
|
@ -0,0 +1,9 @@
|
|||
# This the official list of Bazel authors for copyright purposes.
|
||||
# This file is distinct from the CONTRIBUTORS files.
|
||||
# See the latter for an explanation.
|
||||
|
||||
# Names should be added to this file as:
|
||||
# Name or Organization <email address>
|
||||
# The email address is not required for organizations.
|
||||
|
||||
Google Inc.
|
|
@ -0,0 +1,16 @@
|
|||
licenses(["notice"])
|
||||
|
||||
exports_files([
|
||||
"LICENSE",
|
||||
"lib.bzl",
|
||||
])
|
||||
|
||||
filegroup(
|
||||
name = "test_deps",
|
||||
srcs = [
|
||||
"BUILD",
|
||||
"//lib:test_deps",
|
||||
] + glob(["*.bzl"]),
|
||||
test_only = True,
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
|
@ -0,0 +1,27 @@
|
|||
Want to contribute? Great! First, read this page (including the small print at the end).
|
||||
|
||||
### Before you contribute
|
||||
**Before we can use your code, you must sign the
|
||||
[Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual?csw=1)
|
||||
(CLA)**, which you can do online.
|
||||
|
||||
The CLA is necessary mainly because you own the copyright to your changes,
|
||||
even after your contribution becomes part of our codebase, so we need your
|
||||
permission to use and distribute your code. We also need to be sure of
|
||||
various other things — for instance that you'll tell us if you know that
|
||||
your code infringes on other people's patents. You don't have to sign
|
||||
the CLA until after you've submitted your code for review and a member has
|
||||
approved it, but you must do it before we can put your code into our codebase.
|
||||
|
||||
Before you start working on a larger contribution, you should get in touch
|
||||
with us first. Use the issue tracker to explain your idea so we can help and
|
||||
possibly guide you.
|
||||
|
||||
### Code reviews and other contributions.
|
||||
**All submissions, including submissions by project members, require review.**
|
||||
Please follow the instructions in [the contributors documentation](http://bazel.io/contributing.html).
|
||||
|
||||
### The small print
|
||||
Contributions made by corporations are covered by a different agreement than
|
||||
the one above, the
|
||||
[Software Grant and Corporate Contributor License Agreement](https://cla.developers.google.com/about/google-corporate).
|
|
@ -0,0 +1,15 @@
|
|||
# People who have agreed to one of the CLAs and can contribute patches.
|
||||
# The AUTHORS file lists the copyright holders; this file
|
||||
# lists people. For example, Google employees are listed here
|
||||
# but not in AUTHORS, because Google holds the copyright.
|
||||
#
|
||||
# https://developers.google.com/open-source/cla/individual
|
||||
# https://developers.google.com/open-source/cla/corporate
|
||||
#
|
||||
# Names should be added to this file as:
|
||||
# Name <email address>
|
||||
|
||||
Dmitry Lomov <dslomov@google.com>
|
||||
Jon Brandvein <brandjon@google.com>
|
||||
Laurent Le Brun <laurentlb@google.com>
|
||||
Tony Allevato <allevato@google.com>
|
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
|
@ -0,0 +1,72 @@
|
|||
# Skylib
|
||||
|
||||
Skylib is a standard library that provides functions useful for manipulating
|
||||
collections, file paths, and other features that are useful when writing custom
|
||||
build rules in Bazel.
|
||||
|
||||
> This library is currently under early development. Be aware that the APIs
|
||||
> in these modules may change during this time.
|
||||
|
||||
Each of the `.bzl` files in the `lib` directory defines a "module"—a
|
||||
`struct` that contains a set of related functions and/or other symbols that can
|
||||
be loaded as a single unit, for convenience. The top-level file `lib.bzl` acts
|
||||
as an index from which the other modules can be imported.
|
||||
|
||||
To use the functionality here, import the modules you need from `lib.bzl` and
|
||||
access the symbols by dotting into those structs:
|
||||
|
||||
```python
|
||||
load("@bazel_skylib//:lib.bzl", "paths", "shell")
|
||||
|
||||
p = paths.basename("foo.bar")
|
||||
s = shell.quote(p)
|
||||
```
|
||||
|
||||
## List of modules
|
||||
|
||||
* [collections](blob/master/lib/collections.bzl)
|
||||
* [dicts](blob/master/lib/dicts.bzl)
|
||||
* [paths](blob/master/lib/paths.bzl)
|
||||
* [selects](blob/master/lib/selects.bzl)
|
||||
* [sets](blob/master/lib/sets.bzl)
|
||||
* [shell](blob/master/lib/shell.bzl)
|
||||
* [unittest](blob/master/lib/unittest.bzl)
|
||||
|
||||
## Writing a new module
|
||||
|
||||
Steps to add a module to Skylib:
|
||||
|
||||
1. Create a new `.bzl` file in the `lib` directory.
|
||||
|
||||
1. Write the functions or other symbols (such as constants) in that file,
|
||||
defining them privately (prefixed by an underscore).
|
||||
|
||||
1. Create the exported module struct, mapping the public names of the symbols
|
||||
to their implementations. For example, if your module was named `things` and
|
||||
had a function named `manipulate`, your `things.bzl` file would look like
|
||||
this:
|
||||
|
||||
```python
|
||||
def _manipulate():
|
||||
...
|
||||
|
||||
things = struct(
|
||||
manipulate=_manipulate,
|
||||
)
|
||||
```
|
||||
|
||||
1. Add a line to `lib.bzl` to make the new module accessible from the index:
|
||||
|
||||
```python
|
||||
load("@bazel_skylib//lib:things.bzl", "things")
|
||||
```
|
||||
|
||||
1. Clients can then use the module by loading it from `lib.bzl`:
|
||||
|
||||
```python
|
||||
load("@bazel_skylib//:lib.bzl", "things")
|
||||
|
||||
things.manipulate()
|
||||
```
|
||||
|
||||
1. Add unit tests for your module in the `tests` directory.
|
|
@ -0,0 +1,27 @@
|
|||
# Copyright 2017 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.
|
||||
|
||||
"""Index from which multiple modules can be loaded."""
|
||||
|
||||
load("//lib:collections.bzl", "collections")
|
||||
load("//lib:dicts.bzl", "dicts")
|
||||
load("//lib:paths.bzl", "paths")
|
||||
load("//lib:selects.bzl", "selects")
|
||||
load("//lib:sets.bzl", "sets")
|
||||
load("//lib:shell.bzl", "shell")
|
||||
load("//lib:structs.bzl", "structs")
|
||||
|
||||
# The unittest module is treated differently to give more convenient names to
|
||||
# the assert functions, while keeping them in the same .bzl file.
|
||||
load("//lib:unittest.bzl", "asserts", "unittest")
|
|
@ -0,0 +1,8 @@
|
|||
licenses(["notice"])
|
||||
|
||||
filegroup(
|
||||
name = "test_deps",
|
||||
srcs = ["BUILD"] + glob(["*.bzl"]),
|
||||
test_only = True,
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
|
@ -0,0 +1,70 @@
|
|||
# Copyright 2017 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.
|
||||
|
||||
"""Skylib module containing functions that operate on collections."""
|
||||
|
||||
|
||||
def _after_each(separator, iterable):
|
||||
"""Inserts `separator` after each item in `iterable`.
|
||||
|
||||
Args:
|
||||
separator: The value to insert after each item in `iterable`.
|
||||
iterable: The list into which to intersperse the separator.
|
||||
Returns:
|
||||
A new list with `separator` after each item in `iterable`.
|
||||
"""
|
||||
result = []
|
||||
for x in iterable:
|
||||
result.append(x)
|
||||
result.append(separator)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _before_each(separator, iterable):
|
||||
"""Inserts `separator` before each item in `iterable`.
|
||||
|
||||
Args:
|
||||
separator: The value to insert before each item in `iterable`.
|
||||
iterable: The list into which to intersperse the separator.
|
||||
Returns:
|
||||
A new list with `separator` before each item in `iterable`.
|
||||
"""
|
||||
result = []
|
||||
for x in iterable:
|
||||
result.append(separator)
|
||||
result.append(x)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _uniq(iterable):
|
||||
"""Returns a list of unique elements in `iterable`.
|
||||
|
||||
Requires all the elements to be hashable.
|
||||
|
||||
Args:
|
||||
iterable: An iterable to filter.
|
||||
Returns:
|
||||
A new list with all unique elements from `iterable`.
|
||||
"""
|
||||
unique_elements = {element: None for element in iterable}
|
||||
return unique_elements.keys()
|
||||
|
||||
|
||||
collections = struct(
|
||||
after_each=_after_each,
|
||||
before_each=_before_each,
|
||||
uniq=_uniq,
|
||||
)
|
|
@ -0,0 +1,42 @@
|
|||
# Copyright 2017 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.
|
||||
|
||||
"""Skylib module containing functions that operate on dictionaries."""
|
||||
|
||||
|
||||
def _add(*dictionaries):
|
||||
"""Returns a new `dict` that has all the entries of the given dictionaries.
|
||||
|
||||
If the same key is present in more than one of the input dictionaries, the
|
||||
last of them in the argument list overrides any earlier ones.
|
||||
|
||||
This function is designed to take zero or one arguments as well as multiple
|
||||
dictionaries, so that it follows arithmetic identities and callers can avoid
|
||||
special cases for their inputs: the sum of zero dictionaries is the empty
|
||||
dictionary, and the sum of a single dictionary is a copy of itself.
|
||||
|
||||
Args:
|
||||
*dictionaries: Zero or more dictionaries to be added.
|
||||
Returns:
|
||||
A new `dict` that has all the entries of the given dictionaries.
|
||||
"""
|
||||
result = {}
|
||||
for d in dictionaries:
|
||||
result.update(d)
|
||||
return result
|
||||
|
||||
|
||||
dicts = struct(
|
||||
add=_add,
|
||||
)
|
|
@ -0,0 +1,245 @@
|
|||
# Copyright 2017 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.
|
||||
|
||||
"""Skylib module containing file path manipulation functions.
|
||||
|
||||
NOTE: The functions in this module currently only support paths with Unix-style
|
||||
path separators (forward slash, "/"); they do not handle Windows-style paths
|
||||
with backslash separators or drive letters.
|
||||
"""
|
||||
|
||||
|
||||
def _basename(p):
|
||||
"""Returns the basename (i.e., the file portion) of a path.
|
||||
|
||||
Note that if `p` ends with a slash, this function returns an empty string.
|
||||
This matches the behavior of Python's `os.path.basename`, but differs from
|
||||
the Unix `basename` command (which would return the path segment preceding
|
||||
the final slash).
|
||||
|
||||
Args:
|
||||
p: The path whose basename should be returned.
|
||||
Returns:
|
||||
The basename of the path, which includes the extension.
|
||||
"""
|
||||
return p.rpartition("/")[-1]
|
||||
|
||||
|
||||
def _dirname(p):
|
||||
"""Returns the dirname of a path.
|
||||
|
||||
The dirname is the portion of `p` up to but not including the file portion
|
||||
(i.e., the basename). Any slashes immediately preceding the basename are not
|
||||
included, unless omitting them would make the dirname empty.
|
||||
|
||||
Args:
|
||||
p: The path whose dirname should be returned.
|
||||
Returns:
|
||||
The dirname of the path.
|
||||
"""
|
||||
prefix, sep, _ = p.rpartition("/")
|
||||
if not prefix:
|
||||
return sep
|
||||
else:
|
||||
# If there are multiple consecutive slashes, strip them all out as Python's
|
||||
# os.path.dirname does.
|
||||
return prefix.rstrip("/")
|
||||
|
||||
|
||||
def _is_absolute(path):
|
||||
"""Returns `True` if `path` is an absolute path.
|
||||
|
||||
Args:
|
||||
path: A path (which is a string).
|
||||
Returns:
|
||||
`True` if `path` is an absolute path.
|
||||
"""
|
||||
return path.startswith("/")
|
||||
|
||||
|
||||
def _join(path, *others):
|
||||
"""Joins one or more path components intelligently.
|
||||
|
||||
This function mimics the behavior of Python's `os.path.join` function on POSIX
|
||||
platform. It returns the concatenation of `path` and any members of `others`,
|
||||
inserting directory separators before each component except the first. The
|
||||
separator is not inserted if the path up until that point is either empty or
|
||||
already ends in a separator.
|
||||
|
||||
If any component is an absolute path, all previous components are discarded.
|
||||
|
||||
Args:
|
||||
path: A path segment.
|
||||
*others: Additional path segments.
|
||||
Returns:
|
||||
A string containing the joined paths.
|
||||
"""
|
||||
result = path
|
||||
|
||||
for p in others:
|
||||
if _is_absolute(p):
|
||||
result = p
|
||||
elif not result or result.endswith("/"):
|
||||
result += p
|
||||
else:
|
||||
result += "/" + p
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _normalize(path):
|
||||
"""Normalizes a path, eliminating double slashes and other redundant segments.
|
||||
|
||||
This function mimics the behavior of Python's `os.path.normpath` function on
|
||||
POSIX platforms; specifically:
|
||||
|
||||
- If the entire path is empty, "." is returned.
|
||||
- All "." segments are removed, unless the path consists solely of a single
|
||||
"." segment.
|
||||
- Trailing slashes are removed, unless the path consists solely of slashes.
|
||||
- ".." segments are removed as long as there are corresponding segments
|
||||
earlier in the path to remove; otherwise, they are retained as leading ".."
|
||||
segments.
|
||||
- Single and double leading slashes are preserved, but three or more leading
|
||||
slashes are collapsed into a single leading slash.
|
||||
- Multiple adjacent internal slashes are collapsed into a single slash.
|
||||
|
||||
Args:
|
||||
path: A path.
|
||||
Returns:
|
||||
The normalized path.
|
||||
"""
|
||||
if not path:
|
||||
return "."
|
||||
|
||||
if path.startswith("//") and not path.startswith("///"):
|
||||
initial_slashes = 2
|
||||
elif path.startswith("/"):
|
||||
initial_slashes = 1
|
||||
else:
|
||||
initial_slashes = 0
|
||||
is_relative = (initial_slashes == 0)
|
||||
|
||||
components = path.split("/")
|
||||
new_components = []
|
||||
|
||||
for component in components:
|
||||
if component in ("", "."):
|
||||
continue
|
||||
if component == "..":
|
||||
if new_components and new_components[-1] != "..":
|
||||
# Only pop the last segment if it isn't another "..".
|
||||
new_components.pop()
|
||||
elif is_relative:
|
||||
# Preserve leading ".." segments for relative paths.
|
||||
new_components.append(component)
|
||||
else:
|
||||
new_components.append(component)
|
||||
|
||||
path = "/".join(new_components)
|
||||
if not is_relative:
|
||||
path = ("/" * initial_slashes) + path
|
||||
|
||||
return path or "."
|
||||
|
||||
|
||||
def _relativize(path, start):
|
||||
"""Returns the portion of `path` that is relative to `start`.
|
||||
|
||||
Because we do not have access to the underlying file system, this
|
||||
implementation differs slightly from Python's `os.path.relpath` in that it
|
||||
will fail if `path` is not beneath `start` (rather than use parent segments to
|
||||
walk up to the common file system root).
|
||||
|
||||
Relativizing paths that start with parent directory references is not allowed.
|
||||
|
||||
Args:
|
||||
path: The path to relativize.
|
||||
start: The ancestor path against which to relativize.
|
||||
Returns:
|
||||
The portion of `path` that is relative to `start`.
|
||||
"""
|
||||
segments = _normalize(path).split("/")
|
||||
start_segments = _normalize(start).split("/")
|
||||
if start_segments == ["."]:
|
||||
start_segments = []
|
||||
start_length = len(start_segments)
|
||||
|
||||
if (path.startswith("..") or start.startswith("..")):
|
||||
fail("Cannot relativize paths above the current (unknown) directory")
|
||||
|
||||
if (path.startswith("/") != start.startswith("/") or
|
||||
len(segments) < start_length):
|
||||
fail("Path '%s' is not beneath '%s'" % (path, start))
|
||||
|
||||
for ancestor_segment, segment in zip(start_segments, segments):
|
||||
if ancestor_segment != segment:
|
||||
fail("Path '%s' is not beneath '%s'" % (path, start))
|
||||
|
||||
length = len(segments) - start_length
|
||||
result_segments = segments[-length:]
|
||||
return "/".join(result_segments)
|
||||
|
||||
|
||||
def _replace_extension(p, new_extension):
|
||||
"""Replaces the extension of the file at the end of a path.
|
||||
|
||||
If the path has no extension, the new extension is added to it.
|
||||
|
||||
Args:
|
||||
p: The path whose extension should be replaced.
|
||||
new_extension: The new extension for the file. The new extension should
|
||||
begin with a dot if you want the new filename to have one.
|
||||
Returns:
|
||||
The path with the extension replaced (or added, if it did not have one).
|
||||
"""
|
||||
return _split_extension(p)[0] + new_extension
|
||||
|
||||
|
||||
def _split_extension(p):
|
||||
"""Splits the path `p` into a tuple containing the root and extension.
|
||||
|
||||
Leading periods on the basename are ignored, so
|
||||
`path.split_extension(".bashrc")` returns `(".bashrc", "")`.
|
||||
|
||||
Args:
|
||||
p: The path whose root and extension should be split.
|
||||
Returns:
|
||||
A tuple `(root, ext)` such that the root is the path without the file
|
||||
extension, and `ext` is the file extension (which, if non-empty, contains
|
||||
the leading dot). The returned tuple always satisfies the relationship
|
||||
`root + ext == p`.
|
||||
"""
|
||||
b = _basename(p)
|
||||
last_dot_in_basename = b.rfind(".")
|
||||
|
||||
# If there is no dot or the only dot in the basename is at the front, then
|
||||
# there is no extension.
|
||||
if last_dot_in_basename <= 0:
|
||||
return (p, "")
|
||||
|
||||
dot_distance_from_end = len(b) - last_dot_in_basename
|
||||
return (p[:-dot_distance_from_end], p[-dot_distance_from_end:])
|
||||
|
||||
|
||||
paths = struct(
|
||||
basename=_basename,
|
||||
dirname=_dirname,
|
||||
is_absolute=_is_absolute,
|
||||
join=_join,
|
||||
normalize=_normalize,
|
||||
relativize=_relativize,
|
||||
replace_extension=_replace_extension,
|
||||
split_extension=_split_extension,
|
||||
)
|
|
@ -0,0 +1,83 @@
|
|||
# Copyright 2017 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.
|
||||
|
||||
"""Skylib module containing convenience interfaces for select()."""
|
||||
|
||||
def _with_or(input_dict):
|
||||
"""Drop-in replacement for `select()` that supports ORed keys.
|
||||
|
||||
Args:
|
||||
input_dict: The same dictionary `select()` takes, except keys may take
|
||||
either the usual form `"//foo:config1"` or
|
||||
`("//foo:config1", "//foo:config2", ...)` to signify
|
||||
`//foo:config1` OR `//foo:config2` OR `...`.
|
||||
|
||||
Example:
|
||||
|
||||
```build
|
||||
deps = selects.with_or({
|
||||
"//configs:one": [":dep1"],
|
||||
("//configs:two", "//configs:three"): [":dep2or3"],
|
||||
"//configs:four": [":dep4"],
|
||||
"//conditions:default": [":default"]
|
||||
})
|
||||
```
|
||||
|
||||
Key labels may appear at most once anywhere in the input.
|
||||
|
||||
Returns:
|
||||
A native `select()` that expands
|
||||
|
||||
`("//configs:two", "//configs:three"): [":dep2or3"]`
|
||||
|
||||
to
|
||||
|
||||
```build
|
||||
"//configs:two": [":dep2or3"],
|
||||
"//configs:three": [":dep2or3"],
|
||||
```
|
||||
"""
|
||||
return select(_with_or_dict(input_dict))
|
||||
|
||||
|
||||
def _with_or_dict(input_dict):
|
||||
"""Variation of `with_or` that returns the dict of the `select()`.
|
||||
|
||||
Unlike `select()`, the contents of the dict can be inspected by Skylark
|
||||
macros.
|
||||
|
||||
Args:
|
||||
input_dict: Same as `with_or`.
|
||||
|
||||
Returns:
|
||||
A dictionary usable by a native `select()`.
|
||||
"""
|
||||
output_dict = {}
|
||||
for (key, value) in input_dict.items():
|
||||
if type(key) == type(()):
|
||||
for config_setting in key:
|
||||
if config_setting in output_dict.keys():
|
||||
fail("key %s appears multiple times" % config_setting)
|
||||
output_dict[config_setting] = value
|
||||
else:
|
||||
if key in output_dict.keys():
|
||||
fail("key %s appears multiple times" % config_setting)
|
||||
output_dict[key] = value
|
||||
return output_dict
|
||||
|
||||
|
||||
selects = struct(
|
||||
with_or=_with_or,
|
||||
with_or_dict=_with_or_dict
|
||||
)
|
|
@ -0,0 +1,145 @@
|
|||
# Copyright 2017 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.
|
||||
|
||||
"""Skylib module containing common set algorithms.
|
||||
|
||||
CAUTION: Operating on sets, particularly sets contained in providers, may
|
||||
asymptotically slow down the analysis phase. While constructing large sets with
|
||||
addition/union is fast (there is no linear-time copy involved), the
|
||||
`difference` function and various comparison predicates involve linear-time
|
||||
traversals.
|
||||
|
||||
For convenience, the functions in this module can take either sets or lists as
|
||||
inputs; operations that take lists treat them as if they were sets (i.e.,
|
||||
duplicate elements are ignored). Functions that return new sets always return
|
||||
them as the `set` type, regardless of the types of the inputs.
|
||||
"""
|
||||
|
||||
|
||||
def _precondition_only_sets_or_lists(*args):
|
||||
"""Verifies that all arguments are either sets or lists.
|
||||
|
||||
The build will fail if any of the arguments is neither a set nor a list.
|
||||
|
||||
Args:
|
||||
*args: A list of values that must be sets or lists.
|
||||
"""
|
||||
for a in args:
|
||||
t = type(a)
|
||||
if t not in("depset", "list"):
|
||||
fail("Expected arguments to be depset or list, but found type %s: %r" %
|
||||
(t, a))
|
||||
|
||||
|
||||
def _is_equal(a, b):
|
||||
"""Returns whether two sets are equal.
|
||||
|
||||
Args:
|
||||
a: A depset or a list.
|
||||
b: A depset or a list.
|
||||
Returns:
|
||||
True if `a` is equal to `b`, False otherwise.
|
||||
"""
|
||||
_precondition_only_sets_or_lists(a, b)
|
||||
return sorted(depset(a)) == sorted(depset(b))
|
||||
|
||||
|
||||
def _is_subset(a, b):
|
||||
"""Returns whether `a` is a subset of `b`.
|
||||
|
||||
Args:
|
||||
a: A depset or a list.
|
||||
b: A depset or a list.
|
||||
|
||||
Returns:
|
||||
True if `a` is a subset of `b`, False otherwise.
|
||||
"""
|
||||
_precondition_only_sets_or_lists(a, b)
|
||||
for e in a:
|
||||
if e not in b:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _disjoint(a, b):
|
||||
"""Returns whether two sets are disjoint.
|
||||
|
||||
Two sets are disjoint if they have no elements in common.
|
||||
|
||||
Args:
|
||||
a: A set or list.
|
||||
b: A set or list.
|
||||
|
||||
Returns:
|
||||
True if `a` and `b` are disjoint, False otherwise.
|
||||
"""
|
||||
_precondition_only_sets_or_lists(a, b)
|
||||
for e in a:
|
||||
if e in b:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _intersection(a, b):
|
||||
"""Returns the intersection of two sets.
|
||||
|
||||
Args:
|
||||
a: A set or list.
|
||||
b: A set or list.
|
||||
|
||||
Returns:
|
||||
A set containing the elements that are in both `a` and `b`.
|
||||
"""
|
||||
_precondition_only_sets_or_lists(a, b)
|
||||
return depset([e for e in a if e in b])
|
||||
|
||||
|
||||
def _union(*args):
|
||||
"""Returns the union of several sets.
|
||||
|
||||
Args:
|
||||
*args: An arbitrary number of sets or lists.
|
||||
|
||||
Returns:
|
||||
The set union of all sets or lists in `*args`.
|
||||
"""
|
||||
_precondition_only_sets_or_lists(*args)
|
||||
r = depset()
|
||||
for a in args:
|
||||
r += a
|
||||
return r
|
||||
|
||||
|
||||
def _difference(a, b):
|
||||
"""Returns the elements in `a` that are not in `b`.
|
||||
|
||||
Args:
|
||||
a: A set or list.
|
||||
b: A set or list.
|
||||
|
||||
Returns:
|
||||
A set containing the elements that are in `a` but not in `b`.
|
||||
"""
|
||||
_precondition_only_sets_or_lists(a, b)
|
||||
return depset([e for e in a if e not in b])
|
||||
|
||||
|
||||
sets = struct(
|
||||
difference = _difference,
|
||||
disjoint = _disjoint,
|
||||
intersection = _intersection,
|
||||
is_equal = _is_equal,
|
||||
is_subset = _is_subset,
|
||||
union = _union,
|
||||
)
|
|
@ -0,0 +1,55 @@
|
|||
# Copyright 2017 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.
|
||||
|
||||
"""Skylib module containing shell utility functions."""
|
||||
|
||||
|
||||
def _array_literal(iterable):
|
||||
"""Creates a string from a sequence that can be used as a shell array.
|
||||
|
||||
For example, `shell.array_literal(["a", "b", "c"])` would return the string
|
||||
`("a" "b" "c")`, which can be used in a shell script wherever an array
|
||||
literal is needed.
|
||||
|
||||
Note that all elements in the array are quoted (using `shell.quote`) for
|
||||
safety, even if they do not need to be.
|
||||
|
||||
Args:
|
||||
iterable: A sequence of elements. Elements that are not strings will be
|
||||
converted to strings first, by calling `str()`.
|
||||
Returns:
|
||||
A string that represents the sequence as a shell array; that is,
|
||||
parentheses containing the quoted elements.
|
||||
"""
|
||||
return "(" + " ".join([_quote(str(i)) for i in iterable]) + ")"
|
||||
|
||||
|
||||
def _quote(s):
|
||||
"""Quotes the given string for use in a shell command.
|
||||
|
||||
This function quotes the given string (in case it contains spaces or other
|
||||
shell metacharacters.)
|
||||
|
||||
Args:
|
||||
s: The string to quote.
|
||||
Returns:
|
||||
A quoted version of the string that can be passed to a shell command.
|
||||
"""
|
||||
return "'" + s.replace("'", "'\\''") + "'"
|
||||
|
||||
|
||||
shell = struct(
|
||||
array_literal=_array_literal,
|
||||
quote=_quote,
|
||||
)
|
|
@ -0,0 +1,36 @@
|
|||
# Copyright 2017 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.
|
||||
|
||||
"""Skylib module containing functions that operate on structs."""
|
||||
|
||||
|
||||
def _to_dict(s):
|
||||
"""Converts a `struct` to a `dict`.
|
||||
|
||||
Args:
|
||||
s: A `struct`.
|
||||
Returns:
|
||||
A `dict` whose keys and values are the same as the fields in `s`. The
|
||||
transformation is only applied to the struct's fields and not to any
|
||||
nested values.
|
||||
"""
|
||||
attributes = dir(s)
|
||||
attributes.remove("to_json")
|
||||
attributes.remove("to_proto")
|
||||
return {key: getattr(s, key) for key in attributes}
|
||||
|
||||
|
||||
structs = struct(
|
||||
to_dict=_to_dict,
|
||||
)
|
|
@ -0,0 +1,270 @@
|
|||
# Copyright 2017 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.
|
||||
|
||||
"""Unit testing support.
|
||||
|
||||
Unlike most Skylib files, this exports two modules: `unittest` which contains
|
||||
functions to declare and define unit tests, and `asserts` which contains the
|
||||
assertions used to within tests.
|
||||
"""
|
||||
|
||||
load(":sets.bzl", "sets")
|
||||
|
||||
|
||||
def _make(impl, attrs=None):
|
||||
"""Creates a unit test rule from its implementation function.
|
||||
|
||||
Each unit test is defined in an implementation function that must then be
|
||||
associated with a rule so that a target can be built. This function handles
|
||||
the boilerplate to create and return a test rule and captures the
|
||||
implementation function's name so that it can be printed in test feedback.
|
||||
|
||||
The optional `attrs` argument can be used to define dependencies for this
|
||||
test, in order to form unit tests of rules.
|
||||
|
||||
An example of a unit test:
|
||||
|
||||
```
|
||||
def _your_test(ctx):
|
||||
env = unittest.begin(ctx)
|
||||
|
||||
# Assert statements go here
|
||||
|
||||
unittest.end(env)
|
||||
|
||||
your_test = unittest.make(_your_test)
|
||||
```
|
||||
|
||||
Recall that names of test rules must end in `_test`.
|
||||
|
||||
Args:
|
||||
impl: The implementation function of the unit test.
|
||||
attrs: An optional dictionary to supplement the attrs passed to the
|
||||
unit test's `rule()` constructor.
|
||||
Returns:
|
||||
A rule definition that should be stored in a global whose name ends in
|
||||
`_test`.
|
||||
"""
|
||||
|
||||
# Derive the name of the implementation function for better test feedback.
|
||||
# Skylark currently stringifies a function as "<function NAME>", so we use
|
||||
# that knowledge to parse the "NAME" portion out. If this behavior ever
|
||||
# changes, we'll need to update this.
|
||||
# TODO(bazel-team): Expose a ._name field on functions to avoid this.
|
||||
impl_name = str(impl)
|
||||
impl_name = impl_name.partition("<function ")[-1]
|
||||
impl_name = impl_name.rpartition(">")[0]
|
||||
|
||||
attrs = dict(attrs) if attrs else {}
|
||||
attrs["_impl_name"] = attr.string(default=impl_name)
|
||||
|
||||
return rule(
|
||||
impl,
|
||||
attrs=attrs,
|
||||
_skylark_testable=True,
|
||||
test=True,
|
||||
)
|
||||
|
||||
|
||||
def _suite(name, *test_rules):
|
||||
"""Defines a `test_suite` target that contains multiple tests.
|
||||
|
||||
After defining your test rules in a `.bzl` file, you need to create targets
|
||||
from those rules so that `blaze test` can execute them. Doing this manually
|
||||
in a BUILD file would consist of listing each test in your `load` statement
|
||||
and then creating each target one by one. To reduce duplication, we recommend
|
||||
writing a macro in your `.bzl` file to instantiate all targets, and calling
|
||||
that macro from your BUILD file so you only have to load one symbol.
|
||||
|
||||
For the case where your unit tests do not take any (non-default) attributes --
|
||||
i.e., if your unit tests do not test rules -- you can use this function to
|
||||
create the targets and wrap them in a single test_suite target. In your
|
||||
`.bzl` file, write:
|
||||
|
||||
```
|
||||
def your_test_suite():
|
||||
unittest.suite(
|
||||
"your_test_suite",
|
||||
your_test,
|
||||
your_other_test,
|
||||
yet_another_test,
|
||||
)
|
||||
```
|
||||
|
||||
Then, in your `BUILD` file, simply load the macro and invoke it to have all
|
||||
of the targets created:
|
||||
|
||||
```
|
||||
load("//path/to/your/package:tests.bzl", "your_test_suite")
|
||||
your_test_suite()
|
||||
```
|
||||
|
||||
If you pass _N_ unit test rules to `unittest.suite`, _N_ + 1 targets will be
|
||||
created: a `test_suite` target named `${name}` (where `${name}` is the name
|
||||
argument passed in here) and targets named `${name}_test_${i}`, where `${i}`
|
||||
is the index of the test in the `test_rules` list, which is used to uniquely
|
||||
name each target.
|
||||
|
||||
Args:
|
||||
name: The name of the `test_suite` target, and the prefix of all the test
|
||||
target names.
|
||||
*test_rules: A list of test rules defines by `unittest.test`.
|
||||
"""
|
||||
test_names = []
|
||||
for index, test_rule in enumerate(test_rules):
|
||||
test_name = "%s_test_%d" % (name, index)
|
||||
test_rule(name=test_name)
|
||||
test_names.append(test_name)
|
||||
|
||||
native.test_suite(
|
||||
name=name,
|
||||
tests=[":%s" % t for t in test_names]
|
||||
)
|
||||
|
||||
|
||||
def _begin(ctx):
|
||||
"""Begins a unit test.
|
||||
|
||||
This should be the first function called in a unit test implementation
|
||||
function. It initializes a "test environment" that is used to collect
|
||||
assertion failures so that they can be reported and logged at the end of the
|
||||
test.
|
||||
|
||||
Args:
|
||||
ctx: The Skylark context. Pass the implementation function's `ctx` argument
|
||||
in verbatim.
|
||||
Returns:
|
||||
A test environment struct that must be passed to assertions and finally to
|
||||
`unittest.end`. Do not rely on internal details about the fields in this
|
||||
struct as it may change.
|
||||
"""
|
||||
return struct(ctx=ctx, failures=[])
|
||||
|
||||
|
||||
def _end(env):
|
||||
"""Ends a unit test and logs the results.
|
||||
|
||||
This must be called before the end of a unit test implementation function so
|
||||
that the results are reported.
|
||||
|
||||
Args:
|
||||
env: The test environment returned by `unittest.begin`.
|
||||
"""
|
||||
cmd = "\n".join([
|
||||
"cat << EOF",
|
||||
"\n".join(env.failures),
|
||||
"EOF",
|
||||
"exit %d" % len(env.failures),
|
||||
])
|
||||
env.ctx.file_action(
|
||||
output=env.ctx.outputs.executable,
|
||||
content=cmd,
|
||||
executable=True,
|
||||
)
|
||||
|
||||
|
||||
def _fail(env, msg):
|
||||
"""Unconditionally causes the current test to fail.
|
||||
|
||||
Args:
|
||||
env: The test environment returned by `unittest.begin`.
|
||||
msg: The message to log describing the failure.
|
||||
"""
|
||||
full_msg = "In test %s: %s" % (env.ctx.attr._impl_name, msg)
|
||||
print(full_msg)
|
||||
env.failures.append(full_msg)
|
||||
|
||||
|
||||
def _assert_true(env,
|
||||
condition,
|
||||
msg="Expected condition to be true, but was false."):
|
||||
"""Asserts that the given `condition` is true.
|
||||
|
||||
Args:
|
||||
env: The test environment returned by `unittest.begin`.
|
||||
condition: A value that will be evaluated in a Boolean context.
|
||||
msg: An optional message that will be printed that describes the failure.
|
||||
If omitted, a default will be used.
|
||||
"""
|
||||
if not condition:
|
||||
_fail(env, msg)
|
||||
|
||||
|
||||
def _assert_false(env,
|
||||
condition,
|
||||
msg="Expected condition to be false, but was true."):
|
||||
"""Asserts that the given `condition` is false.
|
||||
|
||||
Args:
|
||||
env: The test environment returned by `unittest.begin`.
|
||||
condition: A value that will be evaluated in a Boolean context.
|
||||
msg: An optional message that will be printed that describes the failure.
|
||||
If omitted, a default will be used.
|
||||
"""
|
||||
if condition:
|
||||
_fail(env, msg)
|
||||
|
||||
|
||||
def _assert_equals(env, expected, actual, msg=None):
|
||||
"""Asserts that the given `expected` and `actual` values are equal.
|
||||
|
||||
Args:
|
||||
env: The test environment returned by `unittest.begin`.
|
||||
expected: The expected value of some computation.
|
||||
actual: The actual value returned by some computation.
|
||||
msg: An optional message that will be printed that describes the failure.
|
||||
If omitted, a default will be used.
|
||||
"""
|
||||
if expected != actual:
|
||||
expectation_msg = 'Expected "%s", but got "%s"' % (expected, actual)
|
||||
if msg:
|
||||
full_msg = "%s (%s)" % (msg, expectation_msg)
|
||||
else:
|
||||
full_msg = expectation_msg
|
||||
_fail(env, full_msg)
|
||||
|
||||
|
||||
def _assert_set_equals(env, expected, actual, msg=None):
|
||||
"""Asserts that the given `expected` and `actual` sets are equal.
|
||||
|
||||
Args:
|
||||
env: The test environment returned by `unittest.begin`.
|
||||
expected: The expected set resulting from some computation.
|
||||
actual: The actual set returned by some computation.
|
||||
msg: An optional message that will be printed that describes the failure.
|
||||
If omitted, a default will be used.
|
||||
"""
|
||||
if type(actual) != type(depset()) or not sets.is_equal(expected, actual):
|
||||
expectation_msg = "Expected %r, but got %r" % (expected, actual)
|
||||
if msg:
|
||||
full_msg = "%s (%s)" % (msg, expectation_msg)
|
||||
else:
|
||||
full_msg = expectation_msg
|
||||
_fail(env, full_msg)
|
||||
|
||||
|
||||
asserts = struct(
|
||||
equals=_assert_equals,
|
||||
false=_assert_false,
|
||||
set_equals=_assert_set_equals,
|
||||
true=_assert_true,
|
||||
)
|
||||
|
||||
unittest = struct(
|
||||
make=_make,
|
||||
suite=_suite,
|
||||
begin=_begin,
|
||||
end=_end,
|
||||
fail=_fail,
|
||||
)
|
|
@ -0,0 +1,23 @@
|
|||
load(":collections_tests.bzl", "collections_test_suite")
|
||||
load(":dicts_tests.bzl", "dicts_test_suite")
|
||||
load(":paths_tests.bzl", "paths_test_suite")
|
||||
load(":selects_tests.bzl", "selects_test_suite")
|
||||
load(":sets_tests.bzl", "sets_test_suite")
|
||||
load(":shell_tests.bzl", "shell_test_suite")
|
||||
load(":structs_tests.bzl", "structs_test_suite")
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
collections_test_suite()
|
||||
|
||||
dicts_test_suite()
|
||||
|
||||
paths_test_suite()
|
||||
|
||||
selects_test_suite()
|
||||
|
||||
sets_test_suite()
|
||||
|
||||
shell_test_suite()
|
||||
|
||||
structs_test_suite()
|
|
@ -0,0 +1,81 @@
|
|||
# Copyright 2017 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.
|
||||
|
||||
"""Unit tests for collections.bzl."""
|
||||
|
||||
load("//:lib.bzl", "collections", "asserts", "unittest")
|
||||
|
||||
|
||||
def _after_each_test(ctx):
|
||||
"""Unit tests for collections.after_each."""
|
||||
env = unittest.begin(ctx)
|
||||
|
||||
asserts.equals(env, [], collections.after_each("1", []))
|
||||
asserts.equals(env, ["a", "1"], collections.after_each("1", ["a"]))
|
||||
asserts.equals(env, ["a", "1", "b", "1"],
|
||||
collections.after_each("1", ["a", "b"]))
|
||||
|
||||
# We don't care what type the separator is, we just put it there; so None
|
||||
# should be just as valid as anything else.
|
||||
asserts.equals(env, ["a", None, "b", None],
|
||||
collections.after_each(None, ["a", "b"]))
|
||||
|
||||
unittest.end(env)
|
||||
|
||||
after_each_test = unittest.make(_after_each_test)
|
||||
|
||||
|
||||
def _before_each_test(ctx):
|
||||
"""Unit tests for collections.before_each."""
|
||||
env = unittest.begin(ctx)
|
||||
|
||||
asserts.equals(env, [], collections.before_each("1", []))
|
||||
asserts.equals(env, ["1", "a"], collections.before_each("1", ["a"]))
|
||||
asserts.equals(env, ["1", "a", "1", "b"],
|
||||
collections.before_each("1", ["a", "b"]))
|
||||
|
||||
# We don't care what type the separator is, we just put it there; so None
|
||||
# should be just as valid as anything else.
|
||||
asserts.equals(env, [None, "a", None, "b"],
|
||||
collections.before_each(None, ["a", "b"]))
|
||||
|
||||
unittest.end(env)
|
||||
|
||||
before_each_test = unittest.make(_before_each_test)
|
||||
|
||||
|
||||
def _uniq_test(ctx):
|
||||
env = unittest.begin(ctx)
|
||||
asserts.equals(env, collections.uniq([0, 1, 2, 3]), [0, 1, 2, 3])
|
||||
asserts.equals(env, collections.uniq([]), [])
|
||||
asserts.equals(env, collections.uniq([1, 1, 1, 1, 1]), [1])
|
||||
asserts.equals(env, collections.uniq([True, 5, "foo", 5, False, struct(a=1),
|
||||
True, struct(b=2), "bar", (1,), "foo",
|
||||
struct(a=1), (1,)]),
|
||||
[True, 5, "foo", False, struct(a=1), struct(b=2),
|
||||
"bar", (1,)])
|
||||
|
||||
unittest.end(env)
|
||||
|
||||
uniq_test = unittest.make(_uniq_test)
|
||||
|
||||
|
||||
def collections_test_suite():
|
||||
"""Creates the test targets and test suite for collections.bzl tests."""
|
||||
unittest.suite(
|
||||
"collections_tests",
|
||||
after_each_test,
|
||||
before_each_test,
|
||||
uniq_test,
|
||||
)
|
|
@ -0,0 +1,67 @@
|
|||
# Copyright 2017 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.
|
||||
|
||||
"""Unit tests for dicts.bzl."""
|
||||
|
||||
load("//:lib.bzl", "dicts", "asserts", "unittest")
|
||||
|
||||
|
||||
def _add_test(ctx):
|
||||
"""Unit tests for dicts.add."""
|
||||
env = unittest.begin(ctx)
|
||||
|
||||
# Test zero- and one-argument behavior.
|
||||
asserts.equals(env, {}, dicts.add())
|
||||
asserts.equals(env, {"a": 1}, dicts.add({"a": 1}))
|
||||
|
||||
# Test simple two-argument behavior.
|
||||
asserts.equals(env, {"a": 1, "b": 2}, dicts.add({"a": 1}, {"b": 2}))
|
||||
|
||||
# Test simple more-than-two-argument behavior.
|
||||
asserts.equals(env, {"a": 1, "b": 2, "c": 3, "d": 4},
|
||||
dicts.add({"a": 1}, {"b": 2}, {"c": 3}, {"d": 4}))
|
||||
|
||||
# Test same-key overriding.
|
||||
asserts.equals(env, {"a": 100}, dicts.add({"a": 1}, {"a": 100}))
|
||||
asserts.equals(env, {"a": 10}, dicts.add({"a": 1}, {"a": 100}, {"a": 10}))
|
||||
asserts.equals(env, {"a": 100, "b": 10},
|
||||
dicts.add({"a": 1}, {"a": 100}, {"b": 10}))
|
||||
asserts.equals(env, {"a": 10}, dicts.add({"a": 1}, {}, {"a": 10}))
|
||||
asserts.equals(env, {"a": 10, "b": 5},
|
||||
dicts.add({"a": 1}, {"a": 10, "b": 5}))
|
||||
|
||||
# Test some other boundary cases.
|
||||
asserts.equals(env, {"a": 1}, dicts.add({"a": 1}, {}))
|
||||
|
||||
# Since dictionaries are passed around by reference, make sure that the
|
||||
# result of dicts.add is always a *copy* by modifying it afterwards and
|
||||
# ensuring that the original argument doesn't also reflect the change. We do
|
||||
# this to protect against someone who might attempt to optimize the function
|
||||
# by returning the argument itself in the one-argument case.
|
||||
original = {"a": 1}
|
||||
result = dicts.add(original)
|
||||
result["a"] = 2
|
||||
asserts.equals(env, 1, original["a"])
|
||||
|
||||
unittest.end(env)
|
||||
|
||||
add_test = unittest.make(_add_test)
|
||||
|
||||
|
||||
def dicts_test_suite():
|
||||
"""Creates the test targets and test suite for dicts.bzl tests."""
|
||||
unittest.suite(
|
||||
"dicts_tests",
|
||||
add_test,
|
||||
)
|
|
@ -0,0 +1,280 @@
|
|||
# Copyright 2017 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.
|
||||
|
||||
"""Unit tests for paths.bzl."""
|
||||
|
||||
load("//:lib.bzl", "paths", "asserts", "unittest")
|
||||
|
||||
|
||||
def _basename_test(ctx):
|
||||
"""Unit tests for paths.basename."""
|
||||
env = unittest.begin(ctx)
|
||||
|
||||
# Verify some degenerate cases.
|
||||
asserts.equals(env, "", paths.basename(""))
|
||||
asserts.equals(env, "", paths.basename("/"))
|
||||
asserts.equals(env, "bar", paths.basename("foo///bar"))
|
||||
|
||||
# Verify some realistic cases.
|
||||
asserts.equals(env, "foo", paths.basename("foo"))
|
||||
asserts.equals(env, "foo", paths.basename("/foo"))
|
||||
asserts.equals(env, "foo", paths.basename("bar/foo"))
|
||||
asserts.equals(env, "foo", paths.basename("/bar/foo"))
|
||||
|
||||
# Verify that we correctly duplicate Python's os.path.basename behavior,
|
||||
# where a trailing slash means the basename is empty.
|
||||
asserts.equals(env, "", paths.basename("foo/"))
|
||||
asserts.equals(env, "", paths.basename("/foo/"))
|
||||
|
||||
unittest.end(env)
|
||||
|
||||
basename_test = unittest.make(_basename_test)
|
||||
|
||||
|
||||
def _dirname_test(ctx):
|
||||
"""Unit tests for paths.dirname."""
|
||||
env = unittest.begin(ctx)
|
||||
|
||||
# Verify some degenerate cases.
|
||||
asserts.equals(env, "", paths.dirname(""))
|
||||
asserts.equals(env, "/", paths.dirname("/"))
|
||||
asserts.equals(env, "foo", paths.dirname("foo///bar"))
|
||||
|
||||
# Verify some realistic cases.
|
||||
asserts.equals(env, "", paths.dirname("foo"))
|
||||
asserts.equals(env, "/", paths.dirname("/foo"))
|
||||
asserts.equals(env, "bar", paths.dirname("bar/foo"))
|
||||
asserts.equals(env, "/bar", paths.dirname("/bar/foo"))
|
||||
|
||||
# Verify that we correctly duplicate Python's os.path.dirname behavior,
|
||||
# where a trailing slash means the dirname is the same as the original
|
||||
# path (without the trailing slash).
|
||||
asserts.equals(env, "foo", paths.dirname("foo/"))
|
||||
asserts.equals(env, "/foo", paths.dirname("/foo/"))
|
||||
|
||||
unittest.end(env)
|
||||
|
||||
dirname_test = unittest.make(_dirname_test)
|
||||
|
||||
|
||||
def _is_absolute_test(ctx):
|
||||
"""Unit tests for paths.is_absolute."""
|
||||
env = unittest.begin(ctx)
|
||||
|
||||
# Try a degenerate case.
|
||||
asserts.false(env, paths.is_absolute(""))
|
||||
|
||||
# Try some relative paths.
|
||||
asserts.false(env, paths.is_absolute("foo"))
|
||||
asserts.false(env, paths.is_absolute("foo/"))
|
||||
asserts.false(env, paths.is_absolute("foo/bar"))
|
||||
|
||||
# Try some absolute paths.
|
||||
asserts.true(env, paths.is_absolute("/"))
|
||||
asserts.true(env, paths.is_absolute("/foo"))
|
||||
asserts.true(env, paths.is_absolute("/foo/"))
|
||||
asserts.true(env, paths.is_absolute("/foo/bar"))
|
||||
|
||||
unittest.end(env)
|
||||
|
||||
is_absolute_test = unittest.make(_is_absolute_test)
|
||||
|
||||
|
||||
def _join_test(ctx):
|
||||
"""Unit tests for paths.join."""
|
||||
env = unittest.begin(ctx)
|
||||
|
||||
# Try a degenerate case.
|
||||
asserts.equals(env, "", paths.join(""))
|
||||
|
||||
# Try some basic paths.
|
||||
asserts.equals(env, "foo", paths.join("foo"))
|
||||
asserts.equals(env, "foo/bar", paths.join("foo", "bar"))
|
||||
asserts.equals(env, "foo/bar/baz", paths.join("foo", "bar", "baz"))
|
||||
|
||||
# Make sure an initially absolute path stays absolute.
|
||||
asserts.equals(env, "/foo", paths.join("/foo"))
|
||||
asserts.equals(env, "/foo/bar", paths.join("/foo", "bar"))
|
||||
|
||||
# Make sure an absolute path later in the list resets the result.
|
||||
asserts.equals(env, "/baz", paths.join("foo", "bar", "/baz"))
|
||||
asserts.equals(env, "/baz", paths.join("foo", "/bar", "/baz"))
|
||||
asserts.equals(env, "/bar/baz", paths.join("foo", "/bar", "baz"))
|
||||
asserts.equals(env, "/bar", paths.join("/foo", "/bar"))
|
||||
|
||||
# Make sure a leading empty segment doesn't make it absolute.
|
||||
asserts.equals(env, "foo", paths.join("", "foo"))
|
||||
|
||||
# Try some trailing slash scenarios.
|
||||
asserts.equals(env, "foo/", paths.join("foo", ""))
|
||||
asserts.equals(env, "foo/", paths.join("foo/"))
|
||||
asserts.equals(env, "foo/", paths.join("foo/", ""))
|
||||
asserts.equals(env, "foo//", paths.join("foo//", ""))
|
||||
asserts.equals(env, "foo//", paths.join("foo//"))
|
||||
asserts.equals(env, "foo/bar/baz/", paths.join("foo/", "bar/", "baz", ""))
|
||||
asserts.equals(env, "foo/bar/baz/", paths.join("foo/", "bar/", "baz/"))
|
||||
asserts.equals(env, "foo/bar/baz/", paths.join("foo/", "bar/", "baz/", ""))
|
||||
|
||||
# Make sure that adjacent empty segments don't add extra path separators.
|
||||
asserts.equals(env, "foo/", paths.join("foo", "", ""))
|
||||
asserts.equals(env, "foo", paths.join("", "", "foo"))
|
||||
asserts.equals(env, "foo/bar", paths.join("foo", "", "", "bar"))
|
||||
|
||||
unittest.end(env)
|
||||
|
||||
join_test = unittest.make(_join_test)
|
||||
|
||||
|
||||
def _normalize_test(ctx):
|
||||
"""Unit tests for paths.normalize."""
|
||||
env = unittest.begin(ctx)
|
||||
|
||||
# Try the most basic case.
|
||||
asserts.equals(env, ".", paths.normalize(""))
|
||||
|
||||
# Try some basic adjacent-slash removal.
|
||||
asserts.equals(env, "foo/bar", paths.normalize("foo//bar"))
|
||||
asserts.equals(env, "foo/bar", paths.normalize("foo////bar"))
|
||||
|
||||
# Try some "." removal.
|
||||
asserts.equals(env, "foo/bar", paths.normalize("foo/./bar"))
|
||||
asserts.equals(env, "foo/bar", paths.normalize("./foo/bar"))
|
||||
asserts.equals(env, "foo/bar", paths.normalize("foo/bar/."))
|
||||
asserts.equals(env, "/", paths.normalize("/."))
|
||||
|
||||
# Try some ".." removal.
|
||||
asserts.equals(env, "bar", paths.normalize("foo/../bar"))
|
||||
asserts.equals(env, "foo", paths.normalize("foo/bar/.."))
|
||||
asserts.equals(env, ".", paths.normalize("foo/.."))
|
||||
asserts.equals(env, ".", paths.normalize("foo/bar/../.."))
|
||||
asserts.equals(env, "..", paths.normalize("foo/../.."))
|
||||
asserts.equals(env, "/", paths.normalize("/foo/../.."))
|
||||
asserts.equals(env, "../../c", paths.normalize("a/b/../../../../c/d/.."))
|
||||
|
||||
# Make sure one or two initial slashes are preserved, but three or more are
|
||||
# collapsed to a single slash.
|
||||
asserts.equals(env, "/foo", paths.normalize("/foo"))
|
||||
asserts.equals(env, "//foo", paths.normalize("//foo"))
|
||||
asserts.equals(env, "/foo", paths.normalize("///foo"))
|
||||
|
||||
# Trailing slashes should be removed unless the entire path is a trailing
|
||||
# slash.
|
||||
asserts.equals(env, "/", paths.normalize("/"))
|
||||
asserts.equals(env, "foo", paths.normalize("foo/"))
|
||||
asserts.equals(env, "foo/bar", paths.normalize("foo/bar/"))
|
||||
|
||||
unittest.end(env)
|
||||
|
||||
normalize_test = unittest.make(_normalize_test)
|
||||
|
||||
|
||||
def _relativize_test(ctx):
|
||||
"""Unit tests for paths.relativize."""
|
||||
env = unittest.begin(ctx)
|
||||
|
||||
# Make sure that relative-to-current-directory works in all forms.
|
||||
asserts.equals(env, "foo", paths.relativize("foo", ""))
|
||||
asserts.equals(env, "foo", paths.relativize("foo", "."))
|
||||
|
||||
# Try some regular cases.
|
||||
asserts.equals(env, "bar", paths.relativize("foo/bar", "foo"))
|
||||
asserts.equals(env, "baz", paths.relativize("foo/bar/baz", "foo/bar"))
|
||||
asserts.equals(env, "bar/baz", paths.relativize("foo/bar/baz", "foo"))
|
||||
|
||||
# Try a case where a parent directory is normalized away.
|
||||
asserts.equals(env, "baz", paths.relativize("foo/bar/../baz", "foo"))
|
||||
|
||||
# TODO(allevato): Test failure cases, once that is possible.
|
||||
|
||||
unittest.end(env)
|
||||
|
||||
relativize_test = unittest.make(_relativize_test)
|
||||
|
||||
|
||||
def _replace_extension_test(ctx):
|
||||
"""Unit tests for paths.replace_extension."""
|
||||
env = unittest.begin(ctx)
|
||||
|
||||
# Try some degenerate cases.
|
||||
asserts.equals(env, ".foo", paths.replace_extension("", ".foo"))
|
||||
asserts.equals(env, "/.foo", paths.replace_extension("/", ".foo"))
|
||||
asserts.equals(env, "foo.bar", paths.replace_extension("foo", ".bar"))
|
||||
|
||||
# Try a directory with an extension and basename that doesn't have one.
|
||||
asserts.equals(env, "foo.bar/baz.quux",
|
||||
paths.replace_extension("foo.bar/baz", ".quux"))
|
||||
|
||||
# Now try some things with legit extensions.
|
||||
asserts.equals(env, "a.z", paths.replace_extension("a.b", ".z"))
|
||||
asserts.equals(env, "a.b.z", paths.replace_extension("a.b.c", ".z"))
|
||||
asserts.equals(env, "a/b.z", paths.replace_extension("a/b.c", ".z"))
|
||||
asserts.equals(env, "a.b/c.z", paths.replace_extension("a.b/c.d", ".z"))
|
||||
asserts.equals(env, ".a/b.z", paths.replace_extension(".a/b.c", ".z"))
|
||||
asserts.equals(env, ".a.z", paths.replace_extension(".a.b", ".z"))
|
||||
|
||||
# Verify that we don't insert a period on the extension if none is provided.
|
||||
asserts.equals(env, "foobaz", paths.replace_extension("foo.bar", "baz"))
|
||||
|
||||
unittest.end(env)
|
||||
|
||||
replace_extension_test = unittest.make(_replace_extension_test)
|
||||
|
||||
|
||||
def _split_extension_test(ctx):
|
||||
"""Unit tests for paths.split_extension."""
|
||||
env = unittest.begin(ctx)
|
||||
|
||||
# Try some degenerate cases.
|
||||
asserts.equals(env, ("", ""), paths.split_extension(""))
|
||||
asserts.equals(env, ("/", ""), paths.split_extension("/"))
|
||||
asserts.equals(env, ("foo", ""), paths.split_extension("foo"))
|
||||
|
||||
# Try some paths whose basenames start with ".".
|
||||
asserts.equals(env, (".", ""), paths.split_extension("."))
|
||||
asserts.equals(env, (".bashrc", ""), paths.split_extension(".bashrc"))
|
||||
asserts.equals(env, ("foo/.bashrc", ""), paths.split_extension("foo/.bashrc"))
|
||||
asserts.equals(env, (".foo/.bashrc", ""),
|
||||
paths.split_extension(".foo/.bashrc"))
|
||||
|
||||
# Try some directories with extensions with basenames that don't have one.
|
||||
asserts.equals(env, ("foo.bar/baz", ""), paths.split_extension("foo.bar/baz"))
|
||||
asserts.equals(env, ("foo.bar/.bashrc", ""),
|
||||
paths.split_extension("foo.bar/.bashrc"))
|
||||
|
||||
# Now try some things that will actually get split.
|
||||
asserts.equals(env, ("a", ".b"), paths.split_extension("a.b"))
|
||||
asserts.equals(env, ("a.b", ".c"), paths.split_extension("a.b.c"))
|
||||
asserts.equals(env, ("a/b", ".c"), paths.split_extension("a/b.c"))
|
||||
asserts.equals(env, ("a.b/c", ".d"), paths.split_extension("a.b/c.d"))
|
||||
asserts.equals(env, (".a/b", ".c"), paths.split_extension(".a/b.c"))
|
||||
asserts.equals(env, (".a", ".b"), paths.split_extension(".a.b"))
|
||||
|
||||
unittest.end(env)
|
||||
|
||||
split_extension_test = unittest.make(_split_extension_test)
|
||||
|
||||
|
||||
def paths_test_suite():
|
||||
"""Creates the test targets and test suite for paths.bzl tests."""
|
||||
unittest.suite(
|
||||
"paths_tests",
|
||||
basename_test,
|
||||
dirname_test,
|
||||
is_absolute_test,
|
||||
join_test,
|
||||
normalize_test,
|
||||
relativize_test,
|
||||
replace_extension_test,
|
||||
split_extension_test,
|
||||
)
|
|
@ -0,0 +1,53 @@
|
|||
# Copyright 2017 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.
|
||||
|
||||
"""Unit tests for selects.bzl."""
|
||||
|
||||
load("//:lib.bzl", "selects", "asserts", "unittest")
|
||||
|
||||
|
||||
def _with_or_test(ctx):
|
||||
"""Unit tests for with_or."""
|
||||
env = unittest.begin(ctx)
|
||||
|
||||
# We actually test on with_or_dict because Skylark can't get the
|
||||
# dictionary from a select().
|
||||
|
||||
# Test select()-compatible input syntax.
|
||||
input_dict = {":foo": ":d1", "//conditions:default": ":d1"}
|
||||
asserts.equals(env, input_dict, selects.with_or_dict(input_dict))
|
||||
|
||||
# Test OR syntax.
|
||||
or_dict = {(":foo", ":bar"): ":d1"}
|
||||
asserts.equals(env, {":foo": ":d1", ":bar": ":d1"},
|
||||
selects.with_or_dict(or_dict))
|
||||
|
||||
# Test mixed syntax.
|
||||
mixed_dict = {":foo": ":d1", (":bar", ":baz"): ":d2",
|
||||
"//conditions:default": ":d3"}
|
||||
asserts.equals(env, {":foo": ":d1", ":bar": ":d2", ":baz": ":d2",
|
||||
"//conditions:default": ":d3"},
|
||||
selects.with_or_dict(mixed_dict))
|
||||
|
||||
unittest.end(env)
|
||||
|
||||
with_or_test = unittest.make(_with_or_test)
|
||||
|
||||
|
||||
def selects_test_suite():
|
||||
"""Creates the test targets and test suite for selects.bzl tests."""
|
||||
unittest.suite(
|
||||
"selects_tests",
|
||||
with_or_test,
|
||||
)
|
|
@ -0,0 +1,160 @@
|
|||
# Copyright 2017 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.
|
||||
|
||||
"""Unit tests for sets.bzl."""
|
||||
|
||||
load("//:lib.bzl", "sets", "asserts", "unittest")
|
||||
|
||||
|
||||
def _is_equal_test(ctx):
|
||||
"""Unit tests for sets.is_equal.
|
||||
|
||||
Note that if this test fails, the results for the other `sets` tests will be
|
||||
inconclusive because they use `asserts.set_equals`, which in turn calls
|
||||
`sets.is_equal`.
|
||||
"""
|
||||
env = unittest.begin(ctx)
|
||||
|
||||
asserts.true(env, sets.is_equal([], []))
|
||||
asserts.false(env, sets.is_equal([], [1]))
|
||||
asserts.false(env, sets.is_equal([1], []))
|
||||
asserts.true(env, sets.is_equal([1], [1]))
|
||||
asserts.false(env, sets.is_equal([1], [1, 2]))
|
||||
asserts.false(env, sets.is_equal([1], [2]))
|
||||
asserts.false(env, sets.is_equal([1], depset([1, 2])))
|
||||
|
||||
# Verify that the implementation is not using == on the sets directly.
|
||||
asserts.true(env, sets.is_equal(depset([1]), depset([1])))
|
||||
|
||||
# If passing a list, verify that duplicate elements are ignored.
|
||||
asserts.true(env, sets.is_equal([1, 1], [1]))
|
||||
|
||||
unittest.end(env)
|
||||
|
||||
is_equal_test = unittest.make(_is_equal_test)
|
||||
|
||||
|
||||
def _is_subset_test(ctx):
|
||||
"""Unit tests for sets.is_subset."""
|
||||
env = unittest.begin(ctx)
|
||||
|
||||
asserts.true(env, sets.is_subset([], []))
|
||||
asserts.true(env, sets.is_subset([], [1]))
|
||||
asserts.false(env, sets.is_subset([1], []))
|
||||
asserts.true(env, sets.is_subset([1], [1]))
|
||||
asserts.true(env, sets.is_subset([1], [1, 2]))
|
||||
asserts.false(env, sets.is_subset([1], [2]))
|
||||
asserts.true(env, sets.is_subset([1], depset([1, 2])))
|
||||
|
||||
# If passing a list, verify that duplicate elements are ignored.
|
||||
asserts.true(env, sets.is_subset([1, 1], [1, 2]))
|
||||
|
||||
unittest.end(env)
|
||||
|
||||
is_subset_test = unittest.make(_is_subset_test)
|
||||
|
||||
|
||||
def _disjoint_test(ctx):
|
||||
"""Unit tests for sets.disjoint."""
|
||||
env = unittest.begin(ctx)
|
||||
|
||||
asserts.true(env, sets.disjoint([], []))
|
||||
asserts.true(env, sets.disjoint([], [1]))
|
||||
asserts.true(env, sets.disjoint([1], []))
|
||||
asserts.false(env, sets.disjoint([1], [1]))
|
||||
asserts.false(env, sets.disjoint([1], [1, 2]))
|
||||
asserts.true(env, sets.disjoint([1], [2]))
|
||||
asserts.true(env, sets.disjoint([1], depset([2])))
|
||||
|
||||
# If passing a list, verify that duplicate elements are ignored.
|
||||
asserts.false(env, sets.disjoint([1, 1], [1, 2]))
|
||||
|
||||
unittest.end(env)
|
||||
|
||||
disjoint_test = unittest.make(_disjoint_test)
|
||||
|
||||
|
||||
def _intersection_test(ctx):
|
||||
"""Unit tests for sets.intersection."""
|
||||
env = unittest.begin(ctx)
|
||||
|
||||
asserts.set_equals(env, [], sets.intersection([], []))
|
||||
asserts.set_equals(env, [], sets.intersection([], [1]))
|
||||
asserts.set_equals(env, [], sets.intersection([1], []))
|
||||
asserts.set_equals(env, [1], sets.intersection([1], [1]))
|
||||
asserts.set_equals(env, [1], sets.intersection([1], [1, 2]))
|
||||
asserts.set_equals(env, [], sets.intersection([1], [2]))
|
||||
asserts.set_equals(env, [1], sets.intersection([1], depset([1])))
|
||||
|
||||
# If passing a list, verify that duplicate elements are ignored.
|
||||
asserts.set_equals(env, [1], sets.intersection([1, 1], [1, 2]))
|
||||
|
||||
unittest.end(env)
|
||||
|
||||
intersection_test = unittest.make(_intersection_test)
|
||||
|
||||
|
||||
def _union_test(ctx):
|
||||
"""Unit tests for sets.union."""
|
||||
env = unittest.begin(ctx)
|
||||
|
||||
asserts.set_equals(env, [], sets.union())
|
||||
asserts.set_equals(env, [1], sets.union([1]))
|
||||
asserts.set_equals(env, [], sets.union([], []))
|
||||
asserts.set_equals(env, [1], sets.union([], [1]))
|
||||
asserts.set_equals(env, [1], sets.union([1], []))
|
||||
asserts.set_equals(env, [1], sets.union([1], [1]))
|
||||
asserts.set_equals(env, [1, 2], sets.union([1], [1, 2]))
|
||||
asserts.set_equals(env, [1, 2], sets.union([1], [2]))
|
||||
asserts.set_equals(env, [1], sets.union([1], depset([1])))
|
||||
|
||||
# If passing a list, verify that duplicate elements are ignored.
|
||||
asserts.set_equals(env, [1, 2], sets.union([1, 1], [1, 2]))
|
||||
|
||||
unittest.end(env)
|
||||
|
||||
union_test = unittest.make(_union_test)
|
||||
|
||||
|
||||
def _difference_test(ctx):
|
||||
"""Unit tests for sets.difference."""
|
||||
env = unittest.begin(ctx)
|
||||
|
||||
asserts.set_equals(env, [], sets.difference([], []))
|
||||
asserts.set_equals(env, [], sets.difference([], [1]))
|
||||
asserts.set_equals(env, [1], sets.difference([1], []))
|
||||
asserts.set_equals(env, [], sets.difference([1], [1]))
|
||||
asserts.set_equals(env, [], sets.difference([1], [1, 2]))
|
||||
asserts.set_equals(env, [1], sets.difference([1], [2]))
|
||||
asserts.set_equals(env, [], sets.difference([1], depset([1])))
|
||||
|
||||
# If passing a list, verify that duplicate elements are ignored.
|
||||
asserts.set_equals(env, [2], sets.difference([1, 2], [1, 1]))
|
||||
|
||||
unittest.end(env)
|
||||
|
||||
difference_test = unittest.make(_difference_test)
|
||||
|
||||
|
||||
def sets_test_suite():
|
||||
"""Creates the test targets and test suite for sets.bzl tests."""
|
||||
unittest.suite(
|
||||
"sets_tests",
|
||||
disjoint_test,
|
||||
intersection_test,
|
||||
is_equal_test,
|
||||
is_subset_test,
|
||||
difference_test,
|
||||
union_test,
|
||||
)
|
|
@ -0,0 +1,108 @@
|
|||
# Copyright 2017 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.
|
||||
|
||||
"""Unit tests for shell.bzl."""
|
||||
|
||||
load("//:lib.bzl", "shell", "asserts", "unittest")
|
||||
|
||||
|
||||
def _shell_array_literal_test(ctx):
|
||||
"""Unit tests for shell.array_literal."""
|
||||
env = unittest.begin(ctx)
|
||||
|
||||
asserts.equals(env, "()", shell.array_literal([]))
|
||||
asserts.equals(env, "('1')", shell.array_literal([1]))
|
||||
asserts.equals(env, "('1' '2' '3')", shell.array_literal([1, 2, 3]))
|
||||
asserts.equals(env, "('$foo')", shell.array_literal(["$foo"]))
|
||||
asserts.equals(env, "('qu\"o\"te')", shell.array_literal(['qu"o"te']))
|
||||
|
||||
unittest.end(env)
|
||||
|
||||
shell_array_literal_test = unittest.make(_shell_array_literal_test)
|
||||
|
||||
|
||||
def _shell_quote_test(ctx):
|
||||
"""Unit tests for shell.quote."""
|
||||
env = unittest.begin(ctx)
|
||||
|
||||
asserts.equals(env, "'foo'", shell.quote("foo"))
|
||||
asserts.equals(env, "'foo bar'", shell.quote("foo bar"))
|
||||
asserts.equals(env, "'three spaces'", shell.quote("three spaces"))
|
||||
asserts.equals(env, "' leading'", shell.quote(" leading"))
|
||||
asserts.equals(env, "'trailing '", shell.quote("trailing "))
|
||||
asserts.equals(env, "'new\nline'", shell.quote("new\nline"))
|
||||
asserts.equals(env, "'tab\tcharacter'", shell.quote("tab\tcharacter"))
|
||||
asserts.equals(env, "'$foo'", shell.quote("$foo"))
|
||||
asserts.equals(env, "'qu\"o\"te'", shell.quote('qu"o"te'))
|
||||
asserts.equals(env, "'it'\\''s'", shell.quote("it's"))
|
||||
asserts.equals(env, "'foo\\bar'", shell.quote(r"foo\bar"))
|
||||
asserts.equals(env, "'back`echo q`uote'", shell.quote(r"back`echo q`uote"))
|
||||
|
||||
unittest.end(env)
|
||||
|
||||
shell_quote_test = unittest.make(_shell_quote_test)
|
||||
|
||||
|
||||
def _shell_spawn_e2e_test_impl(ctx):
|
||||
"""Test spawning a real shell."""
|
||||
args = [
|
||||
"foo",
|
||||
"foo bar",
|
||||
"three spaces",
|
||||
" leading",
|
||||
"trailing ",
|
||||
"new\nline",
|
||||
"tab\tcharacter",
|
||||
"$foo",
|
||||
'qu"o"te',
|
||||
"it's",
|
||||
r"foo\bar",
|
||||
"back`echo q`uote",
|
||||
]
|
||||
script_content = "\n".join([
|
||||
"#!/bin/bash",
|
||||
"myarray=" + shell.array_literal(args),
|
||||
'output=$(echo "${myarray[@]}")',
|
||||
# For logging:
|
||||
'echo "DEBUG: output=[${output}]" >&2',
|
||||
# The following is a shell representation of what the echo of the quoted
|
||||
# array will look like. It looks a bit confusing considering it's shell
|
||||
# quoted into Python. Shell using single quotes to minimize shell
|
||||
# escaping, so only the single quote needs to be escaped as '\'', all
|
||||
# others are essentially kept literally.
|
||||
"expected='foo foo bar three spaces leading trailing new",
|
||||
"line tab\tcharacter $foo qu\"o\"te it'\\''s foo\\bar back`echo q`uote'",
|
||||
'[[ "${output}" == "${expected}" ]]',
|
||||
])
|
||||
ctx.file_action(
|
||||
output = ctx.outputs.executable,
|
||||
content = script_content,
|
||||
executable = True,
|
||||
)
|
||||
return struct()
|
||||
|
||||
shell_spawn_e2e_test = rule(
|
||||
test = True,
|
||||
implementation = _shell_spawn_e2e_test_impl,
|
||||
)
|
||||
|
||||
|
||||
def shell_test_suite():
|
||||
"""Creates the test targets and test suite for shell.bzl tests."""
|
||||
unittest.suite(
|
||||
"shell_tests",
|
||||
shell_array_literal_test,
|
||||
shell_quote_test,
|
||||
shell_spawn_e2e_test,
|
||||
)
|
|
@ -0,0 +1,49 @@
|
|||
# Copyright 2017 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.
|
||||
|
||||
"""Unit tests for structs.bzl."""
|
||||
|
||||
load("//:lib.bzl", "structs", "asserts", "unittest")
|
||||
|
||||
|
||||
def _add_test(ctx):
|
||||
"""Unit tests for dicts.add."""
|
||||
env = unittest.begin(ctx)
|
||||
|
||||
# Test zero- and one-argument behavior.
|
||||
asserts.equals(env, {}, structs.to_dict(struct()))
|
||||
asserts.equals(env, {"a": 1}, structs.to_dict(struct(a=1)))
|
||||
|
||||
# Test simple two-argument behavior.
|
||||
asserts.equals(env, {"a": 1, "b": 2}, structs.to_dict(struct(a=1, b=2)))
|
||||
|
||||
# Test simple more-than-two-argument behavior.
|
||||
asserts.equals(env, {"a": 1, "b": 2, "c": 3, "d": 4},
|
||||
structs.to_dict(struct(a=1, b=2, c=3, d=4)))
|
||||
|
||||
# Test transformation is not applied transitively.
|
||||
asserts.equals(env, {"a": 1, "b": struct(bb=1)},
|
||||
structs.to_dict(struct(a=1, b=struct(bb=1))))
|
||||
|
||||
unittest.end(env)
|
||||
|
||||
add_test = unittest.make(_add_test)
|
||||
|
||||
|
||||
def structs_test_suite():
|
||||
"""Creates the test targets and test suite for structs.bzl tests."""
|
||||
unittest.suite(
|
||||
"structs_tests",
|
||||
add_test,
|
||||
)
|
Loading…
Reference in New Issue