Bug 1759175 pt1 - Repository integration r=glandium,supply-chain-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D174916
This commit is contained in:
Alex Franchuk 2024-03-07 14:03:53 +00:00
Родитель 187916a0ea
Коммит 5390c33a4e
39 изменённых файлов: 2523 добавлений и 7 удалений

59
Cargo.lock сгенерированный
Просмотреть файл

@ -798,6 +798,16 @@ checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961"
name = "cmake"
version = "0.1.999"
[[package]]
name = "cocoabind"
version = "0.1.0"
dependencies = [
"bindgen 0.69.4",
"block",
"mozbuild",
"objc",
]
[[package]]
name = "codespan-reporting"
version = "0.11.1"
@ -962,6 +972,41 @@ dependencies = [
"mach2",
]
[[package]]
name = "crashreporter"
version = "1.0.0"
dependencies = [
"anyhow",
"block",
"bytes",
"cfg-if 1.0.0",
"cocoabind",
"embed-manifest",
"env_logger",
"fluent",
"gtkbind",
"intl-memoizer",
"lazy_static",
"libloading",
"log",
"mozbuild",
"mozilla-central-workspace-hack",
"objc",
"phf",
"phf_codegen",
"serde",
"serde_json",
"sha2",
"time 0.3.23",
"tokio",
"unic-langid",
"uuid",
"warp",
"windows-sys 0.52.0",
"yaml-rust",
"zip",
]
[[package]]
name = "crc32fast"
version = "1.3.2"
@ -1484,6 +1529,12 @@ version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
[[package]]
name = "embed-manifest"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41cd446c890d6bed1d8b53acef5f240069ebef91d6fae7c5f52efe61fe8b5eae"
[[package]]
name = "encoding_c"
version = "0.9.8"
@ -2467,6 +2518,14 @@ dependencies = [
"bitflags 1.999.999",
]
[[package]]
name = "gtkbind"
version = "0.1.0"
dependencies = [
"bindgen 0.69.4",
"mozbuild",
]
[[package]]
name = "guid_win"
version = "0.2.0"

Просмотреть файл

@ -13,6 +13,7 @@ members = [
"security/manager/ssl/osclientcerts",
"testing/geckodriver",
"toolkit/components/uniffi-bindgen-gecko-js",
"toolkit/crashreporter/client-rust/app",
"toolkit/crashreporter/mozwer-rust",
"toolkit/crashreporter/rust_minidump_writer_linux",
"toolkit/library/gtest/rust",

Просмотреть файл

@ -13,6 +13,20 @@ def generate_bool(name):
return f"pub const {name}: bool = {'true' if value else 'false'};\n"
def generate_string_array(name):
value = buildconfig.substs.get(name) or []
return (
f"pub const {name}: [&str; {len(value)}] = ["
+ ",".join(map(escape_rust_string, value))
+ "];\n"
)
def generate_string(name):
value = buildconfig.substs.get(name) or ""
return f"pub const {name}: &str = {escape_rust_string(value)};\n"
def escape_rust_string(value):
"""escape the string into a Rust literal"""
# This could be more generous, but we're only escaping paths with it.
@ -85,7 +99,7 @@ def generate(output):
)
)
# Finally, write out some useful booleans from the buildconfig.
# Finally, write out some useful values from the buildconfig.
output.write(generate_bool("MOZ_FOLD_LIBS"))
output.write(generate_bool("NIGHTLY_BUILD"))
output.write(generate_bool("RELEASE_OR_BETA"))
@ -93,3 +107,10 @@ def generate(output):
output.write(generate_bool("MOZ_DEV_EDITION"))
output.write(generate_bool("MOZ_ESR"))
output.write(generate_bool("MOZ_DIAGNOSTIC_ASSERT_ENABLED"))
# Used by toolkit/crashreporter/client
output.write(generate_bool("MOZ_CRASHREPORTER_MOCK"))
output.write(generate_string_array("CC_BASE_FLAGS"))
output.write(generate_string_array("MOZ_GTK3_CFLAGS"))
output.write(generate_string_array("MOZ_GTK3_LIBS"))
output.write(generate_string("MOZ_APP_NAME"))

Просмотреть файл

@ -162,6 +162,8 @@ optional = true
features = [
"Wdk_System_Threading",
"Win32_Foundation",
"Win32_Globalization",
"Win32_Graphics_Gdi",
"Win32_Security",
"Win32_Storage_FileSystem",
"Win32_System_Com",
@ -175,6 +177,8 @@ features = [
"Win32_System_SystemInformation",
"Win32_System_SystemServices",
"Win32_System_Threading",
"Win32_UI_Controls",
"Win32_UI_Input_KeyboardAndMouse",
"Win32_UI_Shell",
"Win32_UI_WindowsAndMessaging",
]
@ -182,6 +186,7 @@ features = [
[features]
builtins-static = ["dep:bindgen", "dep:bitflags", "dep:memchr", "dep:nom", "dep:regex", "dep:smallvec"]
crashreporter = ["dep:env_logger", "dep:hyper", "dep:log", "dep:serde_json", "dep:time", "dep:uuid", "dep:windows-sys"]
geckodriver = ["dep:bitflags", "dep:bytes", "dep:cc", "dep:chrono", "dep:flate2", "dep:futures-channel", "dep:futures-core", "dep:futures-sink", "dep:futures-util", "dep:getrandom", "dep:hashbrown", "dep:hyper", "dep:indexmap", "dep:log", "dep:memchr", "dep:mio", "dep:num-integer", "dep:num-traits", "dep:once_cell", "dep:regex", "dep:semver", "dep:serde_json", "dep:smallvec", "dep:time", "dep:tokio", "dep:tokio-util", "dep:tracing", "dep:url", "dep:uuid", "dep:windows-sys"]
gkrust = ["dep:arrayvec", "dep:bindgen", "dep:bitflags", "dep:bytes", "dep:cc", "dep:chrono", "dep:env_logger", "dep:flate2", "dep:futures-channel", "dep:futures-core", "dep:futures-sink", "dep:futures-util", "dep:getrandom", "dep:hashbrown", "dep:indexmap", "dep:log", "dep:memchr", "dep:nom", "dep:num-integer", "dep:num-traits", "dep:once_cell", "dep:regex", "dep:scopeguard", "dep:semver", "dep:serde_json", "dep:smallvec", "dep:time", "dep:url", "dep:uuid", "dep:windows-sys"]
gkrust-gtest = ["gkrust"]

Просмотреть файл

@ -1533,6 +1533,12 @@ who = "Mike Hommey <mh+mozilla@glandium.org>"
criteria = "safe-to-deploy"
delta = "1.8.0 -> 1.8.1"
[[audits.embed-manifest]]
who = "Alex Franchuk <afranchuk@mozilla.com>"
criteria = "safe-to-deploy"
version = "1.4.0"
notes = "Necessary dependencies, all environment variable access is for build script vars set by cargo."
[[audits.encoding_c]]
who = "Henri Sivonen <hsivonen@hsivonen.fi>"
criteria = "safe-to-deploy"
@ -2386,6 +2392,12 @@ criteria = "safe-to-deploy"
version = "0.5.4"
notes = "I own this crate (I am contain-rs) and 0.5.4 passes miri. This code is very old and used by lots of people, so I'm pretty confident in it, even though it's in maintenance-mode and missing some nice-to-have APIs."
[[audits.linked-hash-map]]
who = "Alex Franchuk <afranchuk@mozilla.com>"
criteria = "safe-to-deploy"
delta = "0.5.4 -> 0.5.6"
notes = "New unsafe code has debug assertions and meets invariants. All other changes are formatting-related."
[[audits.linked-hash-map]]
who = "Mike Hommey <mh+mozilla@glandium.org>"
criteria = "safe-to-run"
@ -4708,6 +4720,15 @@ who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
criteria = "safe-to-deploy"
version = "0.10.1"
[[audits.zip]]
who = "Alex Franchuk <afranchuk@mozilla.com>"
criteria = "safe-to-deploy"
version = "0.6.4"
notes = """
No unsafe code nor unwarranted dependencies. Side-effectful std usage is only
present where expected (zip archive reading/writing and unpacking)
"""
[[audits.zip]]
who = "Mike Hommey <mh+mozilla@glandium.org>"
criteria = "safe-to-run"

Просмотреть файл

@ -817,7 +817,3 @@ criteria = "safe-to-deploy"
[[exemptions.xml-rs]]
version = "0.8.4"
criteria = "safe-to-deploy"
[[exemptions.zip]]
version = "0.6.2"
criteria = "safe-to-run"

1
third_party/rust/embed-manifest/.cargo-checksum.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
{"files":{"CHANGELOG.md":"a1cbcc712504ce6658dc209cab886c724654e6e0ef7b26315fd86d27e04a6e09","Cargo.toml":"4f091eabd120a0c1beefc14b92728e312f0c4f57ec8d85e2b3c5008e1ab01fd0","LICENSE":"d041a5a86caaf9cc7720f3637d689252083a664d9ccd946a36d1c7c708d204cb","README.md":"8cf8a7b20cfb7fa43c4ee9585bf92ea6c5a5c4ca3ef700974edb02025d5146c8","rustfmt.toml":"84f2508ad6e506e71f2005a75ef20ba32eedb3bd6547af9ea04596517be883bb","src/embed/coff.rs":"55ba01eea1a5e16ef7ae4438f5cc51ec60b6eca45f5cabd706ee5b82da4f010b","src/embed/error.rs":"aecb4928e70b02b784557352608f6d4fb9b88b44ae3297a33969a0f2219c416c","src/embed/mod.rs":"a3f02a410c7b681f87ecfed415c97c4252569dee231b864c2b12e5d80ed4065e","src/embed/test.rs":"969dc4e2faf9ef9e9164b0e1ad01082a0a5e6ef5eb963526cb207a0ec380d809","src/lib.rs":"2786336ef4e787b0e3ccec9ab02043ae064f50df73a63ffe8c374217adfb353a","src/manifest/mod.rs":"c82936859d2ef795836972e08323c9698cc0513ba125cfed43414cd05d27e0b1","src/manifest/test.rs":"f4ab58ed4fc99b087ba30ab595026e980573aad980b8fa6f59a5b748404012ff","src/manifest/xml.rs":"1bce12120e17a49da43eabbd1b04f712b3f6ece7fcbca9186319e301890e20c5","testdata/sample.exe.manifest":"01e80ef76de2b628d452c7e80022654b9e0c8aee72ec64ee522c7083d835f4df"},"package":"41cd446c890d6bed1d8b53acef5f240069ebef91d6fae7c5f52efe61fe8b5eae"}

59
third_party/rust/embed-manifest/CHANGELOG.md поставляемый Normal file
Просмотреть файл

@ -0,0 +1,59 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.4.0] - 2023-06-22
### Added
- Use `empty_manifest()` to start from a manifest with no default values.
Fixes [issue #6](https://gitlab.com/careyevans/embed-manifest/-/issues/6).
### Fixed
- Generate an object file with a single `.rsrc` section on GNU targets.
This lets it replace the default manifest from MinGW build tools.
Fixes [issue #5](https://gitlab.com/careyevans/embed-manifest/-/issues/5).
## [1.3.1] - 2022-08-07
### Added
- Format the code with Rustfmt.
- Assume `gnullvm` target environment should work like `gnu`.
- Add Windows 11 22H2 SDK version for maxversiontested.
### Changed
- Update `object` dependency and simplify unit tests.
## [1.3.0] - 2022-05-01
### Changed
- Use our own code again to generate COFF object files for GNU targets, but with
better knowledge of how such files are structured, reducing dependencies and
compile time.
- Link directly to the COFF object file instead of an archive file with one member.
### Fixed
- Make the custom `Error` type public.
## [1.2.1] - 2022-04-18
### Added
- Add checks for Windows builds to the documentation, for programs that
should still build for non-Windows targets.
## [1.2.0] - 2022-04-17
### Added
- Generate the manifest XML from Rust code rather than requiring the developer
to supply a correct manifest file.
## [1.1.0] - 2022-03-24
### Changed
- Use [Gimli Object](https://crates.io/crates/object) crate to build COFF
objects containing resources for GNU targets, removing a lot of magic numbers
and generating output more like LLVM `windres`.
## [1.0.0] - 2021-12-18
### Added
- Initial version.
[1.0.0]: https://gitlab.com/careyevans/embed-manifest/-/releases/v1.0.0
[1.1.0]: https://gitlab.com/careyevans/embed-manifest/-/releases/v1.1.0
[1.2.0]: https://gitlab.com/careyevans/embed-manifest/-/releases/v1.2.0
[1.2.1]: https://gitlab.com/careyevans/embed-manifest/-/releases/v1.2.1
[1.3.0]: https://gitlab.com/careyevans/embed-manifest/-/releases/v1.3.0
[1.3.1]: https://gitlab.com/careyevans/embed-manifest/-/releases/v1.3.1
[1.4.0]: https://gitlab.com/careyevans/embed-manifest/-/releases/v1.4.0

38
third_party/rust/embed-manifest/Cargo.toml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,38 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
rust-version = "1.56"
name = "embed-manifest"
version = "1.4.0"
authors = ["Carey Evans <carey@carey.geek.nz>"]
description = "Build script library to easily embed a Windows manifest."
readme = "README.md"
keywords = [
"build",
"manifest",
"windows",
]
categories = ["development-tools::build-utils"]
license = "MIT"
repository = "https://gitlab.com/careyevans/embed-manifest"
[dev-dependencies.object]
version = "0.31.1"
features = [
"read_core",
"coff",
]
default-features = false
[dev-dependencies.tempfile]
version = "3.5.0"

23
third_party/rust/embed-manifest/LICENSE поставляемый Normal file
Просмотреть файл

@ -0,0 +1,23 @@
MIT License
Copyright (c) 2021, 2022 Carey Evans
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

62
third_party/rust/embed-manifest/README.md поставляемый Normal file
Просмотреть файл

@ -0,0 +1,62 @@
Windows Manifest Embedding for Rust
===================================
The `embed-manifest` crate provides a straightforward way to embed
a Windows manifest in an executable, whatever the build environment,
without dependencies on external tools from LLVM or MinGW.
If you need to embed more resources than just a manifest, you may
find the [winres](https://crates.io/crates/winres) or
[embed-resource](https://crates.io/crates/embed-resource)
crates more suitable. They have additional dependencies and setup
requirements that may make them a little more difficult to use, though.
Why use it?
-----------
The Rust compiler doesnt add a manifest to a Windows executable, which
means that it runs with a few compatibility options and settings that
make it look like the program is running on an older version of Windows.
By adding an application manifest using this crate, even when cross-compiling:
- 32-bit programs with names that look like installers dont try to run
as an administrator.
- 32-bit programs arent shown a virtualised view of the filesystem and
registry.
- Many non-Unicode APIs accept UTF-8 strings, the same as Rust uses.
- The program sees the real Windows version, not Windows Vista.
- Built-in message boxes and other UI elements can display without blurring
on high-DPI monitors.
- Other features like long path names in APIs can be enabled.
Usage
-----
To embed a default manifest, include this code in a `build.rs` build
script:
```rust
use embed_manifest::{embed_manifest, new_manifest};
fn main() {
if std::env::var_os("CARGO_CFG_WINDOWS").is_some() {
embed_manifest(new_manifest("Contoso.Sample"))
.expect("unable to embed manifest file");
}
println!("cargo:rerun-if-changed=build.rs");
}
```
See the [crate documentation](https://docs.rs/embed-manifest) for
information about how to customise the embedded manifest.
License
-------
For the avoidance of doubt, while this crate itself is licensed to
you under the MIT License, this does not affect the copyright status
and licensing of your own code when this is used from a Cargo build
script.

2
third_party/rust/embed-manifest/rustfmt.toml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,2 @@
max_width = 132
newline_style = "Unix"

192
third_party/rust/embed-manifest/src/embed/coff.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,192 @@
//! Just as much COFF object file support as is needed to write a resource data segment
//! for GNU Windows targets. Inspired by the `write::Object` code from the `object` crate.
//!
//! Integers are converted from u64 to u32 and added without checking because the manifest
//! data cannot get anywhere close to overflowing unless the supplied application name or
//! number of dependencies was extremely long. If this code was used more generally or if
//! the input was less trustworthy then more checked conversions and checked arithmetic
//! would be needed.
use std::io::{self, Seek, SeekFrom, Write};
use std::time::SystemTime;
#[derive(Debug, Clone, Copy)]
pub enum MachineType {
I386,
X86_64,
Aarch64,
}
impl MachineType {
pub fn machine(&self) -> u16 {
match self {
Self::I386 => 0x014c,
Self::X86_64 => 0x8664,
Self::Aarch64 => 0xaa64,
}
}
pub fn relocation_type(&self) -> u16 {
match self {
Self::I386 => 7,
Self::X86_64 => 3,
Self::Aarch64 => 2,
}
}
}
pub struct CoffWriter<W> {
/// wrapped writer or buffer
writer: W,
/// machine type for file header
machine_type: MachineType,
// size in bytes of resource section data
size_of_raw_data: u32,
// number of relocations at the end of the section
number_of_relocations: u16,
}
impl<W: Write + Seek> CoffWriter<W> {
/// Create a new instance wrapping a writer.
pub fn new(mut writer: W, machine_type: MachineType) -> io::Result<Self> {
// Add space for file header and section table.
writer.write_all(&[0; 60])?;
Ok(Self {
writer,
machine_type,
size_of_raw_data: 0,
number_of_relocations: 0,
})
}
/// Add data to a section and return its offset within the section.
pub fn add_data(&mut self, data: &[u8]) -> io::Result<u32> {
let start = self.size_of_raw_data;
self.writer.write_all(data)?;
self.size_of_raw_data = start + data.len() as u32;
Ok(start)
}
// Pad the resource data to a multiple of `n` bytes.
pub fn align_to(&mut self, n: u32) -> io::Result<()> {
let offset = self.size_of_raw_data % n;
if offset != 0 {
let padding = n - offset;
for _ in 0..padding {
self.writer.write_all(&[0])?;
}
self.size_of_raw_data += padding;
}
Ok(())
}
/// Write a relocation for a symbol at the end of the section.
pub fn add_relocation(&mut self, address: u32) -> io::Result<()> {
self.number_of_relocations += 1;
self.writer.write_all(&address.to_le_bytes())?;
self.writer.write_all(&[0, 0, 0, 0])?;
self.writer.write_all(&self.machine_type.relocation_type().to_le_bytes())
}
/// Write the object and section headers and write the symbol table.
pub fn finish(mut self) -> io::Result<W> {
// Get the timestamp for the header. `as` is correct here, as the low 32 bits
// should be used.
let timestamp = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.map_or(0, |d| d.as_secs() as u32);
// Copy file location of the symbol table.
let pointer_to_symbol_table = self.writer.stream_position()? as u32;
// Write the symbols and auxiliary data for the section.
self.writer.write_all(b".rsrc\0\0\0")?; // name
self.writer.write_all(&[0, 0, 0, 0])?; // address
self.writer.write_all(&[1, 0])?; // section number (1-based)
self.writer.write_all(&[0, 0, 3, 1])?; // type = 0, class = static, aux symbols = 1
self.writer.write_all(&self.size_of_raw_data.to_le_bytes())?;
self.writer.write_all(&self.number_of_relocations.to_le_bytes())?;
self.writer.write_all(&[0; 12])?;
// Write the empty string table.
self.writer.write_all(&[0; 4])?;
// Write the object header.
let end_of_file = self.writer.seek(SeekFrom::Start(0))?;
self.writer.write_all(&self.machine_type.machine().to_le_bytes())?;
self.writer.write_all(&[1, 0])?; // number of sections
self.writer.write_all(&timestamp.to_le_bytes())?;
self.writer.write_all(&pointer_to_symbol_table.to_le_bytes())?;
self.writer.write_all(&[2, 0, 0, 0])?; // number of symbol table entries
self.writer.write_all(&[0; 4])?; // optional header size = 0, characteristics = 0
// Write the section header.
self.writer.write_all(b".rsrc\0\0\0")?;
self.writer.write_all(&[0; 8])?; // virtual size = 0 and virtual address = 0
self.writer.write_all(&self.size_of_raw_data.to_le_bytes())?;
self.writer.write_all(&[60, 0, 0, 0])?; // pointer to raw data
self.writer.write_all(&(self.size_of_raw_data + 60).to_le_bytes())?; // pointer to relocations
self.writer.write_all(&[0; 4])?; // pointer to line numbers
self.writer.write_all(&self.number_of_relocations.to_le_bytes())?;
self.writer.write_all(&[0; 2])?; // number of line numbers
self.writer.write_all(&[0x40, 0, 0x30, 0xc0])?; // characteristics
// Return the inner writer and dispose of this object.
self.writer.seek(SeekFrom::Start(end_of_file))?;
Ok(self.writer)
}
}
/// Returns the bytes for a resource directory table.
///
/// Most of the fields are set to zero, including the timestamp, to aid
/// with making builds reproducible.
///
/// ```c
/// typedef struct {
/// DWORD Characteristics,
/// DWORD TimeDateStamp,
/// WORD MajorVersion,
/// WORD MinorVersion,
/// WORD NumberOfNamedEntries,
/// WORD NumberOfIdEntries
/// } IMAGE_RESOURCE_DIRECTORY;
/// ```
pub fn resource_directory_table(number_of_id_entries: u16) -> [u8; 16] {
let mut table = [0; 16];
table[14..16].copy_from_slice(&number_of_id_entries.to_le_bytes());
table
}
/// Returns the bytes for a resource directory entry for an ID.
///
/// ```c
/// typedef struct {
/// DWORD Name,
/// DWORD OffsetToData
/// } IMAGE_RESOURCE_DIRECTORY_ENTRY;
/// ```
pub fn resource_directory_id_entry(id: u32, offset: u32, subdirectory: bool) -> [u8; 8] {
let mut entry = [0; 8];
entry[0..4].copy_from_slice(&id.to_le_bytes());
let flag: u32 = if subdirectory { 0x80000000 } else { 0 };
entry[4..8].copy_from_slice(&((offset & 0x7fffffff) | flag).to_le_bytes());
entry
}
/// Returns the bytes for a resource data entry.
///
/// ```c
/// typedef struct {
/// DWORD OffsetToData,
/// DWORD Size,
/// DWORD CodePage,
/// DWORD Reserved
/// } IMAGE_RESOURCE_DATA_ENTRY;
/// ```
pub fn resource_data_entry(rva: u32, size: u32) -> [u8; 16] {
let mut entry = [0; 16];
entry[0..4].copy_from_slice(&rva.to_le_bytes());
entry[4..8].copy_from_slice(&size.to_le_bytes());
entry
}

57
third_party/rust/embed-manifest/src/embed/error.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,57 @@
//! Error handling for application manifest embedding.
use std::fmt::{self, Display, Formatter};
use std::io::{self, ErrorKind};
/// The error type which is returned when application manifest embedding fails.
#[derive(Debug)]
pub struct Error {
repr: Repr,
}
#[derive(Debug)]
enum Repr {
IoError(io::Error),
UnknownTarget,
}
impl Error {
pub(crate) fn unknown_target() -> Error {
Error {
repr: Repr::UnknownTarget,
}
}
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self.repr {
Repr::IoError(ref e) => write!(f, "I/O error: {}", e),
Repr::UnknownTarget => f.write_str("unknown target"),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self.repr {
Repr::IoError(ref e) => Some(e),
_ => None,
}
}
}
impl From<io::Error> for Error {
fn from(e: io::Error) -> Self {
Error { repr: Repr::IoError(e) }
}
}
impl From<Error> for io::Error {
fn from(e: Error) -> Self {
match e.repr {
Repr::IoError(ioe) => ioe,
_ => io::Error::new(ErrorKind::Other, e),
}
}
}

139
third_party/rust/embed-manifest/src/embed/mod.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,139 @@
use std::env;
use std::fs::{self, File};
use std::io::{self, stdout, BufWriter, Cursor, Write};
use std::path::{Path, PathBuf};
use crate::manifest::ManifestBuilder;
use self::coff::{resource_data_entry, resource_directory_id_entry, resource_directory_table, CoffWriter, MachineType};
use self::error::Error;
mod coff;
pub mod error;
#[cfg(test)]
mod test;
/// Embeds the manifest described by `manifest` by converting it to XML,
/// then saving it to a file and passing the correct options to the linker
/// on MSVC targets, or by building a static library and instructing Cargo
/// to link the executable against it on GNU targets.
pub fn embed_manifest(manifest: ManifestBuilder) -> Result<(), Error> {
let out_dir = get_out_dir()?;
let target = get_target()?;
if matches!(target.os, TargetOs::WindowsMsvc) {
let manifest_file = out_dir.join("manifest.xml");
write!(BufWriter::new(File::create(&manifest_file)?), "{}", manifest)?;
link_manifest_msvc(&manifest_file, &mut stdout().lock())
} else {
let manifest_data = manifest.to_string();
link_manifest_gnu(manifest_data.as_bytes(), &out_dir, target.arch, &mut stdout().lock())
}
}
/// Directly embeds the manifest in the provided `file` by passing the correct
/// options to the linker on MSVC targets, or by building a static library
/// and instructing Cargo to link the executable against it on GNU targets.
pub fn embed_manifest_file<P: AsRef<Path>>(file: P) -> Result<(), io::Error> {
let out_dir = get_out_dir()?;
let target = get_target()?;
if matches!(target.os, TargetOs::WindowsMsvc) {
Ok(link_manifest_msvc(file.as_ref(), &mut stdout().lock())?)
} else {
let manifest = fs::read(file.as_ref())?;
Ok(link_manifest_gnu(&manifest, &out_dir, target.arch, &mut stdout().lock())?)
}
}
fn get_out_dir() -> Result<PathBuf, io::Error> {
match env::var_os("OUT_DIR") {
Some(dir) => Ok(PathBuf::from(dir)),
None => env::current_dir(),
}
}
enum TargetOs {
WindowsGnu,
WindowsMsvc,
}
struct Target {
arch: MachineType,
os: TargetOs,
}
fn get_target() -> Result<Target, Error> {
match env::var("TARGET") {
Ok(target) => parse_target(&target),
_ => Err(Error::unknown_target()),
}
}
fn parse_target(target: &str) -> Result<Target, Error> {
let mut iter = target.splitn(3, '-');
let arch = match iter.next() {
Some("i686") => MachineType::I386,
Some("aarch64") => MachineType::Aarch64,
Some("x86_64") => MachineType::X86_64,
_ => return Err(Error::unknown_target()),
};
if iter.next() != Some("pc") {
return Err(Error::unknown_target());
}
let os = match iter.next() {
Some("windows-gnu") => TargetOs::WindowsGnu,
Some("windows-gnullvm") => TargetOs::WindowsGnu,
Some("windows-msvc") => TargetOs::WindowsMsvc,
_ => return Err(Error::unknown_target()),
};
Ok(Target { arch, os })
}
fn link_manifest_msvc<W: Write>(manifest_path: &Path, out: &mut W) -> Result<(), Error> {
writeln!(out, "cargo:rustc-link-arg-bins=/MANIFEST:EMBED")?;
writeln!(
out,
"cargo:rustc-link-arg-bins=/MANIFESTINPUT:{}",
manifest_path.canonicalize()?.display()
)?;
writeln!(out, "cargo:rustc-link-arg-bins=/MANIFESTUAC:NO")?;
Ok(())
}
fn link_manifest_gnu<W: Write>(manifest: &[u8], out_dir: &Path, arch: MachineType, out: &mut W) -> Result<(), Error> {
// Generate a COFF object file containing the manifest in a .rsrc section.
let object_data = create_object_file(manifest, arch)?;
let path = out_dir.join("embed-manifest.o");
fs::create_dir_all(out_dir)?;
fs::write(&path, object_data)?;
// Link the manifest with the executable.
writeln!(out, "cargo:rustc-link-arg-bins={}", path.display())?;
Ok(())
}
fn create_object_file(manifest: &[u8], arch: MachineType) -> Result<Vec<u8>, io::Error> {
// Start object file with .rsrc section.
let mut obj = CoffWriter::new(Cursor::new(Vec::with_capacity(4096)), arch)?;
// Create resource directories for type ID 24, name ID 1, language ID 1033.
obj.add_data(&resource_directory_table(1))?;
obj.add_data(&resource_directory_id_entry(24, 24, true))?;
obj.add_data(&resource_directory_table(1))?;
obj.add_data(&resource_directory_id_entry(1, 48, true))?;
obj.add_data(&resource_directory_table(1))?;
obj.add_data(&resource_directory_id_entry(1033, 72, false))?;
// Add resource data entry with relocated address.
let address = obj.add_data(&resource_data_entry(88, manifest.len() as u32))?;
// Add the manifest data.
obj.add_data(manifest)?;
obj.align_to(8)?;
// Write manifest data relocation at the end of the section.
obj.add_relocation(address)?;
// Finish writing file and return the populated object data.
Ok(obj.finish()?.into_inner())
}

173
third_party/rust/embed-manifest/src/embed/test.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,173 @@
use std::fs;
use std::path::{Path, PathBuf};
use object::coff::CoffFile;
use object::pe::{ImageResourceDataEntry, ImageResourceDirectory, ImageResourceDirectoryEntry, IMAGE_RESOURCE_DATA_IS_DIRECTORY};
use object::{
pod, Architecture, LittleEndian, Object, ObjectSection, ObjectSymbol, RelocationEncoding, RelocationKind, SectionKind,
};
use tempfile::{tempdir, TempDir};
use crate::embed::coff::MachineType;
use crate::embed::{create_object_file, link_manifest_gnu, link_manifest_msvc, TargetOs};
use crate::new_manifest;
#[test]
fn create_obj() {
let res = do_embed_file(MachineType::X86_64, TargetOs::WindowsGnu);
let data = fs::read(&res.object_file()).unwrap();
let obj = CoffFile::parse(&data[..]).unwrap();
assert_eq!(obj.architecture(), Architecture::X86_64);
let expected_manifest = fs::read(&sample_manifest_path()).unwrap();
check_object_file(obj, &expected_manifest);
}
#[test]
fn link_lib_gnu() {
let res = do_embed_file(MachineType::X86_64, TargetOs::WindowsGnu);
assert!(res.object_file().exists());
let object_option = format!("cargo:rustc-link-arg-bins={}", res.object_file().display());
assert_eq!(res.lines(), &[object_option.as_str()]);
}
#[test]
fn link_file_msvc() {
let res = do_embed_file(MachineType::X86_64, TargetOs::WindowsMsvc);
assert!(!res.object_file().exists());
let mut input_option = String::from("cargo:rustc-link-arg-bins=/MANIFESTINPUT:");
input_option.push_str(res.manifest_path.canonicalize().unwrap().to_str().unwrap());
assert_eq!(
res.lines(),
&[
"cargo:rustc-link-arg-bins=/MANIFEST:EMBED",
input_option.as_str(),
"cargo:rustc-link-arg-bins=/MANIFESTUAC:NO"
]
);
}
struct EmbedResult {
manifest_path: PathBuf,
out_dir: TempDir,
output: String,
}
impl EmbedResult {
fn object_file(&self) -> PathBuf {
self.out_dir.path().join("embed-manifest.o")
}
fn lines(&self) -> Vec<&str> {
self.output.lines().collect()
}
}
fn sample_manifest_path() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("testdata/sample.exe.manifest")
}
fn do_embed_file(arch: MachineType, os: TargetOs) -> EmbedResult {
let manifest_path = sample_manifest_path();
let out_dir = tempdir().unwrap();
let mut buf: Vec<u8> = Vec::new();
if matches!(os, TargetOs::WindowsMsvc) {
link_manifest_msvc(&manifest_path, &mut buf).unwrap();
} else {
link_manifest_gnu(&fs::read(&manifest_path).unwrap(), out_dir.path(), arch, &mut buf).unwrap();
}
EmbedResult {
manifest_path,
out_dir,
output: String::from_utf8(buf).unwrap(),
}
}
#[test]
fn object_file_x86() {
let manifest = new_manifest("Test.X86").to_string().into_bytes();
let file = create_object_file(&manifest, MachineType::I386).unwrap();
let obj = CoffFile::parse(&file[..]).unwrap();
assert_eq!(obj.architecture(), Architecture::I386);
check_object_file(obj, &manifest);
}
#[test]
fn object_file_x86_64() {
let manifest = new_manifest("Test.X86_64").to_string().into_bytes();
let file = create_object_file(&manifest, MachineType::X86_64).unwrap();
let obj = CoffFile::parse(&file[..]).unwrap();
assert_eq!(obj.architecture(), Architecture::X86_64);
check_object_file(obj, &manifest);
}
#[test]
fn object_file_aarch64() {
let manifest = new_manifest("Test.AARCH64").to_string().into_bytes();
let file = create_object_file(&manifest, MachineType::Aarch64).unwrap();
let obj = CoffFile::parse(&file[..]).unwrap();
assert_eq!(obj.architecture(), Architecture::Aarch64);
check_object_file(obj, &manifest);
}
fn check_object_file(obj: CoffFile, expected_manifest: &[u8]) {
// There should be one sections `.rsrc`.
assert_eq!(
obj.sections().map(|s| s.name().unwrap().to_string()).collect::<Vec<_>>(),
&[".rsrc"]
);
// There should be one section symbol.
assert_eq!(
obj.symbols().map(|s| s.name().unwrap().to_string()).collect::<Vec<_>>(),
&[".rsrc"]
);
// The resource section must be a data section.
let rsrc = obj.section_by_name(".rsrc").unwrap();
assert_eq!(rsrc.address(), 0);
assert_eq!(rsrc.kind(), SectionKind::Data);
// The data RVA in the resource data entry must be relocatable.
let (addr, reloc) = rsrc.relocations().next().unwrap();
assert_eq!(reloc.kind(), RelocationKind::ImageOffset);
assert_eq!(reloc.encoding(), RelocationEncoding::Generic);
assert_eq!(addr, 0x48); // size of the directory table, three directories, and no strings
assert_eq!(reloc.addend(), 0);
// The resource directory contains one manifest resource type subdirectory.
let data = rsrc.data().unwrap();
let (dir, rest) = pod::from_bytes::<ImageResourceDirectory>(data).unwrap();
assert_eq!(0, dir.number_of_named_entries.get(LittleEndian));
assert_eq!(1, dir.number_of_id_entries.get(LittleEndian));
let (entries, _) = pod::slice_from_bytes::<ImageResourceDirectoryEntry>(rest, 1).unwrap();
assert_eq!(24, entries[0].name_or_id.get(LittleEndian));
let offset = entries[0].offset_to_data_or_directory.get(LittleEndian);
assert_eq!(IMAGE_RESOURCE_DATA_IS_DIRECTORY, offset & IMAGE_RESOURCE_DATA_IS_DIRECTORY);
let offset = (offset & !IMAGE_RESOURCE_DATA_IS_DIRECTORY) as usize;
// The manifest subdirectory contains one image (not DLL) manifest subdirectory.
let (dir, rest) = pod::from_bytes::<ImageResourceDirectory>(&data[offset..]).unwrap();
assert_eq!(0, dir.number_of_named_entries.get(LittleEndian));
assert_eq!(1, dir.number_of_id_entries.get(LittleEndian));
let (entries, _) = pod::slice_from_bytes::<ImageResourceDirectoryEntry>(rest, 1).unwrap();
assert_eq!(1, entries[0].name_or_id.get(LittleEndian));
let offset = entries[0].offset_to_data_or_directory.get(LittleEndian);
assert_eq!(IMAGE_RESOURCE_DATA_IS_DIRECTORY, offset & IMAGE_RESOURCE_DATA_IS_DIRECTORY);
let offset = (offset & !IMAGE_RESOURCE_DATA_IS_DIRECTORY) as usize;
// The image manifest subdirectory contains one US English manifest data entry.
let (dir, rest) = pod::from_bytes::<ImageResourceDirectory>(&data[offset..]).unwrap();
assert_eq!(0, dir.number_of_named_entries.get(LittleEndian));
assert_eq!(1, dir.number_of_id_entries.get(LittleEndian));
let (entries, _) = pod::slice_from_bytes::<ImageResourceDirectoryEntry>(rest, 1).unwrap();
assert_eq!(0x0409, entries[0].name_or_id.get(LittleEndian));
let offset = entries[0].offset_to_data_or_directory.get(LittleEndian);
assert_eq!(0, offset & IMAGE_RESOURCE_DATA_IS_DIRECTORY);
let offset = offset as usize;
// The manifest data matches what was added.
let (entry, resource_data) = pod::from_bytes::<ImageResourceDataEntry>(&data[offset..]).unwrap();
let end = entry.size.get(LittleEndian) as usize;
let manifest = &resource_data[..end];
assert_eq!(manifest, expected_manifest);
}

134
third_party/rust/embed-manifest/src/lib.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,134 @@
//! The `embed-manifest` crate provides a straightforward way to embed
//! a Windows manifest in an executable, whatever the build environment
//! and even when cross-compiling, without dependencies on external
//! tools from LLVM or MinGW.
//!
//! This should be called from a [build script][1], as shown below.
//!
//! [1]: https://doc.rust-lang.org/cargo/reference/build-scripts.html
//!
//! On MSVC targets, the manifest file is embedded in the executable by
//! instructing Cargo to pass `/MANIFEST` options to `LINK.EXE`. This
//! requires Cargo from Rust 1.56.
//!
//! On GNU targets, the manifest file is added as a resource in a COFF
//! object file, and Cargo is instructed to link this file into the
//! executable, also using functionality from Rust 1.56.
//!
//! # Usage
//!
//! This crate should be added to the `[build-dependencies]` section in
//! your executables `Cargo.toml`:
//!
//! ```toml
//! [build-dependencies]
//! embed-manifest = "1.3.1"
//! ```
//!
//! In the same directory, create a `build.rs` file to call this crates
//! code when building for Windows, and to only run once:
//!
//! ```
//! use embed_manifest::{embed_manifest, new_manifest};
//!
//! fn main() {
//! # let tempdir = tempfile::tempdir().unwrap();
//! # std::env::set_var("OUT_DIR", tempdir.path());
//! # std::env::set_var("TARGET", "x86_64-pc-windows-gnu");
//! # std::env::set_var("CARGO_CFG_WINDOWS", "");
//! if std::env::var_os("CARGO_CFG_WINDOWS").is_some() {
//! embed_manifest(new_manifest("Contoso.Sample")).expect("unable to embed manifest file");
//! }
//! println!("cargo:rerun-if-changed=build.rs");
//! }
//! ```
//!
//! To customise the application manifest, use the methods on it to change things like
//! enabling the segment heap:
//!
//! ```
//! use embed_manifest::{embed_manifest, new_manifest, manifest::HeapType};
//!
//! fn main() {
//! # let tempdir = tempfile::tempdir().unwrap();
//! # std::env::set_var("OUT_DIR", tempdir.path());
//! # std::env::set_var("TARGET", "x86_64-pc-windows-gnu");
//! # std::env::set_var("CARGO_CFG_WINDOWS", "");
//! if std::env::var_os("CARGO_CFG_WINDOWS").is_some() {
//! embed_manifest(new_manifest("Contoso.Sample").heap_type(HeapType::SegmentHeap))
//! .expect("unable to embed manifest file");
//! }
//! println!("cargo:rerun-if-changed=build.rs");
//! }
//! ```
//!
//! or making it always use legacy single-byte API encoding and only declaring compatibility
//! up to Windows 8.1, without checking whether this is a Windows build:
//!
//! ```
//! use embed_manifest::{embed_manifest, new_manifest};
//! use embed_manifest::manifest::{ActiveCodePage::Legacy, SupportedOS::*};
//!
//! fn main() {
//! # let tempdir = tempfile::tempdir().unwrap();
//! # std::env::set_var("OUT_DIR", tempdir.path());
//! # std::env::set_var("TARGET", "x86_64-pc-windows-gnu");
//! let manifest = new_manifest("Contoso.Sample")
//! .active_code_page(Legacy)
//! .supported_os(Windows7..=Windows81);
//! embed_manifest(manifest).expect("unable to embed manifest file");
//! println!("cargo:rerun-if-changed=build.rs");
//! }
//! ```
#![allow(clippy::needless_doctest_main)]
pub use embed::error::Error;
pub use embed::{embed_manifest, embed_manifest_file};
use crate::manifest::ManifestBuilder;
mod embed;
pub mod manifest;
/// Creates a new [`ManifestBuilder`] with sensible defaults, allowing customisation
/// before the Windows application manifest XML is generated.
///
/// The initial values used by the manifest are:
/// - Version number from the `CARGO_PKG_VERSION_MAJOR`, `CARGO_PKG_VERSION_MINOR` and
/// `CARGO_PKG_VERSION_PATCH` environment variables. This can then be changed with
/// [`version()`][ManifestBuilder::version].
/// - A dependency on version 6 of the Common Controls so that message boxes and dialogs
/// will use the latest design, and have the best available support for high DPI displays.
/// This can be removed with
/// [`remove_dependency`][ManifestBuilder::remove_dependency].
/// - [Compatible with Windows from 7 to 11][ManifestBuilder::supported_os],
/// matching the Rust compiler [tier 1 targets][tier1].
/// - A “[maximum version tested][ManifestBuilder::max_version_tested]” of Windows 10
/// version 1903.
/// - An [active code page][ManifestBuilder::active_code_page] of UTF-8, so that
/// single-byte Windows APIs will generally interpret Rust strings correctly, starting
/// from Windows 10 version 1903.
/// - [Version 2 of per-monitor high DPI awareness][manifest::DpiAwareness::PerMonitorV2Only],
/// so that user interface elements can be scaled correctly by the application and
/// Common Controls from Windows 10 version 1703.
/// - [Long path awareness][ManifestBuilder::long_path_aware] from Windows 10 version
/// 1607 [when separately enabled][longpaths].
/// - [Printer driver isolation][ManifestBuilder::printer_driver_isolation] enabled
/// to improve reliability and security.
/// - An [execution level][ManifestBuilder::requested_execution_level] of “as invoker”
/// so that the UAC elevation dialog will never be displayed, regardless of the name
/// of the program, and [UAC Virtualisation][uac] is disabled in 32-bit programs.
///
/// [tier1]: https://doc.rust-lang.org/nightly/rustc/platform-support.html
/// [longpaths]: https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later
/// [uac]: https://docs.microsoft.com/en-us/windows/security/identity-protection/user-account-control/how-user-account-control-works#virtualization
pub fn new_manifest(name: &str) -> ManifestBuilder {
ManifestBuilder::new(name)
}
/// Creates a new [`ManifestBuilder`] without any settings, allowing creation of
/// a manifest with only desired content.
pub fn empty_manifest() -> ManifestBuilder {
ManifestBuilder::empty()
}

882
third_party/rust/embed-manifest/src/manifest/mod.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,882 @@
//! A builder for Windows application manifest XML files.
//!
//! This module allows the construction of application manifests from code with the
//! [`ManifestBuilder`], for use from a Cargo build script. Once configured, the builder
//! should be passed to [`embed_manifest()`][crate::embed_manifest] to generate the
//! correct instructions for Cargo. For any other use, the builder will output the XML
//! when formatted for [`Display`], or with [`to_string()`][ToString]. For more
//! information about the different elements of an application manifest, see
//! [Application Manifests][1] in the Microsoft Windows App Development documentation.
//!
//! [1]: https://docs.microsoft.com/en-us/windows/win32/sbscs/application-manifests
//!
//! To generate the manifest XML separately, the XML can be output with `write!` or
//! copied to a string with [`to_string()`][ToString]. To produce the manifest XML with
//! extra whitespace for formatting, format it with the alternate flag:
//!
//! ```
//! # use embed_manifest::new_manifest;
//! let builder = new_manifest("Company.OrgUnit.Program");
//! assert_eq!(format!("{:#}", builder), r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
//! <assembly xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" manifestVersion="1.0">
//! <assemblyIdentity name="Company.OrgUnit.Program" type="win32" version="1.4.0.0"/>
//! <dependency>
//! <dependentAssembly>
//! <assemblyIdentity language="*" name="Microsoft.Windows.Common-Controls" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" type="win32" version="6.0.0.0"/>
//! </dependentAssembly>
//! </dependency>
//! <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
//! <application>
//! <maxversiontested Id="10.0.18362.1"/>
//! <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
//! <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
//! <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
//! <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
//! </application>
//! </compatibility>
//! <asmv3:application>
//! <asmv3:windowsSettings>
//! <activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
//! <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">permonitorv2</dpiAwareness>
//! <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
//! <printerDriverIsolation xmlns="http://schemas.microsoft.com/SMI/2011/WindowsSettings">true</printerDriverIsolation>
//! </asmv3:windowsSettings>
//! </asmv3:application>
//! <asmv3:trustInfo>
//! <asmv3:security>
//! <asmv3:requestedPrivileges>
//! <asmv3:requestedExecutionLevel level="asInvoker" uiAccess="false"/>
//! </asmv3:requestedPrivileges>
//! </asmv3:security>
//! </asmv3:trustInfo>
//! </assembly>"#.replace("\n", "\r\n"))
//! ```
use std::fmt::{Display, Formatter};
use std::ops::RangeBounds;
use std::{env, fmt};
use crate::manifest::xml::XmlFormatter;
mod xml;
#[cfg(test)]
mod test;
/// An opaque container to describe the Windows application manifest for the
/// executable. A new instance with reasonable defaults is created with
/// [`new_manifest()`][crate::new_manifest].
#[derive(Debug)]
pub struct ManifestBuilder {
identity: Option<AssemblyIdentity>,
dependent_assemblies: Vec<AssemblyIdentity>,
compatibility: ApplicationCompatibility,
windows_settings: WindowsSettings,
requested_execution_level: Option<RequestedExecutionLevel>,
}
impl ManifestBuilder {
pub(crate) fn new(name: &str) -> Self {
ManifestBuilder {
identity: Some(AssemblyIdentity::application(name)),
dependent_assemblies: vec![AssemblyIdentity::new(
"Microsoft.Windows.Common-Controls",
[6, 0, 0, 0],
0x6595b64144ccf1df,
)],
compatibility: ApplicationCompatibility {
max_version_tested: Some(MaxVersionTested::Windows10Version1903),
supported_os: vec![
SupportedOS::Windows7,
SupportedOS::Windows8,
SupportedOS::Windows81,
SupportedOS::Windows10,
],
},
windows_settings: WindowsSettings::new(),
requested_execution_level: Some(RequestedExecutionLevel {
level: ExecutionLevel::AsInvoker,
ui_access: false,
}),
}
}
pub(crate) fn empty() -> Self {
ManifestBuilder {
identity: None,
dependent_assemblies: Vec::new(),
compatibility: ApplicationCompatibility {
max_version_tested: None,
supported_os: Vec::new(),
},
windows_settings: WindowsSettings::empty(),
requested_execution_level: None,
}
}
// Set the dot-separated [application name][identity] in the manifest.
//
// [identity]: https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests#assemblyIdentity
pub fn name(mut self, name: &str) -> Self {
match self.identity {
Some(ref mut identity) => identity.name = name.to_string(),
None => self.identity = Some(AssemblyIdentity::application_version(name, 0, 0, 0, 0)),
}
self
}
/// Set the four-part application version number in the manifest.
pub fn version(mut self, major: u16, minor: u16, build: u16, revision: u16) -> Self {
match self.identity {
Some(ref mut identity) => identity.version = Version(major, minor, build, revision),
None => {
self.identity = Some(AssemblyIdentity::application_version("", major, minor, build, revision));
}
}
self
}
/// Add a dependency on a specific version of a side-by-side assembly
/// to the application manifest. For more information on side-by-side
/// assemblies, see [Using Side-by-side Assemblies][sxs].
///
/// [sxs]: https://docs.microsoft.com/en-us/windows/win32/sbscs/using-side-by-side-assemblies
pub fn dependency(mut self, identity: AssemblyIdentity) -> Self {
self.dependent_assemblies.push(identity);
self
}
/// Remove a dependency on a side-by-side assembly. This can be used to
/// remove the default dependency on Common Controls version 6:
///
/// ```
/// # use embed_manifest::new_manifest;
/// new_manifest("Company.OrgUnit.Program")
/// .remove_dependency("Microsoft.Windows.Common-Controls")
/// # ;
/// ```
pub fn remove_dependency(mut self, name: &str) -> Self {
self.dependent_assemblies.retain(|d| d.name != name);
self
}
/// Set the “maximum version tested” based on a Windows SDK version.
/// This compatibility setting enables the use of XAML Islands, as described in
/// [Host a standard WinRT XAML control in a C++ desktop (Win32) app][xaml].
///
/// [xaml]: https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/host-standard-control-with-xaml-islands-cpp
pub fn max_version_tested(mut self, version: MaxVersionTested) -> Self {
self.compatibility.max_version_tested = Some(version);
self
}
/// Remove the “maximum version tested” from the application compatibility.
pub fn remove_max_version_tested(mut self) -> Self {
self.compatibility.max_version_tested = None;
self
}
/// Set the range of supported versions of Windows for application compatibility.
/// The default value declares compatibility with every version from
/// [Windows 7][SupportedOS::Windows7] to [Windows 10 and 11][SupportedOS::Windows10].
pub fn supported_os<R: RangeBounds<SupportedOS>>(mut self, os_range: R) -> Self {
use SupportedOS::*;
self.compatibility.supported_os.clear();
for os in [WindowsVista, Windows7, Windows8, Windows81, Windows10] {
if os_range.contains(&os) {
self.compatibility.supported_os.push(os);
}
}
self
}
/// Set the code page used for single-byte Windows API, starting from Windows 10
/// version 1903. The default setting of [UTF-8][`ActiveCodePage::Utf8`] makes Rust
/// strings work directly with APIs like `MessageBoxA`.
pub fn active_code_page(mut self, code_page: ActiveCodePage) -> Self {
self.windows_settings.active_code_page = code_page;
self
}
/// Configures how Windows should display this program on monitors where the
/// graphics need scaling, whether by the application drawing its user
/// interface at different sizes or by the Windows system rendering the graphics
/// to a bitmap then resizing that for display.
pub fn dpi_awareness(mut self, setting: DpiAwareness) -> Self {
self.windows_settings.dpi_awareness = setting;
self
}
/// Attempts to scale GDI primitives by the per-monitor scaling values,
/// from Windows 10 version 1703. It seems to be best to leave this disabled.
pub fn gdi_scaling(mut self, setting: Setting) -> Self {
self.windows_settings.gdi_scaling = setting.enabled();
self
}
/// Select the memory allocator use by the standard heap allocation APIs,
/// including the default Rust allocator. Selecting a different algorithm
/// may make performance and memory use better or worse, so any changes
/// should be carefully tested.
pub fn heap_type(mut self, setting: HeapType) -> Self {
self.windows_settings.heap_type = setting;
self
}
/// Enable paths longer than 260 characters with some wide-character Win32 APIs,
/// when also enabled in the Windows registry. For more details, see
/// [Maximum Path Length Limitation][1] in the Windows App Development
/// documentation.
///
/// As of Rust 1.58, the [Rust standard library bypasses this limitation][2] itself
/// by using Unicode paths beginning with `\\?\`.
///
/// [1]: https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
/// [2]: https://github.com/rust-lang/rust/pull/89174
pub fn long_path_aware(mut self, setting: Setting) -> Self {
self.windows_settings.long_path_aware = setting.enabled();
self
}
/// Enable printer driver isolation for the application, improving security and
/// stability when printing by loading the printer driver in a separate
/// application. This is poorly documented, but is described in a blog post,
/// “[Application-level Printer Driver Isolation][post]”.
///
/// [post]: https://peteronprogramming.wordpress.com/2018/01/22/application-level-printer-driver-isolation/
pub fn printer_driver_isolation(mut self, setting: Setting) -> Self {
self.windows_settings.printer_driver_isolation = setting.enabled();
self
}
/// Configure whether the application should receive mouse wheel scroll events
/// with a minimum delta of 1, 40 or 120, as described in
/// [Windows precision touchpad devices][touchpad].
///
/// [touchpad]: https://docs.microsoft.com/en-us/windows/win32/w8cookbook/windows-precision-touchpad-devices
pub fn scrolling_awareness(mut self, setting: ScrollingAwareness) -> Self {
self.windows_settings.scrolling_awareness = setting;
self
}
/// Allows the application to disable the filtering that normally
/// removes UWP windows from the results of the `EnumWindows` API.
pub fn window_filtering(mut self, setting: Setting) -> Self {
self.windows_settings.disable_window_filtering = setting.disabled();
self
}
/// Selects the authorities to execute the program with, rather than
/// [guessing based on a filename][installer] like `setup.exe`,
/// `update.exe` or `patch.exe`.
///
/// [installer]: https://docs.microsoft.com/en-us/windows/security/identity-protection/user-account-control/how-user-account-control-works#installer-detection-technology
pub fn requested_execution_level(mut self, level: ExecutionLevel) -> Self {
match self.requested_execution_level {
Some(ref mut requested_execution_level) => requested_execution_level.level = level,
None => self.requested_execution_level = Some(RequestedExecutionLevel { level, ui_access: false }),
}
self
}
/// Allows the application to access the user interface of applications
/// running with elevated permissions when this program does not, typically
/// for accessibility. The program must additionally be correctly signed
/// and installed in a trusted location like the Program Files directory.
pub fn ui_access(mut self, access: bool) -> Self {
match self.requested_execution_level {
Some(ref mut requested_execution_level) => requested_execution_level.ui_access = access,
None => {
self.requested_execution_level = Some(RequestedExecutionLevel {
level: ExecutionLevel::AsInvoker,
ui_access: access,
})
}
}
self
}
}
impl Display for ManifestBuilder {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let mut w = XmlFormatter::new(f);
w.start_document()?;
let mut attrs = vec![("xmlns", "urn:schemas-microsoft-com:asm.v1")];
if !self.windows_settings.is_empty() || self.requested_execution_level.is_some() {
attrs.push(("xmlns:asmv3", "urn:schemas-microsoft-com:asm.v3"));
}
attrs.push(("manifestVersion", "1.0"));
w.start_element("assembly", &attrs)?;
if let Some(ref identity) = self.identity {
identity.xml_to(&mut w)?;
}
if !self.dependent_assemblies.is_empty() {
w.element("dependency", &[], |w| {
for d in self.dependent_assemblies.as_slice() {
w.element("dependentAssembly", &[], |w| d.xml_to(w))?;
}
Ok(())
})?;
}
if !self.compatibility.is_empty() {
self.compatibility.xml_to(&mut w)?;
}
if !self.windows_settings.is_empty() {
self.windows_settings.xml_to(&mut w)?;
}
if let Some(ref requested_execution_level) = self.requested_execution_level {
requested_execution_level.xml_to(&mut w)?;
}
w.end_element("assembly")
}
}
/// Identity of a side-by-side assembly dependency for the application.
#[derive(Debug)]
pub struct AssemblyIdentity {
r#type: AssemblyType,
name: String,
language: Option<String>,
processor_architecture: Option<AssemblyProcessorArchitecture>,
version: Version,
public_key_token: Option<PublicKeyToken>,
}
impl AssemblyIdentity {
fn application(name: &str) -> AssemblyIdentity {
let major = env::var("CARGO_PKG_VERSION_MAJOR").map_or(0, |s| s.parse().unwrap_or(0));
let minor = env::var("CARGO_PKG_VERSION_MINOR").map_or(0, |s| s.parse().unwrap_or(0));
let patch = env::var("CARGO_PKG_VERSION_PATCH").map_or(0, |s| s.parse().unwrap_or(0));
AssemblyIdentity {
r#type: AssemblyType::Win32,
name: name.to_string(),
language: None,
processor_architecture: None,
version: Version(major, minor, patch, 0),
public_key_token: None,
}
}
fn application_version(name: &str, major: u16, minor: u16, build: u16, revision: u16) -> AssemblyIdentity {
AssemblyIdentity {
r#type: AssemblyType::Win32,
name: name.to_string(),
language: None,
processor_architecture: None,
version: Version(major, minor, build, revision),
public_key_token: None,
}
}
/// Creates a new value for a [manifest dependency][ManifestBuilder::dependency],
/// with the `version` as an array of numbers like `[1, 0, 0, 0]` for 1.0.0.0,
/// and the public key token as a 64-bit number like `0x6595b64144ccf1df`.
pub fn new(name: &str, version: [u16; 4], public_key_token: u64) -> AssemblyIdentity {
AssemblyIdentity {
r#type: AssemblyType::Win32,
name: name.to_string(),
language: Some("*".to_string()),
processor_architecture: Some(AssemblyProcessorArchitecture::All),
version: Version(version[0], version[1], version[2], version[3]),
public_key_token: Some(PublicKeyToken(public_key_token)),
}
}
/// Change the language from `"*"` to the language code in `language`.
pub fn language(mut self, language: &str) -> Self {
self.language = Some(language.to_string());
self
}
/// Change the processor architecture from `"*"` to the architecture in `arch`.
pub fn processor_architecture(mut self, arch: AssemblyProcessorArchitecture) -> Self {
self.processor_architecture = Some(arch);
self
}
fn xml_to(&self, w: &mut XmlFormatter) -> fmt::Result {
let version = self.version.to_string();
let public_key_token = self.public_key_token.as_ref().map(|token| token.to_string());
let mut attrs: Vec<(&str, &str)> = Vec::with_capacity(6);
if let Some(ref language) = self.language {
attrs.push(("language", language));
}
attrs.push(("name", &self.name));
if let Some(ref arch) = self.processor_architecture {
attrs.push(("processorArchitecture", arch.as_str()))
}
if let Some(ref token) = public_key_token {
attrs.push(("publicKeyToken", token));
}
attrs.push(("type", self.r#type.as_str()));
attrs.push(("version", &version));
w.empty_element("assemblyIdentity", &attrs)
}
}
#[derive(Debug)]
struct Version(u16, u16, u16, u16);
impl fmt::Display for Version {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}.{}.{}.{}", self.0, self.1, self.2, self.3)
}
}
#[derive(Debug)]
struct PublicKeyToken(u64);
impl fmt::Display for PublicKeyToken {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{:016x}", self.0)
}
}
/// Processor architecture for an assembly identity.
#[derive(Debug)]
#[non_exhaustive]
pub enum AssemblyProcessorArchitecture {
/// Any processor architecture, as `"*"`.
All,
/// 32-bit x86 processors, as `"x86"`.
X86,
/// 64-bit x64 processors, as `"x64"`.
Amd64,
/// 32-bit ARM processors, as `"arm"`.
Arm,
/// 64-bit ARM processors, as `"arm64"`.
Arm64,
}
impl AssemblyProcessorArchitecture {
pub fn as_str(&self) -> &'static str {
match self {
Self::All => "*",
Self::X86 => "x86",
Self::Amd64 => "amd64",
Self::Arm => "arm",
Self::Arm64 => "arm64",
}
}
}
impl fmt::Display for AssemblyProcessorArchitecture {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(self.as_str())
}
}
#[derive(Debug)]
#[non_exhaustive]
enum AssemblyType {
Win32,
}
impl AssemblyType {
fn as_str(&self) -> &'static str {
"win32"
}
}
#[derive(Debug)]
struct ApplicationCompatibility {
max_version_tested: Option<MaxVersionTested>,
supported_os: Vec<SupportedOS>,
}
impl ApplicationCompatibility {
fn is_empty(&self) -> bool {
self.supported_os.is_empty()
}
fn xml_to(&self, w: &mut XmlFormatter) -> fmt::Result {
w.element(
"compatibility",
&[("xmlns", "urn:schemas-microsoft-com:compatibility.v1")],
|w| {
w.element("application", &[], |w| {
if self.supported_os.contains(&SupportedOS::Windows10) {
if let Some(ref version) = self.max_version_tested {
w.empty_element("maxversiontested", &[("Id", version.as_str())])?;
}
}
for os in self.supported_os.iter() {
w.empty_element("supportedOS", &[("Id", os.as_str())])?
}
Ok(())
})
},
)
}
}
/// Windows build versions for [`max_version_tested()`][ManifestBuilder::max_version_tested]
/// from the [Windows SDK archive](https://developer.microsoft.com/en-us/windows/downloads/sdk-archive/).
#[derive(Debug)]
#[non_exhaustive]
pub enum MaxVersionTested {
/// Windows 10 version 1903, with build version 10.0.18362.0.
Windows10Version1903,
/// Windows 10 version 2004, with build version 10.0.19041.0.
Windows10Version2004,
/// Windows 10 version 2104, with build version 10.0.20348.0.
Windows10Version2104,
/// Windows 11, with build version 10.0.22000.194.
Windows11,
/// Windows 11 version 22H2, with build version 10.0.22621.1.
Windows11Version22H2,
}
impl MaxVersionTested {
/// Return the Windows version as a string.
pub fn as_str(&self) -> &'static str {
match self {
Self::Windows10Version1903 => "10.0.18362.1",
Self::Windows10Version2004 => "10.0.19041.0",
Self::Windows10Version2104 => "10.0.20348.0",
Self::Windows11 => "10.0.22000.194",
Self::Windows11Version22H2 => "10.0.22621.1",
}
}
}
impl Display for MaxVersionTested {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(self.as_str())
}
}
/// Operating system versions for Windows compatibility.
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)]
#[non_exhaustive]
pub enum SupportedOS {
/// Windows Vista and Windows Server 2008.
WindowsVista,
/// Windows 7 and Windows Server 2008 R2.
Windows7,
/// Windows 8 and Windows Server 2012.
Windows8,
/// Windows 8.1 and Windows Server 2012 R2.
Windows81,
/// Windows 10 and 11, and Windows Server 2016, 2019 and 2022.
Windows10,
}
impl SupportedOS {
/// Returns the GUID string for the Windows version.
pub fn as_str(&self) -> &'static str {
match self {
Self::WindowsVista => "{e2011457-1546-43c5-a5fe-008deee3d3f0}",
Self::Windows7 => "{35138b9a-5d96-4fbd-8e2d-a2440225f93a}",
Self::Windows8 => "{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}",
Self::Windows81 => "{1f676c76-80e1-4239-95bb-83d0f6d0da78}",
Self::Windows10 => "{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}",
}
}
}
impl Display for SupportedOS {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(self.as_str())
}
}
static WS2005: (&str, &str) = ("xmlns", "http://schemas.microsoft.com/SMI/2005/WindowsSettings");
static WS2011: (&str, &str) = ("xmlns", "http://schemas.microsoft.com/SMI/2011/WindowsSettings");
static WS2013: (&str, &str) = ("xmlns", "http://schemas.microsoft.com/SMI/2013/WindowsSettings");
static WS2016: (&str, &str) = ("xmlns", "http://schemas.microsoft.com/SMI/2016/WindowsSettings");
static WS2017: (&str, &str) = ("xmlns", "http://schemas.microsoft.com/SMI/2017/WindowsSettings");
static WS2019: (&str, &str) = ("xmlns", "http://schemas.microsoft.com/SMI/2019/WindowsSettings");
static WS2020: (&str, &str) = ("xmlns", "http://schemas.microsoft.com/SMI/2020/WindowsSettings");
#[derive(Debug)]
struct WindowsSettings {
active_code_page: ActiveCodePage,
disable_window_filtering: bool,
dpi_awareness: DpiAwareness,
gdi_scaling: bool,
heap_type: HeapType,
long_path_aware: bool,
printer_driver_isolation: bool,
scrolling_awareness: ScrollingAwareness,
}
impl WindowsSettings {
fn new() -> Self {
Self {
active_code_page: ActiveCodePage::Utf8,
disable_window_filtering: false,
dpi_awareness: DpiAwareness::PerMonitorV2Only,
gdi_scaling: false,
heap_type: HeapType::LowFragmentationHeap,
long_path_aware: true,
printer_driver_isolation: true,
scrolling_awareness: ScrollingAwareness::UltraHighResolution,
}
}
fn empty() -> Self {
Self {
active_code_page: ActiveCodePage::System,
disable_window_filtering: false,
dpi_awareness: DpiAwareness::UnawareByDefault,
gdi_scaling: false,
heap_type: HeapType::LowFragmentationHeap,
long_path_aware: false,
printer_driver_isolation: false,
scrolling_awareness: ScrollingAwareness::UltraHighResolution,
}
}
fn is_empty(&self) -> bool {
matches!(
self,
Self {
active_code_page: ActiveCodePage::System,
disable_window_filtering: false,
dpi_awareness: DpiAwareness::UnawareByDefault,
gdi_scaling: false,
heap_type: HeapType::LowFragmentationHeap,
long_path_aware: false,
printer_driver_isolation: false,
scrolling_awareness: ScrollingAwareness::UltraHighResolution,
}
)
}
fn xml_to(&self, w: &mut XmlFormatter) -> fmt::Result {
w.element("asmv3:application", &[], |w| {
w.element("asmv3:windowsSettings", &[], |w| {
self.active_code_page.xml_to(w)?;
if self.disable_window_filtering {
w.element("disableWindowFiltering", &[WS2011], |w| w.text("true"))?;
}
self.dpi_awareness.xml_to(w)?;
if self.gdi_scaling {
w.element("gdiScaling", &[WS2017], |w| w.text("true"))?;
}
if matches!(self.heap_type, HeapType::SegmentHeap) {
w.element("heapType", &[WS2020], |w| w.text("SegmentHeap"))?;
}
if self.long_path_aware {
w.element("longPathAware", &[WS2016], |w| w.text("true"))?;
}
if self.printer_driver_isolation {
w.element("printerDriverIsolation", &[WS2011], |w| w.text("true"))?;
}
self.scrolling_awareness.xml_to(w)
})
})
}
}
/// Configure whether a Windows setting is enabled or disabled, avoiding confusion
/// over which of these options `true` and `false` represent.
#[derive(Debug)]
pub enum Setting {
Disabled = 0,
Enabled = 1,
}
impl Setting {
/// Returns `true` if the setting should be disabled.
fn disabled(&self) -> bool {
matches!(self, Setting::Disabled)
}
/// Returns `true` if the setting should be enabled.
fn enabled(&self) -> bool {
matches!(self, Setting::Enabled)
}
}
/// The code page used by single-byte APIs in the program.
#[derive(Debug)]
#[non_exhaustive]
pub enum ActiveCodePage {
/// Use the code page from the configured system locale, or UTF-8 if “Use Unicode UTF-8
/// for worldwide language support” is configured.
System,
/// Use UTF-8 from Windows 10 version 1903 and on Windows 11.
Utf8,
/// Use the code page from the configured system locale, even when “Use Unicode UTF-8
/// for worldwide language support” is configured.
Legacy,
/// Use the code page from the configured system locale on Windows 10, or from this
/// locale on Windows 11.
Locale(String),
}
impl ActiveCodePage {
pub fn as_str(&self) -> &str {
match self {
Self::System => "",
Self::Utf8 => "UTF-8",
Self::Legacy => "Legacy",
Self::Locale(s) => s,
}
}
fn xml_to(&self, w: &mut XmlFormatter) -> fmt::Result {
match self {
Self::System => Ok(()),
_ => w.element("activeCodePage", &[WS2019], |w| w.text(self.as_str())),
}
}
}
impl Display for ActiveCodePage {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(self.as_str())
}
}
/// Options for how Windows will handle drawing on monitors when the graphics
/// need scaling to display at the correct size.
///
/// See [High DPI Desktop Application Development on Windows][dpi] for more details
/// about the impact of these options.
///
/// [dpi]: https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows
#[derive(Debug)]
#[non_exhaustive]
pub enum DpiAwareness {
/// DPI awareness is not configured, so Windows will scale the application unless
/// changed via the `SetProcessDpiAware` or `SetProcessDpiAwareness` API.
UnawareByDefault,
/// DPI awareness is not configured, with Windows 8.1, 10 and 11 not able
/// to change the setting via API.
Unaware,
/// The program uses the system DPI, or the DPI of the monitor they start on if
/// “Fix scaling for apps” is enabled. If the DPI does not match the current
/// monitor then Windows will scale the application.
System,
/// The program uses the system DPI on Windows Vista, 7 and 8, and version 1 of
/// per-monitor DPI awareness on Windows 8.1, 10 and 11. Using version 1 of the
/// API is not recommended.
PerMonitor,
/// The program uses the system DPI on Windows Vista, 7 and 8, version 1 of
/// per-monitor DPI awareness on Windows 8.1 and Windows 10 version 1507 and 1511,
/// and version 2 of per-monitor DPI awareness from Windows 10 version 1607.
PerMonitorV2,
/// The program uses the system DPI on Windows Vista, 7, 8, 8.1 and Windows 10
/// version 1507 and 1511, and version 2 of per-monitor DPI awareness from
/// Windows 10 version 1607.
PerMonitorV2Only,
}
impl DpiAwareness {
fn xml_to(&self, w: &mut XmlFormatter) -> fmt::Result {
let settings = match self {
Self::UnawareByDefault => (None, None),
Self::Unaware => (Some("false"), None),
Self::System => (Some("true"), None),
Self::PerMonitor => (Some("true/pm"), None),
Self::PerMonitorV2 => (Some("true/pm"), Some("permonitorv2,permonitor")),
Self::PerMonitorV2Only => (None, Some("permonitorv2")),
};
if let Some(dpi_aware) = settings.0 {
w.element("dpiAware", &[WS2005], |w| w.text(dpi_aware))?;
}
if let Some(dpi_awareness) = settings.1 {
w.element("dpiAwareness", &[WS2016], |w| w.text(dpi_awareness))?;
}
Ok(())
}
}
/// The heap type for the default memory allocator.
#[derive(Debug)]
#[non_exhaustive]
pub enum HeapType {
/// The default heap type since Windows Vista.
LowFragmentationHeap,
/// The modern segment heap, which may reduce total memory allocation in some programs.
/// This is supported since Windows 10 version 2004. See
/// [Improving Memory Usage in Microsoft Edge][edge].
///
/// [edge]: https://blogs.windows.com/msedgedev/2020/06/17/improving-memory-usage/
SegmentHeap,
}
/// Whether the application supports scroll wheel events with a minimum delta
/// of 1, 40 or 120.
#[derive(Debug)]
#[non_exhaustive]
pub enum ScrollingAwareness {
/// The application can only handle scroll wheel events with the original delta of 120.
LowResolution,
/// The application can handle high precision scroll wheel events with a delta of 40.
HighResolution,
/// The application can handle ultra high precision scroll wheel events with a delta as low as 1.
UltraHighResolution,
}
impl ScrollingAwareness {
fn xml_to(&self, w: &mut XmlFormatter) -> fmt::Result {
match self {
Self::LowResolution => w.element("ultraHighResolutionScrollingAware", &[WS2013], |w| w.text("false")),
Self::HighResolution => w.element("highResolutionScrollingAware", &[WS2013], |w| w.text("true")),
Self::UltraHighResolution => Ok(()),
}
}
}
#[derive(Debug)]
struct RequestedExecutionLevel {
level: ExecutionLevel,
ui_access: bool,
}
impl RequestedExecutionLevel {
fn xml_to(&self, w: &mut XmlFormatter) -> fmt::Result {
w.element("asmv3:trustInfo", &[], |w| {
w.element("asmv3:security", &[], |w| {
w.element("asmv3:requestedPrivileges", &[], |w| {
w.empty_element(
"asmv3:requestedExecutionLevel",
&[
("level", self.level.as_str()),
("uiAccess", if self.ui_access { "true" } else { "false" }),
],
)
})
})
})
}
}
/// The requested execution level for the application when started.
///
/// The behaviour of each option is described in
/// [Designing UAC Applications for Windows Vista Step 6: Create and Embed an Application Manifest][step6].
///
/// [step6]: https://msdn.microsoft.com/en-us/library/bb756929.aspx
#[derive(Debug)]
pub enum ExecutionLevel {
/// The application will always run with the same authorities as the program invoking it.
AsInvoker,
/// The program will run without special authorities for a standard user, but will try to
/// run with administrator authority if the user is an administrator. This is rarely used.
HighestAvailable,
/// The application will run as an administrator, prompting for elevation if necessary.
RequireAdministrator,
}
impl ExecutionLevel {
pub fn as_str(&self) -> &'static str {
match self {
Self::AsInvoker => "asInvoker",
Self::HighestAvailable => "highestAvailable",
Self::RequireAdministrator => "requireAdministrator",
}
}
}
impl Display for ExecutionLevel {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(self.as_str())
}
}

117
third_party/rust/embed-manifest/src/manifest/test.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,117 @@
use super::{ExecutionLevel, SupportedOS};
use crate::{empty_manifest, new_manifest};
fn enc(s: &str) -> String {
let mut buf = String::with_capacity(1024);
buf.push('\u{feff}');
for l in s.lines() {
buf.push_str(l);
buf.push_str("\r\n");
}
buf.truncate(buf.len() - 2);
buf
}
fn encp(s: &str) -> String {
s.replace("\n", "\r\n")
}
#[test]
fn empty_manifest_canonical() {
let builder = empty_manifest();
assert_eq!(
format!("{}", builder),
enc(r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"></assembly>"#)
);
}
#[test]
fn empty_manifest_pretty() {
let builder = empty_manifest();
assert_eq!(
format!("{:#}", builder),
encp(
r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"/>"#
)
);
}
#[test]
fn only_execution_level() {
let builder = empty_manifest().requested_execution_level(ExecutionLevel::AsInvoker);
assert_eq!(
format!("{:#}", builder),
encp(
r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" manifestVersion="1.0">
<asmv3:trustInfo>
<asmv3:security>
<asmv3:requestedPrivileges>
<asmv3:requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</asmv3:requestedPrivileges>
</asmv3:security>
</asmv3:trustInfo>
</assembly>"#
)
);
}
#[test]
fn only_windows10() {
let builder = empty_manifest().supported_os(SupportedOS::Windows10..);
assert_eq!(
format!("{:#}", builder),
encp(
r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
</application>
</compatibility>
</assembly>"#
)
);
}
#[test]
fn no_comctl32_6() {
let builder = new_manifest("Company.OrgUnit.Program")
.version(1, 0, 0, 2)
.remove_dependency("Microsoft.Windows.Common-Controls");
assert_eq!(
format!("{:#}", builder),
encp(
r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" manifestVersion="1.0">
<assemblyIdentity name="Company.OrgUnit.Program" type="win32" version="1.0.0.2"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<maxversiontested Id="10.0.18362.1"/>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
</application>
</compatibility>
<asmv3:application>
<asmv3:windowsSettings>
<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">permonitorv2</dpiAwareness>
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
<printerDriverIsolation xmlns="http://schemas.microsoft.com/SMI/2011/WindowsSettings">true</printerDriverIsolation>
</asmv3:windowsSettings>
</asmv3:application>
<asmv3:trustInfo>
<asmv3:security>
<asmv3:requestedPrivileges>
<asmv3:requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</asmv3:requestedPrivileges>
</asmv3:security>
</asmv3:trustInfo>
</assembly>"#
)
);
}

140
third_party/rust/embed-manifest/src/manifest/xml.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,140 @@
use std::fmt::{self, Display, Formatter, Write};
/// Simple but still over-engineered XML generator for a [`Formatter`], for generating
/// Windows Manifest XML. This can easily generate invalid XML.
///
/// When used correctly, this should generate the same output as MTs `-canonicalize`
/// option.
pub struct XmlFormatter<'a, 'f> {
f: &'f mut Formatter<'a>,
state: State,
depth: usize,
}
#[derive(Eq, PartialEq)]
enum State {
Init,
StartTag,
Text,
}
impl<'a, 'f> XmlFormatter<'a, 'f> {
pub fn new(f: &'f mut Formatter<'a>) -> Self {
Self {
f,
state: State::Init,
depth: 0,
}
}
fn pretty(&mut self) -> fmt::Result {
if self.f.alternate() {
self.f.write_str("\r\n")?;
for _ in 0..self.depth {
self.f.write_str(" ")?;
}
}
Ok(())
}
pub fn start_document(&mut self) -> fmt::Result {
if !self.f.alternate() {
self.f.write_char('\u{FEFF}')?;
}
self.f
.write_str("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\r\n")
}
pub fn element<F: FnOnce(&mut Self) -> fmt::Result>(&mut self, name: &str, attrs: &[(&str, &str)], f: F) -> fmt::Result {
self.start_element(name, attrs)?;
f(self)?;
self.end_element(name)
}
pub fn empty_element(&mut self, name: &str, attrs: &[(&str, &str)]) -> fmt::Result {
self.start_element(name, attrs)?;
self.end_element(name)
}
pub fn start_element(&mut self, name: &str, attrs: &[(&str, &str)]) -> fmt::Result {
if self.state == State::StartTag {
self.f.write_char('>')?;
}
if self.depth != 0 {
self.pretty()?;
}
write!(self.f, "<{}", name)?;
for (name, value) in attrs {
write!(self.f, " {}=\"{}\"", name, Xml(value))?;
}
self.depth += 1;
self.state = State::StartTag;
Ok(())
}
pub fn end_element(&mut self, name: &str) -> fmt::Result {
self.depth -= 1;
match self.state {
State::Init => {
self.pretty()?;
write!(self.f, "</{}>", name)
}
State::Text => {
self.state = State::Init;
write!(self.f, "</{}>", name)
}
State::StartTag => {
self.state = State::Init;
if self.f.alternate() {
self.f.write_str("/>")
} else {
write!(self.f, "></{}>", name)
}
}
}
}
pub fn text(&mut self, s: &str) -> fmt::Result {
if self.state == State::StartTag {
self.state = State::Text;
self.f.write_char('>')?;
}
Xml(s).fmt(self.f)
}
}
/// Temporary wrapper for outputting a string with XML attribute encoding.
/// This does not do anything with the control characters which are not
/// valid in XML, encoded or not.
struct Xml<'a>(&'a str);
impl<'a> Display for Xml<'a> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
// Process the string in blocks separated by special characters, so that the parts that
// don't need encoding can be written all at once, not character by character, and with
// no checks for whether string slices are aligned on character boundaries.
for s in self.0.split_inclusive(&['<', '&', '>', '"', '\r'][..]) {
// Check whether the last character in the substring needs encoding. This will be
// `None` at the end of the input string.
let mut iter = s.chars();
let ch = match iter.next_back() {
Some('<') => Some("&lt;"),
Some('&') => Some("&amp;"),
Some('>') => Some("&gt;"),
Some('"') => Some("&quot;"),
Some('\r') => Some("&#13;"),
_ => None,
};
// Write the substring except the last character, then the encoded character;
// or the entire substring if it is not terminated by a special character.
match ch {
Some(enc) => {
f.write_str(iter.as_str())?;
f.write_str(enc)?;
}
None => f.write_str(s)?,
}
}
Ok(())
}
}

