Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a windows manifest file #13131

Merged
merged 1 commit into from
Dec 8, 2023
Merged

Conversation

ChrisDenton
Copy link
Member

This adds a Windows application manifest file to the built cargo.exe for windows msvc. This manifest file is used to enable modern features of Windows. rustc has been using a similar manifest for about a year now.

The manifest file does the following:

  • States our compatibility with Windows versions 7, 8, 8.1, 10 and 11. This allows avoiding unnecessary compatibility shims.
  • Sets the code page to UTF-8. This should have no real impact on existing code (which should work with any code page). That said it may avoid issues if dependencies do use the local code page because conversions to/from Unicode are lossy so if a Unicode code point has no local code page equivalent, information is lost.
  • Enable long path awareness. Mostly rust itself side-steps long path issues by using \\?\ paths internally. However, this doesn't work for the current directory whereas using this option does.

You can test that a manifest file has been embedded by extracting it to a new file:

mt -nologo -inputresource:cargo.exe -out:embedded.manifest

You can also examine the .rsrc (aka resource) section using dumpbin

dumpbin /SECTION:.rsrc /ALL cargo.exe

Additional info

This also sets the /Wx linker option which turns linker warnings into errors. When setting linker options manually, I prefer to also set this because, unless there are also linker errors, rustc will not show linker warnings by default. Linker warnings should be rare and usually do indicate an actual problem so not ignoring them should be fine.

This avoids the need for compatibility shims, sets the code page to UTF-8 and enables long path awareness.
@rustbot
Copy link
Collaborator

rustbot commented Dec 7, 2023

r? @ehuss

(rustbot has picked a reviewer for you, use r? to override)

@rustbot rustbot added A-infrastructure Area: infrastructure around the cargo repo, ci, releases, etc. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Dec 7, 2023
@ehuss
Copy link
Contributor

ehuss commented Dec 7, 2023

Enable long path awareness.

A minor concern I had is that there is a fairly significant amount of C code that cargo uses. What are the chances this would cause a problem (like a buffer overflow) with that? It seems pretty unlikely to me, but I don't know which APIs might be at risk. Is it just looking out for usage of MAX_PATH?

@ChrisDenton
Copy link
Member Author

Yes.

I think the risk is low. If C code does use fixed sized MAX_PATH buffers, then it should still be sending the right array bounds to the Windows API or checking a user provided path fits in the buffer. That said, buffer overflows are a common issue in general so I don't want to discount the possibility entirely. Though it helps that long path awareness has been a thing for years at this point.

@ChrisDenton
Copy link
Member Author

Btw, I'm not sure why check-version-bump failed. It doesn't seem related to this PR? But maybe I'm missing something.

curl-sys error
error: failed to run custom build command for `curl-sys v0.4.69+curl-8.5.0`

