diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000..8144ba3056 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,3 @@ + diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index f79ec050ba..f57ca6d789 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -202,13 +202,13 @@ jobs: AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: us-west-1 - name: Configure AWS credentials - if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.mode == 'release' + if: github.event_name == 'push' && matrix.mode == 'release' && (github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/main') uses: aws-actions/configure-aws-credentials@v6 with: role-to-assume: arn:aws:iam::890664054962:role/ci--rust-lang--rustup aws-region: us-east-1 - name: Deploy build to rustup-builds bucket for release team - if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.mode == 'release' + if: github.event_name == 'push' && matrix.mode == 'release' && (github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/main') run: | aws --debug s3 cp --recursive dist s3://rustup-builds/${{ github.sha }}/dist env: @@ -401,13 +401,13 @@ jobs: AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: us-west-1 - name: Configure AWS credentials - if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.mode == 'release' + if: github.event_name == 'push' && matrix.mode == 'release' && (github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/main') uses: aws-actions/configure-aws-credentials@v6 with: role-to-assume: arn:aws:iam::890664054962:role/ci--rust-lang--rustup aws-region: us-east-1 - name: Deploy build to rustup-builds bucket for release team - if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.mode == 'release' + if: github.event_name == 'push' && matrix.mode == 'release' && (github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/main') run: | aws --debug s3 cp --recursive dist s3://rustup-builds/${{ github.sha }}/dist env: @@ -606,13 +606,13 @@ jobs: AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: us-west-1 - name: Configure AWS credentials - if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.mode == 'release' + if: github.event_name == 'push' && matrix.mode == 'release' && (github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/main') uses: aws-actions/configure-aws-credentials@v6 with: role-to-assume: arn:aws:iam::890664054962:role/ci--rust-lang--rustup aws-region: us-east-1 - name: Deploy build to rustup-builds bucket for release team - if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.mode == 'release' + if: github.event_name == 'push' && matrix.mode == 'release' && (github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/main') run: | aws --debug s3 cp --recursive dist s3://rustup-builds/${{ github.sha }}/dist env: @@ -776,13 +776,13 @@ jobs: AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: us-west-1 - name: Configure AWS credentials - if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.mode == 'release' + if: github.event_name == 'push' && matrix.mode == 'release' && (github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/main') uses: aws-actions/configure-aws-credentials@v6 with: role-to-assume: arn:aws:iam::890664054962:role/ci--rust-lang--rustup aws-region: us-east-1 - name: Deploy build to rustup-builds bucket for release team - if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.mode == 'release' + if: github.event_name == 'push' && matrix.mode == 'release' && (github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/main') run: | aws --debug s3 cp --recursive deploy/ s3://rustup-builds/${{ github.sha }} env: @@ -948,13 +948,13 @@ jobs: AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: us-west-1 - name: Configure AWS credentials - if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.mode == 'release' + if: github.event_name == 'push' && matrix.mode == 'release' && (github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/main') uses: aws-actions/configure-aws-credentials@v6 with: role-to-assume: arn:aws:iam::890664054962:role/ci--rust-lang--rustup aws-region: us-east-1 - name: Deploy build to rustup-builds bucket for release team - if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.mode == 'release' + if: github.event_name == 'push' && matrix.mode == 'release' && (github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/main') run: | aws --debug s3 cp --recursive deploy/ s3://rustup-builds/${{ github.sha }} env: @@ -1003,6 +1003,7 @@ jobs: - armv7-linux-androideabi # skip-pr skip-main - x86_64-linux-android # skip-pr skip-main - riscv64gc-unknown-linux-gnu # skip-pr skip-main + - riscv64gc-unknown-linux-musl # skip-pr skip-main - loongarch64-unknown-linux-gnu # skip-pr skip-main - loongarch64-unknown-linux-musl # skip-pr skip-main include: @@ -1146,13 +1147,13 @@ jobs: AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: us-west-1 - name: Configure AWS credentials - if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.mode == 'release' + if: github.event_name == 'push' && matrix.mode == 'release' && (github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/main') uses: aws-actions/configure-aws-credentials@v6 with: role-to-assume: arn:aws:iam::890664054962:role/ci--rust-lang--rustup aws-region: us-east-1 - name: Deploy build to rustup-builds bucket for release team - if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.mode == 'release' + if: github.event_name == 'push' && matrix.mode == 'release' && (github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/main') run: | aws --debug s3 cp --recursive deploy/ s3://rustup-builds/${{ github.sha }} env: @@ -1272,13 +1273,13 @@ jobs: AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: us-west-1 - name: Configure AWS credentials - if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.mode == 'release' + if: github.event_name == 'push' && matrix.mode == 'release' && (github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/main') uses: aws-actions/configure-aws-credentials@v6 with: role-to-assume: arn:aws:iam::890664054962:role/ci--rust-lang--rustup aws-region: us-east-1 - name: Deploy build to rustup-builds bucket for release team - if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.mode == 'release' + if: github.event_name == 'push' && matrix.mode == 'release' && (github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/main') run: | aws --debug s3 cp --recursive deploy/ s3://rustup-builds/${{ github.sha }} env: @@ -1403,13 +1404,13 @@ jobs: AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: us-west-1 - name: Configure AWS credentials - if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.mode == 'release' + if: github.event_name == 'push' && matrix.mode == 'release' && (github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/main') uses: aws-actions/configure-aws-credentials@v6 with: role-to-assume: arn:aws:iam::890664054962:role/ci--rust-lang--rustup aws-region: us-east-1 - name: Deploy build to rustup-builds bucket for release team - if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.mode == 'release' + if: github.event_name == 'push' && matrix.mode == 'release' && (github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/main') run: | aws --debug s3 cp --recursive deploy/ s3://rustup-builds/${{ github.sha }} env: diff --git a/Cargo.lock b/Cargo.lock index c4ab9ec825..a6febfa09c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -76,9 +76,9 @@ dependencies = [ [[package]] name = "anstyle-svg" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db7ec5fa88fc0837393f6b110b0764aeb0a673952bc91300628641dd0fffbf85" +checksum = "ab68e0b71ea68eb6b399ffe1957f26330aeed91f5e578d442a1c2f3df69f6ec1" dependencies = [ "anstyle", "anstyle-lossy", @@ -100,9 +100,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.103" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] name = "as-slice" @@ -115,9 +115,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79b3f8a79cccc2898f31920fc69f304859b3bd567490f75ebf51ae1c792a9ac" +checksum = "3976abdc8fe7d1133d43d304afd42abdf5bc3e1319d263d223bde07b5efc4be8" dependencies = [ "compression-codecs", "compression-core", @@ -127,13 +127,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.89" +version = "0.1.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +checksum = "ae36dc4177970ef04fde5178d3e2429882def40e57a451f919c098f72baa6cec" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -150,9 +150,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "aws-lc-rs" -version = "1.17.1" +version = "1.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4342d8937fc7e5dd9b1c60292261c0670c882a2cd1719cfc11b1af41731e32ad" +checksum = "00bdb5da18dac48ca2cc7cd4a98e533e8635a58e2361d13a1a4ee3888e0d72f1" dependencies = [ "aws-lc-sys", "zeroize", @@ -160,9 +160,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.42.0" +version = "0.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d9ceb1da931507a12f4fccea479dccd00da1943e1b4ae72d8e502d707361444" +checksum = "43103168cc76fe62678a375e722fc9cb3a0146159ac5828bc4f0dfd755c2224c" dependencies = [ "cc", "cmake", @@ -194,9 +194,9 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitflags" -version = "2.13.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" [[package]] name = "block-buffer" @@ -209,9 +209,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.12.3" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cee35f73844aa3014bb606320a6c1f010249dbdf43342fe54b5a4f6a8ed4b79" +checksum = "1f7dc094d718f2e1c1559ad110e27eeaae14a5465d3d56dd6dbd793079fbd530" dependencies = [ "memchr", "regex-automata", @@ -232,15 +232,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" [[package]] name = "cc" -version = "1.2.66" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996" +checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9" dependencies = [ "find-msvc-tools", "jobserver", @@ -262,9 +262,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" [[package]] name = "chacha20" @@ -288,9 +288,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.1" +version = "4.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +checksum = "301b56658598e48f3648647ac6fc887be7e7108eddfa4e9b63fcf3ec58c0cadf" dependencies = [ "clap_builder", "clap_derive", @@ -308,9 +308,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.6.0" +version = "4.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +checksum = "94a65403d1a1bd28f7dc68eb8506e8874808ee5eecb59298de588e2e1407a078" dependencies = [ "anstream", "anstyle", @@ -321,23 +321,23 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.6.7" +version = "4.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db8b397918185f0161ff3d6fcaa9e4bfc09b8367caf6e1d4a2848e5477ed027b" +checksum = "b1f84a88507dbd05c695f2cb5e8558e747179134005e9893882dec964190ed89" dependencies = [ "clap", ] [[package]] name = "clap_derive" -version = "4.6.1" +version = "4.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" +checksum = "d012d2b9d65aca7f18f4d9878a045bc17899bba951561ba5ec3c2ba1eed9a061" dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -442,9 +442,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -452,18 +452,18 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.18" +version = "0.9.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" [[package]] name = "crypto-common" @@ -532,13 +532,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -562,9 +562,9 @@ dependencies = [ [[package]] name = "either" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" +checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" [[package]] name = "encode_unicode" @@ -574,22 +574,22 @@ checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" [[package]] name = "enum-map" -version = "2.7.3" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9" +checksum = "d4532960034c15ba2f55e9eee631040ecc473c32e6c4b200828c759840f13a06" dependencies = [ "enum-map-derive", ] [[package]] name = "enum-map-derive" -version = "0.17.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" +checksum = "4bcb37146b17400613e7a7eb6a585f9319d3edaea6fc718e37a1ecf7f27c6373" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -630,9 +630,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" [[package]] name = "filetime" @@ -713,9 +713,9 @@ checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "futures-channel" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae" dependencies = [ "futures-core", "futures-sink", @@ -723,15 +723,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" [[package]] name = "futures-executor" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +checksum = "6754879cc9f2c66f88c6e5c35344bb0bdb0708b0352b1201815667c7eabc7458" dependencies = [ "futures-core", "futures-task", @@ -740,38 +740,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" +checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a" [[package]] name = "futures-macro" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "futures-sink" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" +checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307" [[package]] name = "futures-task" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" [[package]] name = "futures-util" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" dependencies = [ "futures-core", "futures-io", @@ -836,7 +836,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn", + "syn 2.0.119", "time", ] @@ -907,15 +907,15 @@ dependencies = [ [[package]] name = "html-escape" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c1ff2d1cbf39efe5af0900ced8a069b5e61557a17544eb0c4a50239937389e" +checksum = "c9356095b4b41197bba32173600e1582792cda618f65d12f68e2e77d273413c5" [[package]] name = "http" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" dependencies = [ "bytes", "itoa", @@ -923,9 +923,9 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" dependencies = [ "bytes", "http", @@ -933,9 +933,9 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2" dependencies = [ "bytes", "futures-core", @@ -958,18 +958,18 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hybrid-array" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c" +checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b" dependencies = [ "typenum", ] [[package]] name = "hyper" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" +checksum = "d22053281f852e11534f5198498373cbb59295120a20771d90f7ed1897490a72" dependencies = [ "atomic-waker", "bytes", @@ -1182,9 +1182,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" +checksum = "6a756c3fac73139e83f14c2d742155dd2b78d3ee56597b419a0579b7bdd6dd78" [[package]] name = "is_terminal_polyfill" @@ -1228,7 +1228,7 @@ dependencies = [ "jni-sys", "log", "simd_cesu8", - "thiserror 2.0.18", + "thiserror 2.0.19", "walkdir", "windows-link", ] @@ -1243,7 +1243,7 @@ dependencies = [ "quote", "rustc_version", "simd_cesu8", - "syn", + "syn 2.0.119", ] [[package]] @@ -1262,16 +1262,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" dependencies = [ "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "jobserver" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.3", "libc", ] @@ -1294,9 +1294,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.186" +version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "libz-sys" @@ -1350,9 +1350,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.2" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "miniz_oxide" @@ -1366,9 +1366,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427" dependencies = [ "libc", "wasi", @@ -1498,7 +1498,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1515,9 +1515,9 @@ checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "openssl-src" -version = "300.5.4+3.5.4" +version = "300.6.1+3.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507b3792995dae9b0df8a1c1e3771e8418b7c2d9f0baeba32e6fe8b06c7cb72" +checksum = "46eb8fb9fb3b61ce1c0f8a026c4c1a0714d3a9e138e7fbde78753ce2babc3846" dependencies = [ "cc", ] @@ -1545,7 +1545,7 @@ dependencies = [ "futures-sink", "js-sys", "pin-project-lite", - "thiserror 2.0.18", + "thiserror 2.0.19", "tracing", ] @@ -1575,7 +1575,7 @@ dependencies = [ "opentelemetry_sdk", "prost", "reqwest", - "thiserror 2.0.18", + "thiserror 2.0.19", "tokio", "tonic", "tonic-types", @@ -1606,8 +1606,8 @@ dependencies = [ "opentelemetry", "percent-encoding", "portable-atomic", - "rand 0.9.4", - "thiserror 2.0.18", + "rand 0.9.5", + "thiserror 2.0.19", "tokio", "tokio-stream", ] @@ -1635,7 +1635,7 @@ checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1652,15 +1652,15 @@ checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" [[package]] name = "platforms" -version = "3.12.0" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9245c6e7c5a6bcdd7977fdf6d1e1c67f4cc2d0d58c041df0ea5940953033e6ca" +checksum = "acff5dfd42135d89cd7ad74a8c52813bbec91bf62c018ddd89390127767caab5" [[package]] name = "portable-atomic" -version = "1.13.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" +checksum = "3d20d5497ef88037a52ff98267d066e7f11fcc5e99bbfbd58a42336193aacec3" [[package]] name = "potential_utf" @@ -1688,9 +1688,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] @@ -1705,7 +1705,7 @@ dependencies = [ "bit-vec", "bitflags", "num-traits", - "rand 0.9.4", + "rand 0.9.5", "rand_chacha", "rand_xorshift", "regex-syntax", @@ -1734,7 +1734,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1765,9 +1765,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.46" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] @@ -1786,9 +1786,9 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" dependencies = [ "rand_chacha", "rand_core 0.9.5", @@ -1861,9 +1861,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.4" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" dependencies = [ "aho-corasick", "memchr", @@ -1873,9 +1873,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.14" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad" dependencies = [ "aho-corasick", "memchr", @@ -1951,7 +1951,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cab9bd343c737660e523ee69f788018f3db686d537d2fd0f99c9f747c1bda4f" dependencies = [ - "rand 0.9.4", + "rand 0.9.5", ] [[package]] @@ -2002,9 +2002,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.41" +version = "0.23.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f" +checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06" dependencies = [ "aws-lc-rs", "log", @@ -2029,9 +2029,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" dependencies = [ "zeroize", ] @@ -2083,7 +2083,6 @@ dependencies = [ "anstyle", "anyhow", "cc", - "cfg-if 1.0.4", "chrono", "clap", "clap-cargo", @@ -2108,7 +2107,6 @@ dependencies = [ "libz-sys", "opener", "openssl", - "openssl-src", "opentelemetry", "opentelemetry-otlp", "opentelemetry_sdk", @@ -2135,7 +2133,7 @@ dependencies = [ "strsim", "tar", "tempfile", - "thiserror 2.0.18", + "thiserror 2.0.19", "threadpool", "tokio", "tokio-retry", @@ -2159,9 +2157,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" [[package]] name = "rusty-fork" @@ -2230,9 +2228,9 @@ checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" dependencies = [ "serde_core", "serde_derive", @@ -2240,29 +2238,29 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] name = "serde_json" -version = "1.0.150" +version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" dependencies = [ "itoa", "memchr", @@ -2308,15 +2306,15 @@ checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "simd-adler32" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" [[package]] name = "simd_cesu8" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" dependencies = [ "rustc_version", "simdutf8", @@ -2375,9 +2373,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", "windows-sys 0.61.2", @@ -2403,9 +2401,20 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.118" +version = "2.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" dependencies = [ "proc-macro2", "quote", @@ -2429,7 +2438,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -2487,11 +2496,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.18" +version = "2.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9" dependencies = [ - "thiserror-impl 2.0.18", + "thiserror-impl 2.0.19", ] [[package]] @@ -2502,25 +2511,25 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "thiserror-impl" -version = "2.0.18" +version = "2.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] name = "thread_local" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070" dependencies = [ "cfg-if 1.0.4", ] @@ -2536,9 +2545,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.53" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" dependencies = [ "deranged", "num-conv", @@ -2556,9 +2565,9 @@ checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.31" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c431b87111666e491a90baa837f914fb45cd5dc3c268591b0220ff5057f2085f" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" dependencies = [ "num-conv", "time-core", @@ -2576,9 +2585,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.52.3" +version = "1.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" dependencies = [ "bytes", "libc", @@ -2591,13 +2600,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.7.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -2633,9 +2642,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +checksum = "a3d06f0b082ba57c26b79407372e57cf2a1e28124f78e9479fe80322cf53420b" dependencies = [ "futures-core", "pin-project-lite", @@ -2644,22 +2653,23 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" dependencies = [ "bytes", "futures-core", "futures-sink", + "libc", "pin-project-lite", "tokio", ] [[package]] name = "toml" -version = "1.1.2+spec-1.1.0" +version = "1.1.4+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +checksum = "3aace63f4bbcdfc2c965b059de67119c89c4017a70d633be6c104910f67056f5" dependencies = [ "indexmap", "serde_core", @@ -2681,18 +2691,18 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.1.2+spec-1.1.0" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" dependencies = [ "winnow", ] [[package]] name = "toml_writer" -version = "1.1.1+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" +checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" [[package]] name = "tonic" @@ -2815,7 +2825,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -3042,7 +3052,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.119", "wasm-bindgen-shared", ] @@ -3090,9 +3100,9 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d46a5a140e6f7afeccd8eae97eff335163939eac8b929834875168b29b3d267" +checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b" dependencies = [ "rustls-pki-types", ] @@ -3256,9 +3266,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" [[package]] name = "wit-bindgen" @@ -3310,28 +3320,28 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.52" +version = "0.8.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.52" +version = "0.8.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -3351,7 +3361,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", "synstructure", ] @@ -3391,20 +3401,20 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "zlib-rs" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5431d5661c32445236631278f27946e444ddafe4684cac70b185272d4f9c52d5" +checksum = "b142a20ec14a91d5bc708c1dc21b080c550113d8aa77afa29635673a65dd02c5" [[package]] name = "zmij" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" [[package]] name = "zstd" diff --git a/Cargo.toml b/Cargo.toml index 2110cb28d8..069ba4a801 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,7 +45,6 @@ anstream = "1" anstyle = "1.0.11" anyhow = "1.0.69" cc = "1" -cfg-if = "1.0" chrono = { version = "0.4", default-features = false, features = ["std"] } clap = { version = "4", features = ["derive", "wrap_help", "string"] } clap-cargo = "0.18.3" @@ -53,7 +52,7 @@ clap_complete = "4" console = "0.16" curl = { version = "0.4.44", optional = true } effective-limits = "0.5.5" -enum-map = "2.5.0" +enum-map = "3.0.0" env_proxy = { version = "0.4.1", optional = true } faster-hex = "0.10.0" flate2 = { version = "1.1.1", default-features = false, features = ["zlib-rs"] } @@ -61,6 +60,9 @@ fs_at = "0.2.1" futures-util = "0.3.31" git-testament = "0.2" home = "0.5.4" +http-body-util = "0.1.0" +hyper = { version = "1.0", default-features = false, features = ["server", "http1"] } +hyper-util = { version = "0.1.1", features = ["tokio"] } indicatif = "0.18" itertools = "0.15" libc = "0.2" @@ -68,13 +70,10 @@ opener = "0.8.0" # `openssl` is used by `curl` or `reqwest` backend although it isn't imported by rustup: this # allows controlling the vendoring status without exposing the presence of the download crate. openssl = { version = "0.10", optional = true } -# HACK: Temporarily pinned due to ppc64 ELFv1/v2 ABI issue in 300.5.5, to be -# removed when lands. -openssl-src = { version = "=300.5.4", optional = true } opentelemetry = { version = "0.32", optional = true } opentelemetry-otlp = { version = "0.32", features = ["grpc-tonic"], optional = true } opentelemetry_sdk = { version = "0.32", features = ["rt-tokio"], optional = true } -platforms = "3.4" +platforms = "4" pulldown-cmark = { version = "0.13", default-features = false } rand = "0.10" rayon = "1.10.0" @@ -145,17 +144,14 @@ version = "0.61" libz-sys = "=1.1.24" [dev-dependencies] -enum-map = "2.5.0" -http-body-util = "0.1.0" -hyper = { version = "1.0", default-features = false, features = ["server", "http1"] } -hyper-util = { version = "0.1.1", features = ["tokio"] } +enum-map = "3.0.0" proptest = "1.1.0" rustls-webpki = { version = "0.103.3" } tokio-rustls = "0.26.4" webpki-root-certs = "1" [build-dependencies] -platforms = "3.4" +platforms = "4" [lints.rust] rust_2018_idioms = { level = "deny", priority = -1 } diff --git a/ci/actions-templates/linux-builds-template.yaml b/ci/actions-templates/linux-builds-template.yaml index 214d666fab..cf6730e66a 100644 --- a/ci/actions-templates/linux-builds-template.yaml +++ b/ci/actions-templates/linux-builds-template.yaml @@ -44,6 +44,7 @@ jobs: # skip-main skip-pr skip-stable - armv7-linux-androideabi # skip-pr skip-main - x86_64-linux-android # skip-pr skip-main - riscv64gc-unknown-linux-gnu # skip-pr skip-main + - riscv64gc-unknown-linux-musl # skip-pr skip-main - loongarch64-unknown-linux-gnu # skip-pr skip-main - loongarch64-unknown-linux-musl # skip-pr skip-main include: @@ -187,13 +188,13 @@ jobs: # skip-main skip-pr skip-stable AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: us-west-1 - name: Configure AWS credentials - if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.mode == 'release' + if: github.event_name == 'push' && matrix.mode == 'release' && (github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/main') uses: aws-actions/configure-aws-credentials@v6 with: role-to-assume: arn:aws:iam::890664054962:role/ci--rust-lang--rustup aws-region: us-east-1 - name: Deploy build to rustup-builds bucket for release team - if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.mode == 'release' + if: github.event_name == 'push' && matrix.mode == 'release' && (github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/main') run: | aws --debug s3 cp --recursive deploy/ s3://rustup-builds/${{ github.sha }} env: diff --git a/ci/actions-templates/macos-builds-template.yaml b/ci/actions-templates/macos-builds-template.yaml index f7738b8ba0..f6411a929b 100644 --- a/ci/actions-templates/macos-builds-template.yaml +++ b/ci/actions-templates/macos-builds-template.yaml @@ -115,13 +115,13 @@ jobs: # skip-x86_64 skip-aarch64 AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: us-west-1 - name: Configure AWS credentials - if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.mode == 'release' + if: github.event_name == 'push' && matrix.mode == 'release' && (github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/main') uses: aws-actions/configure-aws-credentials@v6 with: role-to-assume: arn:aws:iam::890664054962:role/ci--rust-lang--rustup aws-region: us-east-1 - name: Deploy build to rustup-builds bucket for release team - if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.mode == 'release' + if: github.event_name == 'push' && matrix.mode == 'release' && (github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/main') run: | aws --debug s3 cp --recursive deploy/ s3://rustup-builds/${{ github.sha }} env: diff --git a/ci/actions-templates/windows-builds-template.yaml b/ci/actions-templates/windows-builds-template.yaml index 60eed06a08..938dd01e12 100644 --- a/ci/actions-templates/windows-builds-template.yaml +++ b/ci/actions-templates/windows-builds-template.yaml @@ -193,13 +193,13 @@ jobs: # skip-main skip-pr skip-stable AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: us-west-1 - name: Configure AWS credentials - if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.mode == 'release' + if: github.event_name == 'push' && matrix.mode == 'release' && (github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/main') uses: aws-actions/configure-aws-credentials@v6 with: role-to-assume: arn:aws:iam::890664054962:role/ci--rust-lang--rustup aws-region: us-east-1 - name: Deploy build to rustup-builds bucket for release team - if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.mode == 'release' + if: github.event_name == 'push' && matrix.mode == 'release' && (github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/main') run: | aws --debug s3 cp --recursive dist s3://rustup-builds/${{ github.sha }}/dist env: diff --git a/ci/cloudfront-invalidation.txt b/ci/cloudfront-invalidation.txt index 9b86ac321c..ae1892448d 100644 --- a/ci/cloudfront-invalidation.txt +++ b/ci/cloudfront-invalidation.txt @@ -57,6 +57,10 @@ rustup/dist/powerpc64le-unknown-linux-gnu/rustup-init rustup/dist/powerpc64le-unknown-linux-gnu/rustup-init.sha256 rustup/dist/powerpc64le-unknown-linux-musl/rustup-init rustup/dist/powerpc64le-unknown-linux-musl/rustup-init.sha256 +rustup/dist/riscv64gc-unknown-linux-gnu/rustup-init +rustup/dist/riscv64gc-unknown-linux-gnu/rustup-init.sha256 +rustup/dist/riscv64gc-unknown-linux-musl/rustup-init +rustup/dist/riscv64gc-unknown-linux-musl/rustup-init.sha256 rustup/dist/s390x-unknown-linux-gnu/rustup-init rustup/dist/s390x-unknown-linux-gnu/rustup-init.sha256 rustup/dist/sparcv9-sun-solaris/rustup-init.exe diff --git a/ci/docker/riscv64gc-unknown-linux-musl/Dockerfile b/ci/docker/riscv64gc-unknown-linux-musl/Dockerfile new file mode 100644 index 0000000000..3219088f87 --- /dev/null +++ b/ci/docker/riscv64gc-unknown-linux-musl/Dockerfile @@ -0,0 +1,8 @@ +FROM rust-riscv64gc-unknown-linux-musl + +# Building `aws-lc-rs` for Linux depends on `gcc-multilib`, `libclang` and `bindgen`. +# See: https://aws.github.io/aws-lc-rs/requirements/linux +RUN apt-get update && apt-get install -qy gcc-multilib libclang-dev + +ENV CC_riscv64gc_unknown_linux_musl=riscv64-unknown-linux-musl-gcc \ + CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_MUSL_LINKER=riscv64-unknown-linux-musl-gcc diff --git a/ci/fetch-rust-docker.bash b/ci/fetch-rust-docker.bash index eb2f69f8e4..3a1c022309 100644 --- a/ci/fetch-rust-docker.bash +++ b/ci/fetch-rust-docker.bash @@ -45,7 +45,8 @@ case "$TARGET" in x86_64-unknown-illumos) image=dist-x86_64-illumos ;; x86_64-unknown-linux-gnu) image=dist-x86_64-linux ;; x86_64-unknown-netbsd) image=dist-x86_64-netbsd ;; - riscv64gc-unknown-linux-gnu) image=dist-riscv64-linux ;; + riscv64gc-unknown-linux-gnu) image=dist-riscv64-linux-gnu ;; + riscv64gc-unknown-linux-musl) image=dist-riscv64-linux-musl ;; loongarch64-unknown-linux-gnu) image=dist-loongarch64-linux ;; loongarch64-unknown-linux-musl) image=dist-loongarch64-musl ;; *) exit ;; diff --git a/doc/dev-guide/src/SUMMARY.md b/doc/dev-guide/src/SUMMARY.md index 15e9b2b4b1..6e92db0665 100644 --- a/doc/dev-guide/src/SUMMARY.md +++ b/doc/dev-guide/src/SUMMARY.md @@ -6,4 +6,4 @@ - [Version numbers](version-numbers.md) - [Release process](release-process.md) - [Tips and tricks](tips-and-tricks.md) -- [Tracing](tracing.md) \ No newline at end of file +- [Tracing](tracing.md) diff --git a/doc/dev-guide/src/index.md b/doc/dev-guide/src/index.md index 0541d4e8f5..3bd36e4faa 100644 --- a/doc/dev-guide/src/index.md +++ b/doc/dev-guide/src/index.md @@ -48,3 +48,32 @@ Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as in the README, without any additional terms or conditions. + +## AI policy + +Using AI (LLMs) as tools for coding is welcome. A high bar is held for all contributions to this +project. Moreover, the project maintainers remain responsible for any code that is published as +part of a release. Contributors are expected to be responsible for any code they publish. + +AI should not be used to generate comments when communicating with maintainers. Comments are +expected to be written by humans. Comments that are believed to be written by AI may be hidden +without notice. + +If you are opening an issue, you should be able to describe the problem in your own words. + +If you are opening a pull request, you are expected to be able to explain the proposed changes in +your own words. This includes the pull request body and responses to questions. Make sure you have +reviewed the PR yourself before submitting it for review to the maintainers. Do not copy responses +from the AI when replying to questions from maintainers. As an exception, issues marked as `E-easy` +are meant for new contributors as a learning opportunity; the use of an LLM when submitting PR for +such issues is disallowed except without explicit permission from the team. Failure to comply may +result in the PR being closed directly without further notice. + +If you wish to include context from an interaction with AI in your comments, it must be in a +quote block (using `>`) and disclosed as such. It must be accompanied by human commentary +explaining the relevance and implications of the context. Do not share long snippets. + +AI is useful when communicating as a non-native English speaker. If you are using AI to edit your +comments for this purpose, please take the time to ensure it reflects your own voice and ideas. +When using AI for translation, we recommend writing in your native language and including the AI +translation in a quote block. diff --git a/doc/user-guide/src/concepts/components.md b/doc/user-guide/src/concepts/components.md index 2d9b74beeb..9dce622a9c 100644 --- a/doc/user-guide/src/concepts/components.md +++ b/doc/user-guide/src/concepts/components.md @@ -62,6 +62,8 @@ toolchains. The following is an overview of the different components: * `rustc-dev` --- This component contains the compiler as a library. Most users will not need this; it is only needed for development *of* tools that link to the compiler, such as making modifications to [Clippy]. +* `enzyme` --- This component enables the `std::autodiff` module in nightly Rust. + See [autodiff][enzyme] for more detail. ### Previous components @@ -101,3 +103,4 @@ details. [rustup components history]: https://rust-lang.github.io/rustup-components-history/ [profiles]: profiles.md [nightly availability]: channels.md#nightly-availability +[enzyme]: https://rustc-dev-guide.rust-lang.org/autodiff/installation.html diff --git a/doc/user-guide/src/installation/other.md b/doc/user-guide/src/installation/other.md index 22ac13df73..fc9b918378 100644 --- a/doc/user-guide/src/installation/other.md +++ b/doc/user-guide/src/installation/other.md @@ -163,6 +163,10 @@ You can manually download `rustup-init` for a given target from - [sha256 file](https://static.rust-lang.org/rustup/dist/powerpc64le-unknown-linux-gnu/rustup-init.sha256) - [powerpc64le-unknown-linux-musl](https://static.rust-lang.org/rustup/dist/powerpc64le-unknown-linux-musl/rustup-init) - [sha256 file](https://static.rust-lang.org/rustup/dist/powerpc64le-unknown-linux-musl/rustup-init.sha256) +- [riscv64gc-unknown-linux-gnu](https://static.rust-lang.org/rustup/dist/riscv64gc-unknown-linux-gnu/rustup-init) + - [sha256 file](https://static.rust-lang.org/rustup/dist/riscv64gc-unknown-linux-gnu/rustup-init.sha256) +- [riscv64gc-unknown-linux-musl](https://static.rust-lang.org/rustup/dist/riscv64gc-unknown-linux-musl/rustup-init) + - [sha256 file](https://static.rust-lang.org/rustup/dist/riscv64gc-unknown-linux-musl/rustup-init.sha256) - [s390x-unknown-linux-gnu](https://static.rust-lang.org/rustup/dist/s390x-unknown-linux-gnu/rustup-init) - [sha256 file](https://static.rust-lang.org/rustup/dist/s390x-unknown-linux-gnu/rustup-init.sha256) - [sparcv9-sun-solaris](https://static.rust-lang.org/rustup/dist/sparcv9-sun-solaris/rustup-init) diff --git a/doc/user-guide/src/installation/windows.md b/doc/user-guide/src/installation/windows.md index 59ac3bde82..b9d5d52c40 100644 --- a/doc/user-guide/src/installation/windows.md +++ b/doc/user-guide/src/installation/windows.md @@ -25,13 +25,7 @@ target that tuple by default. You can change this behavior with `rustup set default-host` or during installation. -For example, to explicitly select the 32-bit MSVC host: - -```console -$ rustup set default-host i686-pc-windows-msvc -``` - -Or to choose the 64 bit GNU toolchain: +For example, to choose the 64 bit GNU toolchain: ```console $ rustup set default-host x86_64-pc-windows-gnu diff --git a/src/bin/rustup-init.rs b/src/bin/rustup-init.rs index e844ddc19e..a0d408abc8 100644 --- a/src/bin/rustup-init.rs +++ b/src/bin/rustup-init.rs @@ -16,7 +16,6 @@ use std::process::ExitCode; use anyhow::{Context, Result, anyhow}; -use cfg_if::cfg_if; // Public macros require availability of the internal symbols use rs_tracing::{ close_trace_file, close_trace_file_internal, open_trace_file, trace_to_file_internal, @@ -105,12 +104,9 @@ async fn run_rustup_inner( Some(n) if n.starts_with("rustup-gc-") => { // This is the final uninstallation stage on windows where // rustup deletes its own exe - cfg_if! { - if #[cfg(windows)] { - self_update::complete_windows_uninstall(process) - } else { - unreachable!("Attempted to use Windows-specific code on a non-Windows platform. Aborting.") - } + cfg_select! { + windows => self_update::complete_windows_uninstall(process), + _ => unreachable!("Attempted to use Windows-specific code on a non-Windows platform. Aborting."), } } Some(n) => { diff --git a/src/cli.rs b/src/cli.rs index 88a7586ce2..31807fa261 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -2,6 +2,7 @@ #[macro_use] pub mod log; pub mod common; +mod docs; pub mod errors; mod help; mod job; diff --git a/src/cli/common.rs b/src/cli/common.rs index 8ec17a13ec..09e9abe31e 100644 --- a/src/cli/common.rs +++ b/src/cli/common.rs @@ -173,7 +173,7 @@ fn show_channel_updates( // this is a bit strange: we don't supply the version we // presumably had (for Installed and Unchanged), so we query it // again. Perhaps we can do better. - let version = match Toolchain::new(cfg, name.into()) { + let version = match Toolchain::new(cfg, name.clone().into()) { Ok(t) => t.rustc_version(), Err(_) => String::from("(toolchain not installed)"), }; diff --git a/src/cli/docs.rs b/src/cli/docs.rs new file mode 100644 index 0000000000..c343da84bd --- /dev/null +++ b/src/cli/docs.rs @@ -0,0 +1,381 @@ +//! `rustup doc` and `rustup man`: opening toolchain documentation and man pages. +//! +//! `rustup doc --serve` serves the documentation over a local HTTP server +//! instead of opening it directly as a `file://` URL. Some browsers (e.g. +//! Snap/Flatpak builds of Firefox or Brave) run in a sandbox that can't +//! access `file://` URLs under `~/.rustup`; serving the same static files +//! over `http://127.0.0.1` sidesteps that restriction entirely. +//! +//! The server binds to `127.0.0.1` only (never exposed on the network) and +//! rejects any request path that would resolve outside the served directory. + +use std::{ + borrow::Cow, + convert::Infallible, + io::Write as _, + path::{Component, Path, PathBuf}, + sync::Arc, +}; + +use anyhow::{Context, Result, anyhow}; +use clap::Args; +use http_body_util::Full; +use hyper::{ + Request, Response, StatusCode, + body::{Bytes, Incoming}, + header::{CONTENT_LENGTH, CONTENT_TYPE}, + server::conn::http1, + service::service_fn, +}; +use hyper_util::rt::TokioIo; +use tokio::net::TcpListener; +use tracing::info; + +use super::topical_doc; +use crate::{ + config::{ActiveSource, Cfg}, + dist::{PartialToolchainDesc, manifest::ComponentStatus}, + process::Process, + toolchain::DistributableToolchain, + utils::{self, ExitCode}, +}; + +macro_rules! docs_data { + ( + $( + $( #[$meta:meta] )* + ($ident:ident, $help:expr, $path:expr $(,)?) + ),+ $(,)? + ) => { + #[derive(Debug, Args)] + pub(crate) struct DocPage { + $( + #[doc = $help] + #[arg(long, group = "page")] + $( #[$meta] )* + $ident: bool, + )+ + } + + impl DocPage { + fn path_str(&self) -> Option<&'static str> { + $( if self.$ident { return Some($path); } )+ + None + } + } + }; +} + +docs_data![ + // flags can be used to open specific documents, e.g. `rustup doc --nomicon` + // tuple elements: document name used as flag, help message, document index path + ( + alloc, + "The Rust core allocation and collections library", + "alloc/index.html" + ), + ( + book, + "The Rust Programming Language book", + "book/index.html" + ), + (cargo, "The Cargo Book", "cargo/index.html"), + (clippy, "The Clippy Documentation", "clippy/index.html"), + (core, "The Rust Core Library", "core/index.html"), + ( + edition_guide, + "The Rust Edition Guide", + "edition-guide/index.html" + ), + ( + embedded_book, + "The Embedded Rust Book", + "embedded-book/index.html" + ), + ( + error_codes, + "The Rust Error Codes Index", + "error_codes/index.html" + ), + ( + nomicon, + "The Dark Arts of Advanced and Unsafe Rust Programming", + "nomicon/index.html" + ), + #[arg(long = "proc_macro")] + ( + proc_macro, + "A support library for macro authors when defining new macros", + "proc_macro/index.html" + ), + (reference, "The Rust Reference", "reference/index.html"), + (releases, "Rust Release Notes", "releases.html"), + ( + rust_by_example, + "A collection of runnable examples that illustrate various Rust concepts and standard libraries", + "rust-by-example/index.html" + ), + ( + rustc, + "The compiler for the Rust programming language", + "rustc/index.html" + ), + ( + rustc_docs, + "The API documentation for the Rust compiler and other toolchain components", + "rustc-docs/index.html" + ), + ( + rustdoc, + "Documentation generator for Rust projects", + "rustdoc/index.html" + ), + (std, "Standard library API documentation", "std/index.html"), + ( + style_guide, + "The Rust Style Guide", + "style-guide/index.html" + ), + ( + test, + "Support code for rustc's built in unit-test and micro-benchmarking framework", + "test/index.html" + ), + ( + unstable_book, + "The Unstable Book", + "unstable-book/index.html" + ), +]; + +impl DocPage { + fn path(&self) -> Option<&'static Path> { + self.path_str().map(Path::new) + } + + fn name(&self) -> Option<&'static str> { + Some(self.path_str()?.rsplit_once('/')?.0) + } + + fn resolve<'t>(&self, root: &Path, topic: &'t str) -> Option<(PathBuf, Option<&'t str>)> { + // Use `.parent()` to chop off the default top-level `index.html`. + let mut base = root.join(Path::new(self.path()?).parent()?); + base.extend(topic.split("::")); + let base_index_html = base.join("index.html"); + + if base_index_html.is_file() { + return Some((base_index_html, None)); + } + + let base_html = base.with_extension("html"); + if base_html.is_file() { + return Some((base_html, None)); + } + + let parent_html = base.parent()?.with_extension("html"); + if parent_html.is_file() { + return Some((parent_html, topic.rsplit_once("::").map(|(_, s)| s))); + } + + None + } +} + +pub(crate) async fn doc( + cfg: &Cfg<'_>, + path_only: bool, + serve: bool, + toolchain: Option, + mut topic: Option<&str>, + doc_page: &DocPage, +) -> Result { + let toolchain = toolchain.map(|desc| (desc, ActiveSource::CommandLine)); + let toolchain = cfg.toolchain_from_partial(toolchain).await?.0; + + if let Ok(distributable) = DistributableToolchain::try_from(&toolchain) + && let [_] = distributable + .components()? + .into_iter() + .filter(|cstatus| cstatus.component.short_name() == "rust-docs" && !cstatus.installed) + .take(1) + .collect::>() + .as_slice() + { + info!( + "`rust-docs` not installed in toolchain `{}`\nhelp: run `rustup component add --toolchain {} rust-docs` to install it", + distributable.desc(), + distributable.desc() + ); + return Err(anyhow!( + "unable to view documentation which is not installed" + )); + }; + + let (doc_path, fragment) = match (topic, doc_page.name()) { + (Some(topic), Some(name)) => { + let (doc_path, fragment) = doc_page + .resolve(&toolchain.doc_path("")?, topic) + .context(format!("no document for {name} on {topic}"))?; + (Cow::Owned(doc_path), fragment) + } + (Some(topic), None) => { + let doc_path = topical_doc::local_path(&toolchain.doc_path("").unwrap(), topic)?; + (Cow::Owned(doc_path), None) + } + (None, name) => { + topic = name; + let doc_path = doc_page.path().unwrap_or_else(|| Path::new("index.html")); + (Cow::Borrowed(doc_path), None) + } + }; + + if path_only { + let doc_path = toolchain.doc_path(&doc_path)?; + writeln!(cfg.process.stdout().lock(), "{}", doc_path.display())?; + return Ok(ExitCode::SUCCESS); + } + + if serve { + let root = toolchain.doc_path("")?; + serve_and_open(root, &doc_path, fragment, cfg.process).await?; + return Ok(ExitCode::SUCCESS); + } + + if let Some(name) = topic { + writeln!( + cfg.process.stderr().lock(), + "Opening docs named `{name}` in your browser" + )?; + } else { + writeln!(cfg.process.stderr().lock(), "Opening docs in your browser")?; + } + toolchain.open_docs(&doc_path, fragment)?; + Ok(ExitCode::SUCCESS) +} + +#[cfg(not(windows))] +pub(crate) async fn man( + cfg: &Cfg<'_>, + command: &str, + toolchain: Option, +) -> Result { + let toolchain = toolchain.map(|desc| (desc, ActiveSource::CommandLine)); + let toolchain = cfg.toolchain_from_partial(toolchain).await?.0; + let path = toolchain.man_path(); + utils::assert_is_directory(&path)?; + + let mut manpaths = std::ffi::OsString::from(path); + manpaths.push(":"); // prepend to the default MANPATH list + if let Some(path) = cfg.process.var_os("MANPATH") { + manpaths.push(path); + } + std::process::Command::new("man") + .env("MANPATH", manpaths) + .arg(command) + .status() + .expect("failed to open man page"); + Ok(ExitCode::SUCCESS) +} + +/// Blocks forever, accepting and serving connections until the process is +/// killed by Ctrl-C. +async fn serve_and_open( + root: PathBuf, + initial_path: &Path, + fragment: Option<&str>, + process: &Process, +) -> Result<()> { + let listener = TcpListener::bind(("127.0.0.1", 0)) + .await + .context("failed to bind local documentation server")?; + let addr = listener + .local_addr() + .context("failed to read local address")?; + let root = Arc::::from(root); + + let mut url = format!( + "http://{addr}/{}", + initial_path.to_string_lossy().replace('\\', "/") + ); + if let Some(fragment) = fragment { + url.push('#'); + url.push_str(fragment); + } + + writeln!( + process.stderr().lock(), + "Serving docs at {url} (press Ctrl-C to stop)" + )?; + utils::open_browser(&url)?; + + loop { + let (stream, _) = match listener.accept().await { + Ok(accepted) => accepted, + Err(err) => { + tracing::warn!("doc server: failed to accept connection: {err}"); + continue; + } + }; + let io = TokioIo::new(stream); + let root = root.clone(); + let svc = service_fn(move |req| serve(req, root.clone())); + + tokio::spawn(async move { + if let Err(err) = http1::Builder::new().serve_connection(io, svc).await { + tracing::warn!("doc server: connection error: {err}"); + } + }); + } +} + +async fn serve( + req: Request, + root: Arc, +) -> Result>, Infallible> { + let request_path = req.uri().path().trim_start_matches('/'); + let mut path = root.to_path_buf(); + for segment in Path::new(request_path).components() { + match segment { + Component::Normal(part) => path.push(part), + _ => return Ok(not_found()), + } + } + if !path.starts_with(&root) { + return Ok(not_found()); + } + if path.is_dir() { + path.push("index.html"); + } + + let Ok(contents) = tokio::fs::read(&path).await else { + return Ok(not_found()); + }; + + let response = Response::builder() + .status(StatusCode::OK) + .header( + CONTENT_TYPE, + match path.extension().and_then(|ext| ext.to_str()) { + Some("html") => "text/html; charset=utf-8", + Some("css") => "text/css", + Some("js") => "text/javascript", + Some("svg") => "image/svg+xml", + Some("png") => "image/png", + Some("jpg" | "jpeg") => "image/jpeg", + Some("woff2") => "font/woff2", + Some("txt") => "text/plain; charset=utf-8", + _ => "application/octet-stream", + }, + ) + .header(CONTENT_LENGTH, contents.len()) + .body(Full::new(Bytes::from(contents))) + .expect("building a static response can't fail"); + Ok(response) +} + +fn not_found() -> Response> { + Response::builder() + .status(StatusCode::NOT_FOUND) + .body(Full::new(Bytes::from_static(b"404 not found"))) + .expect("building a static response can't fail") +} diff --git a/src/cli/help.rs b/src/cli/help.rs index 015e847077..52d1f0ca42 100644 --- a/src/cli/help.rs +++ b/src/cli/help.rs @@ -232,7 +232,11 @@ pub(crate) fn doc_help() -> String { the default browser. By default, it opens the documentation index. Use the various - flags to open specific pieces of documentation." + flags to open specific pieces of documentation. + + If your browser is sandboxed (e.g. installed via Snap or Flatpak) it + may be unable to open `file://` URLs under `~/.rustup`. Pass `--serve` + to serve the documentation over a local HTTP server instead." ) } diff --git a/src/cli/proxy_mode.rs b/src/cli/proxy_mode.rs index 0e480e920f..4b7ac55d49 100644 --- a/src/cli/proxy_mode.rs +++ b/src/cli/proxy_mode.rs @@ -1,4 +1,4 @@ -use std::{path::PathBuf, process::ExitStatus}; +use std::{path::PathBuf, process::ExitStatus, str::FromStr}; use anyhow::Result; @@ -23,7 +23,7 @@ pub async fn main(arg0: &str, current_dir: PathBuf, process: &Process) -> Result .as_ref() .map(|arg| arg.to_string_lossy()) .filter(|arg| arg.starts_with('+')) - .map(|name| ResolvableLocalToolchainName::try_from(&name.as_ref()[1..])) + .map(|name| ResolvableLocalToolchainName::from_str(&name[1..])) .transpose()?; // Build command args now while we know whether or not to skip arg 1. diff --git a/src/cli/rustup_mode.rs b/src/cli/rustup_mode.rs index 343eccd504..8dbe6dad5d 100644 --- a/src/cli/rustup_mode.rs +++ b/src/cli/rustup_mode.rs @@ -12,7 +12,7 @@ use std::{ use anstream::ColorChoice; use anstyle::Style; -use anyhow::{Context, Error, Result, anyhow}; +use anyhow::{Error, Result, anyhow}; use clap::{ Args, CommandFactory, Parser, Subcommand, ValueEnum, builder::{PossibleValue, ValueHint}, @@ -29,6 +29,7 @@ use tracing_subscriber::{EnvFilter, Registry, reload::Handle}; use crate::{ cli::{ common::{self, PackageUpdate, update_console_filter}, + docs, errors::CliError, help::{ check_help, completions_help, default_help, doc_help, install_help, @@ -39,14 +40,13 @@ use crate::{ update_help, }, self_update::{self, SelfUpdateMode, check_rustup_update}, - topical_doc, }, command, component_for_bin, config::{ActiveSource, Cfg}, dist::{ AutoInstallMode, DistOptions, PartialToolchainDesc, Profile, TargetTuple, download::DownloadCfg, - manifest::{Component, ComponentStatus, ManifestWithHash}, + manifest::{Component, ManifestWithHash}, }, errors::{DEFAULT_STABLE_HINT, RustupError}, install::{InstallMethod, UpdateStatus}, @@ -111,7 +111,7 @@ struct Rustup { fn plus_toolchain_value_parser(s: &str) -> clap::error::Result { use clap::{Error, error::ErrorKind}; if let Some(stripped) = s.strip_prefix('+') { - ResolvableToolchainName::try_from(stripped) + ResolvableToolchainName::from_str(stripped) .map_err(|e| Error::raw(ErrorKind::InvalidValue, e)) } else { Err(Error::raw( @@ -252,6 +252,11 @@ enum RustupSubcmd { #[arg(long)] path: bool, + /// Serve the documentation over a local HTTP server instead of + /// opening it directly as a `file://` URL + #[arg(long, conflicts_with = "path")] + serve: bool, + #[arg(long, help = official_toolchain_arg_help())] toolchain: Option, @@ -259,7 +264,7 @@ enum RustupSubcmd { topic: Option, #[command(flatten)] - page: DocPage, + page: docs::DocPage, }, /// View the man page for a given command @@ -468,6 +473,10 @@ struct UpdateOpts { /// Set the installed toolchain as the override for the current directory #[arg(long)] r#override: bool, + + /// Set the installed toolchain as the default toolchain + #[arg(long)] + default: bool, } #[derive(Debug, Default, Args)] @@ -801,18 +810,19 @@ pub async fn main( RustupSubcmd::Which { command, toolchain } => which(cfg, &command, toolchain).await, RustupSubcmd::Doc { path, + serve, toolchain, topic, page, - } => doc(cfg, path, toolchain, topic.as_deref(), &page).await, + } => docs::doc(cfg, path, serve, toolchain, topic.as_deref(), &page).await, #[cfg(not(windows))] - RustupSubcmd::Man { command, toolchain } => man(cfg, &command, toolchain).await, + RustupSubcmd::Man { command, toolchain } => docs::man(cfg, &command, toolchain).await, RustupSubcmd::Self_ { subcmd } => match subcmd { SelfSubcmd::Update => self_update::update(cfg).await, SelfSubcmd::Uninstall { no_prompt, no_modify_path, - } => self_update::uninstall(no_prompt, no_modify_path, process), + } => self_update::uninstall(no_prompt, no_modify_path, cfg), SelfSubcmd::UpgradeData => cfg.upgrade_data().map(|_| ExitCode::SUCCESS), }, RustupSubcmd::Set { subcmd } => match subcmd { @@ -854,7 +864,7 @@ async fn default_( cfg.set_default(None)?; } MaybeResolvableToolchainName::Some(ResolvableToolchainName::Custom(toolchain_name)) => { - Toolchain::new(cfg, (&toolchain_name).into())?; + Toolchain::new(cfg, toolchain_name.clone().into())?; cfg.set_default(Some(&toolchain_name.into()))?; } MaybeResolvableToolchainName::Some(ResolvableToolchainName::Official(toolchain)) => { @@ -1040,7 +1050,6 @@ async fn update( let self_update_mode = SelfUpdateMode::from_cfg(cfg)?; let should_self_update = !opts.no_self_update; let force_non_host = opts.force_non_host; - let set_override = opts.r#override; cfg.profile_override = opts.profile; let cfg = &cfg; @@ -1099,11 +1108,13 @@ async fn update( Ok(status.clone()), )?; - if set_override { + if opts.r#override { cfg.make_override(&cfg.current_dir, &desc.clone().into())?; } - if cfg.get_default()?.is_none() && matches!(status, UpdateStatus::Installed) { + if opts.default + || (cfg.get_default()?.is_none() && matches!(status, UpdateStatus::Installed)) + { cfg.set_default(Some(&desc.into()))?; } } @@ -1717,236 +1728,6 @@ fn override_remove(cfg: &Cfg<'_>, path: Option<&Path>, nonexistent: bool) -> Res Ok(ExitCode::SUCCESS) } -macro_rules! docs_data { - ( - $( - $( #[$meta:meta] )* - ($ident:ident, $help:expr, $path:expr $(,)?) - ),+ $(,)? - ) => { - #[derive(Debug, Args)] - struct DocPage { - $( - #[doc = $help] - #[arg(long, group = "page")] - $( #[$meta] )* - $ident: bool, - )+ - } - - impl DocPage { - fn path_str(&self) -> Option<&'static str> { - $( if self.$ident { return Some($path); } )+ - None - } - } - }; -} - -docs_data![ - // flags can be used to open specific documents, e.g. `rustup doc --nomicon` - // tuple elements: document name used as flag, help message, document index path - ( - alloc, - "The Rust core allocation and collections library", - "alloc/index.html" - ), - ( - book, - "The Rust Programming Language book", - "book/index.html" - ), - (cargo, "The Cargo Book", "cargo/index.html"), - (clippy, "The Clippy Documentation", "clippy/index.html"), - (core, "The Rust Core Library", "core/index.html"), - ( - edition_guide, - "The Rust Edition Guide", - "edition-guide/index.html" - ), - ( - embedded_book, - "The Embedded Rust Book", - "embedded-book/index.html" - ), - ( - error_codes, - "The Rust Error Codes Index", - "error_codes/index.html" - ), - ( - nomicon, - "The Dark Arts of Advanced and Unsafe Rust Programming", - "nomicon/index.html" - ), - #[arg(long = "proc_macro")] - ( - proc_macro, - "A support library for macro authors when defining new macros", - "proc_macro/index.html" - ), - (reference, "The Rust Reference", "reference/index.html"), - (releases, "Rust Release Notes", "releases.html"), - ( - rust_by_example, - "A collection of runnable examples that illustrate various Rust concepts and standard libraries", - "rust-by-example/index.html" - ), - ( - rustc, - "The compiler for the Rust programming language", - "rustc/index.html" - ), - ( - rustc_docs, - "The API documentation for the Rust compiler and other toolchain components", - "rustc-docs/index.html" - ), - ( - rustdoc, - "Documentation generator for Rust projects", - "rustdoc/index.html" - ), - (std, "Standard library API documentation", "std/index.html"), - ( - style_guide, - "The Rust Style Guide", - "style-guide/index.html" - ), - ( - test, - "Support code for rustc's built in unit-test and micro-benchmarking framework", - "test/index.html" - ), - ( - unstable_book, - "The Unstable Book", - "unstable-book/index.html" - ), -]; - -impl DocPage { - fn path(&self) -> Option<&'static Path> { - self.path_str().map(Path::new) - } - - fn name(&self) -> Option<&'static str> { - Some(self.path_str()?.rsplit_once('/')?.0) - } - - fn resolve<'t>(&self, root: &Path, topic: &'t str) -> Option<(PathBuf, Option<&'t str>)> { - // Use `.parent()` to chop off the default top-level `index.html`. - let mut base = root.join(Path::new(self.path()?).parent()?); - base.extend(topic.split("::")); - let base_index_html = base.join("index.html"); - - if base_index_html.is_file() { - return Some((base_index_html, None)); - } - - let base_html = base.with_extension("html"); - if base_html.is_file() { - return Some((base_html, None)); - } - - let parent_html = base.parent()?.with_extension("html"); - if parent_html.is_file() { - return Some((parent_html, topic.rsplit_once("::").map(|(_, s)| s))); - } - - None - } -} - -async fn doc( - cfg: &Cfg<'_>, - path_only: bool, - toolchain: Option, - mut topic: Option<&str>, - doc_page: &DocPage, -) -> Result { - let toolchain = toolchain.map(|desc| (desc, ActiveSource::CommandLine)); - let toolchain = cfg.toolchain_from_partial(toolchain).await?.0; - - if let Ok(distributable) = DistributableToolchain::try_from(&toolchain) - && let [_] = distributable - .components()? - .into_iter() - .filter(|cstatus| cstatus.component.short_name() == "rust-docs" && !cstatus.installed) - .take(1) - .collect::>() - .as_slice() - { - info!( - "`rust-docs` not installed in toolchain `{}`\nhelp: run `rustup component add --toolchain {} rust-docs` to install it", - distributable.desc(), - distributable.desc() - ); - return Err(anyhow!( - "unable to view documentation which is not installed" - )); - }; - - let (doc_path, fragment) = match (topic, doc_page.name()) { - (Some(topic), Some(name)) => { - let (doc_path, fragment) = doc_page - .resolve(&toolchain.doc_path("")?, topic) - .context(format!("no document for {name} on {topic}"))?; - (Cow::Owned(doc_path), fragment) - } - (Some(topic), None) => { - let doc_path = topical_doc::local_path(&toolchain.doc_path("").unwrap(), topic)?; - (Cow::Owned(doc_path), None) - } - (None, name) => { - topic = name; - let doc_path = doc_page.path().unwrap_or_else(|| Path::new("index.html")); - (Cow::Borrowed(doc_path), None) - } - }; - - if path_only { - let doc_path = toolchain.doc_path(&doc_path)?; - writeln!(cfg.process.stdout().lock(), "{}", doc_path.display())?; - return Ok(ExitCode::SUCCESS); - } - - if let Some(name) = topic { - writeln!( - cfg.process.stderr().lock(), - "Opening docs named `{name}` in your browser" - )?; - } else { - writeln!(cfg.process.stderr().lock(), "Opening docs in your browser")?; - } - toolchain.open_docs(&doc_path, fragment)?; - Ok(ExitCode::SUCCESS) -} - -#[cfg(not(windows))] -async fn man( - cfg: &Cfg<'_>, - command: &str, - toolchain: Option, -) -> Result { - let toolchain = toolchain.map(|desc| (desc, ActiveSource::CommandLine)); - let toolchain = cfg.toolchain_from_partial(toolchain).await?.0; - let path = toolchain.man_path(); - utils::assert_is_directory(&path)?; - - let mut manpaths = std::ffi::OsString::from(path); - manpaths.push(":"); // prepend to the default MANPATH list - if let Some(path) = cfg.process.var_os("MANPATH") { - manpaths.push(path); - } - std::process::Command::new("man") - .env("MANPATH", manpaths) - .arg(command) - .status() - .expect("failed to open man page"); - Ok(ExitCode::SUCCESS) -} - fn set_auto_self_update(cfg: &Cfg<'_>, auto_self_update_mode: SelfUpdateMode) -> Result { if cfg!(feature = "no-self-update") { let mut args = cfg.process.args_os(); @@ -2041,7 +1822,7 @@ async fn display_version(current_dir: PathBuf, process: &Process) -> Result<()> cfg.toolchain_override = cfg .process .args() - .find_map(|arg| arg.strip_prefix('+').map(ResolvableToolchainName::try_from)) + .find_map(|arg| arg.strip_prefix('+').map(ResolvableToolchainName::from_str)) .transpose()?; match cfg.maybe_ensure_active_toolchain(None).await { diff --git a/src/cli/self_update.rs b/src/cli/self_update.rs index cfe7ef0ed5..0d4a4efd9f 100644 --- a/src/cli/self_update.rs +++ b/src/cli/self_update.rs @@ -30,21 +30,21 @@ //! Deleting the running binary during uninstall is tricky //! and racy on Windows. -use std::borrow::Cow; -use std::env::{self, consts::EXE_SUFFIX}; #[cfg(not(windows))] use std::io; -use std::io::Write; -use std::path::{Component, MAIN_SEPARATOR, Path, PathBuf}; -use std::process::Command; -use std::str::FromStr; -use std::{fmt, fs}; +use std::{ + borrow::Cow, + env::{self, consts::EXE_SUFFIX}, + fmt, fs, + io::Write, + path::{Component, MAIN_SEPARATOR, Path, PathBuf}, + process::Command, + str::FromStr, +}; use anstyle::Style; use anyhow::{Context, Result, anyhow}; -use cfg_if::cfg_if; -use clap::ValueEnum; -use clap::builder::PossibleValue; +use clap::{ValueEnum, builder::PossibleValue}; use clap_cargo::style::{GOOD, WARN}; use itertools::Itertools; use same_file::Handle; @@ -74,6 +74,9 @@ use crate::{ utils::{self, ExitCode}, }; +#[macro_use] +mod msg; + #[cfg(unix)] mod shell; @@ -106,7 +109,182 @@ pub(crate) struct InstallOpts<'a> { } impl InstallOpts<'_> { - fn install(self, cfg: &mut Cfg<'_>) -> Result> { + /// Installs rustup to the system according to the current options. + /// + /// The installation process is composed of the following steps: + /// - Copying the running binary to the binary directory. + /// - Hard-linking the various Rust tools to that copied binary. + /// - Adding the binary directory to the `$PATH` unless `no_modify_path` is set. + pub(crate) async fn install(mut self, no_prompt: bool, cfg: &mut Cfg<'_>) -> Result { + #[cfg_attr(not(unix), allow(unused_mut))] + let mut exit_code = ExitCode::SUCCESS; + + self.validate(cfg.process).map_err(|e| { + anyhow!( + "Pre-checks for host and toolchain failed: {e}\n\ + If you are unsure of suitable values, the 'stable' toolchain is the default.\n\ + Valid host tuples look something like: {}", + TargetTuple::from_host_or_build(cfg.process) + ) + })?; + + if cfg + .process + .var_os("RUSTUP_INIT_SKIP_EXISTENCE_CHECKS") + .is_none_or(|s| s != "yes") + { + check_existence_of_rustc_or_cargo_in_path(no_prompt, cfg.process)?; + check_existence_of_settings_file(cfg)?; + } + + #[cfg(unix)] + { + exit_code &= unix::do_anti_sudo_check(no_prompt, cfg.process)?; + } + + let mut term = cfg.process.stdout(); + + #[cfg(windows)] + windows::maybe_install_msvc(&mut term, no_prompt, &self, &*cfg).await?; + + if !no_prompt { + let msg = pre_install_msg(self.no_modify_path, cfg.process)?; + + md(&mut term, msg); + let mut customized_install = false; + loop { + md(&mut term, current_install_opts(&self, cfg.process)); + match common::confirm_advanced(customized_install, cfg.process)? { + Confirm::No => { + info!("aborting installation"); + return Ok(ExitCode::SUCCESS); + } + Confirm::Yes => break, + Confirm::Advanced => { + customized_install = true; + self.customize(cfg.process)?; + } + } + } + } + + let no_modify_path = self.no_modify_path; + if let Err(e) = self.install_rust(cfg).await { + report_error(&e, cfg.process); + + // On windows, where installation happens in a console + // that may have opened just for this purpose, give + // the user an opportunity to see the error before the + // window closes. + #[cfg(windows)] + if !no_prompt { + windows::ensure_prompt(cfg.process)?; + } + + return Ok(ExitCode::FAILURE); + } + + let cargo_home = canonical_cargo_home(cfg.process)?; + #[cfg(windows)] + let cargo_home = cargo_home.replace('\\', r"\\"); + #[cfg(windows)] + let msg = if no_modify_path { + format!( + post_install_msg_win_no_modify_path!(), + cargo_home = cargo_home + ) + } else { + format!(post_install_msg_win!(), cargo_home = cargo_home) + }; + #[cfg(not(windows))] + let source_env_lines = shell::build_source_env_lines(cfg.process); + #[cfg(not(windows))] + let msg = if no_modify_path { + format!( + post_install_msg_unix_no_modify_path!(), + cargo_home = cargo_home, + source_env_lines = source_env_lines, + ) + } else { + format!( + post_install_msg_unix!(), + cargo_home = cargo_home, + source_env_lines = source_env_lines, + ) + }; + md(&mut term, msg); + + #[cfg(unix)] + warn_if_default_linker_missing(cfg.process); + + #[cfg(windows)] + if !no_prompt { + // On windows, where installation happens in a console + // that may have opened just for this purpose, require + // the user to press a key to continue. + windows::ensure_prompt(cfg.process)?; + } + + Ok(exit_code) + } + + /// Installs the rustup binary and proxies, and installs a toolchain if specified. + async fn install_rust(self, cfg: &mut Cfg<'_>) -> Result<()> { + install_bins(cfg.process)?; + + #[cfg(unix)] + unix::do_write_env_files(cfg.process)?; + + if !self.no_modify_path { + do_add_to_path(cfg.process)?; + } + + // If RUSTUP_HOME is not set, make sure it exists + if cfg.process.var_os("RUSTUP_HOME").is_none() { + let home = cfg + .process + .home_dir() + .map(|p| p.join(".rustup")) + .ok_or_else(|| anyhow::anyhow!("could not find home dir to put .rustup in"))?; + + fs::create_dir_all(home).context("unable to create ~/.rustup")?; + } + + let (components, targets) = (self.components, self.targets); + let toolchain = self.select_toolchain(cfg)?; + if let Some(desc) = toolchain { + let options = + DistOptions::new(components, targets, &desc, cfg.get_profile()?, true, cfg)?; + let status = if Toolchain::exists(cfg, &desc.clone().into())? { + warn!("Updating existing toolchain, profile choice will be ignored"); + // If we have a partial install we might not be able to read content here. We could: + // - fail and folk have to delete the partially present toolchain to recover + // - silently ignore it (and provide inconsistent metadata for reporting the install/update change) + // - delete the partial install and start over + // For now, we error. + let toolchain = DistributableToolchain::new(cfg, desc.clone())?; + InstallMethod::Dist(options.for_update(&toolchain, false)) + .install(None) + .await? + } else { + DistributableToolchain::install(options).await?.status + }; + + check_proxy_sanity(cfg.process, components, &desc)?; + + cfg.set_default(Some(&desc.clone().into()))?; + writeln!(cfg.process.stdout().lock())?; + common::show_channel_update(cfg, PackageUpdate::Toolchain(desc), Ok(status))?; + } + Ok(()) + } + + /// Selects the toolchain to install based on the user's intent. + /// + /// This function first initializes the default profile and default host tuple in the + /// configuration, then returns the toolchain that should be installed, or `None` if none is + /// specified by the user. + fn select_toolchain(self, cfg: &mut Cfg<'_>) -> Result> { let Self { default_host_tuple, default_toolchain, @@ -204,14 +382,16 @@ impl InstallOpts<'_> { process, )?); - self.default_toolchain = Some(MaybeOfficialToolchainName::try_from(common::question_str( - "Default toolchain? (stable/beta/nightly/none)", - &match &self.default_toolchain { - Some(name) => name.to_string(), - None => "stable".to_owned(), - }, - process, - )?)?); + self.default_toolchain = Some(MaybeOfficialToolchainName::from_str( + &common::question_str( + "Default toolchain? (stable/beta/nightly/none)", + &match &self.default_toolchain { + Some(name) => name.to_string(), + None => "stable".to_owned(), + }, + process, + )?, + )?); self.profile = ::from_str(&common::question_str( &format!( @@ -238,7 +418,7 @@ impl InstallOpts<'_> { .unwrap_or_else(|| TargetTuple::from_host_or_build(process)); let partial_channel = match &self.default_toolchain { None | Some(MaybeOfficialToolchainName::None) => { - ResolvableToolchainName::try_from("stable")? + ResolvableToolchainName::from_str("stable")? } Some(MaybeOfficialToolchainName::Some(s)) => s.into(), }; @@ -369,157 +549,6 @@ impl fmt::Display for SelfUpdateMode { } } -// The big installation messages. These are macros because the first -// argument of format! needs to be a literal. - -macro_rules! pre_install_msg_template { - ($platform_msg:literal) => { - concat!( - r" -# Welcome to Rust! - -This will download and install the official compiler for the Rust -programming language, and its package manager, Cargo. - -Rustup metadata and toolchains will be installed into the Rustup -home directory, located at: - - {rustup_home} - -This can be modified with the RUSTUP_HOME environment variable. - -The Cargo home directory is located at: - - {cargo_home} - -This can be modified with the CARGO_HOME environment variable. - -The `cargo`, `rustc`, `rustup` and other commands will be added to -Cargo's bin directory, located at: - - {cargo_home_bin} - -", - $platform_msg, - r#" - -You can uninstall at any time with `rustup self uninstall` and -these changes will be reverted. -"# - ) - }; -} - -#[cfg(not(windows))] -macro_rules! pre_install_msg_unix { - () => { - pre_install_msg_template!( - "This path will then be added to your `PATH` environment variable by -modifying the profile file{plural} located at: - -{rcfiles}" - ) - }; -} - -#[cfg(windows)] -macro_rules! pre_install_msg_win { - () => { - pre_install_msg_template!( - r#"This path will then be added to your `PATH` environment variable by -modifying the `PATH` registry key at `HKEY_CURRENT_USER\Environment`."# - ) - }; -} - -macro_rules! pre_install_msg_no_modify_path { - () => { - pre_install_msg_template!( - "This path needs to be in your `PATH` environment variable, -but will not be added automatically." - ) - }; -} - -#[cfg(not(windows))] -macro_rules! post_install_msg_unix { - () => { - r"# Rust is installed now. Great! - -To get started you may need to restart your current shell. -This would reload your `PATH` environment variable to include -Cargo's bin directory ({cargo_home}/bin). - -To configure your current shell, you need to source the -corresponding `env` file under {cargo_home}. - -Consider running the right command for your shell (note the leading DOT): -{source_env_lines}" - }; -} - -#[cfg(windows)] -macro_rules! post_install_msg_win { - () => { - r"# Rust is installed now. Great! - - -To get started you may need to restart your current shell. -This would reload its `PATH` environment variable to include -Cargo's bin directory ({cargo_home}\\bin). -" - }; -} - -#[cfg(not(windows))] -macro_rules! post_install_msg_unix_no_modify_path { - () => { - r"# Rust is installed now. Great! - -To get started you need Cargo's bin directory ({cargo_home}/bin) in your `PATH` -environment variable. This has not been done automatically. - -To configure your current shell, you need to source -the corresponding `env` file under {cargo_home}. - -Consider running the right command for your shell (note the leading DOT): -{source_env_lines}" - }; -} - -#[cfg(windows)] -macro_rules! post_install_msg_win_no_modify_path { - () => { - r"# Rust is installed now. Great! - -To get started you need Cargo's bin directory ({cargo_home}\\bin) in your `PATH` -environment variable. This has not been done automatically. -" - }; -} - -macro_rules! pre_uninstall_msg { - () => { - r"# Thanks for hacking in Rust! - -This will uninstall all Rust toolchains and data, and remove -`{cargo_home}/bin` from your `PATH` environment variable. - -" - }; -} - -macro_rules! pre_uninstall_msg_no_modify_path { - () => { - r"# Thanks for hacking in Rust! - -This will uninstall all Rust toolchains and data. -Your `PATH` environment variable will not be touched. - -" - }; -} - static DEFAULT_UPDATE_ROOT: &str = "https://static.rust-lang.org/rustup"; fn update_root(process: &Process) -> String { @@ -539,140 +568,15 @@ fn canonical_cargo_home(process: &Process) -> Result> { .unwrap_or_else(|| PathBuf::from(".")) .join(".cargo"); Ok(if default_cargo_home == path { - cfg_if! { - if #[cfg(windows)] { - r"%USERPROFILE%\.cargo".into() - } else { - "$HOME/.cargo".into() - } + cfg_select! { + windows => r"%USERPROFILE%\.cargo".into(), + _ => "$HOME/.cargo".into(), } } else { path.to_string_lossy().into_owned().into() }) } -/// Installing is a simple matter of copying the running binary to -/// `CARGO_HOME`/bin, hard-linking the various Rust tools to it, -/// and adding `CARGO_HOME`/bin to PATH. -pub(crate) async fn install( - no_prompt: bool, - mut opts: InstallOpts<'_>, - cfg: &mut Cfg<'_>, -) -> Result { - #[cfg_attr(not(unix), allow(unused_mut))] - let mut exit_code = ExitCode::SUCCESS; - - opts.validate(cfg.process).map_err(|e| { - anyhow!( - "Pre-checks for host and toolchain failed: {e}\n\ - If you are unsure of suitable values, the 'stable' toolchain is the default.\n\ - Valid host tuples look something like: {}", - TargetTuple::from_host_or_build(cfg.process) - ) - })?; - - if cfg - .process - .var_os("RUSTUP_INIT_SKIP_EXISTENCE_CHECKS") - .is_none_or(|s| s != "yes") - { - check_existence_of_rustc_or_cargo_in_path(no_prompt, cfg.process)?; - check_existence_of_settings_file(cfg)?; - } - - #[cfg(unix)] - { - exit_code &= unix::do_anti_sudo_check(no_prompt, cfg.process)?; - } - - let mut term = cfg.process.stdout(); - - #[cfg(windows)] - windows::maybe_install_msvc(&mut term, no_prompt, &opts, &*cfg).await?; - - if !no_prompt { - let msg = pre_install_msg(opts.no_modify_path, cfg.process)?; - - md(&mut term, msg); - let mut customized_install = false; - loop { - md(&mut term, current_install_opts(&opts, cfg.process)); - match common::confirm_advanced(customized_install, cfg.process)? { - Confirm::No => { - info!("aborting installation"); - return Ok(ExitCode::SUCCESS); - } - Confirm::Yes => { - break; - } - Confirm::Advanced => { - customized_install = true; - opts.customize(cfg.process)?; - } - } - } - } - - let no_modify_path = opts.no_modify_path; - if let Err(e) = maybe_install_rust(opts, cfg).await { - report_error(&e, cfg.process); - - // On windows, where installation happens in a console - // that may have opened just for this purpose, give - // the user an opportunity to see the error before the - // window closes. - #[cfg(windows)] - if !no_prompt { - windows::ensure_prompt(cfg.process)?; - } - - return Ok(ExitCode::FAILURE); - } - - let cargo_home = canonical_cargo_home(cfg.process)?; - #[cfg(windows)] - let cargo_home = cargo_home.replace('\\', r"\\"); - #[cfg(windows)] - let msg = if no_modify_path { - format!( - post_install_msg_win_no_modify_path!(), - cargo_home = cargo_home - ) - } else { - format!(post_install_msg_win!(), cargo_home = cargo_home) - }; - #[cfg(not(windows))] - let source_env_lines = shell::build_source_env_lines(cfg.process); - #[cfg(not(windows))] - let msg = if no_modify_path { - format!( - post_install_msg_unix_no_modify_path!(), - cargo_home = cargo_home, - source_env_lines = source_env_lines, - ) - } else { - format!( - post_install_msg_unix!(), - cargo_home = cargo_home, - source_env_lines = source_env_lines, - ) - }; - md(&mut term, msg); - - #[cfg(unix)] - warn_if_default_linker_missing(cfg.process); - - #[cfg(windows)] - if !no_prompt { - // On windows, where installation happens in a console - // that may have opened just for this purpose, require - // the user to press a key to continue. - windows::ensure_prompt(cfg.process)?; - } - - Ok(exit_code) -} - fn rustc_or_cargo_exists_in_path(process: &Process) -> Result<()> { // Ignore rustc and cargo if present in $HOME/.cargo/bin or a few other directories #[allow(clippy::ptr_arg)] @@ -998,66 +902,22 @@ fn check_proxy_sanity(process: &Process, components: &[&str], desc: &ToolchainDe Ok(()) } -async fn maybe_install_rust(opts: InstallOpts<'_>, cfg: &mut Cfg<'_>) -> Result<()> { - install_bins(cfg.process)?; - - #[cfg(unix)] - unix::do_write_env_files(cfg.process)?; - - if !opts.no_modify_path { - do_add_to_path(cfg.process)?; - } - - // If RUSTUP_HOME is not set, make sure it exists - if cfg.process.var_os("RUSTUP_HOME").is_none() { - let home = cfg - .process - .home_dir() - .map(|p| p.join(".rustup")) - .ok_or_else(|| anyhow::anyhow!("could not find home dir to put .rustup in"))?; - - fs::create_dir_all(home).context("unable to create ~/.rustup")?; - } - - let (components, targets) = (opts.components, opts.targets); - let toolchain = opts.install(cfg)?; - if let Some(desc) = &toolchain { - let options = DistOptions::new(components, targets, desc, cfg.get_profile()?, true, cfg)?; - let status = if Toolchain::exists(cfg, &desc.into())? { - warn!("Updating existing toolchain, profile choice will be ignored"); - // If we have a partial install we might not be able to read content here. We could: - // - fail and folk have to delete the partially present toolchain to recover - // - silently ignore it (and provide inconsistent metadata for reporting the install/update change) - // - delete the partial install and start over - // For now, we error. - let toolchain = DistributableToolchain::new(cfg, desc.clone())?; - InstallMethod::Dist(options.for_update(&toolchain, false)) - .install(None) - .await? - } else { - DistributableToolchain::install(options).await?.status - }; - - check_proxy_sanity(cfg.process, components, desc)?; - - cfg.set_default(Some(&desc.clone().into()))?; - writeln!(cfg.process.stdout().lock())?; - common::show_channel_update(cfg, PackageUpdate::Toolchain(desc.clone()), Ok(status))?; - } - Ok(()) -} - -pub(crate) fn uninstall( - no_prompt: bool, - no_modify_path: bool, - process: &Process, -) -> Result { +/// Uninstall process: +/// 1. Remove all installed toolchains. +/// 2. Remove rustup home. +/// 3. Remove all entries in `$CARGO_HOME` except `bin`. +/// 4. Remove rustup tool links and binary. +/// 5. Try to remove $CARGO_HOME/bin directory if it's empty. +/// 6. Upon successfully removing $CARGO_HOME/bin, clean up $PATH. +/// 7. Try to remove $CARGO_HOME directory if it's empty. +pub(crate) fn uninstall(no_prompt: bool, no_modify_path: bool, cfg: &Cfg<'_>) -> Result { if cfg!(feature = "no-self-update") { error!("self-uninstall is disabled for this build of rustup"); error!("you should probably use your system package manager to uninstall rustup"); return Ok(ExitCode::FAILURE); } + let process = cfg.process; let cargo_home = process.cargo_home()?; if !cargo_home.join(format!("bin/rustup{EXE_SUFFIX}")).exists() { @@ -1081,6 +941,11 @@ pub(crate) fn uninstall( } } + info!("removing toolchains"); + for toolchain in cfg.list_toolchains()? { + Toolchain::ensure_removed(cfg, toolchain.into())?; + } + info!("removing rustup home"); // Delete RUSTUP_HOME @@ -1481,7 +1346,7 @@ mod tests { .unwrap() .resolve(&cfg.default_host_tuple().unwrap()) .unwrap(), - opts.install(&mut cfg) + opts.select_toolchain(&mut cfg) .unwrap() // result .unwrap() // option ); diff --git a/src/cli/self_update/msg.rs b/src/cli/self_update/msg.rs new file mode 100644 index 0000000000..fecaafc695 --- /dev/null +++ b/src/cli/self_update/msg.rs @@ -0,0 +1,150 @@ +//! The big installation messages. These are macros because the first +//! argument of format! needs to be a literal. + +macro_rules! pre_install_msg_template { + ($platform_msg:literal) => { + concat!( + r" +# Welcome to Rust! + +This will download and install the official compiler for the Rust +programming language, and its package manager, Cargo. + +Rustup metadata and toolchains will be installed into the Rustup +home directory, located at: + + {rustup_home} + +This can be modified with the RUSTUP_HOME environment variable. + +The Cargo home directory is located at: + + {cargo_home} + +This can be modified with the CARGO_HOME environment variable. + +The `cargo`, `rustc`, `rustup` and other commands will be added to +Cargo's bin directory, located at: + + {cargo_home_bin} + +", + $platform_msg, + r#" + +You can uninstall at any time with `rustup self uninstall` and +these changes will be reverted. +"# + ) + }; +} + +#[cfg(not(windows))] +macro_rules! pre_install_msg_unix { + () => { + pre_install_msg_template!( + "This path will then be added to your `PATH` environment variable by +modifying the profile file{plural} located at: + +{rcfiles}" + ) + }; +} + +#[cfg(windows)] +macro_rules! pre_install_msg_win { + () => { + pre_install_msg_template!( + r#"This path will then be added to your `PATH` environment variable by +modifying the `PATH` registry key at `HKEY_CURRENT_USER\Environment`."# + ) + }; +} + +macro_rules! pre_install_msg_no_modify_path { + () => { + pre_install_msg_template!( + "This path needs to be in your `PATH` environment variable, +but will not be added automatically." + ) + }; +} + +#[cfg(not(windows))] +macro_rules! post_install_msg_unix { + () => { + r"# Rust is installed now. Great! + +To get started you may need to restart your current shell. +This would reload your `PATH` environment variable to include +Cargo's bin directory ({cargo_home}/bin). + +To configure your current shell, you need to source the +corresponding `env` file under {cargo_home}. + +Consider running the right command for your shell (note the leading DOT): +{source_env_lines}" + }; +} + +#[cfg(windows)] +macro_rules! post_install_msg_win { + () => { + r"# Rust is installed now. Great! + + +To get started you may need to restart your current shell. +This would reload its `PATH` environment variable to include +Cargo's bin directory ({cargo_home}\\bin). +" + }; +} + +#[cfg(not(windows))] +macro_rules! post_install_msg_unix_no_modify_path { + () => { + r"# Rust is installed now. Great! + +To get started you need Cargo's bin directory ({cargo_home}/bin) in your `PATH` +environment variable. This has not been done automatically. + +To configure your current shell, you need to source +the corresponding `env` file under {cargo_home}. + +Consider running the right command for your shell (note the leading DOT): +{source_env_lines}" + }; +} + +#[cfg(windows)] +macro_rules! post_install_msg_win_no_modify_path { + () => { + r"# Rust is installed now. Great! + +To get started you need Cargo's bin directory ({cargo_home}\\bin) in your `PATH` +environment variable. This has not been done automatically. +" + }; +} + +macro_rules! pre_uninstall_msg { + () => { + r"# Thanks for hacking in Rust! + +This will uninstall all Rust toolchains and data, and remove +`{cargo_home}/bin` from your `PATH` environment variable. + +" + }; +} + +macro_rules! pre_uninstall_msg_no_modify_path { + () => { + r"# Thanks for hacking in Rust! + +This will uninstall all Rust toolchains and data. +Your `PATH` environment variable will not be touched. + +" + }; +} diff --git a/src/cli/self_update/unix.rs b/src/cli/self_update/unix.rs index e4e236e2bc..b063f2c462 100644 --- a/src/cli/self_update/unix.rs +++ b/src/cli/self_update/unix.rs @@ -61,10 +61,7 @@ pub(crate) fn do_remove_from_path(process: &Process) -> Result<()> { let file = utils::read_file("rcfile", rc)?; let file_bytes = file.into_bytes(); // FIXME: This is whitespace sensitive where it should not be. - if let Some(idx) = file_bytes - .windows(source_bytes.len()) - .position(|w| w == source_bytes.as_slice()) - { + if let Some(idx) = find_exact_line(&file_bytes, &source_bytes) { // Here we rewrite the file without the offending line. let mut new_bytes = file_bytes[..idx].to_vec(); new_bytes.extend(&file_bytes[idx + source_bytes.len()..]); @@ -153,10 +150,7 @@ fn remove_legacy_source_command(source_cmd: String, process: &Process) -> Result let file = utils::read_file("rcfile", &rc)?; let file_bytes = file.into_bytes(); // FIXME: This is whitespace sensitive where it should not be. - if let Some(idx) = file_bytes - .windows(cmd_bytes.len()) - .position(|w| w == cmd_bytes.as_slice()) - { + if let Some(idx) = find_exact_line(&file_bytes, &cmd_bytes) { // Here we rewrite the file without the offending line. let mut new_bytes = file_bytes[..idx].to_vec(); new_bytes.extend(&file_bytes[idx + cmd_bytes.len()..]); @@ -167,6 +161,16 @@ fn remove_legacy_source_command(source_cmd: String, process: &Process) -> Result Ok(()) } +fn find_exact_line(file: &[u8], line: &[u8]) -> Option { + // The trailing newline enforces the end boundary; check the start boundary here. + assert!(line.ends_with(b"\n")); + file.windows(line.len()) + .enumerate() + .find_map(|(idx, candidate)| { + (candidate == line && (idx == 0 || file[idx - 1] == b'\n')).then_some(idx) + }) +} + fn remove_legacy_paths(process: &Process) -> Result<()> { // Before the work to support more kinds of shells, which was released in // version 1.23.0 of Rustup, we always inserted this line instead, which is diff --git a/src/cli/setup_mode.rs b/src/cli/setup_mode.rs index e0bc08df60..6f5ef1877c 100644 --- a/src/cli/setup_mode.rs +++ b/src/cli/setup_mode.rs @@ -131,5 +131,5 @@ pub async fn main( }; let mut cfg = Cfg::from_env(current_dir, quiet, true, process)?; - self_update::install(no_prompt, opts, &mut cfg).await + opts.install(no_prompt, &mut cfg).await } diff --git a/src/config.rs b/src/config.rs index 30c3b73e6b..e131bd5af6 100644 --- a/src/config.rs +++ b/src/config.rs @@ -197,7 +197,7 @@ impl OverrideCfg { fn from_file(cfg: &Cfg<'_>, file: OverrideFile) -> Result { let toolchain_name = match (file.toolchain.channel, file.toolchain.path) { (Some(name), None) => { - ResolvableToolchainName::try_from(name)?.resolve(&cfg.default_host_tuple()?)? + ResolvableToolchainName::from_str(&name)?.resolve(&cfg.default_host_tuple()?)? } (None, Some(path)) => { if file.toolchain.targets.is_some() @@ -253,7 +253,7 @@ impl OverrideCfg { match self { Self::PathBased(path_based_name) => path_based_name.into(), Self::Custom(custom_name) => custom_name.into(), - Self::Official { toolchain, .. } => (&toolchain).into(), + Self::Official { toolchain, .. } => toolchain.into(), } } } @@ -353,8 +353,8 @@ impl<'a> Cfg<'a> { // Figure out default_host_tuple before Config is populated let default_host = settings_file.with(|s| Ok(default_host_tuple(s, process)))?; // Environment override - let env_override = match process.var_opt("RUSTUP_TOOLCHAIN")? { - Some(tc) => Some(ResolvableLocalToolchainName::try_from(tc)?.resolve(&default_host)?), + let env_override = match &process.var_opt("RUSTUP_TOOLCHAIN")? { + Some(tc) => Some(ResolvableLocalToolchainName::from_str(tc)?.resolve(&default_host)?), None => None, }; @@ -382,7 +382,7 @@ impl<'a> Cfg<'a> { // Run some basic checks against the constructed configuration // For now, that means simply checking that 'stable' can resolve // for the current configuration. - ResolvableToolchainName::try_from("stable")?.resolve( + ResolvableToolchainName::from_str("stable")?.resolve( &cfg.default_host_tuple() .context("Unable parse configuration")?, )?; @@ -637,7 +637,7 @@ impl<'a> Cfg<'a> { // However, settings.toml could conceivably be hand edited to // have an unresolved name. I'm just preserving pre-existing // behaviour by choosing ResolvableToolchainName here. - let toolchain_name = ResolvableToolchainName::try_from(name)? + let toolchain_name = ResolvableToolchainName::from_str(&name)? .resolve(&default_host_tuple(settings, self.process))?; let override_cfg = toolchain_name.into(); return Ok(Some((override_cfg, source))); @@ -689,7 +689,7 @@ impl<'a> Cfg<'a> { } })?; if let Some(toolchain_name_str) = &override_file.toolchain.channel { - let toolchain_name = ResolvableToolchainName::try_from( + let toolchain_name = ResolvableToolchainName::from_str( toolchain_name_str.as_str(), ) .map_err(|_| { @@ -707,7 +707,7 @@ impl<'a> Cfg<'a> { // Permit fully qualified names IFF the toolchain is installed. TODO(robertc): consider // disabling this and backing out https://github.com/rust-lang/rustup/pull/2141 (but provide // the base name in the error to help users) - let resolved_name = &ToolchainName::try_from(toolchain_name_str.as_str())?; + let resolved_name = &ToolchainName::from_str(toolchain_name_str)?; if !self.list_toolchains()?.iter().any(|s| s == resolved_name) { return Err(anyhow!(format!("target tuple in channel name '{name}'"))); } @@ -907,7 +907,7 @@ impl<'a> Cfg<'a> { user_opt }?; toolchain_maybe_str - .map(ResolvableToolchainName::try_from) + .map(|s| ResolvableToolchainName::from_str(&s)) .transpose()? .map(|t| t.resolve(&self.default_host_tuple()?)) .transpose() @@ -928,7 +928,7 @@ impl<'a> Cfg<'a> { .filter_map(io::Result::ok) .filter(|e| e.file_type().map(|f| !f.is_file()).unwrap_or(false)) .filter_map(|e| e.file_name().into_string().ok()) - .filter_map(|n| ToolchainName::try_from(n).ok()) + .filter_map(|n| ToolchainName::from_str(&n).ok()) .collect(); toolchains.sort(); diff --git a/src/dist/manifestation.rs b/src/dist/manifestation.rs index 469d830910..9bb2467e52 100644 --- a/src/dist/manifestation.rs +++ b/src/dist/manifestation.rs @@ -29,14 +29,13 @@ use crate::{ config::Config, download::{DownloadCfg, DownloadStatus, File}, manifest::{Component, CompressionKind, HashedBinary, Manifest}, - prefix::InstallPrefix, + prefix::{DIST_MANIFEST, InstallPrefix}, temp, }, errors::RustupError, utils, }; -pub(crate) const DIST_MANIFEST: &str = "multirust-channel-manifest.toml"; pub(crate) const CONFIG_FILE: &str = "multirust-config.toml"; #[derive(Debug)] @@ -396,8 +395,7 @@ impl Manifestation { #[tracing::instrument(level = "trace")] pub fn load_manifest(&self) -> Result> { let prefix = self.installation.prefix(); - let old_manifest_path = prefix.manifest_file(DIST_MANIFEST); - if utils::path_exists(&old_manifest_path) { + if let Some(old_manifest_path) = prefix.dist_manifest() { let manifest_str = utils::read_file("installed manifest", &old_manifest_path)?; Ok(Some(Manifest::parse(&manifest_str).with_context(|| { RustupError::ParsingFile { diff --git a/src/dist/mod.rs b/src/dist/mod.rs index d74afa014c..090941cf9c 100644 --- a/src/dist/mod.rs +++ b/src/dist/mod.rs @@ -608,6 +608,7 @@ impl TargetTuple { (b"Linux", b"loongarch64") => Some(TUPLE_LOONGARCH64_UNKNOWN_LINUX), (b"Linux", b"ppc64") => Some(TUPLE_POWERPC64_UNKNOWN_LINUX), (b"Linux", b"ppc64le") => Some(TUPLE_POWERPC64LE_UNKNOWN_LINUX), + (b"Linux", b"riscv64") => Some(TUPLE_RISCV64GC_UNKNOWN_LINUX), (b"Darwin", b"x86_64") => Some("x86_64-apple-darwin"), (b"Darwin", b"i686") => Some("i686-apple-darwin"), (b"FreeBSD", b"x86_64") => Some("x86_64-unknown-freebsd"), @@ -712,6 +713,10 @@ const TUPLE_POWERPC64_UNKNOWN_LINUX: &str = "powerpc64-unknown-linux-musl"; const TUPLE_POWERPC64LE_UNKNOWN_LINUX: &str = "powerpc64le-unknown-linux-gnu"; #[cfg(all(not(windows), target_env = "musl"))] const TUPLE_POWERPC64LE_UNKNOWN_LINUX: &str = "powerpc64le-unknown-linux-musl"; +#[cfg(all(not(windows), not(target_env = "musl")))] +const TUPLE_RISCV64GC_UNKNOWN_LINUX: &str = "riscv64gc-unknown-linux-gnu"; +#[cfg(all(not(windows), target_env = "musl"))] +const TUPLE_RISCV64GC_UNKNOWN_LINUX: &str = "riscv64gc-unknown-linux-musl"; // MIPS platforms don't indicate endianness in uname, however binaries only // run on boxes with the same endianness, as expected. @@ -1125,10 +1130,9 @@ async fn try_update_from_dist_( } else { download .dl_v2_manifest( - // Even if manifest has not changed, we must continue to install requested components. - // So if components or targets is not empty, we skip passing `update_hash` so that - // we essentially degenerate to `rustup component add` / `rustup target add` - if components.is_empty() && targets.is_empty() { + // Skip the update hash when the installed manifest is missing or when components + // or targets were requested, since either case still requires the channel manifest. + if prefix.dist_manifest().is_some() && components.is_empty() && targets.is_empty() { Some(update_hash) } else { None diff --git a/src/dist/prefix.rs b/src/dist/prefix.rs index 9b71118017..c0217bfb86 100644 --- a/src/dist/prefix.rs +++ b/src/dist/prefix.rs @@ -11,6 +11,7 @@ const REL_MANIFEST_DIR: &str = match std::path::MAIN_SEPARATOR { }; static V1_COMMON_COMPONENT_LIST: &[&str] = &["cargo", "rustc", "rust-docs"]; +pub(crate) const DIST_MANIFEST: &str = "multirust-channel-manifest.toml"; #[derive(Clone, Debug)] pub struct InstallPrefix { @@ -37,6 +38,11 @@ impl InstallPrefix { path } + pub(crate) fn dist_manifest(&self) -> Option { + let path = self.manifest_file(DIST_MANIFEST); + utils::path_exists(&path).then_some(path) + } + pub(crate) fn rel_manifest_file(&self, name: &str) -> PathBuf { let mut path = PathBuf::from(REL_MANIFEST_DIR); path.push(name); diff --git a/src/dist/target_tuple/known.rs b/src/dist/target_tuple/known.rs index b034a77211..6f46fbfb02 100644 --- a/src/dist/target_tuple/known.rs +++ b/src/dist/target_tuple/known.rs @@ -54,6 +54,7 @@ pub static LIST_ARCHS: &[&str] = &[ "riscv32imac", "riscv32imafc", "riscv32imc", + "riscv32imfc", "riscv64", "riscv64a23", "riscv64gc", @@ -108,9 +109,11 @@ pub static LIST_OSES: &[&str] = &[ "nuttx-eabi", "nuttx-eabihf", "nvidia-cuda", + "oe-linux", "openwrt-linux", "pc-cygwin", "pc-nto", + "pc-qnx", "pc-solaris", "pc-windows", "risc0-zkvm", @@ -138,6 +141,7 @@ pub static LIST_OSES: &[&str] = &[ "unknown-nto", "unknown-nuttx", "unknown-openbsd", + "unknown-qnx", "unknown-qurt", "unknown-redox", "unknown-teeos", @@ -187,10 +191,10 @@ pub static LIST_ENVS: &[&str] = &[ "newlibeabihf", "none", "ohos", + "pauthtest", "qnx700", "qnx710", "qnx710_iosock", - "qnx800", "sgx", "sim", "softfloat", diff --git a/src/install.rs b/src/install.rs index b7f4df76b9..bb5e275c8b 100644 --- a/src/install.rs +++ b/src/install.rs @@ -126,11 +126,12 @@ impl InstallMethod<'_, '_> { fn local_name(&self) -> LocalToolchainName { match self { - InstallMethod::Copy { dest, .. } => (*dest).into(), - InstallMethod::Link { dest, .. } => (*dest).into(), + InstallMethod::Copy { dest, .. } | InstallMethod::Link { dest, .. } => { + (*dest).clone().into() + } InstallMethod::Dist(DistOptions { toolchain: desc, .. - }) => (*desc).into(), + }) => (*desc).clone().into(), } } @@ -140,13 +141,14 @@ impl InstallMethod<'_, '_> { fn dest_path(&self) -> PathBuf { match self { - InstallMethod::Copy { cfg, dest, .. } => cfg.toolchain_path(&(*dest).into()), - InstallMethod::Link { cfg, dest, .. } => cfg.toolchain_path(&(*dest).into()), + InstallMethod::Copy { cfg, dest, .. } | InstallMethod::Link { cfg, dest, .. } => { + cfg.toolchain_path(&(*dest).clone().into()) + } InstallMethod::Dist(DistOptions { cfg, toolchain: desc, .. - }) => cfg.toolchain_path(&(*desc).into()), + }) => cfg.toolchain_path(&(*desc).clone().into()), } } } diff --git a/src/settings.rs b/src/settings.rs index 42dc7ceebf..d4e45fbacd 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -29,7 +29,7 @@ impl SettingsFile { fn write_settings(&self) -> Result<()> { let settings = self.cache.borrow(); - utils::write_file( + utils::write_locked_file( "settings", &self.path, &settings.as_ref().unwrap().stringify()?, @@ -44,7 +44,7 @@ impl SettingsFile { if b.is_none() { drop(b); *self.cache.borrow_mut() = Some(if utils::is_file(&self.path) { - let content = utils::read_file("settings", &self.path)?; + let content = utils::read_locked_file("settings", &self.path)?; Settings::parse(&content).with_context(|| RustupError::ParsingFile { name: "settings", path: self.path.clone(), diff --git a/src/toolchain/distributable.rs b/src/toolchain/distributable.rs index a76776fa07..aa06931ff6 100644 --- a/src/toolchain/distributable.rs +++ b/src/toolchain/distributable.rs @@ -58,7 +58,7 @@ impl<'a> DistributableToolchain<'a> { } pub(crate) fn new(cfg: &'a Cfg<'a>, desc: ToolchainDesc) -> Result { - Toolchain::new(cfg, (&desc).into()).map(|toolchain| Self { toolchain, desc }) + Toolchain::new(cfg, desc.clone().into()).map(|toolchain| Self { toolchain, desc }) } pub(crate) fn desc(&self) -> &ToolchainDesc { @@ -439,12 +439,14 @@ impl<'a> DistributableToolchain<'a> { } pub async fn fetch_dist_manifest(&self) -> anyhow::Result> { + let prefix = InstallPrefix::from(self.toolchain.path()); + let update_hash = if prefix.dist_manifest().is_some() { + Some(self.toolchain.cfg.get_hash_file(&self.desc, false)?) + } else { + None + }; DownloadCfg::new(self.toolchain.cfg) - .dl_v2_manifest( - Some(&self.toolchain.cfg.get_hash_file(&self.desc, false)?), - &self.desc, - self.toolchain.cfg, - ) + .dl_v2_manifest(update_hash.as_deref(), &self.desc, self.toolchain.cfg) .await } diff --git a/src/toolchain/names.rs b/src/toolchain/names.rs index 37d92b9912..71c6415832 100644 --- a/src/toolchain/names.rs +++ b/src/toolchain/names.rs @@ -71,47 +71,6 @@ pub enum InvalidName { PlusPrefix(String), } -macro_rules! from_variant { - ($from:ident, $to:ident, $variant:expr) => { - impl From<$from> for $to { - fn from(value: $from) -> Self { - $variant(value) - } - } - }; -} - -macro_rules! try_from_str { - ($to:ident) => { - try_from_str!(&str, $to); - - impl TryFrom for $to { - type Error = InvalidName; - - fn try_from(value: String) -> std::result::Result { - $to::validate(&value) - } - } - - impl FromStr for $to { - type Err = InvalidName; - - fn from_str(value: &str) -> std::result::Result { - $to::validate(value) - } - } - }; - ($from:ty, $to:ident) => { - impl TryFrom<$from> for $to { - type Error = InvalidName; - - fn try_from(value: $from) -> std::result::Result { - $to::validate(value) - } - } - }; -} - /// Common validate rules for all sorts of toolchain names fn validate(candidate: &str) -> Result<&str, InvalidName> { if let Some(without_plus) = candidate.strip_prefix('+') { @@ -149,14 +108,20 @@ impl ResolvableToolchainName { return Ok(Self::Official(desc)); } - match CustomToolchainName::try_from(candidate) { + match CustomToolchainName::from_str(candidate) { Ok(custom) => Ok(Self::Custom(custom)), Err(_) => Err(InvalidName::ToolchainName(candidate.into())), } } } -try_from_str!(ResolvableToolchainName); +impl FromStr for ResolvableToolchainName { + type Err = InvalidName; + + fn from_str(value: &str) -> Result { + Self::validate(value) + } +} impl From<&PartialToolchainDesc> for ResolvableToolchainName { fn from(value: &PartialToolchainDesc) -> Self { @@ -193,7 +158,13 @@ impl MaybeResolvableToolchainName { } } -try_from_str!(MaybeResolvableToolchainName); +impl FromStr for MaybeResolvableToolchainName { + type Err = InvalidName; + + fn from_str(value: &str) -> Result { + Self::validate(value) + } +} impl Display for MaybeResolvableToolchainName { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -224,7 +195,13 @@ impl MaybeOfficialToolchainName { } } -try_from_str!(MaybeOfficialToolchainName); +impl FromStr for MaybeOfficialToolchainName { + type Err = InvalidName; + + fn from_str(value: &str) -> Result { + Self::validate(value) + } +} impl Display for MaybeOfficialToolchainName { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -252,17 +229,32 @@ impl ToolchainName { return Ok(Self::Official(desc)); } - match CustomToolchainName::try_from(candidate) { + match CustomToolchainName::from_str(candidate) { Ok(custom) => Ok(Self::Custom(custom)), Err(_) => Err(InvalidName::ToolchainName(candidate.into())), } } } -from_variant!(ToolchainDesc, ToolchainName, ToolchainName::Official); -from_variant!(CustomToolchainName, ToolchainName, ToolchainName::Custom); +impl From for ToolchainName { + fn from(value: ToolchainDesc) -> Self { + Self::Official(value) + } +} + +impl From for ToolchainName { + fn from(value: CustomToolchainName) -> Self { + Self::Custom(value) + } +} + +impl FromStr for ToolchainName { + type Err = InvalidName; -try_from_str!(ToolchainName); + fn from_str(value: &str) -> Result { + Self::validate(value) + } +} impl Display for ToolchainName { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -294,7 +286,7 @@ impl ResolvableLocalToolchainName { /// Validates if the string is a resolvable toolchain, or a path based toolchain. fn validate(candidate: &str) -> Result { let candidate = validate(candidate)?; - if let Ok(name) = ResolvableToolchainName::try_from(candidate) { + if let Ok(name) = ResolvableToolchainName::from_str(candidate) { return Ok(Self::Named(name)); } @@ -304,7 +296,13 @@ impl ResolvableLocalToolchainName { } } -try_from_str!(ResolvableLocalToolchainName); +impl FromStr for ResolvableLocalToolchainName { + type Err = InvalidName; + + fn from_str(value: &str) -> Result { + Self::validate(value) + } +} impl Display for ResolvableLocalToolchainName { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -326,15 +324,21 @@ pub(crate) enum LocalToolchainName { Path(PathBasedToolchainName), } -impl From<&ToolchainDesc> for LocalToolchainName { - fn from(value: &ToolchainDesc) -> Self { - ToolchainName::Official(value.to_owned()).into() +impl From for LocalToolchainName { + fn from(value: ToolchainName) -> Self { + Self::Named(value) + } +} + +impl From for LocalToolchainName { + fn from(value: PathBasedToolchainName) -> Self { + Self::Path(value) } } -impl From<&CustomToolchainName> for LocalToolchainName { - fn from(value: &CustomToolchainName) -> Self { - ToolchainName::Custom(value.to_owned()).into() +impl From for LocalToolchainName { + fn from(value: ToolchainDesc) -> Self { + ToolchainName::Official(value).into() } } @@ -344,13 +348,6 @@ impl From for LocalToolchainName { } } -from_variant!(ToolchainName, LocalToolchainName, LocalToolchainName::Named); -from_variant!( - PathBasedToolchainName, - LocalToolchainName, - LocalToolchainName::Path -); - impl PartialEq for LocalToolchainName { fn eq(&self, other: &ToolchainName) -> bool { match self { @@ -397,7 +394,13 @@ impl Deref for CustomToolchainName { } } -try_from_str!(CustomToolchainName); +impl FromStr for CustomToolchainName { + type Err = InvalidName; + + fn from_str(value: &str) -> Result { + Self::validate(value) + } +} impl Display for CustomToolchainName { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -528,12 +531,12 @@ mod tests { #[test] fn test_parse_custom(name in arb_custom_name()) { - CustomToolchainName::try_from(name).unwrap(); + CustomToolchainName::from_str(&name).unwrap(); } #[test] fn test_parse_resolvable_name(name in arb_resolvable_name()) { - ResolvableToolchainName::try_from(name).unwrap(); + ResolvableToolchainName::from_str(&name).unwrap(); } // TODO: This needs some thought @@ -570,7 +573,7 @@ mod tests { "this.is.not-a+semver", ] .into_iter() - .map(|s| ToolchainName::try_from(s).unwrap()) + .map(|s| ToolchainName::from_str(s).unwrap()) .collect::>(); let mut v = vec![ @@ -595,7 +598,7 @@ mod tests { "the cake is a lie", ] .into_iter() - .map(|s| ToolchainName::try_from(s).unwrap()) + .map(|s| ToolchainName::from_str(s).unwrap()) .collect::>(); v.sort(); diff --git a/src/utils/mod.rs b/src/utils/mod.rs index fdb079ccaf..414a5687a4 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -87,6 +87,13 @@ pub fn read_file(name: &'static str, path: &Path) -> Result { }) } +pub(crate) fn read_locked_file(name: &'static str, path: &Path) -> Result { + raw::read_locked_file(path).with_context(|| RustupError::ReadingFile { + name, + path: PathBuf::from(path), + }) +} + pub fn write_file(name: &'static str, path: &Path, contents: &str) -> Result<()> { raw::write_file(path, contents).with_context(|| RustupError::WritingFile { name, @@ -94,6 +101,13 @@ pub fn write_file(name: &'static str, path: &Path, contents: &str) -> Result<()> }) } +pub(crate) fn write_locked_file(name: &'static str, path: &Path, contents: &str) -> Result<()> { + raw::write_locked_file(path, contents).with_context(|| RustupError::WritingFile { + name, + path: PathBuf::from(path), + }) +} + pub(crate) fn append_file(name: &'static str, path: &Path, line: &str) -> Result<()> { raw::append_file(path, line).with_context(|| RustupError::WritingFile { name, diff --git a/src/utils/raw.rs b/src/utils/raw.rs index ba439ae731..70f27f3d42 100644 --- a/src/utils/raw.rs +++ b/src/utils/raw.rs @@ -3,7 +3,7 @@ use std::env; use std::fs; use std::fs::File; use std::io; -use std::io::Write; +use std::io::{Read, Seek, SeekFrom, Write}; use std::path::Path; use std::str; @@ -60,6 +60,21 @@ pub fn path_exists>(path: P) -> bool { fs::metadata(path).is_ok() } +pub(crate) fn read_locked_file(path: &Path) -> io::Result { + let mut file = File::open(path)?; + file.lock_shared()?; + + let size = file + .metadata() + .map(|metadata| usize::try_from(metadata.len()).unwrap_or(usize::MAX)) + .ok(); + let mut contents = String::new(); + contents.try_reserve_exact(size.unwrap_or(0))?; + file.read_to_string(&mut contents)?; + + Ok(contents) +} + pub(crate) fn random_string(length: usize) -> String { const CHARSET: &[u8] = b"abcdefghijklmnopqrstuvwxyz0123456789_"; let mut rng = rand::rng(); @@ -82,6 +97,24 @@ pub fn write_file(path: &Path, contents: &str) -> io::Result<()> { Ok(()) } +pub(crate) fn write_locked_file(path: &Path, contents: &str) -> io::Result<()> { + let mut file = fs::OpenOptions::new() + .read(true) + .write(true) + .create(true) + // Truncation must happen after the exclusive lock is held. + .truncate(false) + .open(path)?; + + file.lock()?; + file.set_len(0)?; + file.seek(SeekFrom::Start(0))?; + Write::write_all(&mut file, contents.as_bytes())?; + file.sync_data()?; + + Ok(()) +} + pub(crate) fn filter_file bool>( src: &Path, dest: &Path, @@ -350,3 +383,20 @@ pub(crate) mod windows { inner(s.as_ref()) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn locked_file_round_trip_truncates_previous_contents() -> io::Result<()> { + let temp_dir = tempfile::tempdir()?; + let path = temp_dir.path().join("state.toml"); + + write_locked_file(&path, "last_release_notified_secs = 123456\n")?; + write_locked_file(&path, "version = \"12\"\n")?; + + assert_eq!(read_locked_file(&path)?, "version = \"12\"\n"); + Ok(()) + } +} diff --git a/tests/suite/cli_paths.rs b/tests/suite/cli_paths.rs index f102dd5234..f530619526 100644 --- a/tests/suite/cli_paths.rs +++ b/tests/suite/cli_paths.rs @@ -348,6 +348,41 @@ error: could not amend shell profile[..] } } + #[tokio::test] + async fn install_preserves_guarded_legacy_source() { + let cx = CliTestContext::new(Scenario::Empty).await; + let profile = cx.config.homedir.join(".bash_profile"); + let guarded_source = "[ -f \"$HOME/.cargo/env\" ] && source \"$HOME/.cargo/env\"\n"; + raw::write_file(&profile, guarded_source).unwrap(); + + let mut cmd = cx.config.cmd("rustup-init", &INIT_NONE[1..]); + cmd.env_remove("CARGO_HOME"); + assert!(cmd.output().unwrap().status.success()); + + let new_profile = fs::read_to_string(&profile).unwrap(); + let expected = guarded_source.to_owned() + &source("$HOME/.cargo", POSIX_SH); + assert_eq!(new_profile, expected); + } + + #[tokio::test] + async fn uninstall_preserves_guarded_source() { + let cx = CliTestContext::new(Scenario::Empty).await; + let profile = cx.config.homedir.join(".bash_profile"); + let guarded_source = "[ -f \"$HOME/.cargo/env\" ] && . \"$HOME/.cargo/env\"\n"; + raw::write_file(&profile, guarded_source).unwrap(); + + let mut cmd = cx.config.cmd("rustup-init", &INIT_NONE[1..]); + cmd.env_remove("CARGO_HOME"); + assert!(cmd.output().unwrap().status.success()); + + let mut cmd = cx.config.cmd("rustup", ["self", "uninstall", "-y"]); + cmd.env_remove("CARGO_HOME"); + assert!(cmd.output().unwrap().status.success()); + + let new_profile = fs::read_to_string(&profile).unwrap(); + assert_eq!(new_profile, guarded_source); + } + #[tokio::test] async fn uninstall_cleans_up_legacy_paths() { let cx = CliTestContext::new(Scenario::Empty).await; diff --git a/tests/suite/cli_rustup.rs b/tests/suite/cli_rustup.rs index 91c3891907..76ca060212 100644 --- a/tests/suite/cli_rustup.rs +++ b/tests/suite/cli_rustup.rs @@ -353,7 +353,7 @@ async fn add_target() { let cx = CliTestContext::new(Scenario::SimpleV2).await; let path = format!( "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", - &this_host_tuple(), + this_host_tuple(), CROSS_ARCH1 ); cx.config @@ -372,7 +372,7 @@ async fn remove_target() { let cx = CliTestContext::new(Scenario::SimpleV2).await; let path = format!( "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", - &this_host_tuple(), + this_host_tuple(), CROSS_ARCH1 ); cx.config @@ -404,13 +404,13 @@ async fn add_remove_multiple_targets() { .is_ok(); let path = format!( "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", - &this_host_tuple(), + this_host_tuple(), CROSS_ARCH1 ); assert!(cx.config.rustupdir.has(path)); let path = format!( "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", - &this_host_tuple(), + this_host_tuple(), CROSS_ARCH2 ); assert!(cx.config.rustupdir.has(path)); @@ -421,13 +421,13 @@ async fn add_remove_multiple_targets() { .is_ok(); let path = format!( "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", - &this_host_tuple(), + this_host_tuple(), CROSS_ARCH1 ); assert!(!cx.config.rustupdir.has(path)); let path = format!( "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", - &this_host_tuple(), + this_host_tuple(), CROSS_ARCH2 ); assert!(!cx.config.rustupdir.has(path)); @@ -474,7 +474,7 @@ async fn add_target_explicit() { let cx = CliTestContext::new(Scenario::SimpleV2).await; let path = format!( "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", - &this_host_tuple(), + this_host_tuple(), CROSS_ARCH1 ); cx.config @@ -500,7 +500,7 @@ async fn remove_target_explicit() { let cx = CliTestContext::new(Scenario::SimpleV2).await; let path = format!( "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", - &this_host_tuple(), + this_host_tuple(), CROSS_ARCH1 ); cx.config @@ -4173,14 +4173,7 @@ async fn missing_manifest_shows_reinstall_help() { .await .is_ok(); - let manifest_path = cx - .config - .rustupdir - .join("toolchains") - .join(format!("nightly-{}", this_host_tuple())) - .join("lib") - .join("rustlib") - .join("multirust-channel-manifest.toml"); + let manifest_path = nightly_manifest_path(&cx); fs::remove_file(&manifest_path).unwrap(); @@ -4195,3 +4188,51 @@ help: try reinstalling or updating the toolchain "#]]) .is_err(); } + +#[tokio::test] +async fn reinstall_restores_missing_manifest() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + + cx.config + .expect(["rustup", "toolchain", "install", "nightly"]) + .await + .is_ok(); + + let manifest_path = nightly_manifest_path(&cx); + + fs::remove_file(&manifest_path).unwrap(); + + cx.config + .expect(["rustup", "toolchain", "install", "nightly"]) + .await + .is_ok(); + + assert!(manifest_path.exists()); +} + +#[tokio::test] +async fn update_all_restores_missing_manifest() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + + cx.config + .expect(["rustup", "toolchain", "install", "nightly"]) + .await + .is_ok(); + + let manifest_path = nightly_manifest_path(&cx); + fs::remove_file(&manifest_path).unwrap(); + + cx.config.expect(["rustup", "update"]).await.is_ok(); + + assert!(manifest_path.exists()); +} + +fn nightly_manifest_path(cx: &CliTestContext) -> PathBuf { + cx.config + .rustupdir + .join("toolchains") + .join(format!("nightly-{}", this_host_tuple())) + .join("lib") + .join("rustlib") + .join("multirust-channel-manifest.toml") +} diff --git a/tests/suite/cli_rustup_ui/rustup_doc_cmd_help_flag.stdout.term.svg b/tests/suite/cli_rustup_ui/rustup_doc_cmd_help_flag.stdout.term.svg index 1a2571939b..479447b6cb 100644 --- a/tests/suite/cli_rustup_ui/rustup_doc_cmd_help_flag.stdout.term.svg +++ b/tests/suite/cli_rustup_ui/rustup_doc_cmd_help_flag.stdout.term.svg @@ -1,4 +1,4 @@ - +