11
third_party/rust/embed-manifest/testdata/sample.exe.manifest поставляемый Normal file
Просмотреть файл

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" manifestVersion="1.0">
<assemblyIdentity name="Sample.Test" type="win32" version="1.0.0.0"/>
<asmv3:trustInfo>
<asmv3:security>
<asmv3:requestedPrivileges>
<asmv3:requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</asmv3:requestedPrivileges>
</asmv3:security>
</asmv3:trustInfo>
</assembly>

Просмотреть файл

@ -0,0 +1,66 @@
[package]
name = "crashreporter"
version = "1.0.0"
edition = "2021"
[[bin]]
name = "crashreporter"
[dependencies]
anyhow = "1.0"
cfg-if = "1.0"
env_logger = { version = "0.10", default-features = false }
fluent = "0.16.0"
intl-memoizer = "0.5"
lazy_static = "1.4"
libloading = "0.7"
log = "0.4.17"
mozbuild = "0.1"
mozilla-central-workspace-hack = { version = "0.1", features = ["crashreporter"], optional = true }
phf = "0.11"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
sha2 = "0.10"
time = { version = "0.3", features = ["formatting", "serde"] }
unic-langid = { version = "0.9.1" }
uuid = { version = "1", features = ["v4", "serde"] }
zip = { version = "0.6", default-features = false }
[target."cfg(target_os = \"macos\")".dependencies]
block = "0.1"
cocoa = { package = "cocoabind", path = "../cocoabind" }
objc = "0.2"
[target."cfg(target_os = \"linux\")".dependencies]
gtk = { package = "gtkbind", path = "../gtkbind" }
[target."cfg(target_os = \"windows\")".dependencies.windows-sys]
version = "0.52"
features = [
"Win32_Foundation",
"Win32_Globalization",
"Win32_Graphics_Gdi",
"Win32_System_Com",
"Win32_System_LibraryLoader",
"Win32_System_SystemServices",
"Win32_System_Threading",
"Win32_UI_Controls",
"Win32_UI_Input_KeyboardAndMouse",
"Win32_UI_Shell",
"Win32_UI_WindowsAndMessaging"
]
[features]
# Required for tests
mock = []
[build-dependencies]
embed-manifest = "1.4"
mozbuild = "0.1"
phf_codegen = "0.11"
yaml-rust = "0.4"
[dev-dependencies]
bytes = "1.4"
tokio = { version = "1.29", features = ["rt", "net", "time", "sync"] }
warp = { version = "0.3", default-features = false }

Просмотреть файл

@ -0,0 +1,15 @@
# vim:set ts=8 sw=8 sts=8 noet:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
include $(topsrcdir)/config/rules.mk
ifeq ($(OS_ARCH),Darwin)
libs::
$(NSINSTALL) -D $(DIST)/bin/crashreporter.app
rsync --archive --cvs-exclude --exclude '*.in' $(srcdir)/macos_app_bundle/ $(DIST)/bin/crashreporter.app/Contents/
$(call py_action,preprocessor crashreporter.app/Contents/Resources/English.lproj/InfoPlist.strings,-Fsubstitution --output-encoding utf-16 -DAPP_NAME='$(MOZ_APP_DISPLAYNAME)' $(srcdir)/macos_app_bundle/Resources/English.lproj/InfoPlist.strings.in -o $(DIST)/bin/crashreporter.app/Contents/Resources/English.lproj/InfoPlist.strings)
$(NSINSTALL) -D $(DIST)/bin/crashreporter.app/Contents/MacOS
$(NSINSTALL) $(DIST)/bin/crashreporter $(DIST)/bin/crashreporter.app/Contents/MacOS
endif

Просмотреть файл

@ -0,0 +1,89 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::{env, path::Path};
fn main() {
windows_manifest();
crash_ping_annotations();
set_mock_cfg();
}
fn windows_manifest() {
use embed_manifest::{embed_manifest, manifest, new_manifest};
if std::env::var_os("CARGO_CFG_WINDOWS").is_none() {
return;
}
// See https://docs.rs/embed-manifest/1.4.0/embed_manifest/fn.new_manifest.html for what the
// default manifest includes. The defaults include almost all of the settings used in the old
// crash reporter.
let manifest = new_manifest("CrashReporter")
// Use legacy active code page because GDI doesn't support per-process UTF8 (and older
// win10 may not support this setting anyway).
.active_code_page(manifest::ActiveCodePage::Legacy)
// GDI scaling is not enabled by default but we need it to make the GDI-drawn text look
// nice on high-DPI displays.
.gdi_scaling(manifest::Setting::Enabled);
embed_manifest(manifest).expect("unable to embed windows manifest file");
println!("cargo:rerun-if-changed=build.rs");
}
/// Generate crash ping annotation information from the yaml definition file.
fn crash_ping_annotations() {
use std::fs::File;
use std::io::{BufWriter, Write};
use yaml_rust::{Yaml, YamlLoader};
let crash_annotations = Path::new("../../CrashAnnotations.yaml")
.canonicalize()
.unwrap();
println!("cargo:rerun-if-changed={}", crash_annotations.display());
let crash_ping_file = Path::new(&env::var("OUT_DIR").unwrap()).join("ping_annotations.rs");
let yaml = std::fs::read_to_string(crash_annotations).unwrap();
let Yaml::Hash(entries) = YamlLoader::load_from_str(&yaml)
.unwrap()
.into_iter()
.next()
.unwrap()
else {
panic!("unexpected crash annotations root type");
};
let ping_annotations = entries.into_iter().filter_map(|(k, v)| {
v["ping"]
.as_bool()
.unwrap_or(false)
.then(|| k.into_string().unwrap())
});
let mut phf_set = phf_codegen::Set::new();
for annotation in ping_annotations {
phf_set.entry(annotation);
}
let mut file = BufWriter::new(File::create(&crash_ping_file).unwrap());
writeln!(
&mut file,
"static PING_ANNOTATIONS: phf::Set<&'static str> = {};",
phf_set.build()
)
.unwrap();
}
/// Set the mock configuration option when tests are enabled or when the mock feature is enabled.
fn set_mock_cfg() {
// Very inconveniently, there's no way to detect `cfg(test)` from build scripts. See
// https://github.com/rust-lang/cargo/issues/4789. This seems like an arbitrary and pointless
// limitation, and only complicates the evaluation of mock behavior. Because of this, we have a
// `mock` feature which is activated by `toolkit/library/rust/moz.build`.
if env::var_os("CARGO_FEATURE_MOCK").is_some() || mozbuild::config::MOZ_CRASHREPORTER_MOCK {
println!("cargo:rustc-cfg=mock");
}
}