Caused by:
  process didn't exit successfully: `/home/runner/work/cargo/cargo/target/semver-checks/local-crates_io-0_39_2/target/semver-checks/target/debug/build/curl-sys-070b7960e8af9c11/build-script-build` (exit status: 101)
  --- stdout
  cargo:rerun-if-changed=curl
  cargo:rerun-if-env-changed=LIBCURL_NO_PKG_CONFIG
  cargo:rerun-if-env-changed=PKG_CONFIG_x86_64-unknown-linux-gnu
  cargo:rerun-if-env-changed=PKG_CONFIG_x86_64_unknown_linux_gnu
  cargo:rerun-if-env-changed=HOST_PKG_CONFIG
  cargo:rerun-if-env-changed=PKG_CONFIG
  cargo:rerun-if-env-changed=LIBCURL_STATIC
  cargo:rerun-if-env-changed=LIBCURL_DYNAMIC
  cargo:rerun-if-env-changed=PKG_CONFIG_ALL_STATIC
  cargo:rerun-if-env-changed=PKG_CONFIG_ALL_DYNAMIC
  cargo:rerun-if-env-changed=PKG_CONFIG_PATH_x86_64-unknown-linux-gnu
  cargo:rerun-if-env-changed=PKG_CONFIG_PATH_x86_64_unknown_linux_gnu
  cargo:rerun-if-env-changed=HOST_PKG_CONFIG_PATH
  cargo:rerun-if-env-changed=PKG_CONFIG_PATH
  cargo:rerun-if-env-changed=PKG_CONFIG_LIBDIR_x86_64-unknown-linux-gnu
  cargo:rerun-if-env-changed=PKG_CONFIG_LIBDIR_x86_64_unknown_linux_gnu
  cargo:rerun-if-env-changed=HOST_PKG_CONFIG_LIBDIR
  cargo:rerun-if-env-changed=PKG_CONFIG_LIBDIR
  cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR_x86_64-unknown-linux-gnu
  cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR_x86_64_unknown_linux_gnu
  cargo:rerun-if-env-changed=HOST_PKG_CONFIG_SYSROOT_DIR
  cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR
  Couldn't find libcurl from pkgconfig (`PKG_CONFIG_ALLOW_SYSTEM_CFLAGS="1" PKG_CONFIG_ALLOW_SYSTEM_LIBS="1" "pkg-config" "--libs" "--cflags" "libcurl"` did not exit successfully: exit status: 1
  error: could not find system library 'libcurl' required by the 'curl-sys' crate

  --- stderr
  Package libcurl was not found in the pkg-config search path.
  Perhaps you should add the directory containing `libcurl.pc'
  to the PKG_CONFIG_PATH environment variable
  No package 'libcurl' found
  ), compiling it from source...
  cargo:root=/home/runner/work/cargo/cargo/target/semver-checks/local-crates_io-0_39_2/target/semver-checks/target/debug/build/curl-sys-5c163e2bf80bc632/out
  cargo:include=/home/runner/work/cargo/cargo/target/semver-checks/local-crates_io-0_39_2/target/semver-checks/target/debug/build/curl-sys-5c163e2bf80bc632/out/include
  cargo:static=1
  cargo:rustc-cfg=libcurl_vendored

  --- stderr
  fatal: not a git repository (or any of the parent directories): .git
  thread 'main' panicked at /home/runner/.cargo/registry/src/index.crates.io-6f17d22bba15001f/curl-sys-0.4.69+curl-8.5.0/build.rs:86:10:
  called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: "No such file or directory" }
  note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
warning: build failed, waiting for other jobs to finish...

error: process didn't exit successfully: `cargo semver-checks check-release --workspace` (exit status: 1)

@weihanglo
Copy link
Member

Probably due to alexcrichton/curl-rust#542.

@ehuss
Copy link
Contributor

ehuss commented Dec 7, 2023

OK, I did a little bit of auditing. I think these should mostly be OK, but I'm always a little worried about what I don't know here.

home uses a fixed-width MAX_PATH buffer, but SHGetFolderPathW is documented to not return anything longer. I don't know if Windows just returns an error if it is too long? Or maybe it doesn't allow special folders like a home directory to be defined to be too long?

is-terminal uses a fixed width buffer, but passes the length into GetFileInformationByHandleEx. (Wish we could drop that dependency.)

sqlite is limited to MAX_PATH in a lot of places, but I'm going to assume all its usage is correct. It can be compiled with a define to use a longer path, but I don't think that is immediately necessary. There is a special VFS to support 32K paths, but that requires special handling. We'll need to consider this if we start using sqlite in the target directory, and we want to support target directories that exceed MAX_PATH.

curl has a disturbingly large amount of MAX_PATH routines that I don't feel inclined to review. Hopefully someone out there has already done that. 🤷

git2 also has a bunch of MAX_PATH buffers, but looks to usually validate lengths. However, I also don't feel inclined to review. It is a little worrisome that it appears to support paths longer than MAX_PATH even though there are code paths that seem to have MAX_PATH buffers. I'm not sure how it does that. It seems to honor core.longpaths which defaults to false. I'm not sure if we should consider setting that ourselves?

There's a few other little things (like openssl), but none jumped out to me.


It looks like this might help a few long-path scenarios, but doesn't cover everything. IIUC, link.exe does not handle paths longer than MAX_PATH. The current working directory cannot be longer than MAX_PATH (which is an issue for running cargo itself). AFAIK, Rust does not allow launching processes with paths longer than MAX_PATH (which is an issue for build scripts and such). Does that sound roughly correct?

@ChrisDenton
Copy link
Member Author

I think home should be using SHGetKnownFolderPath but it shouldn't matter too much either way. A lot of things would break if the home folder were longer than MAX_PATH.

is_terminal is fine. It's looking for a (roughly) known pipe path. If the path is longer then that's just an error and the function assumes it won't match the path being looked for. (btw, can't you replace it with the std IsTerminal trait?)

Looking through a few of the curl/sqlite/git2 hits, I'm not too worried by what I've seen so far. I think the main issue would be UB due to incorrect assumptions which I'm not seeing. I'm not so worried about cases where the buffer is simply too small. That's an error but it's no worse than the current situation and can be fixed.

libgit2 is however funny in that it has to artificially enforce the MAX_PATH limit if core.longpaths isn't set. And it always enforces MAX_PATH for the .git directory just in case someone disables the option. I assume this limitation will exist so long as the option does.


link.exe does not handle paths longer than MAX_PATH.

True. Though this could change.

The current working directory cannot be longer than MAX_PATH (which is an issue for running cargo itself).

With longpathaware it can be.

AFAIK, Rust does not allow launching processes with paths longer than MAX_PATH (which is an issue for build scripts and such).

It should do now.

@ChrisDenton
Copy link
Member Author

ChrisDenton commented Dec 7, 2023

Oh, wait the combination of both running a process and setting the current directory might not work together (i.e. Command::current_dir). I'd need to test that. std::env::set_current_dir should work though.

EDIT: Yeah, launching a process with a current directory longer than MAX_PATH doesn't work.

@epage
Copy link
Contributor

epage commented Dec 7, 2023

is_terminal is fine. It's looking for a (roughly) known pipe path. If the path is longer then that's just an error and the function assumes it won't match the path being looked for. (btw, can't you replace it with the std IsTerminal trait?)

supports-hyperlinks is small enough that I'm willing to drop it if they aren't willing to bump MSRV. let me know if you want me to explore this.

@ehuss
Copy link
Contributor

ehuss commented Dec 7, 2023

The current working directory cannot be longer than MAX_PATH (which is an issue for running cargo itself).

With longpathaware it can be.

I'm unable to get this to work. Perhaps it depends on the shell? I have Windows 10 22H2 (19045.3693), with the LongPathsEnabled registry entry set, running in Windows Terminal, using cargo built with this PR. Running cargo prints:

  • cmd prints The current directory is invalid.
  • git bash prints Error: Current working directory has a path longer than allowed for a Win32 working directory. Can't start native Windows application from here.
  • Powershell 7.4 prints nothing. There is a window that opens and closes instantly, too fast to see. Nothing else happens.
  • Powershell 5.1 prints cargo.exe failed to run: The directory name is invalid with a bunch more information.

Rust does not allow launching processes with paths longer than MAX_PATH (which is an issue for build scripts and such).

Ah, that does seem to work now if there is a manifest. However, for processes that don't have a manifest (like build scripts launching other processes), it fails. I get Command::status() returns NotFound.

@ChrisDenton
Copy link
Member Author

See my next comment. Using std::env::set_current_dir in a longpathaware application works. But launching a new process with a long current directory does not.

@ChrisDenton
Copy link
Member Author

ChrisDenton commented Dec 8, 2023

I've created an issue about home in case that's something people want to consider. I don't feel particularly strongly either way but I guess I have a general preference towards not using deprecated functions even if they stick around forever.


Ok, so I think that having long path awareness is on the whole better than not having it. It can help in a few cases and, from what I've seen, the C dependencies seem fine with it. I admittedly haven't fully audited it all so I can't be certain but given that long path awareness has been around since ~2016, it's not like we're blazing a trail here. Still, I can understand if Cargo wants to be more cautious. I'd be happy to update this PR to remove the longPathAware option and consider that question separately if that would be preferable.

The issue of setting the current directory is more concerning. Cargo uses set_current_dir in one place, when the -C option is used to override the current directory. Maybe cargo could warn when the current directory is set to a path greater than MAX_PATH?

@ehuss
Copy link
Contributor

ehuss commented Dec 8, 2023

I'll go ahead and approve. I think on balance, it probably should be safe, and does help with a few, although limited circumstances.

In the future we may want to consider moving this file (and possibly build.rs?) to a separate directory. I'm not sure how we will want to organize stuff like that, since I can really predict what we might need in the future. But if someone on the cargo team wants to do that sooner, that sounds fine to me, too.

Regarding set_current_dir, I'll follow up with filing some more issues.

@bors r+

@bors
Copy link
Contributor

bors commented Dec 8, 2023

📌 Commit 6f7f927 has been approved by ehuss

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Dec 8, 2023
@bors
Copy link
Contributor

bors commented Dec 8, 2023

⌛ Testing commit 6f7f927 with merge 232f913...

@bors
Copy link
Contributor

bors commented Dec 8, 2023

☀️ Test successful - checks-actions
Approved by: ehuss
Pushing 232f913 to master...

@bors bors merged commit 232f913 into rust-lang:master Dec 8, 2023
19 of 20 checks passed
@ChrisDenton ChrisDenton deleted the windows-manifest branch December 8, 2023 18:57
bors added a commit to rust-lang-ci/rust that referenced this pull request Dec 9, 2023
Update cargo

12 commits in 9787229614b27854cf73d57ffae430d7c1e6caa4..6feabf94773286928b7d73d0d313c0c5562b66af
2023-12-06 02:29:23 +0000 to 2023-12-08 22:38:37 +0000
- fix: explicitly remap current dir by using `.` (rust-lang/cargo#13114)
- Don't rely on mtime to test changes (rust-lang/cargo#13143)
- refactor: Pull PackageIdSpec into schema (rust-lang/cargo#13128)
- fix: Print rustc messages colored on wincon (rust-lang/cargo#13140)
- Add a windows manifest file (rust-lang/cargo#13131)
- Avoid writing CACHEDIR.TAG if it already exists (rust-lang/cargo#13132)
- re-enable flaky tests thanks to update to `gix-config`. (rust-lang/cargo#11821) (rust-lang/cargo#13130)
- fix bash completion in directory with spaces (rust-lang/cargo#13126)
- test: re-ignore git auth tests for gitoxide (rust-lang/cargo#13129)
- fix(toml): Disallow inheriting of dependency public status (rust-lang/cargo#13125)
- re-enable previously disabled tests with Windows-specific fix (rust-lang/cargo#13117)
- refactor: Clarify PackageId constructor names (rust-lang/cargo#13123)

r? ghost
bors added a commit to rust-lang-ci/rust that referenced this pull request Dec 9, 2023
Update cargo

13 commits in 9787229614b27854cf73d57ffae430d7c1e6caa4..66ad359b408ccdf867cceb30cc2dff1ed4afd82d
2023-12-06 02:29:23 +0000 to 2023-12-09 12:30:01 +0000
- chore: downgrade to openssl v1.1.1 (rust-lang/cargo#13144)
- fix: explicitly remap current dir by using `.` (rust-lang/cargo#13114)
- Don't rely on mtime to test changes (rust-lang/cargo#13143)
- refactor: Pull PackageIdSpec into schema (rust-lang/cargo#13128)
- fix: Print rustc messages colored on wincon (rust-lang/cargo#13140)
- Add a windows manifest file (rust-lang/cargo#13131)
- Avoid writing CACHEDIR.TAG if it already exists (rust-lang/cargo#13132)
- re-enable flaky tests thanks to update to `gix-config`. (rust-lang/cargo#11821) (rust-lang/cargo#13130)
- fix bash completion in directory with spaces (rust-lang/cargo#13126)
- test: re-ignore git auth tests for gitoxide (rust-lang/cargo#13129)
- fix(toml): Disallow inheriting of dependency public status (rust-lang/cargo#13125)
- re-enable previously disabled tests with Windows-specific fix (rust-lang/cargo#13117)
- refactor: Clarify PackageId constructor names (rust-lang/cargo#13123)
bors added a commit to rust-lang-ci/rust that referenced this pull request Dec 12, 2023
Update cargo

20 commits in 9787229614b27854cf73d57ffae430d7c1e6caa4..1aa9df1a5be205cce621f0bc0ea6062a5e22a98c
2023-12-06 02:29:23 +0000 to 2023-12-12 14:52:31 +0000
- crates-io: Add support for other 2xx HTTP status codes (rust-lang/cargo#13158)
- Remove the deleted feature test_2018_feature from the test (rust-lang/cargo#13156)
- refactor(schema): Remove reliance on cargo types (rust-lang/cargo#13154)
- fix(toml)!: Disallow `[lints]` in virtual workspaces (rust-lang/cargo#13155)
- Limit exported-private-dependencies lints to libraries (rust-lang/cargo#13135)
- chore: update to gix-index@0.27.1 (rust-lang/cargo#13148)
- Update curl-sys to bring in curl 8.5.0 (rust-lang/cargo#13147)
- chore: downgrade to openssl v1.1.1 (rust-lang/cargo#13144)
- fix: explicitly remap current dir by using `.` (rust-lang/cargo#13114)
- Don't rely on mtime to test changes (rust-lang/cargo#13143)
- refactor: Pull PackageIdSpec into schema (rust-lang/cargo#13128)
- fix: Print rustc messages colored on wincon (rust-lang/cargo#13140)
- Add a windows manifest file (rust-lang/cargo#13131)
- Avoid writing CACHEDIR.TAG if it already exists (rust-lang/cargo#13132)
- re-enable flaky tests thanks to update to `gix-config`. (rust-lang/cargo#11821) (rust-lang/cargo#13130)
- fix bash completion in directory with spaces (rust-lang/cargo#13126)
- test: re-ignore git auth tests for gitoxide (rust-lang/cargo#13129)
- fix(toml): Disallow inheriting of dependency public status (rust-lang/cargo#13125)
- re-enable previously disabled tests with Windows-specific fix (rust-lang/cargo#13117)
- refactor: Clarify PackageId constructor names (rust-lang/cargo#13123)
manifest.to_str().unwrap()
);
// Turn linker warnings into errors.
println!("cargo:rustc-link-arg-bin=cargo=/WX");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warnings as errors outside of CI doesn't sound like a good idea. New warnings might get introduced in the future.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not familiar with Windows in terms of how often it gets new linker warnings. If it turns out too annoying we can always change.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The trouble here is that this might make versions of Cargo harder to build in the future. If a new version of MSVC gets released that throws a warning here, we'll need to patch all old versions of Cargo to even make them build. See https://embeddedartistry.com/blog/2017/05/22/werror-is-not-your-friend/ ("-Werror Introduces a Toolchain Version Dependency"). It's better to only enable /WX in CI, this way you can always change it, but don't leave users who want to compile Cargo with errors.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assumed "we" referring to someone needing to build olders Cargo on their own. Although we only support the latest stable Cargo, we should try our best to play nice with downstream packagers. Could you open an issue and the team can discuss there?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-infrastructure Area: infrastructure around the cargo repo, ci, releases, etc. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants