In Windows, I look application version in executable properties. In build rust apps that field is empty.
I believe you will have to create a manifest with the version information yourself. Rustc doesn't generate any manifest for you.
The winres
crate can be used to automate adding this information through a build.rs
file.
For completeness, I'll mention that you can use the embed-resource as well. I extract variables from Cargo.toml
, our source management metadata and other places and generate our own resource files, then use embed-resource
to generate the resource file and embed it into the executable.
With all that said, the winres
crate that @17cupsofcoffee mentioned looks considerably more ergonomic. If winres
can do what you need, you should definitely go with that, imo.
I unsuccessfully tried to use embed-resource
crate. The documentation has detailed step-by-step explanation, but it is for experienced users.
I do not understand for instance what is resource file.
Quickstart
In your build script, assuming the resource file is called
checksums.rc
I have a project my_test_exe
and main.rc
.
Does checksums.rc
mean main.rc
or my_test_exe.rc
for me ?
Example: Embedding a Windows Manifest
Courtesy of @jpoles1.
The following steps are used to embed a manifest in your compiled rust .exe file. In this example the manifest will cause admin permissions to be requested for the final executable:
....
In your project root directory, add a file named
build.rs
with the following:extern crate embed_resource; fn main() { embed_resource::compile("app-name-manifest.rc"); }
- In ...
What does "app-name-manifest.rc"
mean for me?
"my_test_exe-manifest.rc"
?
"main-manifest.rc"
?
"my_test_exe.rc"
?
"main.rc"
?
... ?
I tried to compile various (all those) variants in rust learning purposes
Ah, yes -- I understand that this is pretty overwhelming if you haven't worked with resource files before.
The (very) short version is something along the line of: In Windows there's some additional data that can be added to executable files (.exe and .dll) called "resources". These can include static strings, messages, menus, dialog windows, icons, bitmaps, etc. This also includes additional executable file metadata, which is where the version information you asked about is stored.
The way you traditionally built executables without resources was to compile and link your c code from foo.c
to foo.obj
, and then link foo.obj
to foo.exe
. To add resources you created a resource file (foo.rc
) which could look something like this:
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,0
PRODUCTVERSION 1,0,0,0
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
BEGIN
VALUE "CompanyName", "Acme Inc\0"
VALUE "FileDescription", "Blah, blah.\0"
VALUE "FileVersion", "1.0.0.0\0"
VALUE "InternalName", "something\0"
VALUE "LegalCopyright", "(C)2022 Acme Inc\0"
VALUE "OriginalFilename", "foo.exe\0"
VALUE "ProductName", "Blah Product\0"
VALUE "ProductVersion", "1.0.0.0\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 65001
END
END
Just as .c
is compiled to .obj
, the foo.rc
file is compiled to foo.res
using rc.exe
. Then when you run the linker to produce your .exe
you include the .res
file to generate an .exe
that includes your embedded resources.
embed-resource
handles the background magic of calling the resource compiler for you, and embeds the compiled resource file into your binary. However, you must generate the .rc
file and tell it the name of it. So, in short, you can tell your build.rs
script to generate a projectname.rc
containing something along the line of what it shown in the snippet above. Then you tell embed-resource about it using:
embed_resource::compile("projectname.rc");
(Though note that you'll probably want to generate the .rc
file under OUT_DIR
or something).
Once this has been done, build the crate and then you can right-click on the generated executable and select properties and you'll see the added fields in there.
Note that The Proper Way(tm) to do this is to extract the version from Cargo.toml/environment and generate an .rc
file using that field -- but start with generating a hard-coded .rc
file.
If you don't get it to work, I can put together a minimal example when I get back to the office in a few days.
No, no, no.. Thank You very mach.. I will try to dig myself... Sorry for disturbing you.
Outline of what I'm using follows. it uses a build script so that the embedded version info is the same as the package version (and description, etc.) specified in Cargo.toml. This could be improved to (for example) extract the target binary name to fill in the OriginalFilename
field in the versioninfo. It works by generating a versions.h
file that is included by this mostly template resources.rc file.
Note that the resource file refers to "icon.ico"
for the applicationicon, which is in the same folder as resources.rc, build.rs, and Cargo.toml.
Note that the resource file has a commented-out reference to an app manifest, so if you need one of those to do things like marking the executable as supporting long paths or the utf-8 codepage (or any number of other app-manifest settings), just uncomment these lines and put the manifest definition in the same folder.
Note - assumes the Microsoft build tools are installed and configured.
Cargo.toml
[package]
version = "0.1.0"
...
build = "build.rs"
[target.'cfg(windows)'.build-dependencies]
windres = "0.2"
build.rs
#[cfg(windows)]
extern crate windres;
use std::env;
use std::fs;
use std::path::Path;
#[cfg(windows)]
use windres::Build;
#[cfg(windows)]
fn main() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let resource_header = Path::new(&out_dir).join("versions.h");
// Write include file for resource parameters based on cargo settings
let major = env!("CARGO_PKG_VERSION_MAJOR");
let minor = env!("CARGO_PKG_VERSION_MINOR");
let patch = env!("CARGO_PKG_VERSION_PATCH");
let full = env!("CARGO_PKG_VERSION");
let description = env!("CARGO_PKG_DESCRIPTION");
let name = env!("CARGO_PKG_NAME");
fs::write(resource_header, format!("
#define VERSION_MAJOR {major}
#define VERSION_MINOR {minor}
#define VERSION_PATCH {patch}
#define VERSION_FULL \"{full}\"
#define VERSION_DESCRIPTION \"{description}\"
#define VERSION_NAME \"{name}\"
")).unwrap();
Build::new()
.include(out_dir)
.compile("resources.rc")
.unwrap();
// println!("cargo:rerun-if-changed=resources.rc"); // windres already does this
println!("cargo:rerun-if-changed=hank.ico");
println!("cargo:rerun-if-changed=app.manifest");
}
resources.rc
#include "versions.h"
#define IDR_MAIN_ICON 100
IDR_MAIN_ICON ICON "icon.ico"
// Version
1 VERSIONINFO
FILEVERSION VERSION_MAJOR,VERSION_MINOR,0,VERSION_PATCH
PRODUCTVERSION VERSION_MAJOR,VERSION_MINOR,0,VERSION_PATCH
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "<my-company>"
VALUE "FileDescription", VERSION_DESCRIPTION
VALUE "FileVersion", VERSION_FULL
VALUE "InternalName", VERSION_NAME
VALUE "LegalCopyright", "Copyright (C) 2022"
VALUE "OriginalFilename", "<name>.exe"
VALUE "ProductName", VERSION_NAME
VALUE "ProductVersion", VERSION_FULL
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
// Manifest
//#define RT_MANIFEST 24
//1 RT_MANIFEST "app.manifest"
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.