Просмотреть файл

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleDisplayName</key>
<string>crashreporter</string>
<key>CFBundleExecutable</key>
<string>crashreporter</string>
<key>CFBundleIconFile</key>
<string>crashreporter.icns</string>
<key>CFBundleIdentifier</key>
<string>org.mozilla.crashreporter</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>crashreporter</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>LSHasLocalizedDisplayName</key>
<true/>
<key>NSRequiresAquaSystemAppearance</key>
<false/>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>LSUIElement</key>
<true/>
</dict>
</plist>

Просмотреть файл

@ -0,0 +1,2 @@
APPL????

Просмотреть файл

@ -0,0 +1,8 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Localized versions of Info.plist keys */
CFBundleName = "Crash Reporter";
CFBundleDisplayName = "@APP_NAME@ Crash Reporter";

Двоичный файл не отображается.

Просмотреть файл

@ -0,0 +1,7 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
RUST_PROGRAMS = ["crashreporter"]

Просмотреть файл

@ -0,0 +1,10 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
// Use the WINDOWS windows subsystem. This prevents a console window from opening with the
// application.
#![cfg_attr(windows, windows_subsystem = "windows")]
fn main() {
todo!()
}

Просмотреть файл

@ -0,0 +1,12 @@
[package]
name = "cocoabind"
version = "0.1.0"
edition = "2021"
[dependencies]
block = "0.1"
objc = "0.2"
[build-dependencies]
bindgen = { version = "0.69", default-features = false, features = ["runtime"] }
mozbuild = "0.1.0"

