diff --git a/BUILD b/BUILD new file mode 100644 index 0000000..adc03ca --- /dev/null +++ b/BUILD @@ -0,0 +1,4 @@ +exports_files([ + "Cargo.lock", + "Cargo.toml", +]) diff --git a/src/machoke/Cargo.lock b/Cargo.lock similarity index 100% rename from src/machoke/Cargo.lock rename to Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..52b87b4 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = [ + "src/machoke", +] +resolver = "3" diff --git a/MODULE.bazel b/MODULE.bazel index 678fe4f..1e9e5c0 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -1,5 +1,9 @@ -module(name = "alexion", version = "0.1.0") +module( + name = "alexion", + version = "0.1.0", +) +bazel_dep(name = "bazel_skylib", version = "1.8.2") bazel_dep(name = "rules_rust", version = "0.74.0") rust = use_extension("//bazel:rust.bzl", "rust") diff --git a/README.md b/README.md index 0d5a98c..9991994 100644 --- a/README.md +++ b/README.md @@ -2,16 +2,45 @@ This is my monorepo, started in 2026. -## Monorepo Structure -``` -dev/ Monorepo root -├── .archive/ Archived projects -├── configs/ Declarative configuration -│ └── nixos/ NixOS hosts, modules, guests, registry, and secrets -├── docs/ Obsidian vault / Second brain -├── src/ Projects -│ └── tools/ Projects that are tools -└── third_party/ Open source tools, libraries or otherwise +## Monorepo structure + +```text +dev/ Monorepo root +├── bazel/ Bazel support files +├── configs/ Declarative configuration +│ └── nixos/ NixOS hosts, modules, guests, registry, and secrets +├── docs/ Repository documentation +├── src/ Projects +│ └── dotfiles/ Home and system configuration +└── third_party/ External source and vendoring support + └── crates/ Generated Rust crate sources and Bazel targets ``` +## Rust dependencies + +The repository root contains one Cargo dependency graph. +Each Rust project declares its dependencies in its local `Cargo.toml` file. +Add each Rust project to the `members` list in the root `Cargo.toml` file. +Use explicit member paths because the Bazel generator does not expand workspace globs. + +Update the shared lockfile after changing a project manifest: + +```sh +cargo generate-lockfile +``` + +Regenerate local crate sources and Bazel targets: + +```sh +bazel run //third_party:vendor_crates +``` + +Reference each dependency with an explicit local Bazel label: + +```starlark +deps = ["//third_party/crates:crate_name"] +``` + +Commit the generated files under `third_party/crates/`. +Do not edit generated files manually. diff --git a/third_party/BUILD b/third_party/BUILD new file mode 100644 index 0000000..185241a --- /dev/null +++ b/third_party/BUILD @@ -0,0 +1,16 @@ +load("@rules_rust//crate_universe:defs.bzl", "crates_vendor") + +sh_binary( + name = "bazel_for_cargo_bazel", + srcs = ["bazel-for-cargo-bazel.sh"], +) + +crates_vendor( + name = "vendor_crates", + bazel = ":bazel_for_cargo_bazel", + cargo_lockfile = "//:Cargo.lock", + manifests = ["//:Cargo.toml"], + mode = "local", + tags = ["manual"], + vendor_path = "crates", +) diff --git a/third_party/bazel-for-cargo-bazel.sh b/third_party/bazel-for-cargo-bazel.sh new file mode 100755 index 0000000..5c6052e --- /dev/null +++ b/third_party/bazel-for-cargo-bazel.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +set -euo pipefail + +is_info=false +for argument in "$@"; do + if [[ "$argument" == "info" ]]; then + is_info=true + break + fi +done + +if $is_info; then + bazel "$@" | sed -E \ + 's/^release: release ([0-9]+\.[0-9]+\.[0-9]+).*/release: \1/' +else + exec bazel "$@" +fi diff --git a/third_party/crates/BUILD.bazel b/third_party/crates/BUILD.bazel new file mode 100644 index 0000000..ae6a71a --- /dev/null +++ b/third_party/crates/BUILD.bazel @@ -0,0 +1,31 @@ +############################################################################### +# @generated +# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To +# regenerate this file, run the following: +# +# bazel run @@//third_party:vendor_crates +############################################################################### + +package(default_visibility = ["//visibility:public"]) + +exports_files( + [ + "cargo-bazel.json", + "crates.bzl", + "defs.bzl", + ] + glob( + include = ["*.bazel"], + allow_empty = True, + ), +) + +filegroup( + name = "srcs", + srcs = glob( + include = [ + "*.bazel", + "*.bzl", + ], + allow_empty = True, + ), +) diff --git a/third_party/crates/alias_rules.bzl b/third_party/crates/alias_rules.bzl new file mode 100644 index 0000000..14b04c1 --- /dev/null +++ b/third_party/crates/alias_rules.bzl @@ -0,0 +1,47 @@ +"""Alias that transitions its target to `compilation_mode=opt`. Use `transition_alias="opt"` to enable.""" + +load("@rules_cc//cc:defs.bzl", "CcInfo") +load("@rules_rust//rust:rust_common.bzl", "COMMON_PROVIDERS") + +def _transition_alias_impl(ctx): + # `ctx.attr.actual` is a list of 1 item due to the transition + providers = [ctx.attr.actual[0][provider] for provider in COMMON_PROVIDERS] + if CcInfo in ctx.attr.actual[0]: + providers.append(ctx.attr.actual[0][CcInfo]) + return providers + +def _change_compilation_mode(compilation_mode): + def _change_compilation_mode_impl(_settings, _attr): + return { + "//command_line_option:compilation_mode": compilation_mode, + } + + return transition( + implementation = _change_compilation_mode_impl, + inputs = [], + outputs = [ + "//command_line_option:compilation_mode", + ], + ) + +def _transition_alias_rule(compilation_mode): + return rule( + implementation = _transition_alias_impl, + provides = COMMON_PROVIDERS, + attrs = { + "actual": attr.label( + mandatory = True, + doc = "`rust_library()` target to transition to `compilation_mode=opt`.", + providers = COMMON_PROVIDERS, + cfg = _change_compilation_mode(compilation_mode), + ), + "_allowlist_function_transition": attr.label( + default = "@bazel_tools//tools/allowlists/function_transition_allowlist", + ), + }, + doc = "Transitions a Rust library crate to the `compilation_mode=opt`.", + ) + +transition_alias_dbg = _transition_alias_rule("dbg") +transition_alias_fastbuild = _transition_alias_rule("fastbuild") +transition_alias_opt = _transition_alias_rule("opt") diff --git a/third_party/crates/crates.bzl b/third_party/crates/crates.bzl new file mode 100644 index 0000000..837646b --- /dev/null +++ b/third_party/crates/crates.bzl @@ -0,0 +1,431 @@ +############################################################################### +# @generated +# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To +# regenerate this file, run the following: +# +# bazel run @@//third_party:vendor_crates +############################################################################### +""" +# `crates_repository` API + +- [aliases](#aliases) +- [crate_edition](#crate_edition) +- [crate_deps](#crate_deps) +- [all_crate_deps](#all_crate_deps) +- [crate_repositories](#crate_repositories) + +""" + +load("@bazel_skylib//lib:selects.bzl", "selects") + +############################################################################### +# MACROS API +############################################################################### + +# An identifier that represent common dependencies (unconditional). +_COMMON_CONDITION = "" + +def _flatten_dependency_maps(all_dependency_maps): + """Flatten a list of dependency maps into one dictionary. + + Dependency maps have the following structure: + + ```python + DEPENDENCIES_MAP = { + # The first key in the map is a Bazel package + # name of the workspace this file is defined in. + "workspace_member_package": { + + # Not all dependencies are supported for all platforms. + # the condition key is the condition required to be true + # on the host platform. + "condition": { + + # An alias to a crate target. # The label of the crate target the + # Aliases are only crate names. # package name refers to. + "package_name": "@full//:label", + } + } + } + ``` + + Args: + all_dependency_maps (list): A list of dicts as described above + + Returns: + dict: A dictionary as described above + """ + dependencies = {} + + for workspace_deps_map in all_dependency_maps: + for pkg_name, conditional_deps_map in workspace_deps_map.items(): + if pkg_name not in dependencies: + non_frozen_map = dict() + for key, values in conditional_deps_map.items(): + non_frozen_map.update({key: dict(values.items())}) + dependencies.setdefault(pkg_name, non_frozen_map) + continue + + for condition, deps_map in conditional_deps_map.items(): + # If the condition has not been recorded, do so and continue + if condition not in dependencies[pkg_name]: + dependencies[pkg_name].setdefault(condition, dict(deps_map.items())) + continue + + # Alert on any miss-matched dependencies + inconsistent_entries = [] + for crate_name, crate_label in deps_map.items(): + existing = dependencies[pkg_name][condition].get(crate_name) + if existing and existing != crate_label: + inconsistent_entries.append((crate_name, existing, crate_label)) + dependencies[pkg_name][condition].update({crate_name: crate_label}) + + return dependencies + +def crate_deps(deps, package_name = None): + """Finds the fully qualified label of the requested crates for the package where this macro is called. + + Args: + deps (list): The desired list of crate targets. + package_name (str, optional): The package name of the set of dependencies to look up. + Defaults to `native.package_name()`. + + Returns: + list: A list of labels to generated rust targets (str) + """ + + if not deps: + return [] + + if package_name == None: + package_name = native.package_name() + + # Join both sets of dependencies + dependencies = _flatten_dependency_maps([ + _NORMAL_DEPENDENCIES, + _NORMAL_DEV_DEPENDENCIES, + _PROC_MACRO_DEPENDENCIES, + _PROC_MACRO_DEV_DEPENDENCIES, + _BUILD_DEPENDENCIES, + _BUILD_PROC_MACRO_DEPENDENCIES, + ]).pop(package_name, {}) + + # Combine all conditional packages so we can easily index over a flat list + # TODO: Perhaps this should actually return select statements and maintain + # the conditionals of the dependencies + flat_deps = {} + for deps_set in dependencies.values(): + for crate_name, crate_label in deps_set.items(): + flat_deps.update({crate_name: crate_label}) + + missing_crates = [] + crate_targets = [] + for crate_target in deps: + if crate_target not in flat_deps: + missing_crates.append(crate_target) + else: + crate_targets.append(flat_deps[crate_target]) + + if missing_crates: + fail("Could not find crates `{}` among dependencies of `{}`. Available dependencies were `{}`".format( + missing_crates, + package_name, + dependencies, + )) + + return crate_targets + +def crate_edition(package_name = None): + """Finds the Rust edition for the package where this macro is called. + + Args: + package_name (str, optional): The package name whose edition should be + looked up. Defaults to `native.package_name()` when unset. + + Returns: + str: The Rust edition declared by the package's Cargo.toml file. + """ + if package_name == None: + package_name = native.package_name() + + if package_name not in _CRATE_EDITIONS: + fail("Tried to get crate_edition for package " + package_name + " but that package had no Cargo.toml file") + + return _CRATE_EDITIONS[package_name] + +def all_crate_deps( + normal = False, + normal_dev = False, + proc_macro = False, + proc_macro_dev = False, + build = False, + build_proc_macro = False, + package_name = None): + """Finds the fully qualified label of all requested direct crate dependencies \ + for the package where this macro is called. + + If no parameters are set, all normal dependencies are returned. Setting any one flag will + otherwise impact the contents of the returned list. + + Args: + normal (bool, optional): If True, normal dependencies are included in the + output list. + normal_dev (bool, optional): If True, normal dev dependencies will be + included in the output list. + proc_macro (bool, optional): If True, proc_macro dependencies are included + in the output list. + proc_macro_dev (bool, optional): If True, dev proc_macro dependencies are + included in the output list. + build (bool, optional): If True, build dependencies are included + in the output list. + build_proc_macro (bool, optional): If True, build proc_macro dependencies are + included in the output list. + package_name (str, optional): The package name of the set of dependencies to look up. + Defaults to `native.package_name()` when unset. + + Returns: + list: A list of labels to generated rust targets (str) + """ + + if package_name == None: + package_name = native.package_name() + + # Determine the relevant maps to use + all_dependency_maps = [] + if normal: + all_dependency_maps.append(_NORMAL_DEPENDENCIES) + if normal_dev: + all_dependency_maps.append(_NORMAL_DEV_DEPENDENCIES) + if proc_macro: + all_dependency_maps.append(_PROC_MACRO_DEPENDENCIES) + if proc_macro_dev: + all_dependency_maps.append(_PROC_MACRO_DEV_DEPENDENCIES) + if build: + all_dependency_maps.append(_BUILD_DEPENDENCIES) + if build_proc_macro: + all_dependency_maps.append(_BUILD_PROC_MACRO_DEPENDENCIES) + + # Default to always using normal dependencies + if not all_dependency_maps: + all_dependency_maps.append(_NORMAL_DEPENDENCIES) + + dependencies = _flatten_dependency_maps(all_dependency_maps).pop(package_name, None) + + if not dependencies: + if dependencies == None: + fail("Tried to get all_crate_deps for package " + package_name + " but that package had no Cargo.toml file") + else: + return [] + + crate_deps = list(dependencies.pop(_COMMON_CONDITION, {}).values()) + for condition, deps in dependencies.items(): + crate_deps += selects.with_or({ + tuple(_CONDITIONS[condition]): deps.values(), + "//conditions:default": [], + }) + + return crate_deps + +def aliases( + normal = False, + normal_dev = False, + proc_macro = False, + proc_macro_dev = False, + build = False, + build_proc_macro = False, + package_name = None): + """Produces a map of Crate alias names to their original label + + If no dependency kinds are specified, `normal` and `proc_macro` are used by default. + Setting any one flag will otherwise determine the contents of the returned dict. + + Args: + normal (bool, optional): If True, normal dependencies are included in the + output list. + normal_dev (bool, optional): If True, normal dev dependencies will be + included in the output list.. + proc_macro (bool, optional): If True, proc_macro dependencies are included + in the output list. + proc_macro_dev (bool, optional): If True, dev proc_macro dependencies are + included in the output list. + build (bool, optional): If True, build dependencies are included + in the output list. + build_proc_macro (bool, optional): If True, build proc_macro dependencies are + included in the output list. + package_name (str, optional): The package name of the set of dependencies to look up. + Defaults to `native.package_name()` when unset. + + Returns: + dict: The aliases of all associated packages + """ + if package_name == None: + package_name = native.package_name() + + # Determine the relevant maps to use + all_aliases_maps = [] + if normal: + all_aliases_maps.append(_NORMAL_ALIASES) + if normal_dev: + all_aliases_maps.append(_NORMAL_DEV_ALIASES) + if proc_macro: + all_aliases_maps.append(_PROC_MACRO_ALIASES) + if proc_macro_dev: + all_aliases_maps.append(_PROC_MACRO_DEV_ALIASES) + if build: + all_aliases_maps.append(_BUILD_ALIASES) + if build_proc_macro: + all_aliases_maps.append(_BUILD_PROC_MACRO_ALIASES) + + # Default to always using normal aliases + if not all_aliases_maps: + all_aliases_maps.append(_NORMAL_ALIASES) + all_aliases_maps.append(_PROC_MACRO_ALIASES) + + aliases = _flatten_dependency_maps(all_aliases_maps).pop(package_name, None) + + if not aliases: + return dict() + + common_items = aliases.pop(_COMMON_CONDITION, {}).items() + + # If there are only common items in the dictionary, immediately return them + if not len(aliases.keys()) == 1: + return dict(common_items) + + # Build a single select statement where each conditional has accounted for the + # common set of aliases. + crate_aliases = {"//conditions:default": dict(common_items)} + for condition, deps in aliases.items(): + condition_triples = _CONDITIONS[condition] + for triple in condition_triples: + if triple in crate_aliases: + crate_aliases[triple].update(deps) + else: + crate_aliases.update({triple: dict(deps.items() + common_items)}) + + return select(crate_aliases) + +############################################################################### +# WORKSPACE MEMBER DEPS, ALIASES, AND EDITIONS +############################################################################### + +_CRATE_EDITIONS = { + "src/machoke": "2024", +} + +_NORMAL_DEPENDENCIES = { + "src/machoke": { + }, +} + +_NORMAL_ALIASES = { + "src/machoke": { + }, +} + +_NORMAL_DEV_DEPENDENCIES = { + "src/machoke": { + }, +} + +_NORMAL_DEV_ALIASES = { + "src/machoke": { + }, +} + +_PROC_MACRO_DEPENDENCIES = { + "src/machoke": { + }, +} + +_PROC_MACRO_ALIASES = { + "src/machoke": { + }, +} + +_PROC_MACRO_DEV_DEPENDENCIES = { + "src/machoke": { + }, +} + +_PROC_MACRO_DEV_ALIASES = { + "src/machoke": { + }, +} + +_BUILD_DEPENDENCIES = { + "src/machoke": { + }, +} + +_BUILD_ALIASES = { + "src/machoke": { + }, +} + +_BUILD_PROC_MACRO_DEPENDENCIES = { + "src/machoke": { + }, +} + +_BUILD_PROC_MACRO_ALIASES = { + "src/machoke": { + }, +} + +_CONDITIONS = { + "aarch64-apple-darwin": ["@rules_rust//rust/platform:aarch64-apple-darwin"], + "aarch64-apple-ios": ["@rules_rust//rust/platform:aarch64-apple-ios"], + "aarch64-apple-ios-macabi": ["@rules_rust//rust/platform:aarch64-apple-ios-macabi"], + "aarch64-apple-ios-sim": ["@rules_rust//rust/platform:aarch64-apple-ios-sim"], + "aarch64-linux-android": ["@rules_rust//rust/platform:aarch64-linux-android"], + "aarch64-pc-windows-msvc": ["@rules_rust//rust/platform:aarch64-pc-windows-msvc"], + "aarch64-unknown-fuchsia": ["@rules_rust//rust/platform:aarch64-unknown-fuchsia"], + "aarch64-unknown-linux-gnu": ["@rules_rust//rust/platform:aarch64-unknown-linux-gnu"], + "aarch64-unknown-nixos-gnu": ["@rules_rust//rust/platform:aarch64-unknown-nixos-gnu"], + "aarch64-unknown-none": ["@rules_rust//rust/platform:aarch64-unknown-none"], + "aarch64-unknown-nto-qnx710": ["@rules_rust//rust/platform:aarch64-unknown-nto-qnx710"], + "aarch64-unknown-uefi": ["@rules_rust//rust/platform:aarch64-unknown-uefi"], + "arm-unknown-linux-gnueabi": ["@rules_rust//rust/platform:arm-unknown-linux-gnueabi"], + "arm-unknown-linux-musleabi": ["@rules_rust//rust/platform:arm-unknown-linux-musleabi"], + "armv7-linux-androideabi": ["@rules_rust//rust/platform:armv7-linux-androideabi"], + "armv7-unknown-linux-gnueabi": ["@rules_rust//rust/platform:armv7-unknown-linux-gnueabi"], + "i686-apple-darwin": ["@rules_rust//rust/platform:i686-apple-darwin"], + "i686-linux-android": ["@rules_rust//rust/platform:i686-linux-android"], + "i686-pc-windows-msvc": ["@rules_rust//rust/platform:i686-pc-windows-msvc"], + "i686-unknown-freebsd": ["@rules_rust//rust/platform:i686-unknown-freebsd"], + "i686-unknown-linux-gnu": ["@rules_rust//rust/platform:i686-unknown-linux-gnu"], + "loongarch64-unknown-linux-gnu": ["@rules_rust//rust/platform:loongarch64-unknown-linux-gnu"], + "mips-unknown-linux-gnu": ["@rules_rust//rust/platform:mips-unknown-linux-gnu"], + "powerpc-unknown-linux-gnu": ["@rules_rust//rust/platform:powerpc-unknown-linux-gnu"], + "riscv32imac-unknown-none-elf": ["@rules_rust//rust/platform:riscv32imac-unknown-none-elf"], + "riscv32imc-unknown-none-elf": ["@rules_rust//rust/platform:riscv32imc-unknown-none-elf"], + "riscv64gc-unknown-linux-gnu": ["@rules_rust//rust/platform:riscv64gc-unknown-linux-gnu"], + "riscv64gc-unknown-none-elf": ["@rules_rust//rust/platform:riscv64gc-unknown-none-elf"], + "s390x-unknown-linux-gnu": ["@rules_rust//rust/platform:s390x-unknown-linux-gnu"], + "sparc64-unknown-linux-gnu": ["@rules_rust//rust/platform:sparc64-unknown-linux-gnu"], + "sparc64-unknown-netbsd": ["@rules_rust//rust/platform:sparc64-unknown-netbsd"], + "sparc64-unknown-openbsd": ["@rules_rust//rust/platform:sparc64-unknown-openbsd"], + "thumbv6m-none-eabi": ["@rules_rust//rust/platform:thumbv6m-none-eabi"], + "thumbv7em-none-eabi": ["@rules_rust//rust/platform:thumbv7em-none-eabi"], + "thumbv7em-none-eabihf": ["@rules_rust//rust/platform:thumbv7em-none-eabihf"], + "thumbv7m-none-eabi": ["@rules_rust//rust/platform:thumbv7m-none-eabi"], + "thumbv8m.main-none-eabi": ["@rules_rust//rust/platform:thumbv8m.main-none-eabi"], + "thumbv8m.main-none-eabihf": ["@rules_rust//rust/platform:thumbv8m.main-none-eabihf"], + "wasm32-unknown-emscripten": ["@rules_rust//rust/platform:wasm32-unknown-emscripten"], + "wasm32-unknown-unknown": ["@rules_rust//rust/platform:wasm32-unknown-unknown"], + "wasm32-wasip1": ["@rules_rust//rust/platform:wasm32-wasip1"], + "wasm32-wasip1-threads": ["@rules_rust//rust/platform:wasm32-wasip1-threads"], + "wasm32-wasip2": ["@rules_rust//rust/platform:wasm32-wasip2"], + "x86_64-apple-darwin": ["@rules_rust//rust/platform:x86_64-apple-darwin"], + "x86_64-apple-ios": ["@rules_rust//rust/platform:x86_64-apple-ios"], + "x86_64-apple-ios-macabi": ["@rules_rust//rust/platform:x86_64-apple-ios-macabi"], + "x86_64-linux-android": ["@rules_rust//rust/platform:x86_64-linux-android"], + "x86_64-pc-windows-msvc": ["@rules_rust//rust/platform:x86_64-pc-windows-msvc"], + "x86_64-unknown-freebsd": ["@rules_rust//rust/platform:x86_64-unknown-freebsd"], + "x86_64-unknown-fuchsia": ["@rules_rust//rust/platform:x86_64-unknown-fuchsia"], + "x86_64-unknown-linux-gnu": ["@rules_rust//rust/platform:x86_64-unknown-linux-gnu"], + "x86_64-unknown-nixos-gnu": ["@rules_rust//rust/platform:x86_64-unknown-nixos-gnu"], + "x86_64-unknown-none": ["@rules_rust//rust/platform:x86_64-unknown-none"], + "x86_64-unknown-uefi": ["@rules_rust//rust/platform:x86_64-unknown-uefi"], +} diff --git a/third_party/crates/defs.bzl b/third_party/crates/defs.bzl new file mode 100644 index 0000000..0b7adb2 --- /dev/null +++ b/third_party/crates/defs.bzl @@ -0,0 +1,23 @@ +############################################################################### +# @generated +# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To +# regenerate this file, run the following: +# +# bazel run @@//third_party:vendor_crates +############################################################################### +"""Deprecated: re-exports the crate_universe macros from `:crates.bzl`.""" + +load( + ":crates.bzl", + _aliases = "aliases", + _all_crate_deps = "all_crate_deps", + _crate_deps = "crate_deps", + _crate_edition = "crate_edition", + _crate_repositories = "crate_repositories", +) + +aliases = _aliases +all_crate_deps = _all_crate_deps +crate_deps = _crate_deps +crate_edition = _crate_edition +crate_repositories = _crate_repositories