Добавить сведения об exe-файле в двоичный файл скомпилированного кода Rust

После компиляции приложения командной строки Rust в двоичный файл *.exe в Windows я проверил детали двоичного файла, и они в основном пусты:

Есть ли способ добавить file description, file version, Product name, Product version через файл Cargo.toml в скомпилированный exe-файл?

Обновлять:

Я использовал winresource и все работало согласно инструкции библиотеки.

В Cargo.toml я добавил ссылку на скрипт сборки в разделе build-dependencies:

[build-dependencies]
winresource = "0.1.14"

И мой скрипт build.rs выглядит так:

extern crate winresource;

fn main() {
    if std::env::var("CARGO_CFG_TARGET_OS").unwrap() == "windows" {
        let mut res = winresource::WindowsResource::new();
        res.set_icon("images/aws_client.ico");
        res.compile().unwrap();
    }
}

Наконец, я добавил небольшой раздел в Cargo.toml, подобный этому:

[package.metadata.winresource]
ProductName = "Command Line AWS Client"
LegalCopyright = "Copyright © 2022"

Результирующие свойства файла выглядят следующим образом:

Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
1
0
80
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Информационные данные исполняемого файла Windows хранятся в виде ресурса в разделе .rsrc программы. Компилятор rustc не создаст такой раздел, это то же самое, что и другие родные компиляторы Windows: gcc, clang или MS cl этого не сделают.

Вместо этого вам нужно записать данные ресурса на специальном языке (файл *.rc) и скомпилировать его с помощью компилятора ресурсов. Это создаст объектный файл, который вы можете связать со своей программой.

Естественно, для этого есть ящик! Вы можете попробовать winresource (который недавно был разветвлен из winres, который кажется неуправляемым).

Или вы можете собрать ресурсы самостоятельно. Есть еще один крейт под названием windres, который найдет для вас компилятор ресурсов. Вы можете добавить это в свои сборки-зависимости, а затем написать build.rs что-то вроде:

fn main() -> std::io::Result<()> {
    println!("cargo:rerun-if-changed=build.rs");
    let target_os = std::env::var("CARGO_CFG_TARGET_OS");
    if target_os.as_deref() == Ok("windows") {
        let name = env!("CARGO_PKG_NAME");
        let version = env!("CARGO_PKG_VERSION");
        let mut sv = version.split('.').collect::<Vec<_>>();
        while sv.len() < 4 {
            sv.push("0");
        }
        let file_version = format!("{}, {}, {}, {}", sv[0], sv[1], sv[2], sv[3]);
        windres::Build::new()
            .define("THE_PROJECT", Some(format!(r#""{name}""#).as_str()))
            .define("THE_VERSION", Some(format!(r#""{version}""#).as_str()))
            .define("THE_FILEVERSION", Some(file_version.as_str()))
            .compile("res/resource.rc")?;
        for entry in std::fs::read_dir("res")? {
            let entry = entry?;
            println!("cargo:rerun-if-changed = {}", entry.path().display());
        }
    }
    Ok(())
}

Затем в файле res/resource.rc:

#pragma code_page(65001)

1 VERSIONINFO
FILEVERSION THE_FILEVERSION
PRODUCTVERSION THE_FILEVERSION
FILEFLAGSMASK 0x0000003Fl //VS_FFI_FILEFLAGSMASK
FILEFLAGS 0x0
FILEOS 0x00040004l //VOS_NT_WINDOWS32
FILETYPE 0x00000001l //VFT_APP
FILESUBTYPE 0x00000000l //VFT2_UNKNOWN
{
    BLOCK "StringFileInfo"
    {
        BLOCK "040904B0"
        {
            VALUE "FileDescription", THE_PROJECT
            VALUE "FileVersion", THE_VERSION
            VALUE "ProductVersion", THE_VERSION
            VALUE "ProductName", THE_PROJECT
        }
    }
    BLOCK "VarFileInfo"
    {
        VALUE "Translation", 0x409, 0x4B0
    }
}

1 ICON "res/the_icon.ico"

Несколько замечаний по этому файлу:

  • Строка #pragma code_page объявляет этот файл в кодировке UTF-8. Существуют и другие значения кодовых страниц, но единственно правильным является UTF-8.
  • Я передаю данные из Cargo.toml в файл RC в виде C-подобных макросов. Вы можете адаптировать их по своему усмотрению.
  • THE_FILEVERSION должно быть ровно 4 целых числа, разделенных запятыми. THE_VERSION однако это свободная строка. Я использую скрипт build.rs для конвертации.
  • Эти числовые константы (0x00000001l) эквивалентны этой константе в комментарии. Я использую константу, чтобы не включать файл winver.h, который вызывает проблемы в некоторых средах.
  • Значения в корне файла, такие как FILEVERSION, представляют собой фиксированную информацию о версии, а блок StringFileInfo содержит список локализованных строк ключ-значение. Есть много предопределенных , и вы даже можете создать свой собственный. Если вы используете локализованные строки, вам также понадобится блок VarFileInfo, объявляющий сам язык. Паре целых чисел 0x409, 0x4B0 соответствует строка "040904B0", что, по-моему, означает "английский, Unicode" ( здесь — список кодов).
  • Я добавил строку со значком EXE в конце, потому что у каждого EXE должен быть значок.

Возможно, вам придется немного поэкспериментировать, чтобы получить желаемую версию, но все части уже есть.

Другие вопросы по теме