diff --git a/.bazelrc b/.bazelrc index c363481091e6..d037a476e86b 100644 --- a/.bazelrc +++ b/.bazelrc @@ -2,6 +2,22 @@ # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# Ensure all builds have Carbon's workspace status attached. We have carefully +# factored the stamping done by this to avoid excessive build performance impact +# and so enable stamping with it by default. CI and systems especially dependent +# on caching should explicitly use `--nostamp`. +build --workspace_status_command=./scripts/workspace_status.py +build --stamp + +# Provide aliases for configuring the release and pre-release version being +# built. For documentation of these flags, see //bazel/version/BUILD. +build --flag_alias=release=//bazel/version:release +build --flag_alias=pre_release=//bazel/version:pre_release +build --flag_alias=rc_number=//bazel/version:rc_number +build --flag_alias=beta_number=//bazel/version:beta_number +build --flag_alias=alpha_number=//bazel/version:alpha_number +build --flag_alias=nightly_date=//bazel/version:nightly_date + # Support running clang-tidy with: # bazel build --config=clang-tidy -k //... # See: https://github.com/erenon/bazel_clang_tidy diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index f77dffd1c0f2..4b5f1845ea99 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -294,6 +294,9 @@ jobs: # https://discord.com/channels/655572317891461132/707150492370862090/1151605725576056934 build --jobs=32 + # Avoid any cache impact from build stamping in CI. + build --nostamp + # General build options. build --verbose_failures test --test_output=errors diff --git a/bazel/version/BUILD b/bazel/version/BUILD new file mode 100644 index 000000000000..365c53cbb7d5 --- /dev/null +++ b/bazel/version/BUILD @@ -0,0 +1,97 @@ +# Part of the Carbon Language project, under the Apache License v2.0 with LLVM +# Exceptions. See /LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +load("@bazel_skylib//rules:common_settings.bzl", "bool_flag", "int_flag", "string_flag") + +package(default_visibility = ["//toolchain/install:__pkg__"]) + +exports_files(["gen_tmpl.py"]) + +# Several flags are provided for customizing the exact version used for the +# build of Carbon. Each of these is documented here, but rather than using the +# label-based names in Bazel invocations (`bazel build --//bazel/version:flag`) +# we suggest using the flag aliases provided in the project's `.bazelrc` and we +# document the flags using those aliases. The aliases match the local flag names +# here. +# +# For more details on the versioning scheme used by Carbon, see: +# - https://docs.google.com/document/d/11S5VAPe5Pm_BZPlajWrqDDVr9qc7-7tS2VshqO0wWkk/edit?resourcekey=0-2YFC9Uvl4puuDnWlr2MmYw +# TODO: Replace with path to the markdown once this lands. +# +# First, we provide a flag to enable a release version: `--release`. It is +# disabled by default, and if enabled it must be the only version flag used. +bool_flag( + name = "release", + build_setting_default = False, +) + +# A `--pre_release=KIND` flag where `KIND` must be one of: +# - `rc` -- a release candidate version. +# Example: `--pre_release=rc --rc_number=2` +# - `beta` -- a beta version. +# Example: `--pre_release=beta --beta_number=2` +# - `alpha` -- an alpha version. +# Example: `--pre_release=alpha --alpha_number=2` +# - `nightly -- a nightly version. +# Example: `--pre_release=nightly --nightly_date=2024.06.17` +# - `dev` -- the default, a development build. +# Example: `--pre_release=dev` +# +# This flag cannot be used along with `--release`, and for all but the `dev` +# kind must be combined with one of the below flags to specify further details +# of the version. +string_flag( + name = "pre_release", + build_setting_default = "dev", + values = [ + "rc", + "beta", + "alpha", + "nightly", + "dev", + ], +) + +# `--rc_number=N` sets the release candidate number to `N`. Requires `--pre_release=rc`. +int_flag( + name = "rc_number", + build_setting_default = -1, +) + +# When `--pre_release=beta` is used, `--beta_number=N` set's the beta +# pre-release number to `N`. +# +# An error if used without `--pre_release=beta`. +int_flag( + name = "beta_number", + build_setting_default = -1, +) + +# When `--pre_release=alpha` is used, `--alpha_number=N` set's the alpha +# pre-release number to `N`. +# +# An error if used without `--pre_release=alpha`. +int_flag( + name = "alpha_number", + build_setting_default = -1, +) + +# When `--pre_release=nightly` is used, `--nightly_date=YYYY.MM.DD` set's the +# nightly build date. The value for this flag must be a string with the exact +# format of `YYYY.MM.DD`. +# +# An error if used without `--pre_release=nightly`. +string_flag( + name = "nightly_date", + build_setting_default = "", +) + +# A config setting to observe the value of the `--stamp` command line flag +# within starlark with a macro and `select`. This is a workaround suggested for +# a Bazel issue: https://github.com/bazelbuild/bazel/issues/11164 +config_setting( + name = "internal_stamp_flag_detect", + values = {"stamp": "1"}, + visibility = ["//visibility:public"], +) diff --git a/bazel/version/compute_version.bzl b/bazel/version/compute_version.bzl new file mode 100644 index 000000000000..f78b91ec7a80 --- /dev/null +++ b/bazel/version/compute_version.bzl @@ -0,0 +1,69 @@ +# Part of the Carbon Language project, under the Apache License v2.0 with LLVM +# Exceptions. See /LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +"""Compute the version string.""" + +load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") +load("//:version_base.bzl", "version_base") + +def _validate_nightly_date(date): + date_components = date.split(".", 2) + if len(date_components) != 3: + fail("Must provide a nightly date in 'YYYY.MM.DD' format, found '{}'.".format(date)) + year = date_components[0] + if len(year) != 4 or not year.isdigit(): + fail("The nightly date year was not a sequence of four digits.") + month = date_components[1] + if len(month) != 2 or not month.isdigit(): + fail("The nightly date month was not a sequence of two digits.") + day = date_components[2] + if len(day) != 2 or not day.isdigit(): + fail("The nightly date day was not a sequence of two digits.") + +def compute_version(ctx): + """Compute the version string. + + Args: + ctx: The context for a rule computing the version. + + Returns: + The version string. + """ + version = version_base + + # See if we need to append a pre-release suffix to the version. + # + # TODO: We should more fully check for erroneous combinations of flags here + # to help ensure users don't get surprising results. + if not ctx.attr._release_flag[BuildSettingInfo].value: + pre_release = ctx.attr._pre_release_flag[BuildSettingInfo].value + pre_release_numbers = { + "alpha": ctx.attr._alpha_number_flag[BuildSettingInfo].value, + "beta": ctx.attr._beta_number_flag[BuildSettingInfo].value, + "rc": ctx.attr._rc_number_flag[BuildSettingInfo].value, + } + if pre_release in pre_release_numbers: + number = pre_release_numbers[pre_release] + if number < 0: + fail("Must provide a non-negative {} number when building that pre-release.".format(pre_release)) + version += "-{0}.{1}".format(pre_release, number) + elif pre_release == "nightly": + date = ctx.attr._nightly_date_flag[BuildSettingInfo].value + _validate_nightly_date(date) + version += "-0.nightly.{}".format(date) + elif pre_release == "dev": + version += "-0.dev" + else: + fail("Invalid pre-release flag: " + pre_release) + + return version + +VERSION_ATTRS = { + "_alpha_number_flag": attr.label(default = ":alpha_number"), + "_beta_number_flag": attr.label(default = ":beta_number"), + "_nightly_date_flag": attr.label(default = ":nightly_date"), + "_pre_release_flag": attr.label(default = ":pre_release"), + "_rc_number_flag": attr.label(default = ":rc_number"), + "_release_flag": attr.label(default = ":release"), +} diff --git a/bazel/version/gen_tmpl.py b/bazel/version/gen_tmpl.py new file mode 100755 index 000000000000..5302887a6b53 --- /dev/null +++ b/bazel/version/gen_tmpl.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 + +"""Generate a file from a template, substituting the provided key/value pairs. + +The file format should match Python's `string.Template` substitution rules: +- `$$` for a literal `$` +- `$identifier` for some key `identifier` to be substituted +- `${identifier}` when adjacent text would be interpreted as part of the + identifier. + +The keys must be strings that are valid identifiers: `[_A-Za-z][_A-Za-z0-9]*` + +The values may not contain newlines or any vertical whitespace. + +The initial key/value pairs are read from the command line using repeated +`--substitute=KEY=DEFAULT-VALUE` flags. + +Updated values for those keys will be read from any files provided to the +`--status-file` flag. This flag can be given multiple times and the values will +be read and updated from the files in order, meaning the last file's value will +win. New keys are never read from these files. The file format parsed is Bazel's +[status file format](https://bazel.build/docs/user-manual#workspace-status): +each line is a single entry starting with a key using only characters `[_A-Z]`, +one space character, and the rest of the line is the value. To assist with using +Bazel status files, if the key parsed from the file begins with `STABLE_`, that +prefix is removed. Any keys which are present in the substitutions provided on +the command line will have their value updated with the string read from the +file. +""" + +__copyright__ = """ +Part of the Carbon Language project, under the Apache License v2.0 with LLVM +Exceptions. See /LICENSE for license information. +SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +""" + +import argparse +import sys +from pathlib import Path +from string import Template + + +def main() -> None: + parser = argparse.ArgumentParser(__doc__) + parser.add_argument( + "--template", + metavar="FILE", + type=Path, + required=True, + help="The template source file to use.", + ) + parser.add_argument( + "--output", + metavar="FILE", + type=Path, + required=True, + help="The output source file to produce.", + ) + parser.add_argument( + "--substitution", + metavar="KEY=DEFAULT-VALUE", + action="append", + help="A substitution that should be supported and its default value.", + ) + parser.add_argument( + "--status-file", + metavar="FILE", + type=Path, + action="append", + default=[], + help="A file of key/value updates in Bazel's status file format.", + ) + parser.add_argument("-v", "--verbose", action="store_true") + args = parser.parse_args() + + # Collect the supported substitutions from the command line. + substitutions = {} + for substitution_arg in args.substitution: + key, value = substitution_arg.split("=", 1) + substitutions.update({key: value}) + + # Read either of the two status files provided to build up substitutions, + # with the stable file last so its values override any duplicates. + for status_file in args.status_file: + if args.verbose: + print(f"Reading status file: {status_file}", file=sys.stderr) + for line in status_file.open(): + # Remove line endings. + line = line.rstrip("\r\n") + # Exactly matches our pattern + (key, value) = line.split(" ", 1) + key = key.removeprefix("STABLE_") + if key in substitutions: + if args.verbose: + print(f"Parsed: '{key}': '{value}'", file=sys.stderr) + substitutions.update({key: value}) + + if args.verbose: + print(f"Reading template file: {args.template}", file=sys.stderr) + with open(args.template) as template_file: + template = template_file.read() + + result = Template(template).substitute(substitutions) + + if args.verbose: + print(f"Writing output file: {args.output}", file=sys.stderr) + with open(args.output, mode="w") as output_file: + output_file.write(result) + + +if __name__ == "__main__": + main() diff --git a/bazel/version/rules.bzl b/bazel/version/rules.bzl new file mode 100644 index 000000000000..553309765479 --- /dev/null +++ b/bazel/version/rules.bzl @@ -0,0 +1,144 @@ +# Part of the Carbon Language project, under the Apache License v2.0 with LLVM +# Exceptions. See /LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +"""Rule to expand Bazel templates with version and build information. + +This rule takes a source code template and turns that into a specific source +code output, substituting version information and build information from Bazel's +`stable-status.txt` and `volatile-status.txt` produced by the +`workpsace_status_command` during the build. When stamping is disabled, the +build information is replaced with constant values to provide better caching. + +The template files should use Python's "template strings" syntax[1]. These rules +provide a fixed set of keys whose values will be substituted, and those keys +will always be substituted with something. They will have the value in the +stable status file if present, otherwise the value in the volatile status file +if present, otherwise the value "unknown". When reading keys from the status +files, a prefix of `STABLE_` will be removed from the key if present. + +[1]: https://docs.python.org/3/library/string.html#template-strings + +The substituted keys, and any guidance on values: +- `VERSION` (the version string for Carbon) +- `BUILD_EMBED_LABEL` (value of --embed_label) +- `BUILD_HOST` (the name of the host machine running the build) +- `BUILD_USER` (the name of the user running the build) +- `GIT_COMMIT_SHA` (output of `git parse-rev --short HEAD` or `unknown`) +- `GIT_DIRTY_SUFFIX` (`.dirty` if dirty client state or `` if unknown) +- `BUILD_TIMESTAMP` (the time of the build in seconds since the Unix Epoch) +- `ATTRIBUTE` (an optional attribute to apply to definitions, defaults to empty) +""" + +load(":compute_version.bzl", "compute_version") + +_STAMP_DOC = """ +Follows behavior of the common 'stamp' attributes on rules. Set to 1 or 0 to +force stamping with actual build info on or off respectively, and to -1 to +follow the value of the command line flag `--stamp`. +""" + +def _is_exec_config(ctx): + """Detect if this is the exec configuration, previously known as "host". + + Sadly, there is not yet a supported way to detect this so replicate the + hacks others currently use. Bazel issue: + https://github.com/bazelbuild/bazel/issues/14444 + """ + return "-exec" in ctx.bin_dir.path or "/host/" in ctx.bin_dir.path + +def _expand_version_build_info_impl(ctx): + """Generates a file from a template, substituting version and build info.""" + inputs = [ctx.file.template] + + # The substitutions provided and their default values. + substitutions = { + "BUILD_EMBED_LABEL": "unknown", + "BUILD_HOST": "unknown", + "BUILD_TIMESTAMP": "unknown", + "BUILD_USER": "unknown", + "GIT_COMMIT_SHA": "unknown", + "GIT_DIRTY_SUFFIX": "", + "MAKE_WEAK": "0", + "VERSION": compute_version(ctx), + } + substitutions.update(ctx.attr.substitutions) + + arguments = [ + "--template=" + ctx.file.template.path, + "--output=" + ctx.outputs.out.path, + ] + [ + "--substitution=" + key + "=" + value + for key, value in substitutions.items() + ] + + # We only want to allow stamping outside of the exec configuration. + if not _is_exec_config(ctx): + # Look at the attribute. + stamp = ctx.attr.stamp + + # If requested, use the command line flag to select. + if stamp == -1: + # Set the default from `--stamp` / `--nostamp` command line flag, + # which we detect through a macro and `config_setting`, and pipe + # through an attribute. + stamp = 1 if ctx.attr.internal_stamp_flag_detect else 0 + + # Add the status files if stamping. + if stamp == 1: + inputs += [ + ctx.info_file, + ctx.version_file, + ] + arguments += [ + "--status-file=" + ctx.info_file.path, + "--status-file=" + ctx.version_file.path, + ] + + ctx.actions.run( + inputs = inputs, + outputs = [ctx.outputs.out], + executable = ctx.executable._gen_tmpl_tool, + arguments = arguments, + progress_message = "Generating templated source file: " + + ctx.outputs.out.short_path, + ) + +expand_version_build_info_internal = rule( + implementation = _expand_version_build_info_impl, + attrs = { + "internal_stamp_flag_detect": attr.bool(default = False), + "out": attr.output(mandatory = True), + "stamp": attr.int(values = [-1, 0, 1], default = -1, doc = _STAMP_DOC), + "substitutions": attr.string_dict( + doc = "Extra substitutions, potentially overriding defaults.", + ), + "template": attr.label( + allow_single_file = True, + ), + "_alpha_number_flag": attr.label(default = ":alpha_number"), + "_beta_number_flag": attr.label(default = ":beta_number"), + "_gen_tmpl_tool": attr.label( + default = Label("//bazel/version:gen_tmpl.py"), + allow_single_file = True, + executable = True, + cfg = "exec", + ), + "_nightly_date_flag": attr.label(default = ":nightly_date"), + "_pre_release_flag": attr.label(default = ":pre_release"), + "_rc_number_flag": attr.label(default = ":rc_number"), + "_release_flag": attr.label(default = ":release"), + }, +) + +# We need a macro wrapping the rule so that we can inject a select that observes +# the `--stamp` command-line value and use that when needed. +def expand_version_build_info(name, **kwargs): + expand_version_build_info_internal( + name = name, + internal_stamp_flag_detect = select({ + "//bazel/version:internal_stamp_flag_detect": True, + "//conditions:default": False, + }), + **kwargs + ) diff --git a/common/BUILD b/common/BUILD index 78c80c3661ca..effab99786f3 100644 --- a/common/BUILD +++ b/common/BUILD @@ -3,6 +3,7 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") +load("//bazel/version:rules.bzl", "expand_version_build_info") package(default_visibility = ["//visibility:public"]) @@ -456,6 +457,77 @@ cc_library( ], ) +# The base version source file only uses non-stamped parts of the version +# information so we expand it once here without any stamping. +expand_version_build_info( + name = "version_cpp_gen", + out = "version.cpp", + stamp = 0, + template = "version.tmpl.cpp", +) + +# Build a nostamp version of the stamp source, but mark its definitions as weak. +# We'll include this in the library to satisfy definitions of library and test +# users, but still allow binaries that want full build stamping to depend on the +# stamp library below to override with strong, stamped definitions. +expand_version_build_info( + name = "version_nostamp_cpp_gen", + out = "version_nostamp.cpp", + stamp = 0, + substitutions = {"MAKE_WEAK": "1"}, + template = "version_stamp.tmpl.cpp", +) + +# Provides APIs for accessing Carbon version information. +# +# These provide full access to the major, minor, and patch version. It also +# provides an API for querying version strings that may contain detailed build +# information such as the commit SHA. +# +# By default, this provides the API and an *unstamped* implementations of +# version strings. As a consequence, depending on this library doesn't introduce +# any dependency on the commit SHA or loss of build caching. +# +# Targets that want full build info stamping in the data produced by these APIs +# should additionally depend on `:version_stamp` below -- the data these APIs +# return will be overridden in any binaries depending on that rule with the +# fully stamped details. +cc_library( + name = "version", + srcs = [ + "version.cpp", + "version_nostamp.cpp", + ], + hdrs = ["version.h"], + deps = [ + "@llvm-project//llvm:Support", + ], +) + +# Generate the fully stamped sourcefile if stamping is enabled in the build. +expand_version_build_info( + name = "version_stamp_cpp_gen", + out = "version_stamp.cpp", + template = "version_stamp.tmpl.cpp", +) + +# Depend on this library to enable fully-stamped build information in the +# version API provided by `:version`. This doesn't provide the API, it injects +# an override of stamped versions of the data. +# +# Note that depending on this will significantly reduce build caching with +# `--stamp` builds. It should be used sparingly, typically in user-facing +# binaries or systems that need to render a maximally detailed version string +# with build information stamped into it. +cc_library( + name = "version_stamp", + srcs = ["version_stamp.cpp"], + deps = [ + ":version", + "@llvm-project//llvm:Support", + ], +) + cc_library( name = "vlog", srcs = ["vlog_internal.h"], diff --git a/common/version.h b/common/version.h new file mode 100644 index 000000000000..ce2a91787334 --- /dev/null +++ b/common/version.h @@ -0,0 +1,27 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef CARBON_COMMON_VERSION_H_ +#define CARBON_COMMON_VERSION_H_ + +#include "llvm/ADT/StringRef.h" + +namespace Carbon { + +struct Version { + static const int Major; + static const int Minor; + static const int Patch; + + static const llvm::StringLiteral String; + + // A dedicated version information string to use in the toolchain as its + // command line rendered version. Composed centrally so it can be composed at + // compile time with potentially build info stamped components. + static const llvm::StringLiteral ToolchainInfo; +}; + +} // namespace Carbon + +#endif // CARBON_COMMON_VERSION_H_ diff --git a/common/version.tmpl.cpp b/common/version.tmpl.cpp new file mode 100644 index 000000000000..acfb15b3fbea --- /dev/null +++ b/common/version.tmpl.cpp @@ -0,0 +1,51 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include "common/version.h" + +#include + +namespace Carbon { + +// A simplistic string-to-integer routine that is consteval for compile-time +// extracting specific components of the version from the string form. We use +// `std::string_view` for its broader `constexpr` API. +static consteval auto ToInt(std::string_view str) -> int { + int result = 0; + while (true) { + result += str.front() - '0'; + str.remove_prefix(1); + if (str.empty()) { + break; + } + result *= 10; + } + return result; +} + +static consteval auto MajorVersion(std::string_view str) -> int { + return ToInt(str.substr(0, str.find('.'))); +} + +static consteval auto MinorVersion(std::string_view str) -> int { + str.remove_prefix(str.find('.') + 1); + return ToInt(str.substr(0, str.find('.'))); +} + +static consteval auto PatchVersion(std::string_view str) -> int { + str.remove_prefix(str.find('.') + 1); + str.remove_prefix(str.find('.') + 1); + // Note that searching for `-` may find the end of the string if there is no + // pre-release component, but that produces the correct result here. + return ToInt(str.substr(0, str.find('-'))); +} + +// The major, minor, and patch versions are always provided and stable. They +// don't depend on build stamping or introduce caching issues. Provide normal +// strong definitions. +constexpr int Version::Major = MajorVersion("$VERSION"); +constexpr int Version::Minor = MinorVersion("$VERSION"); +constexpr int Version::Patch = PatchVersion("$VERSION"); + +} // namespace Carbon diff --git a/common/version_stamp.tmpl.cpp b/common/version_stamp.tmpl.cpp new file mode 100644 index 000000000000..6c9575bd3a10 --- /dev/null +++ b/common/version_stamp.tmpl.cpp @@ -0,0 +1,25 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include "common/version.h" + +namespace Carbon { + +#pragma clang attribute push +// If requested, make the contents of this file weak. +#if $MAKE_WEAK +#pragma clang attribute(__attribute__((weak)), \ + apply_to = any(function, variable)) +#endif + +constexpr llvm::StringLiteral Version::String = + "$VERSION+$GIT_COMMIT_SHA$GIT_DIRTY_SUFFIX"; + +constexpr llvm::StringLiteral Version::ToolchainInfo = R"""( +Carbon Language toolchain version: $VERSION+$GIT_COMMIT_SHA$GIT_DIRTY_SUFFIX +)"""; + +#pragma clang attribute pop + +} // namespace Carbon diff --git a/scripts/fix_cc_deps.py b/scripts/fix_cc_deps.py index db35948f6c58..638ff80fbc5f 100755 --- a/scripts/fix_cc_deps.py +++ b/scripts/fix_cc_deps.py @@ -79,12 +79,14 @@ EXTERNAL_REPOS: Dict[str, ExternalRepo] = { ), } -# TODO: proto rules are aspect-based and their generated files don't show up in -# `bazel query` output. +# TODO: proto rules and template expansions are aspect-based and their generated +# files don't show up in `bazel query` output. # Try using `bazel cquery --output=starlark` to print `target.files`. # For protobuf, need to add support for `alias` rule kind. IGNORE_HEADER_REGEX = re.compile("^(.*\\.pb\\.h)$") -IGNORE_SOURCE_FILE_REGEX = re.compile("^third_party/clangd") +IGNORE_SOURCE_FILE_REGEX = re.compile( + "^(third_party/clangd.*|common/version.*\\.cpp)$" +) class Rule(NamedTuple): diff --git a/scripts/workspace_status.py b/scripts/workspace_status.py new file mode 100755 index 000000000000..314539419f8c --- /dev/null +++ b/scripts/workspace_status.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 + +"""Bazel `--workspace_status_command` script. + +This script is designed to be used in Bazel`s `--workspace_status_command` and +generate any desirable status artifacts. +""" + +__copyright__ = """ +Part of the Carbon Language project, under the Apache License v2.0 with LLVM +Exceptions. See /LICENSE for license information. +SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +""" + +import subprocess + + +def git_commit_sha() -> str: + return subprocess.check_output( + ["git", "rev-parse", "--short", "HEAD"], encoding="utf-8" + ).strip() + + +def git_dirty_suffix() -> str: + status = subprocess.check_output( + ["git", "status", "--porcelain"], encoding="utf-8" + ).strip() + return ".dirty" if len(status) > 0 else "" + + +def main() -> None: + print("STABLE_GIT_COMMIT_SHA " + git_commit_sha()) + print("STABLE_GIT_DIRTY_SUFFIX " + git_dirty_suffix()) + + +if __name__ == "__main__": + main() diff --git a/toolchain/driver/BUILD b/toolchain/driver/BUILD index 796cf98307ff..a7afac7abd09 100644 --- a/toolchain/driver/BUILD +++ b/toolchain/driver/BUILD @@ -63,6 +63,7 @@ cc_library( deps = [ ":clang_runner", "//common:command_line", + "//common:version", "//common:vlog", "//toolchain/base:value_store", "//toolchain/check", @@ -127,6 +128,7 @@ cc_binary( "//common:bazel_working_dir", "//common:exe_path", "//common:init_llvm", + "//common:version_stamp", "//toolchain/install:install_paths", "@llvm-project//llvm:Support", ], diff --git a/toolchain/driver/driver.cpp b/toolchain/driver/driver.cpp index 80319dd07341..cbd01afdd992 100644 --- a/toolchain/driver/driver.cpp +++ b/toolchain/driver/driver.cpp @@ -9,6 +9,7 @@ #include #include "common/command_line.h" +#include "common/version.h" #include "common/vlog.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/ScopeExit.h" @@ -398,24 +399,7 @@ The linked file name. The output is always a linked binary. }; struct Driver::Options { - static constexpr CommandLine::CommandInfo Info = { - .name = "carbon", - // TODO: Set up more detailed version information and use that here. - .version = R"""( -Carbon Language toolchain -- version 0.0.0 -)""", - .help = R"""( -This is the unified Carbon Language toolchain driver. Its subcommands provide -all of the core behavior of the toolchain, including compilation, linking, and -developer tools. Each of these has its own subcommand, and you can pass a -specific subcommand to the `help` subcommand to get details about its usage. -)""", - .help_epilogue = R"""( -For questions, issues, or bug reports, please use our GitHub project: - - https://github.com/carbon-language/carbon-lang -)""", - }; + static const CommandLine::CommandInfo Info; enum class Subcommand : int8_t { Compile, @@ -453,6 +437,25 @@ For questions, issues, or bug reports, please use our GitHub project: LinkOptions link_options; }; +// Note that this is not constexpr so that it can include information generated +// in separate translation units and potentially overridden at link time in the +// version string. +const CommandLine::CommandInfo Driver::Options::Info = { + .name = "carbon", + .version = Version::ToolchainInfo, + .help = R"""( +This is the unified Carbon Language toolchain driver. Its subcommands provide +all of the core behavior of the toolchain, including compilation, linking, and +developer tools. Each of these has its own subcommand, and you can pass a +specific subcommand to the `help` subcommand to get details about its usage. +)""", + .help_epilogue = R"""( +For questions, issues, or bug reports, please use our GitHub project: + + https://github.com/carbon-language/carbon-lang +)""", +}; + auto Driver::ParseArgs(llvm::ArrayRef args, Options& options) -> CommandLine::ParseResult { return CommandLine::Parse( diff --git a/toolchain/install/BUILD b/toolchain/install/BUILD index a607eee8d805..f04c7a5c9533 100644 --- a/toolchain/install/BUILD +++ b/toolchain/install/BUILD @@ -8,6 +8,7 @@ load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") load("@rules_pkg//pkg:mappings.bzl", "pkg_attributes", "pkg_filegroup", "pkg_files", "pkg_mklink", "strip_prefix") load("@rules_pkg//pkg:tar.bzl", "pkg_tar") load("@rules_pkg//pkg:zip.bzl", "pkg_zip") +load("pkg_naming.bzl", "pkg_naming_variables") load("symlink_filegroup.bzl", "symlink_filegroup") package(default_visibility = ["//visibility:public"]) @@ -185,23 +186,32 @@ pkg_filegroup( ], ) +pkg_naming_variables( + name = "packaging_variables", +) + # TODO: We should add support for injecting a version string into both the # output filename and the package directory name. pkg_tar( - name = "carbon_toolchain.tar.bz2.rule", + name = "carbon_toolchain_tar_rule", srcs = [":packaging_files"], - out = "carbon_toolchain.tar.bz2", extension = "tar.bz2", - package_dir = "carbon_toolchain", + package_dir = "carbon_toolchain-$(version)", + package_file_name = "carbon_toolchain-$(version).tar.bz2", + package_variables = ":packaging_variables", + stamp = -1, # Allow `--stamp` builds to produce file timestamps. tags = ["manual"], # Slow, exclude from wildcard builds. ) # TODO: We should add support for injecting a version string into both the # output filename and the package directory name. pkg_zip( - name = "carbon_toolchain.zip.rule", + name = "carbon_toolchain_zip_rule", srcs = [":packaging_files"], out = "carbon_toolchain.zip", - package_dir = "carbon_toolchain", + package_dir = "carbon_toolchain-$(version)", + package_file_name = "carbon_toolchain-$(version).zip", + package_variables = ":packaging_variables", + stamp = -1, # Allow `--stamp` builds to produce file timestamps. tags = ["manual"], # Slow, exclude from wildcard builds. ) diff --git a/toolchain/install/pkg_naming.bzl b/toolchain/install/pkg_naming.bzl new file mode 100644 index 000000000000..d58977598efb --- /dev/null +++ b/toolchain/install/pkg_naming.bzl @@ -0,0 +1,24 @@ +# Part of the Carbon Language project, under the Apache License v2.0 with LLVM +# Exceptions. See /LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +"""Rule to create variables for package naming.""" + +load("@rules_pkg//pkg:providers.bzl", "PackageVariablesInfo") +load("//bazel/version:compute_version.bzl", "VERSION_ATTRS", "compute_version") + +def _pkg_naming_variables_impl(ctx): + # TODO: Add support for digging the target CPU out of the toolchain here, + # remapping it to a more canonical name, and add that to the variables. The + # Bazel target CPU is already directly available, but it isn't likely + # canonical. + # TODO: Include the target OS as well as the target CPU. This likely needs + # similar re-mapping as the CPU does. + return PackageVariablesInfo(values = { + "version": compute_version(ctx), + }) + +pkg_naming_variables = rule( + implementation = _pkg_naming_variables_impl, + attrs = VERSION_ATTRS, +) diff --git a/version_base.bzl b/version_base.bzl new file mode 100644 index 000000000000..34654a754304 --- /dev/null +++ b/version_base.bzl @@ -0,0 +1,22 @@ +# Part of the Carbon Language project, under the Apache License v2.0 with LLVM +# Exceptions. See /LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +"""The base version of the Carbon repository. + +Carbon's version is defined here for export to various layers +where it is needed. This is not the currently *released* version, but the +version under *active development*. + +The current active development version is 0.0.0 -- we haven't yet made enough +progress towards our 0.1 milestone to meaningfully bump the version to that even +to describe the development activity. We also never plan to make even a +non-development pre-release of 0.0.0, only nightly development pre-releases are +expected. + +For more details on Carbon's versioning, see: + https://semver.org/ + TODO: Add Carbon-specific link. +""" + +version_base = "0.0.0"