Просмотреть файл

@ -0,0 +1,74 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use mozbuild::config::CC_BASE_FLAGS as CFLAGS;
const TYPES: &[&str] = &[
"ActionCell",
"Application",
"Array",
"AttributedString",
"Box",
"Button",
"ButtonCell",
"Cell",
"ClassDescription",
"Control",
"DefaultRunLoopMode",
"Dictionary",
"ForegroundColorAttributeName",
"LayoutDimension",
"LayoutGuide",
"LayoutXAxisAnchor",
"LayoutYAxisAnchor",
"MutableAttributedString",
"MutableParagraphStyle",
"MutableString",
"ModalPanelRunLoopMode",
"Panel",
"ProcessInfo",
"ProgressIndicator",
"Proxy",
"RunLoop",
"ScrollView",
"StackView",
"String",
"TextField",
"TextView",
"Value",
"View",
"Window",
];
fn main() {
let mut builder = bindgen::Builder::default()
.header_contents(
"cocoa_bindings.h",
"#define self self_
#import <Cocoa/Cocoa.h>
",
)
.generate_block(true)
.prepend_enum_name(false)
.clang_args(CFLAGS)
.clang_args(["-x", "objective-c"])
.clang_arg("-fblocks")
.derive_default(true)
.allowlist_item("TransformProcessType");
for name in TYPES {
// (I|P) covers generated traits (interfaces and protocols). `(_.*)?` covers categories
// (which are generated as `CLASS_CATEGORY`).
builder = builder.allowlist_item(format!("(I|P)?NS{name}(_.*)?"));
}
let bindings = builder
.generate()
.expect("unable to generate cocoa bindings");
let out_path = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("cocoa_bindings.rs"))
.expect("failed to write cocoa bindings");
println!("cargo:rustc-link-lib=framework=AppKit");
println!("cargo:rustc-link-lib=framework=Cocoa");
println!("cargo:rustc-link-lib=framework=Foundation");
}

