forked from mirrors/gecko-dev
Bug 1759175 pt1 - Repository integration r=glandium,supply-chain-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D174916
This commit is contained in:
parent
81a6b069cf
commit
e32971e70e
39 changed files with 2529 additions and 17 deletions
59
Cargo.lock
generated
59
Cargo.lock
generated
|
|
@ -798,6 +798,16 @@ checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961"
|
||||||
name = "cmake"
|
name = "cmake"
|
||||||
version = "0.1.999"
|
version = "0.1.999"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cocoabind"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bindgen 0.69.4",
|
||||||
|
"block",
|
||||||
|
"mozbuild",
|
||||||
|
"objc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "codespan-reporting"
|
name = "codespan-reporting"
|
||||||
version = "0.11.1"
|
version = "0.11.1"
|
||||||
|
|
@ -962,6 +972,41 @@ dependencies = [
|
||||||
"mach2",
|
"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]]
|
[[package]]
|
||||||
name = "crc32fast"
|
name = "crc32fast"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
|
|
@ -1484,6 +1529,12 @@ version = "1.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "embed-manifest"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41cd446c890d6bed1d8b53acef5f240069ebef91d6fae7c5f52efe61fe8b5eae"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_c"
|
name = "encoding_c"
|
||||||
version = "0.9.8"
|
version = "0.9.8"
|
||||||
|
|
@ -2472,6 +2523,14 @@ dependencies = [
|
||||||
"bitflags 1.999.999",
|
"bitflags 1.999.999",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gtkbind"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bindgen 0.69.4",
|
||||||
|
"mozbuild",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "guid_win"
|
name = "guid_win"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ members = [
|
||||||
"security/manager/ssl/osclientcerts",
|
"security/manager/ssl/osclientcerts",
|
||||||
"testing/geckodriver",
|
"testing/geckodriver",
|
||||||
"toolkit/components/uniffi-bindgen-gecko-js",
|
"toolkit/components/uniffi-bindgen-gecko-js",
|
||||||
|
"toolkit/crashreporter/client-rust/app",
|
||||||
"toolkit/crashreporter/mozwer-rust",
|
"toolkit/crashreporter/mozwer-rust",
|
||||||
"toolkit/crashreporter/rust_minidump_writer_linux",
|
"toolkit/crashreporter/rust_minidump_writer_linux",
|
||||||
"toolkit/library/gtest/rust",
|
"toolkit/library/gtest/rust",
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,20 @@ def generate_bool(name):
|
||||||
return f"pub const {name}: bool = {'true' if value else 'false'};\n"
|
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):
|
def escape_rust_string(value):
|
||||||
"""escape the string into a Rust literal"""
|
"""escape the string into a Rust literal"""
|
||||||
# This could be more generous, but we're only escaping paths with it.
|
# This could be more generous, but we're only escaping paths with it.
|
||||||
|
|
@ -34,14 +48,6 @@ def escape_rust_string(value):
|
||||||
return '"%s"' % result
|
return '"%s"' % result
|
||||||
|
|
||||||
|
|
||||||
def generate_string(buildvar, output):
|
|
||||||
buildconfig_var = buildconfig.substs.get(buildvar)
|
|
||||||
if buildconfig_var is not None:
|
|
||||||
output.write(
|
|
||||||
f"pub const {buildvar}: &str = {escape_rust_string(buildconfig_var)};\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def generate(output):
|
def generate(output):
|
||||||
# Write out a macro which can be used within `include!`-like methods to
|
# Write out a macro which can be used within `include!`-like methods to
|
||||||
# reference the topobjdir.
|
# reference the topobjdir.
|
||||||
|
|
@ -94,10 +100,10 @@ def generate(output):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Write out some useful strings from the buildconfig.
|
# Write out some useful strings from the buildconfig.
|
||||||
generate_string("MOZ_MACBUNDLE_ID", output)
|
output.write(generate_string("MOZ_MACBUNDLE_ID"))
|
||||||
generate_string("MOZ_APP_NAME", output)
|
output.write(generate_string("MOZ_APP_NAME"))
|
||||||
|
|
||||||
# Finally, write out some useful booleans from the buildconfig.
|
# Write out some useful booleans from the buildconfig.
|
||||||
output.write(generate_bool("MOZ_FOLD_LIBS"))
|
output.write(generate_bool("MOZ_FOLD_LIBS"))
|
||||||
output.write(generate_bool("NIGHTLY_BUILD"))
|
output.write(generate_bool("NIGHTLY_BUILD"))
|
||||||
output.write(generate_bool("RELEASE_OR_BETA"))
|
output.write(generate_bool("RELEASE_OR_BETA"))
|
||||||
|
|
@ -105,3 +111,9 @@ def generate(output):
|
||||||
output.write(generate_bool("MOZ_DEV_EDITION"))
|
output.write(generate_bool("MOZ_DEV_EDITION"))
|
||||||
output.write(generate_bool("MOZ_ESR"))
|
output.write(generate_bool("MOZ_ESR"))
|
||||||
output.write(generate_bool("MOZ_DIAGNOSTIC_ASSERT_ENABLED"))
|
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"))
|
||||||
|
|
|
||||||
|
|
@ -162,6 +162,8 @@ optional = true
|
||||||
features = [
|
features = [
|
||||||
"Wdk_System_Threading",
|
"Wdk_System_Threading",
|
||||||
"Win32_Foundation",
|
"Win32_Foundation",
|
||||||
|
"Win32_Globalization",
|
||||||
|
"Win32_Graphics_Gdi",
|
||||||
"Win32_Security",
|
"Win32_Security",
|
||||||
"Win32_Storage_FileSystem",
|
"Win32_Storage_FileSystem",
|
||||||
"Win32_System_Com",
|
"Win32_System_Com",
|
||||||
|
|
@ -175,6 +177,8 @@ features = [
|
||||||
"Win32_System_SystemInformation",
|
"Win32_System_SystemInformation",
|
||||||
"Win32_System_SystemServices",
|
"Win32_System_SystemServices",
|
||||||
"Win32_System_Threading",
|
"Win32_System_Threading",
|
||||||
|
"Win32_UI_Controls",
|
||||||
|
"Win32_UI_Input_KeyboardAndMouse",
|
||||||
"Win32_UI_Shell",
|
"Win32_UI_Shell",
|
||||||
"Win32_UI_WindowsAndMessaging",
|
"Win32_UI_WindowsAndMessaging",
|
||||||
]
|
]
|
||||||
|
|
@ -182,6 +186,7 @@ features = [
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
builtins-static = ["dep:bindgen", "dep:bitflags", "dep:memchr", "dep:nom", "dep:regex", "dep:smallvec"]
|
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"]
|
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 = ["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"]
|
gkrust-gtest = ["gkrust"]
|
||||||
|
|
|
||||||
|
|
@ -1533,6 +1533,12 @@ who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||||
criteria = "safe-to-deploy"
|
criteria = "safe-to-deploy"
|
||||||
delta = "1.8.0 -> 1.8.1"
|
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]]
|
[[audits.encoding_c]]
|
||||||
who = "Henri Sivonen <hsivonen@hsivonen.fi>"
|
who = "Henri Sivonen <hsivonen@hsivonen.fi>"
|
||||||
criteria = "safe-to-deploy"
|
criteria = "safe-to-deploy"
|
||||||
|
|
@ -2386,6 +2392,12 @@ criteria = "safe-to-deploy"
|
||||||
version = "0.5.4"
|
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."
|
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]]
|
[[audits.linked-hash-map]]
|
||||||
who = "Mike Hommey <mh+mozilla@glandium.org>"
|
who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||||
criteria = "safe-to-run"
|
criteria = "safe-to-run"
|
||||||
|
|
@ -4708,6 +4720,15 @@ who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||||
criteria = "safe-to-deploy"
|
criteria = "safe-to-deploy"
|
||||||
version = "0.10.1"
|
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]]
|
[[audits.zip]]
|
||||||
who = "Mike Hommey <mh+mozilla@glandium.org>"
|
who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||||
criteria = "safe-to-run"
|
criteria = "safe-to-run"
|
||||||
|
|
|
||||||
|
|
@ -805,7 +805,3 @@ criteria = "safe-to-deploy"
|
||||||
[[exemptions.xml-rs]]
|
[[exemptions.xml-rs]]
|
||||||
version = "0.8.4"
|
version = "0.8.4"
|
||||||
criteria = "safe-to-deploy"
|
criteria = "safe-to-deploy"
|
||||||
|
|
||||||
[[exemptions.zip]]
|
|
||||||
version = "0.6.2"
|
|
||||||
criteria = "safe-to-run"
|
|
||||||
|
|
|
||||||
1
third_party/rust/embed-manifest/.cargo-checksum.json
vendored
Normal file
1
third_party/rust/embed-manifest/.cargo-checksum.json
vendored
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
vendored
Normal file
59
third_party/rust/embed-manifest/CHANGELOG.md
vendored
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
vendored
Normal file
38
third_party/rust/embed-manifest/Cargo.toml
vendored
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
vendored
Normal file
23
third_party/rust/embed-manifest/LICENSE
vendored
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
vendored
Normal file
62
third_party/rust/embed-manifest/README.md
vendored
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 doesn’t 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 don’t try to run
|
||||||
|
as an administrator.
|
||||||
|
- 32-bit programs aren’t 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
vendored
Normal file
2
third_party/rust/embed-manifest/rustfmt.toml
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
max_width = 132
|
||||||
|
newline_style = "Unix"
|
||||||
192
third_party/rust/embed-manifest/src/embed/coff.rs
vendored
Normal file
192
third_party/rust/embed-manifest/src/embed/coff.rs
vendored
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(×tamp.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
vendored
Normal file
57
third_party/rust/embed-manifest/src/embed/error.rs
vendored
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
vendored
Normal file
139
third_party/rust/embed-manifest/src/embed/mod.rs
vendored
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
vendored
Normal file
173
third_party/rust/embed-manifest/src/embed/test.rs
vendored
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
vendored
Normal file
134
third_party/rust/embed-manifest/src/lib.rs
vendored
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 executable’s `Cargo.toml`:
|
||||||
|
//!
|
||||||
|
//! ```toml
|
||||||
|
//! [build-dependencies]
|
||||||
|
//! embed-manifest = "1.3.1"
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! In the same directory, create a `build.rs` file to call this crate’s
|
||||||
|
//! 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
vendored
Normal file
882
third_party/rust/embed-manifest/src/manifest/mod.rs
vendored
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
vendored
Normal file
117
third_party/rust/embed-manifest/src/manifest/test.rs
vendored
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
vendored
Normal file
140
third_party/rust/embed-manifest/src/manifest/xml.rs
vendored
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 MT’s `-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("<"),
|
||||||
|
Some('&') => Some("&"),
|
||||||
|
Some('>') => Some(">"),
|
||||||
|
Some('"') => Some("""),
|
||||||
|
Some('\r') => Some(" "),
|
||||||
|
_ => 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
vendored
Normal file
11
third_party/rust/embed-manifest/testdata/sample.exe.manifest
vendored
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>
|
||||||
66
toolkit/crashreporter/client-rust/app/Cargo.toml
Normal file
66
toolkit/crashreporter/client-rust/app/Cargo.toml
Normal file
|
|
@ -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 }
|
||||||
17
toolkit/crashreporter/client-rust/app/Makefile.in
Normal file
17
toolkit/crashreporter/client-rust/app/Makefile.in
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
# 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/.
|
||||||
|
|
||||||
|
ifeq ($(OS_ARCH),Darwin)
|
||||||
|
|
||||||
|
include $(topsrcdir)/config/rules.mk
|
||||||
|
|
||||||
|
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
|
||||||
89
toolkit/crashreporter/client-rust/app/build.rs
Normal file
89
toolkit/crashreporter/client-rust/app/build.rs
Normal file
|
|
@ -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";
|
||||||
Binary file not shown.
7
toolkit/crashreporter/client-rust/app/moz.build
Normal file
7
toolkit/crashreporter/client-rust/app/moz.build
Normal file
|
|
@ -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"]
|
||||||
10
toolkit/crashreporter/client-rust/app/src/main.rs
Normal file
10
toolkit/crashreporter/client-rust/app/src/main.rs
Normal file
|
|
@ -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!()
|
||||||
|
}
|
||||||
12
toolkit/crashreporter/client-rust/cocoabind/Cargo.toml
Normal file
12
toolkit/crashreporter/client-rust/cocoabind/Cargo.toml
Normal file
|
|
@ -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"
|
||||||
74
toolkit/crashreporter/client-rust/cocoabind/build.rs
Normal file
74
toolkit/crashreporter/client-rust/cocoabind/build.rs
Normal file
|
|
@ -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");
|
||||||
|
}
|
||||||
10
toolkit/crashreporter/client-rust/cocoabind/src/lib.rs
Normal file
10
toolkit/crashreporter/client-rust/cocoabind/src/lib.rs
Normal file
|
|
@ -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"));
|
||||||
8
toolkit/crashreporter/client-rust/gtkbind/Cargo.toml
Normal file
8
toolkit/crashreporter/client-rust/gtkbind/Cargo.toml
Normal file
|
|
@ -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"
|
||||||
33
toolkit/crashreporter/client-rust/gtkbind/build.rs
Normal file
33
toolkit/crashreporter/client-rust/gtkbind/build.rs
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
/* 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)_.*")
|
||||||
|
// The gtk/glib valist functions generate FFI-unsafe signatures on aarch64 which cause
|
||||||
|
// compile errors. We don't use them anyway.
|
||||||
|
.blocklist_function(".*_valist")
|
||||||
|
.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");
|
||||||
|
}
|
||||||
9
toolkit/crashreporter/client-rust/gtkbind/src/lib.rs
Normal file
9
toolkit/crashreporter/client-rust/gtkbind/src/lib.rs
Normal file
|
|
@ -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"]
|
DIRS += ["rust_minidump_writer_linux"]
|
||||||
|
|
||||||
if CONFIG["OS_TARGET"] != "Android":
|
if CONFIG["OS_TARGET"] != "Android":
|
||||||
DIRS += ["minidump-analyzer"]
|
DIRS += [
|
||||||
|
"client-rust/app",
|
||||||
|
"minidump-analyzer",
|
||||||
|
]
|
||||||
|
|
||||||
DIRS += [
|
DIRS += [
|
||||||
"client",
|
|
||||||
"mozannotation_client",
|
"mozannotation_client",
|
||||||
"mozannotation_server",
|
"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
|
# Target directory doesn't matter a lot here, since we can't share panic=abort
|
||||||
# compilation artifacts with gkrust.
|
# compilation artifacts with gkrust.
|
||||||
RUST_TESTS = [
|
RUST_TESTS = [
|
||||||
|
"crashreporter",
|
||||||
"firefox-on-glean",
|
"firefox-on-glean",
|
||||||
"l10nregistry",
|
"l10nregistry",
|
||||||
"selectors",
|
"selectors",
|
||||||
|
|
@ -34,6 +35,8 @@ RUST_TESTS = [
|
||||||
if CONFIG["TARGET_OS"] in ("WINNT", "OSX"):
|
if CONFIG["TARGET_OS"] in ("WINNT", "OSX"):
|
||||||
RUST_TESTS += ["nmhproxy"]
|
RUST_TESTS += ["nmhproxy"]
|
||||||
|
|
||||||
|
RUST_TEST_FEATURES.append("crashreporter/mock")
|
||||||
|
|
||||||
# Code coverage builds link a bunch of Gecko bindings code from the style
|
# 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.
|
# 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_config("MOZ_OXIDIZED_BREAKPAD", True, when=oxidized_breakpad)
|
||||||
set_define("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
|
# Wine
|
||||||
# ==============================================================
|
# ==============================================================
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue