cargo_subcommand_metadata/lib.rs
1/// Cargo's name for the purpose of ELF notes.
2///
3/// The `name` field of an ELF note is designated to hold the entry's "owner" or
4/// "originator". No formal mechanism exists for avoiding name conflicts. By
5/// convention, vendors use their own name such as "XYZ Computer Company".
6pub const ELF_NOTE_NAME: &str = "rust-lang/cargo";
7
8/// Values used by Cargo as the `type` of its ELF notes.
9///
10/// Each originator controls its own note types. Multiple interpretations of a
11/// single type value can exist. A program must recognize both the `name` and
12/// the `type` to understand a descriptor.
13#[repr(i32)]
14#[non_exhaustive]
15pub enum ElfNoteType {
16 // DESCRIP
17 Description = 0xDE5C819,
18}
19
20/// Embed a description into a compiled Cargo subcommand, to be shown by `cargo
21/// --list`.
22///
23/// The following restrictions apply to a subcommand description:
24///
25/// - String length can be at most 280 bytes in UTF-8, although much shorter is
26/// better.
27/// - Must not contain the characters `\n`, `\r`, or `\x1B` (ESC).
28///
29/// Please consider running `cargo --list` and following the style of the
30/// existing descriptions of the built-in Cargo subcommands.
31///
32/// # Example
33///
34/// ```
35/// // subcommand's main.rs
36///
37/// cargo_subcommand_metadata::description! {
38/// "Draw a spiffy visualization of things"
39/// }
40///
41/// fn main() {
42/// /* … */
43/// }
44/// ```
45#[macro_export]
46macro_rules! description {
47 ($description:expr) => {
48 const _: () = {
49 const CARGO_SUBCOMMAND_DESCRIPTION: &str = $description;
50
51 assert!(
52 CARGO_SUBCOMMAND_DESCRIPTION.len() <= 280,
53 "subcommand description too long, must be at most 280",
54 );
55
56 #[cfg(target_os = "linux")]
57 const _: () = {
58 #[repr(C)]
59 struct ElfNote {
60 namesz: u32,
61 descsz: u32,
62 ty: $crate::ElfNoteType,
63
64 name: [u8; $crate::ELF_NOTE_NAME.len()],
65 // At least 1 to nul-terminate the string as is convention
66 // (though not required), plus zero padding to a multiple of 4
67 // bytes.
68 name_padding: [$crate::private::Padding;
69 1 + match ($crate::ELF_NOTE_NAME.len() + 1) % 4 {
70 0 => 0,
71 r => 4 - r,
72 }],
73
74 desc: [u8; CARGO_SUBCOMMAND_DESCRIPTION.len()],
75 // Zero padding to a multiple of 4 bytes.
76 desc_padding: [$crate::private::Padding;
77 match CARGO_SUBCOMMAND_DESCRIPTION.len() % 4 {
78 0 => 0,
79 r => 4 - r,
80 }],
81 }
82
83 #[used]
84 #[link_section = ".note.cargo.subcommand"]
85 static ELF_NOTE: ElfNote = {
86 ElfNote {
87 namesz: $crate::ELF_NOTE_NAME.len() as u32 + 1,
88 descsz: CARGO_SUBCOMMAND_DESCRIPTION.len() as u32,
89 ty: $crate::ElfNoteType::Description,
90 name: unsafe { *$crate::ELF_NOTE_NAME.as_ptr().cast() },
91 name_padding: $crate::private::padding(),
92 desc: unsafe { *CARGO_SUBCOMMAND_DESCRIPTION.as_ptr().cast() },
93 desc_padding: $crate::private::padding(),
94 }
95 };
96 };
97 };
98 };
99}
100
101// Implementation details. Not public API.
102#[doc(hidden)]
103pub mod private {
104 #[derive(Copy, Clone)]
105 #[repr(u8)]
106 pub enum Padding {
107 Zero = 0,
108 }
109
110 pub const fn padding<const N: usize>() -> [Padding; N] {
111 [Padding::Zero; N]
112 }
113}