Skip to content

Commit

Permalink
Add pregenerated files with all units (#178)
Browse files Browse the repository at this point in the history
The motivating example is to be able to use the library on godbolt.  For
this use case, we typically care far more about being comprehensive than
we do about compile times.

To enable this, we first add an `--all-units` flag to the single file
script.  This flag will cause the script to act as if the `--units` flag
had been populated with every unit that is known to the library.

(Under the hood, the script has a safety mechanism to make sure that we
actually use every command line parameter in the script.  This new
parameter would run afoul of this mechanism, because it's mutually
exclusive with the `--units` argument.  Therefore, we use `--all-units`
to populate `--units`, and then delete `--all-units`.)

Next, we create build rules that use this new flag to generate
single-file versions with all units, with and without the `<iostream>`
dependency.  Each new rule gets its own unit test.

Finally, we update the installation docs to provide this option.  We
provide an appropriate degree of discouragement for general use, and
clarity around suitable use cases.  The godbolt link is currently
"broken" (it doesn't compile), but we expect it to get fixed
automatically once this PR lands.

Test plan:

- [x] Run script with combinations of arguments:
    - [x] Neither `--units` nor `--all-units`: builds with no units.
    - [x] Only `--units`: includes specified units.
    - [x] Only `--all-units`: includes all units.
    - [x] Both `--units` and `--all-units`: exits with error.
- [x] Add unit tests for both single file generated versions. 
- [x] View rendered docs and follow links

Fixes #174.
  • Loading branch information
chiphogg authored Oct 10, 2023
1 parent 45bc7d3 commit 0185918
Show file tree
Hide file tree
Showing 6 changed files with 192 additions and 6 deletions.
55 changes: 52 additions & 3 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ py_binary(
srcs = ["update_docs.py"],
data = [
"mkdocs.yml",
":au_all_units_hh",
":au_all_units_noio_hh",
":au_hh",
":au_noio_hh",
] + glob(["docs/**"]),
Expand All @@ -43,6 +45,8 @@ py_binary(
srcs = ["mike_bin.py"],
data = [
"mkdocs.yml",
":au_all_units_hh",
":au_all_units_noio_hh",
":au_hh",
":au_noio_hh",
] + glob(["docs/**"]),
Expand Down Expand Up @@ -72,7 +76,7 @@ BASE_UNIT_STRING = " ".join(BASE_UNITS)

GIT_ID_CMD = "cat bazel-out/stable-status.txt | grep STABLE_GIT_ID | sed 's/STABLE_GIT_ID \\(.*\\)/\\1/' | tr -d '\\n'"

CMD_ROOT = "$(location tools/bin/make-single-file) {extra_opts} --units {units} --version-id $$({id_cmd}) > $(OUTS)"
CMD_ROOT = "$(location tools/bin/make-single-file) {extra_opts} {units} --version-id $$({id_cmd}) > $(OUTS)"

################################################################################
# Release single-file package `au.hh`
Expand All @@ -84,7 +88,7 @@ genrule(
cmd = CMD_ROOT.format(
extra_opts = "",
id_cmd = GIT_ID_CMD,
units = BASE_UNIT_STRING,
units = "--units " + BASE_UNIT_STRING,
),
stamp = True,
tools = ["tools/bin/make-single-file"],
Expand All @@ -106,7 +110,7 @@ genrule(
cmd = CMD_ROOT.format(
extra_opts = "--noio",
id_cmd = GIT_ID_CMD,
units = BASE_UNIT_STRING,
units = "--units " + BASE_UNIT_STRING,
),
stamp = True,
tools = ["tools/bin/make-single-file"],
Expand All @@ -118,3 +122,48 @@ cc_library(
hdrs = ["docs/au_noio.hh"],
visibility = ["//release:__pkg__"],
)

################################################################################
# Release single-file package `au_all_units.hh`

genrule(
name = "au_all_units_hh",
srcs = ["//au:headers"],
outs = ["docs/au_all_units.hh"],
cmd = CMD_ROOT.format(
extra_opts = "",
id_cmd = GIT_ID_CMD,
units = "--all-units",
),
stamp = True,
tools = ["tools/bin/make-single-file"],
)

cc_library(
name = "au_all_units_hh_lib",
hdrs = ["docs/au_all_units.hh"],
visibility = ["//release:__pkg__"],
)

################################################################################
# Release single-file package `au_all_units_noio.hh`

genrule(
name = "au_all_units_noio_hh",
srcs = ["//au:headers"],
outs = ["docs/au_all_units_noio.hh"],
cmd = CMD_ROOT.format(
extra_opts = "--noio",
id_cmd = GIT_ID_CMD,
units = "--all-units",
),
stamp = True,
tools = ["tools/bin/make-single-file"],
visibility = ["//release:__pkg__"],
)

cc_library(
name = "au_all_units_noio_hh_lib",
hdrs = ["docs/au_all_units_noio.hh"],
visibility = ["//release:__pkg__"],
)
20 changes: 19 additions & 1 deletion docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Here's an overview of the tradeoffs involved.
</tr>
<tr>
<td>Unit selection</td>
<td class="fair">Base units only</td>
<td class="fair">Base units only<br>(or too many units)</td>
<td class="good">Any units desired</td>
<td colspan=2 class="best">Any units desired, <i>without</i> needing "reinstall"</td>
</tr>
Expand Down Expand Up @@ -164,6 +164,24 @@ should get you any other unit you're likely to want. The units we include are:
Again, we recommend following the directions in the next section to get _exactly_ the units you
care about.

??? warning "Pre-built files with all units"
We also provide pre-built files with every unit the library knows about.

We don't advertise this option widely, because the library's compile time slowdown is largely
proportional to the number of units included in a translation unit. Thus, not only will this
configuration be the slowest of all, but _it will get increasingly slower as the library gets
better over time_ (by supporting more and more units out of the box).

Therefore, these files are only for use cases where _you don't care about compile time_. The
primary example is [the Compiler Explorer ("godbolt")](https://godbolt.org/z/687Ef4oqM).

**If you don't care about compile times**, here are the files:

- [`au_all_units.hh`](./au_all_units.hh)
- [`au_all_units_noio.hh`](./au_all_units_noio.hh)
(Same as above, but with `<iostream>` support stripped out)


#### Custom single file

It's easy to package the library in a _custom_ single file with _exactly_ the units you need.
Expand Down
22 changes: 22 additions & 0 deletions release/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,28 @@ cc_library(
hdrs = ["common_test_cases.hh"],
)

cc_test(
name = "au_all_units_hh_test",
size = "small",
srcs = ["au_all_units_hh_test.cc"],
deps = [
":common_test_cases",
"//:au_all_units_hh_lib",
"@com_google_googletest//:gtest_main",
],
)

cc_test(
name = "au_all_units_noio_hh_test",
size = "small",
srcs = ["au_all_units_noio_hh_test.cc"],
deps = [
":common_test_cases",
"//:au_all_units_noio_hh_lib",
"@com_google_googletest//:gtest_main",
],
)

cc_test(
name = "au_hh_test",
size = "small",
Expand Down
40 changes: 40 additions & 0 deletions release/au_all_units_hh_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright 2022 Aurora Operations, Inc.
//
// 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.

#include <chrono>

#include "docs/au_all_units.hh"
#include "gtest/gtest.h"

// clang-format off
//
// This file textually includes all of the test cases which every ready-made
// single-file release should pass. It needs to go after all other includes.
#include "release/common_test_cases.hh"
// clang-format on

namespace au {

namespace {
template <typename T>
std::string stream_to_string(const T &x) {
std::ostringstream oss;
oss << x;
return oss.str();
}
} // namespace

TEST(AuAllUnitsHh, PrintsValueAndUnitLabel) { EXPECT_EQ(stream_to_string(minutes(3)), "3 min"); }

} // namespace au
34 changes: 34 additions & 0 deletions release/au_all_units_noio_hh_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2022 Aurora Operations, Inc.
//
// 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.

#include <chrono>

#include "docs/au_all_units_noio.hh"
#include "gtest/gtest.h"

// clang-format off
//
// This file textually includes all of the test cases which every ready-made
// single-file release should pass. It needs to go after all other includes.
#include "release/common_test_cases.hh"
// clang-format on

namespace au {

TEST(AuAllUnitsNoioHh, IncludesMoreObscureUnits) {
EXPECT_EQ(minutes(120), hours(2));
EXPECT_EQ(fahrenheit_pt(32), celsius_pt(0));
}

} // namespace au
27 changes: 25 additions & 2 deletions tools/bin/make-single-file
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import argparse
import datetime
import os
import re
import subprocess
import sys
Expand Down Expand Up @@ -45,7 +46,7 @@ def main(argv=None):
transitively included files which are within the project, but we leave other
`#include` directives (such as standard library headers) untouched.
"""
args = parse_command_line_args(argv)
args = enumerate_units(parse_command_line_args(argv))
files = parse_files(
filenames=filenames(
main_files=args.main_files, units=args.units, include_io=args.include_io
Expand Down Expand Up @@ -74,7 +75,15 @@ def parse_command_line_args(argv):

parser.add_argument("main_files", nargs="*", help="The main files to aggregate")

parser.add_argument("--units", nargs="*", default=[], help="The units to include")
unit_group = parser.add_mutually_exclusive_group(required=False)
unit_group.add_argument(
"--units", nargs="*", default=[], help="The units to include"
)
unit_group.add_argument(
"--all-units",
action="store_true",
help="Include all units (may slow compilation!)",
)

parser.add_argument(
"--version-id",
Expand All @@ -92,6 +101,20 @@ def parse_command_line_args(argv):
return parser.parse_args()


def enumerate_units(args):
"""
Massage args object so that it's "as if" user had specified all units manually.
This means that if `--all-units` is specified, we populate the `units` list
with every existing entry, and then delete `--all-units`.
"""
if args.all_units:
args.units = [f[:-3] for f in os.listdir("au/units/") if f.endswith('.hh')]

del args.all_units
return args


def parse_files(filenames):
"""
Create a `SourceFile` for each of the transitive includes of `main_file`.
Expand Down

0 comments on commit 0185918

Please sign in to comment.