Просмотреть файл

@ -0,0 +1,10 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(unused_imports)]
include!(concat!(env!("OUT_DIR"), "/cocoa_bindings.rs"));

Просмотреть файл

@ -0,0 +1,8 @@
[package]
name = "gtkbind"
version = "0.1.0"
edition = "2021"
[build-dependencies]
bindgen = { version = "0.69.0", default-features = false, features = ["runtime"] }
mozbuild = "0.1.0"

Просмотреть файл

@ -0,0 +1,30 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use mozbuild::config::{
CC_BASE_FLAGS as CFLAGS, MOZ_GTK3_CFLAGS as GTK_CFLAGS, MOZ_GTK3_LIBS as GTK_LIBS,
};
fn main() {
let bindings = bindgen::Builder::default()
.header_contents("gtk_bindings.h", r#"#include "gtk/gtk.h""#)
.clang_args(CFLAGS)
.clang_args(GTK_CFLAGS)
.allowlist_function("gtk_.*")
.allowlist_function("g_(application|main_context|object|signal|timeout)_.*")
.derive_default(true)
.generate()
.expect("unable to generate gtk bindings");
for flag in GTK_LIBS {
if let Some(lib) = flag.strip_prefix("-l") {
println!("cargo:rustc-link-lib={lib}");
} else if let Some(path) = flag.strip_prefix("-L") {
println!("cargo:rustc-link-search={path}");
}
}
let out_path = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("gtk_bindings.rs"))
.expect("failed to write gtk bindings");
}

Просмотреть файл

@ -0,0 +1,9 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
include!(concat!(env!("OUT_DIR"), "/gtk_bindings.rs"));

Просмотреть файл

@ -67,10 +67,12 @@ if CONFIG["MOZ_CRASHREPORTER"]:
DIRS += ["rust_minidump_writer_linux"]
if CONFIG["OS_TARGET"] != "Android":
DIRS += ["minidump-analyzer"]
DIRS += [
"client-rust/app",
"minidump-analyzer",
]
DIRS += [
"client",
"mozannotation_client",
"mozannotation_server",
]

Просмотреть файл

@ -21,6 +21,7 @@ for feature in gkrust_features:
# Target directory doesn't matter a lot here, since we can't share panic=abort
# compilation artifacts with gkrust.
RUST_TESTS = [
"crashreporter",
"firefox-on-glean",
"l10nregistry",
"selectors",
@ -31,6 +32,8 @@ RUST_TESTS = [
"gkrust",
]
RUST_TEST_FEATURES.append("crashreporter/mock")
# Code coverage builds link a bunch of Gecko bindings code from the style
# crate, which is not used by our tests but would cause link errors.
#

Просмотреть файл

@ -3054,6 +3054,10 @@ def oxidized_breakpad(target):
set_config("MOZ_OXIDIZED_BREAKPAD", True, when=oxidized_breakpad)
set_define("MOZ_OXIDIZED_BREAKPAD", True, when=oxidized_breakpad)
# Environment variable to mock the crashreporter for testing
option(env="MOZ_CRASHREPORTER_MOCK", help="Mock the crashreporter to test native GUIs")
set_config("MOZ_CRASHREPORTER_MOCK", True, when="MOZ_CRASHREPORTER_MOCK")
# Wine
# ==============================================================