cargo-0.91.0/.cargo/config.toml000064400000000000000000000006541046102023000143260ustar 00000000000000[alias] build-man = "run --package xtask-build-man --" stale-label = "run --package xtask-stale-label --" bump-check = "run --package xtask-bump-check --" lint-docs = "run --package xtask-lint-docs --" [env] # HACK: Until this is stabilized, `snapbox`s polyfill could get confused # inside of the rust-lang/rust repo because it looks for the furthest-away `Cargo.toml` CARGO_RUSTC_CURRENT_DIR = { value = "", relative = true } cargo-0.91.0/.cargo_vcs_info.json0000644000000001360000000000100122160ustar { "git": { "sha1": "840b83a10fb0e039a83f4d70ad032892c287570a" }, "path_in_vcs": "" }cargo-0.91.0/.git-blame-ignore-revs000064400000000000000000000114501046102023000151070ustar 00000000000000# Use `git config blame.ignorerevsfile .git-blame-ignore-revs` to make `git blame` ignore the following commits. # refactor: rustfmt for tracing e84cc17db6ac48620e2723f51bce0703f5590060 # Rustfmt with latest nightly. 43c253e69a20321cdfd73d632fa5ec641b1aace3 # reformat with rustfmt df62c36b214449003997d8ee630c7d5c1c08a90c # rustfmt c973a6d0af55785f3072623b4d29127051a84b90 # Apply rustfmt ebd0d58c1c11f98e804c80b3378d00f9e1000a95 # rustfmt 4787dd3acfb32c6d608342bca115094e1ed866e7 # Fix for rustfmt ad44f0e898721f35173b59f32dae344b964dff22 # rustfmt b9554f37abe4f802dd615a550773105a8b2eb386 # Normalize raw string indentation. 6f8c7d5a87526d10e3bbfa4098a1002076386068 # rustfmt dcae5fcf2dc6e040d1cadac14276fd306c058b74 # Rustfmt 0dfdba6f7a7702a7f45d523b028c896084411de8 # Run rustfmt e06a911de616b3f402f1e142b5202445d333f4eb # rustfmt d02f476804ae73d79059e3243ecb4c814f53bea1 # Update for nightly rustfmt. 4b6c26dd16786b416190bd0a36e7646d33b9846a # Run rustfmt d0430dd2b11e6ac004e8f39a712ee6425b81e12d # Run rustfmt c56cb4287fa98a42dafea36d0b88bb4441f63c1f # Run rustfmt c3f8cd3cd2999949c49c8f0e76108ffe7a37bec0 # Run code through rustfmt 648b39e98101f9b6417fc6ebc88fa7f397a5f509 # Run rustfmt ba81441f297c6d9799473a62af021236b8bc9ed8 # Wrap some really long lines. d6d15141f8ef21f1cebeaa4d42606f29e7d00239 # rustfmt for nightly changes. 384c311692e6ff537b51bcf57f804b917a702c9c # Rustfmt lint 9dc70a3dabb291052a4d117b8348c9b461d1b570 # Run rustfmt 3a6cd74434cc1fdd57266740d4827ed162580005 # rustfmt, even if I disagree 189fef1173118ee3be47fa59e22fd2d21bb05bbd # Fix zalgo formatting. 56f8848a51e5db5214ec27bb41c7d99ebac21af8 # Rustfmt adjustments a50be59a70af64b83c07f40bc2faef80a73bc908 # Run rustfmt ebd10526f3526b695872a8ecb381f65aaf74c019 # Rustfmt fixes ac2a4382ee5d927d917df026d2236ff5cd668717 # Rustfmt fixes fc4ee774483dc6b40f1edbd259879dc4cbcf20a5 # Fixed formatting with rustfmt a82de176952f7c244adbd392d444baaafdfc819c # Fix some formatting for some strings. a4e9611453cb4ba3f9bcc34f2851546f57152229 # fix(fingerprint): rustfmt 009876a88af659c10b022ec4c4956f77b983d531 # Make rustfmt happy 2415a2980f455238e9422cc07abcfa4cbec9be6b # run rustfmt dac967ce27d4eeb496dcd98ba91641fbe29df957 # $cargo fmt --all db09895f3c123c36e05175e2c0273bf487068b37 # Reformat everything with rustfmt from stable 1.26 d85336084d8855bfbfd431b85beaa5c094dc5e72 # rustfmt a4947c2b47fd38857c7332d333fda7bcc405de0c # rustfmt 404970f2261e2d6c9ba4eac410065fa6226271d9 # Prettify rustfmted single-line strings b0c181d91ccfe1d27d4c7133049a6bfe5bacf29b # cargo fmt 1e6828485eea0f550ed7be46ef96107b46aeb162 # rustfmt for bin/cargo.rs ef4c09f986c0b1f701c765d894da911c2de44724 # rustfmt cargo_doc.rs 87124d5ade897b3fb38ce5cd0a2f19a9038dd8a7 # run rustfmt on core/source.rs 76fb87e2bab025acaff2d6dd919dde0e328267da # cargo fmt 7a67e88baf0f73196973cadf887bf9a31c13e95f # cargo fmt 7a6ff7f068acde98230d712637e934c58fc84805 # cargo fmt 7c8ee49bffdeeb138db5e22f14abbf88cf99c547 # cargo fmt 72d6c5411a215ce5ab0ff22b1e1e1525883b7c33 # rust fmt ddd5c83b8cf63458bf67ae504bb289ed4e987d38 # cargo fmt 1179e7ef6b88a6940168cbc2e806d48ed20d92ef # cargo fmt 0c07056bfeffee213e21cdf1e48875f59f834282 # cargo fmt c7a79be6f8a8da1d6dee54fe3f5c137d293692b0 # cargo fmt 1839ded4aa5607d3564f02b36e517289ad6ed8e8 # cargo fmt f2b5271a09a13f89a50861ebd71da333836bded3 # Run `cargo fmt --all` 3aa99422ca7808f1bc5621fda3a8f32f27273d9b # cargo fmt 4538ade2d55d7353cfac8a0ce320e3c4a29bc450 # style: cargo fmt 3d042688a851d985013ee8ff64a9caef47b7b495 # run cargo fmt to pass the CI build a4e3b81a55b8c142ab1b84f4f209414a72727e86 # Run 'cargo fmt' 60afaa77335ab464a5b6c913883ad145ae97d750 # Run 'cargo fmt' 511050baefa2ec21c2be8c3eaee2aade1da2c06b # cargo fmt 577968e44828457dfa62fc10ccd29f42a359b746 # cargo fmt d0f7c0ee31c81cd405dc2c163e4ca7638895610c # appease cargo fmt c4922052fc7470264f3df491213f74142ac67ebf # named-profiles: formatting fixes from 'cargo fmt' 29b7e90ba37f311baaf448894bbf04721bed5791 # cargo fmt f39302f432f62e29910c876f766eb8a324cee843 # cargo fmt 6b07c8da63c0896cc31fa6cb8b47a71fe29c88e2 # run cargo fmt --all b48ae60e67096f7cda80a17dfa2a9ff7a916bbf7 # $cargo fmt --all 63a9c7aa6d54ff0580288f1a01795a29b3ea1c75 # Run `cargo fmt` f16efff1509be313f4bed825e7ab974673dd03fc # cargo fmt tests/testsuite/rustflags.rs 3ca98adbbac5752cdef337f5ac803e7843ab601a # Format with `cargo fmt` fecb72464328846dacd0ff8252d105b7818733ab # cargo fmt 9b2295d11fd2816aa3032403a88d7c055cd2cf58 # Run `cargo fmt` 5d201944d7aba8380082dcb9e0c340d92e92850f # Correct formatting with cargo fmt 90954d700ccb71b3df6ecf8ff83891dd3570f887 # Correct formatting with cargo fmt 5c241e102756e340991365d7b376997a9ae95f62 # Fix formatting issues with cargo fmt 5c7979cdf879a6cc9011432098f7ef68ed7f5e37 # cargo fmt a6ad2de0484f1910d42793f3ec73b111403099b7 # cargo fmt b0fbc89c33780ca3e1f2bfeacc67922ee7abe1dc # Rustfmt 2024 1ce80236261a3cd42a95b1f1abcffede87cafef4 cargo-0.91.0/.github/ISSUE_TEMPLATE/bug_report.yml000064400000000000000000000022641046102023000174300ustar 00000000000000name: Bug Report description: Create a report to help us improve labels: ["C-bug", "S-triage"] body: - type: markdown attributes: value: Thanks for filing a 🐛 bug report 😄! - type: textarea id: problem attributes: label: Problem description: > Please provide a clear and concise description of what the bug is, including what currently happens and what you expected to happen. validations: required: true - type: textarea id: steps attributes: label: Steps description: Please list the steps to reproduce the bug. placeholder: | 1. 2. 3. - type: textarea id: possible-solutions attributes: label: Possible Solution(s) description: > Not obligatory, but suggest a fix/reason for the bug, or ideas how to implement the addition or change. - type: textarea id: notes attributes: label: Notes description: Provide any additional notes that might be helpful. - type: textarea id: version attributes: label: Version description: Please paste the output of running `cargo version --verbose`. render: text cargo-0.91.0/.github/ISSUE_TEMPLATE/config.yml000064400000000000000000000005671046102023000165310ustar 00000000000000contact_links: - name: Question url: https://users.rust-lang.org about: > Got a question about Cargo? Ask the community on the user forum. - name: Inspiring Idea url: https://internals.rust-lang.org/c/tools-and-infrastructure/cargo about: > Need more discussions with your next big idea? Reach out the coummunity on the internals forum. cargo-0.91.0/.github/ISSUE_TEMPLATE/feature_request.yml000064400000000000000000000025261046102023000204640ustar 00000000000000name: Feature Request description: Suggest an idea for enhancing Cargo labels: ["C-feature-request", "S-triage"] body: - type: markdown attributes: value: | Thanks for filing a 🙋 feature request 😄! If the feature request is relatively small and already with a possible solution, this might be the place for you. If you are brewing a big feature that needs feedback from the community, [the internal forum] is the best fit, especially for pre-RFC. You can also talk the idea over with other developers in [#t-cargo Zulip stream]. [the internal forum]: https://internals.rust-lang.org/c/tools-and-infrastructure/cargo/15 [#t-cargo Zulip stream]: https://rust-lang.zulipchat.com/#narrow/stream/246057-t-cargo - type: textarea id: problem attributes: label: Problem description: > Please provide a clear description of your use case and the problem this feature request is trying to solve. validations: required: true - type: textarea id: solution attributes: label: Proposed Solution description: > Please provide a clear and concise description of what you want to happen. - type: textarea id: notes attributes: label: Notes description: Provide any additional context or information that might be helpful. cargo-0.91.0/.github/ISSUE_TEMPLATE/new_lint.yml000064400000000000000000000022251046102023000170740ustar 00000000000000name: New lint suggestion description: Suggest a new Cargo lint. labels: ["A-new-lint", "S-triage"] body: - type: markdown attributes: value: Thank you for your lint idea! - type: textarea id: what attributes: label: What it does description: What does this lint do? validations: required: true - type: textarea id: advantage attributes: label: Advantage description: > What is the advantage of the recommended code over the original code? placeholder: | - Remove bounds check inserted by ... - Remove the need to duplicate/store ... - Remove typo ... - type: textarea id: drawbacks attributes: label: Drawbacks description: What might be possible drawbacks of such a lint? - type: textarea id: example attributes: label: Example description: > Include a short example showing when the lint should trigger together with the improved code. value: | ```toml ``` Could be written as: ```toml ``` validations: required: true cargo-0.91.0/.github/ISSUE_TEMPLATE/tracking_issue.yml000064400000000000000000000042041046102023000202660ustar 00000000000000name: Tracking Issue description: A tracking issue for an accepted feature or RFC in Cargo. title: "Tracking Issue for _FEATURE_NAME_" labels: ["C-tracking-issue"] body: - type: markdown attributes: value: > Thank you for creating a tracking issue! Tracking issues are for tracking an accepted feature or RFC from implementation to stabilization. Please do not file a tracking issue until the feature or RFC has been approved. - type: textarea id: summary attributes: label: Summary description: Please provide a very brief summary of the feature. value: | RFC: [#NNNN](https://github.com/rust-lang/rfcs/pull/NNNN) Original issue: #NNNN Implementation: #NNNN Documentation: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#my-feature Please enter a short, one-sentence description here. validations: required: true - type: textarea id: unresolved attributes: label: Unresolved Issues description: List issues that have not yet been resolved. placeholder: | * [ ] Make a list of any known implementation or design issues. - type: textarea id: future attributes: label: Future Extensions description: > An optional section where you can mention where the feature may be extended in the future, but is explicitly not intended to address. - type: textarea id: about attributes: label: About tracking issues description: Please include this notice in the issue. value: | Tracking issues are used to record the overall progress of implementation. They are also used as hubs connecting to other relevant issues, e.g., bugs or open design questions. A tracking issue is however *not* meant for large scale discussion, questions, or bug reports about a feature. Instead, open a dedicated issue for the specific matter and add the relevant feature gate label. cargo-0.91.0/.github/PULL_REQUEST_TEMPLATE.md000064400000000000000000000007041046102023000161500ustar 00000000000000_Thanks for the pull request 🎉!_ _Please read the contribution guide: ._ ### What does this PR try to resolve? _Explain the motivation behind this change._ _A clear overview along with an in-depth explanation are helpful._ ### How to test and review this PR? _Demonstrate how you test this change and guide reviewers through your PR._ _With a smooth review process, a pull request usually gets reviewed quicker._ cargo-0.91.0/.github/renovate.json5000064400000000000000000000060471046102023000151600ustar 00000000000000{ schedule: [ 'before 5am on the first day of the month', ], semanticCommits: 'enabled', configMigration: true, dependencyDashboard: true, ignorePaths: [ '**/tests/**', ], customManagers: [ { customType: 'regex', managerFilePatterns: [ '/Cargo.toml$/', ], matchStrings: [ '\\bMSRV:1\\b.*?(?\\d+\\.\\d+(\\.\\d+)?)', '(?\\d+\\.\\d+(\\.\\d+)?).*?\\bMSRV:1\\b', ], depNameTemplate: 'MSRV:1', // Support 1 version of rustc packageNameTemplate: 'rust-lang/rust', datasourceTemplate: 'github-releases', }, { customType: 'regex', managerFilePatterns: [ '/Cargo.toml$/', ], matchStrings: [ '\\bMSRV:3\\b.*?(?\\d+\\.\\d+(\\.\\d+)?)', '(?\\d+\\.\\d+(\\.\\d+)?).*?\\bMSRV:3\\b', ], depNameTemplate: 'MSRV:3', // Support 3 versions of rustc packageNameTemplate: 'rust-lang/rust', datasourceTemplate: 'github-releases', }, { customType: 'regex', managerFilePatterns: [ '/^.github.workflows.main.yml$/', ], matchStrings: [ 'cargo-semver-checks.releases.download.v(?\\d+\\.\\d+(\\.\\d+)?)', ], depNameTemplate: 'cargo-semver-checks', packageNameTemplate: 'obi1kenobi/cargo-semver-checks', datasourceTemplate: 'github-releases', }, ], packageRules: [ { commitMessageTopic: 'MSRV (1 version)', matchManagers: [ 'custom.regex', ], matchDepNames: [ 'MSRV:1', ], extractVersion: '^(?\\d+\\.\\d+)', // Drop the patch version schedule: [ '* * * * *', ], groupName: 'msrv', }, { commitMessageTopic: 'MSRV (3 versions)', matchManagers: [ 'custom.regex', ], matchDepNames: [ 'MSRV:3', ], extractVersion: '^(?\\d+\\.\\d+)', // Drop the patch version schedule: [ '* * * * *', ], minimumReleaseAge: '85 days', // 2 releases back * 6 weeks per release * 7 days per week + 1 internalChecksFilter: 'strict', groupName: 'msrv', }, { commitMessageTopic: 'cargo-semver-checks', matchManagers: [ 'custom.regex', ], matchDepNames: [ 'cargo-semver-checks', ], extractVersion: '^v(?\\d+\\.\\d+\\.\\d+)', schedule: [ '* * * * *', ], internalChecksFilter: 'strict', }, // Goals: // - Rollup safe upgrades to reduce CI runner load // - Have lockfile and manifest in-sync (implicit rules) { matchManagers: [ 'cargo', ], matchCurrentVersion: '>=0.1.0', matchUpdateTypes: [ 'patch', ], automerge: false, groupName: 'compatible', }, { matchManagers: [ 'cargo', ], matchCurrentVersion: '>=1.0.0', matchUpdateTypes: [ 'minor', ], automerge: false, groupName: 'compatible', }, ], } cargo-0.91.0/.github/workflows/audit.yml000064400000000000000000000011601046102023000162320ustar 00000000000000name: Security audit permissions: contents: read on: pull_request: paths: - '**/Cargo.toml' - '**/Cargo.lock' push: branches: - master jobs: cargo_deny: runs-on: ubuntu-latest strategy: matrix: checks: - advisories - bans licenses sources steps: - uses: actions/checkout@v4 - uses: EmbarkStudios/cargo-deny-action@v2 # Prevent sudden announcement of a new advisory from failing ci: continue-on-error: ${{ matrix.checks == 'advisories' }} with: command: check ${{ matrix.checks }} rust-version: stable cargo-0.91.0/.github/workflows/contrib.yml000064400000000000000000000030161046102023000165660ustar 00000000000000name: Contrib Deploy on: push: branches: - master concurrency: cancel-in-progress: false group: "gh-pages" permissions: contents: read jobs: deploy: permissions: contents: write # for Git to git push runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Install mdbook run: | mkdir mdbook curl -Lf https://github.com/rust-lang/mdBook/releases/download/v0.4.44/mdbook-v0.4.44-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook echo `pwd`/mdbook >> $GITHUB_PATH - name: Deploy docs run: | GENERATE_PY="$(pwd)/ci/generate.py" cd src/doc/contrib mdbook build # Override previous ref to avoid keeping history. git worktree add --orphan -B gh-pages gh-pages git config user.name "Deploy from CI" git config user.email "" cd gh-pages mv ../book contrib git add contrib # Generate HTML for link redirections. python3 "$GENERATE_PY" git add *.html # WARN: The CNAME file is for GitHub to redirect requests to the custom domain. # Missing this may entail security hazard and domain takeover. # See git add CNAME git commit -m "Deploy $GITHUB_SHA to gh-pages" git push origin +gh-pages cargo-0.91.0/.github/workflows/main.yml000064400000000000000000000242731046102023000160620ustar 00000000000000name: CI on: merge_group: pull_request: branches: - "**" defaults: run: shell: bash permissions: contents: read concurrency: group: "${{ github.workflow }}-${{ github.ref }}" cancel-in-progress: true jobs: conclusion: needs: - build_std - clippy - msrv - docs - lint-docs - lockfile - resolver - rustfmt - schema - test - test_gitoxide permissions: contents: none # We need to ensure this job does *not* get skipped if its dependencies fail, # because a skipped job is considered a success by GitHub. So we have to # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run # when the workflow is canceled manually. # # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! if: ${{ !cancelled() }} runs-on: ubuntu-latest steps: # Manually check the status of all dependencies. `if: failure()` does not work. - name: Conclusion run: | # Print the dependent jobs to see them in the CI log jq -C <<< '${{ toJson(needs) }}' # Check if all jobs that we depend on (in the needs array) were successful. jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' # Check Code style quickly by running `rustfmt` over all code rustfmt: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: rustup update stable && rustup default stable - run: rustup component add rustfmt - run: cargo fmt --all --check # Ensure there are no clippy warnings clippy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: rustup update stable && rustup default stable - run: rustup component add clippy - run: cargo clippy --workspace --all-targets --no-deps -- -D warnings stale-label: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: rustup update stable && rustup default stable - run: cargo stale-label lint-docs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: rustup update stable && rustup default stable - run: cargo lint-docs --check # Ensure Cargo.lock is up-to-date lockfile: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: rustup update stable && rustup default stable - run: cargo update -p cargo --locked check-version-bump: runs-on: ubuntu-latest env: BASE_SHA: ${{ github.event.pull_request.base.sha }} HEAD_SHA: ${{ github.event.pull_request.head.sha != '' && github.event.pull_request.head.sha || github.sha }} steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - run: rustup update stable && rustup default stable - name: Install cargo-semver-checks run: | mkdir installed-bins curl -Lf https://github.com/obi1kenobi/cargo-semver-checks/releases/download/v0.42.0/cargo-semver-checks-x86_64-unknown-linux-gnu.tar.gz \ | tar -xz --directory=./installed-bins echo `pwd`/installed-bins >> $GITHUB_PATH - run: ci/validate-version-bump.sh test: runs-on: ${{ matrix.os }} env: CARGO_PROFILE_DEV_DEBUG: 1 CARGO_PROFILE_TEST_DEBUG: 1 CARGO_INCREMENTAL: 0 CARGO_PUBLIC_NETWORK_TESTS: 1 # Workaround for https://github.com/rust-lang/rustup/issues/3036 RUSTUP_WINDOWS_PATH_ADD_BIN: 0 strategy: matrix: include: - name: Linux x86_64 stable os: ubuntu-latest rust: stable other: i686-unknown-linux-gnu - name: Linux x86_64 beta os: ubuntu-latest rust: beta other: i686-unknown-linux-gnu - name: Linux x86_64 nightly os: ubuntu-latest rust: nightly other: i686-unknown-linux-gnu - name: Linux aarch64 stable os: ubuntu-24.04-arm rust: stable other: TODO # cross-compile tests are disabled, this shouldn't matter. - name: Linux aarch64 nightly os: ubuntu-24.04-arm rust: nightly other: TODO # cross-compile tests are disabled, this shouldn't matter. - name: macOS aarch64 stable os: macos-14 rust: stable other: x86_64-apple-darwin - name: macOS x86_64 nightly os: macos-13 rust: nightly other: x86_64-apple-ios - name: macOS aarch64 nightly os: macos-14 rust: nightly other: x86_64-apple-darwin - name: Windows x86_64 MSVC stable os: windows-latest rust: stable-msvc other: i686-pc-windows-msvc - name: Windows x86_64 MSVC nightly os: windows-latest rust: nightly-msvc other: i686-pc-windows-msvc - name: Windows x86_64 gnu nightly # runs out of space while trying to link the test suite os: windows-latest rust: nightly-gnu other: i686-pc-windows-gnu name: Tests ${{ matrix.name }} steps: - uses: actions/checkout@v4 - name: Dump Environment run: ci/dump-environment.sh # Some tests require stable. Make sure it is set to the most recent stable # so that we can predictably handle updates if necessary (and not randomly # when GitHub updates its image). - run: rustup update --no-self-update stable - run: rustup update --no-self-update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} - run: rustup target add ${{ matrix.other }} if: matrix.os != 'ubuntu-24.04-arm' # cross-compile tests are disabled on ARM machines - run: rustup target add wasm32-unknown-unknown - run: rustup target add aarch64-unknown-none # need this for build-std mock tests if: startsWith(matrix.rust, 'nightly') - run: rustup component add rustc-dev llvm-tools-preview rust-docs if: startsWith(matrix.rust, 'nightly') - run: sudo apt update -y && sudo apt install lldb gcc-multilib libsecret-1-0 libsecret-1-dev -y if: matrix.os == 'ubuntu-latest' - run: rustup component add rustfmt || echo "rustfmt not available" - name: Add Windows debuggers bin to PATH shell: pwsh run: Add-Content $env:GITHUB_PATH "C:\Program Files (x86)\Windows Kits\10\Debuggers\x64" if: matrix.os == 'windows-latest' - name: Configure extra test environment run: echo CARGO_CONTAINER_TESTS=1 >> $GITHUB_ENV if: matrix.os == 'ubuntu-latest' - run: cargo test -p cargo - name: Clear intermediate test output run: ci/clean-test-output.sh - name: gitoxide tests (all git-related tests) run: cargo test -p cargo git env: __CARGO_USE_GITOXIDE_INSTEAD_OF_GIT2: 1 # The testsuite generates a huge amount of data, and fetch-smoke-test was # running out of disk space. - name: Clear test output run: ci/clean-test-output.sh # This only tests `cargo fix` because fix-proxy-mode is one of the most # complicated subprocess management in Cargo. - name: Check operability of rustc invocation with argfile run: 'cargo test -p cargo --test testsuite -- fix::' env: __CARGO_TEST_FORCE_ARGFILE: 1 - run: cargo test --workspace --exclude cargo --exclude benchsuite --exclude resolver-tests - name: Check benchmarks run: | # This only tests one benchmark since it can take over 10 minutes to # download all workspaces. cargo test -p benchsuite --all-targets -- cargo cargo check -p capture # The testsuite generates a huge amount of data, and fetch-smoke-test was # running out of disk space. - name: Clear benchmark output run: ci/clean-test-output.sh - name: Fetch smoke test run: ci/fetch-smoke-test.sh schema: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: rustup update stable && rustup default stable - run: cargo test -p cargo-util-schemas -F unstable-schema resolver: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: rustup update stable && rustup default stable - run: cargo test -p resolver-tests test_gitoxide: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: rustup update --no-self-update stable && rustup default stable - run: rustup target add i686-unknown-linux-gnu - run: rustup target add wasm32-unknown-unknown - run: sudo apt update -y && sudo apt install gcc-multilib libsecret-1-0 libsecret-1-dev -y - run: rustup component add rustfmt || echo "rustfmt not available" - run: cargo test -p cargo env: __CARGO_USE_GITOXIDE_INSTEAD_OF_GIT2: 1 build_std: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: rustup update nightly && rustup default nightly - run: rustup component add rust-src - run: cargo build - run: cargo test -p cargo --test build-std env: CARGO_RUN_BUILD_STD_TESTS: 1 docs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: rustup update nightly && rustup default nightly - run: rustup update stable - run: rustup component add rust-docs - run: ci/validate-man.sh # This requires rustfmt, use stable. - name: Run semver-check run: cargo +stable run -p semver-check - name: Ensure intradoc links are valid run: cargo doc --workspace --document-private-items --no-deps env: RUSTDOCFLAGS: -D warnings - name: Install mdbook run: | mkdir mdbook curl -Lf https://github.com/rust-lang/mdBook/releases/download/v0.4.44/mdbook-v0.4.44-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook echo `pwd`/mdbook >> $GITHUB_PATH - run: cd src/doc && mdbook build --dest-dir ../../target/doc - name: Run linkchecker.sh run: | cd target curl -sSLO https://raw.githubusercontent.com/rust-lang/rust/master/src/tools/linkchecker/linkcheck.sh sh linkcheck.sh --all --path ../src/doc cargo msrv: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: taiki-e/install-action@cargo-hack - run: cargo hack check --all-targets --rust-version --workspace --ignore-private --locked cargo-0.91.0/.github/workflows/release.yml000064400000000000000000000014111046102023000165430ustar 00000000000000# Publish Cargo to crates.io whenever a new tag is pushed. Tags are pushed by # the Rust release process (https://github.com/rust-lang/promote-release), # which will cause this workflow to run. name: Release on: push: tags: - "0.*" # Prevent multiple releases from starting at the same time. concurrency: group: release jobs: crates-io: name: Publish on crates.io runs-on: ubuntu-latest permissions: contents: read # Gain access to the crates.io publishing token. environment: name: release steps: - name: Checkout the source code uses: actions/checkout@v4 - name: Publish Cargo to crates.io run: ./publish.py env: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} cargo-0.91.0/.gitignore000064400000000000000000000002301046102023000127710ustar 00000000000000/target /Cargo.lock /config.stamp /Makefile /config.mk /src/doc/build /src/etc/*.pyc /src/registry/target rustc __pycache__ .idea/ .vscode/ *.iml *.swp cargo-0.91.0/.ignore000064400000000000000000000007721046102023000123000ustar 00000000000000# Output generated from src/doc/man # # The goal is to help people find the right file to edit src/doc/man/generated_txt src/doc/src/commands/* src/etc/man !src/doc/src/commands/build-commands.md !src/doc/src/commands/cargo-clippy.md !src/doc/src/commands/cargo-fmt.md !src/doc/src/commands/cargo-miri.md !src/doc/src/commands/general-commands.md !src/doc/src/commands/index.md !src/doc/src/commands/manifest-commands.md !src/doc/src/commands/package-commands.md !src/doc/src/commands/publishg-commands.md cargo-0.91.0/CHANGELOG.md000064400000000000000000000001621046102023000126160ustar 00000000000000# Changelog The changelog has moved to the [Cargo Book](https://doc.rust-lang.org/nightly/cargo/CHANGELOG.html). cargo-0.91.0/CODE_OF_CONDUCT.md000064400000000000000000000002031046102023000136000ustar 00000000000000# The Rust Code of Conduct The Code of Conduct for this repository [can be found online](https://www.rust-lang.org/conduct.html). cargo-0.91.0/CONTRIBUTING.md000064400000000000000000000015031046102023000132360ustar 00000000000000# Contributing to Cargo Contributing documentation has moved to the **[Cargo Contributor Guide]**. [Cargo Contributor Guide]: https://rust-lang.github.io/cargo/contrib/ ## Before hacking on Cargo We encourage people to discuss their design before hacking on code. Typically, you [file an issue] or start a thread on the [internals forum] before submitting a pull request. Please read [the process] of how features and bugs are managed in Cargo. **Only issues that have been explicitly marked as [accepted] will be reviewed.** [internals forum]: https://internals.rust-lang.org/c/tools-and-infrastructure/cargo [file an issue]: https://github.com/rust-lang/cargo/issues [the process]: https://doc.crates.io/contrib/process/index.html [accepted]: https://github.com/rust-lang/cargo/issues?q=is%3Aissue+is%3Aopen+label%3AS-accepted cargo-0.91.0/Cargo.lock0000644000003355430000000000100102060ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "addr2line" version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] name = "adler2" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "allocator-api2" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "annotate-snippets" version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "710e8eae58854cdc1790fcb56cca04d712a17be849eeb81da2a724bf4bae2bc4" dependencies = [ "anstyle", "unicode-width", ] [[package]] name = "anstream" version = "0.6.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anstyle-lossy" version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04d3a5dc826f84d0ea11882bb8054ff7f3d482602e11bb181101303a279ea01f" dependencies = [ "anstyle", ] [[package]] name = "anstyle-parse" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "anstyle-svg" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c681338396641f4e32a29f045d0c70950da7207b4376685b51396c481ee36f1a" dependencies = [ "anstyle", "anstyle-lossy", "anstyle-parse", "html-escape", "unicode-width", ] [[package]] name = "anstyle-wincon" version = "3.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" dependencies = [ "anstyle", "once_cell_polyfill", "windows-sys 0.59.0", ] [[package]] name = "anyhow" version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "arc-swap" version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" [[package]] name = "arrayref" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "atomic-waker" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "backtrace" version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", "windows-targets 0.52.6", ] [[package]] name = "base16ct" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" [[package]] name = "bitflags" version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "bitmaps" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" dependencies = [ "typenum", ] [[package]] name = "blake3" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq", ] [[package]] name = "block-buffer" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] [[package]] name = "bstr" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" dependencies = [ "memchr", "regex-automata 0.4.9", "serde", ] [[package]] name = "bumpalo" version = "3.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cargo" version = "0.91.0" dependencies = [ "annotate-snippets", "anstream", "anstyle", "anyhow", "base64", "blake3", "cargo-credential", "cargo-credential-libsecret", "cargo-credential-macos-keychain", "cargo-credential-wincred", "cargo-platform", "cargo-test-support", "cargo-util", "cargo-util-schemas", "clap", "clap_complete", "color-print", "crates-io", "curl", "curl-sys", "filetime", "flate2", "git2", "git2-curl", "gix", "gix-transport", "glob", "hex", "hmac", "home", "http-auth", "ignore", "im-rc", "indexmap", "itertools", "jiff", "jobserver", "lazycell", "libc", "libgit2-sys", "memchr", "opener", "openssl", "os_info", "pasetors", "pathdiff", "rand", "regex", "rusqlite", "rustc-hash", "rustc-stable-hash", "rustfix", "same-file", "semver", "serde", "serde-untagged", "serde_ignored", "serde_json", "sha1", "shell-escape", "snapbox", "supports-hyperlinks", "supports-unicode", "tar", "tempfile", "thiserror", "time", "toml", "toml_edit", "tracing", "tracing-chrome", "tracing-subscriber", "unicase", "unicode-width", "unicode-xid", "url", "walkdir", "windows-sys 0.60.2", ] [[package]] name = "cargo-credential" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e36f089041deadf16226478a7737a833864fbda09408c7af237b9d615eeb6d69" dependencies = [ "anyhow", "libc", "serde", "serde_json", "thiserror", "time", "windows-sys 0.60.2", ] [[package]] name = "cargo-credential-libsecret" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bad275302dfd55e54dcd555c7129cd76a4b4d7236c6a779370683014cba0a90" dependencies = [ "anyhow", "cargo-credential", "libloading", ] [[package]] name = "cargo-credential-macos-keychain" version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9f95d842bd047476c65e1d4a5f681f1d158f8c784edfc4ae245a2430ca09f02" dependencies = [ "cargo-credential", "security-framework", ] [[package]] name = "cargo-credential-wincred" version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c238839b7d5e5b62836277b4c83c9ed17d9ca7334b298c814c223b2e691ff76" dependencies = [ "cargo-credential", "windows-sys 0.60.2", ] [[package]] name = "cargo-platform" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "122ec45a44b270afd1402f351b782c676b173e3c3fb28d86ff7ebfb4d86a4ee4" dependencies = [ "serde", ] [[package]] name = "cargo-test-macro" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cf9462d076f55ce4e70c1781856c0104b8e6537eb9816f4b7fabc72c26ea45c" [[package]] name = "cargo-test-support" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da0dd551d45bbb303419012f1460e32f20e5dc2fbfa6478cd0720beabc128d5b" dependencies = [ "anstream", "anstyle", "anyhow", "cargo-test-macro", "cargo-util", "crates-io", "filetime", "flate2", "git2", "glob", "itertools", "pasetors", "regex", "serde", "serde_json", "snapbox", "tar", "time", "toml", "url", "walkdir", "windows-sys 0.60.2", ] [[package]] name = "cargo-util" version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fbac95faac578313b0ba60f9a5594a97cae42692f23b133ecd17615dedca50e" dependencies = [ "anyhow", "core-foundation 0.10.1", "filetime", "hex", "ignore", "jobserver", "libc", "miow", "same-file", "sha2", "shell-escape", "tempfile", "tracing", "walkdir", "windows-sys 0.60.2", ] [[package]] name = "cargo-util-schemas" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b45c9672203db3caf908423f25bc31f3b6a814a9d22f2380048236498a312e75" dependencies = [ "semver", "serde", "serde-untagged", "serde-value", "thiserror", "toml", "unicode-xid", "url", ] [[package]] name = "cc" version = "1.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" dependencies = [ "jobserver", "libc", "shlex", ] [[package]] name = "cfg-if" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "clap" version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" dependencies = [ "anstream", "anstyle", "clap_lex", "strsim", "terminal_size", ] [[package]] name = "clap_complete" version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aad5b1b4de04fead402672b48897030eec1f3bfe1550776322f59f6d6e6a5677" dependencies = [ "clap", "clap_lex", "is_executable", "shlex", ] [[package]] name = "clap_lex" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "clru" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbd0f76e066e64fdc5631e3bb46381254deab9ef1158292f27c8c57e3bf3fe59" [[package]] name = "color-print" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3aa954171903797d5623e047d9ab69d91b493657917bdfb8c2c80ecaf9cdb6f4" dependencies = [ "color-print-proc-macro", ] [[package]] name = "color-print-proc-macro" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "692186b5ebe54007e45a59aea47ece9eb4108e141326c304cdc91699a7118a22" dependencies = [ "nom", "proc-macro2", "quote", "syn", ] [[package]] name = "colorchoice" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "const-oid" version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "constant_time_eq" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" [[package]] name = "content_inspector" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7bda66e858c683005a53a9a60c69a4aca7eeaa45d124526e389f7aec8e62f38" dependencies = [ "memchr", ] [[package]] name = "core-foundation" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", ] [[package]] name = "core-foundation" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" dependencies = [ "core-foundation-sys", "libc", ] [[package]] name = "core-foundation-sys" version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] [[package]] name = "crates-io" version = "0.40.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1986712716d18d860258fdbd03fd9d9e20f1ffb974d8a203816c58b68c6b9012" dependencies = [ "curl", "percent-encoding", "serde", "serde_json", "thiserror", "url", ] [[package]] name = "crc32fast" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] [[package]] name = "crossbeam-channel" version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crypto-bigint" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", "rand_core 0.6.4", "subtle", "zeroize", ] [[package]] name = "crypto-common" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "typenum", ] [[package]] name = "ct-codecs" version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b10589d1a5e400d61f9f38f12f884cfd080ff345de8f17efda36fe0e4a02aa8" [[package]] name = "curl" version = "0.4.48" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e2d5c8f48d9c0c23250e52b55e82a6ab4fdba6650c931f5a0a57a43abda812b" dependencies = [ "curl-sys", "libc", "openssl-probe", "openssl-sys", "schannel", "socket2", "windows-sys 0.59.0", ] [[package]] name = "curl-sys" version = "0.4.82+curl-8.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4d63638b5ec65f1a4ae945287b3fd035be4554bbaf211901159c9a2a74fb5be" dependencies = [ "cc", "libc", "libnghttp2-sys", "libz-sys", "openssl-sys", "pkg-config", "vcpkg", "windows-sys 0.59.0", ] [[package]] name = "dashmap" version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" dependencies = [ "cfg-if", "crossbeam-utils", "hashbrown 0.14.5", "lock_api", "once_cell", "parking_lot_core", ] [[package]] name = "der" version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ "const-oid", "pem-rfc7468", "zeroize", ] [[package]] name = "deranged" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", "serde", ] [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "const-oid", "crypto-common", "subtle", ] [[package]] name = "displaydoc" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "dunce" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "ecdsa" version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", "digest", "elliptic-curve", "rfc6979", "signature", "spki", ] [[package]] name = "ed25519-compact" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9b3460f44bea8cd47f45a0c70892f1eff856d97cd55358b2f73f663789f6190" dependencies = [ "getrandom 0.2.16", ] [[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "elliptic-curve" version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", "digest", "ff", "generic-array", "group", "hkdf", "pem-rfc7468", "pkcs8", "rand_core 0.6.4", "sec1", "subtle", "zeroize", ] [[package]] name = "encoding_rs" version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] [[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "erased-serde" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7" dependencies = [ "serde", "typeid", ] [[package]] name = "errno" version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", "windows-sys 0.60.2", ] [[package]] name = "fallible-iterator" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fallible-streaming-iterator" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] name = "faster-hex" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7223ae2d2f179b803433d9c830478527e92b8117eab39460edae7f1614d9fb73" dependencies = [ "heapless", "serde", ] [[package]] name = "fastrand" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "ff" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" dependencies = [ "rand_core 0.6.4", "subtle", ] [[package]] name = "fiat-crypto" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" [[package]] name = "filetime" version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" dependencies = [ "cfg-if", "libc", "libredox", "windows-sys 0.59.0", ] [[package]] name = "flate2" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" dependencies = [ "crc32fast", "libz-rs-sys", "miniz_oxide", ] [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "foreign-types" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ "foreign-types-shared", ] [[package]] name = "foreign-types-shared" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] [[package]] name = "futures-channel" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", ] [[package]] name = "futures-core" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-io" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-sink" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", "futures-io", "futures-sink", "futures-task", "memchr", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "generic-array" version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", "zeroize", ] [[package]] name = "getrandom" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "js-sys", "libc", "wasi 0.11.1+wasi-snapshot-preview1", "wasm-bindgen", ] [[package]] name = "getrandom" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "js-sys", "libc", "r-efi", "wasi 0.14.2+wasi-0.2.4", "wasm-bindgen", ] [[package]] name = "gimli" version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "git2" version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2deb07a133b1520dc1a5690e9bd08950108873d7ed5de38dcc74d3b5ebffa110" dependencies = [ "bitflags", "libc", "libgit2-sys", "log", "openssl-probe", "openssl-sys", "url", ] [[package]] name = "git2-curl" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be8dcabbc09ece4d30a9aa983d5804203b7e2f8054a171f792deff59b56d31fa" dependencies = [ "curl", "git2", "log", "url", ] [[package]] name = "gix" version = "0.73.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "514c29cc879bdc0286b0cbc205585a49b252809eb86c69df4ce4f855ee75f635" dependencies = [ "gix-actor", "gix-attributes", "gix-command", "gix-commitgraph", "gix-config", "gix-credentials", "gix-date", "gix-diff", "gix-dir", "gix-discover", "gix-features", "gix-filter", "gix-fs", "gix-glob", "gix-hash", "gix-hashtable", "gix-ignore", "gix-index", "gix-lock", "gix-negotiate", "gix-object", "gix-odb", "gix-pack", "gix-path", "gix-pathspec", "gix-prompt", "gix-protocol", "gix-ref", "gix-refspec", "gix-revision", "gix-revwalk", "gix-sec", "gix-shallow", "gix-status", "gix-submodule", "gix-tempfile", "gix-trace", "gix-transport", "gix-traverse", "gix-url", "gix-utils", "gix-validate", "gix-worktree", "once_cell", "prodash", "smallvec", "thiserror", ] [[package]] name = "gix-actor" version = "0.35.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58ebbb8f41071c7cf318a0b1db667c34e1df49db7bf387d282a4e61a3b97882c" dependencies = [ "bstr", "gix-date", "gix-utils", "itoa", "thiserror", "winnow", ] [[package]] name = "gix-attributes" version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45442188216d08a5959af195f659cb1f244a50d7d2d0c3873633b1cd7135f638" dependencies = [ "bstr", "gix-glob", "gix-path", "gix-quote", "gix-trace", "kstring", "smallvec", "thiserror", "unicode-bom", ] [[package]] name = "gix-bitmap" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1db9765c69502650da68f0804e3dc2b5f8ccc6a2d104ca6c85bc40700d37540" dependencies = [ "thiserror", ] [[package]] name = "gix-chunk" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b1f1d8764958699dc764e3f727cef280ff4d1bd92c107bbf8acd85b30c1bd6f" dependencies = [ "thiserror", ] [[package]] name = "gix-command" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b31b65ca48a352ae86312b27a514a0c661935f96b481ac8b4371f65815eb196" dependencies = [ "bstr", "gix-path", "gix-quote", "gix-trace", "shell-words", ] [[package]] name = "gix-commitgraph" version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bb23121e952f43a5b07e3e80890336cb847297467a410475036242732980d06" dependencies = [ "bstr", "gix-chunk", "gix-hash", "memmap2", "thiserror", ] [[package]] name = "gix-config" version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfb898c5b695fd4acfc3c0ab638525a65545d47706064dcf7b5ead6cdb136c0" dependencies = [ "bstr", "gix-config-value", "gix-features", "gix-glob", "gix-path", "gix-ref", "gix-sec", "memchr", "once_cell", "smallvec", "thiserror", "unicode-bom", "winnow", ] [[package]] name = "gix-config-value" version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f012703eb67e263c6c1fc96649fec47694dd3e5d2a91abfc65e4a6a6dc85309" dependencies = [ "bitflags", "bstr", "gix-path", "libc", "thiserror", ] [[package]] name = "gix-credentials" version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0039dd3ac606dd80b16353a41b61fc237ca5cb8b612f67a9f880adfad4be4e05" dependencies = [ "bstr", "gix-command", "gix-config-value", "gix-date", "gix-path", "gix-prompt", "gix-sec", "gix-trace", "gix-url", "thiserror", ] [[package]] name = "gix-date" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7235bdf4d9d54a6901928e3a37f91c16f419e6957f520ed929c3d292b84226e" dependencies = [ "bstr", "itoa", "jiff", "smallvec", "thiserror", ] [[package]] name = "gix-diff" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de854852010d44a317f30c92d67a983e691c9478c8a3fb4117c1f48626bcdea8" dependencies = [ "bstr", "gix-attributes", "gix-command", "gix-filter", "gix-fs", "gix-hash", "gix-index", "gix-object", "gix-path", "gix-pathspec", "gix-tempfile", "gix-trace", "gix-traverse", "gix-worktree", "imara-diff", "thiserror", ] [[package]] name = "gix-dir" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dad34e4f373f94902df1ba1d2a1df3a1b29eacd15e316ac5972d842e31422dd7" dependencies = [ "bstr", "gix-discover", "gix-fs", "gix-ignore", "gix-index", "gix-object", "gix-path", "gix-pathspec", "gix-trace", "gix-utils", "gix-worktree", "thiserror", ] [[package]] name = "gix-discover" version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffb180c91ca1a2cf53e828bb63d8d8f8fa7526f49b83b33d7f46cbeb5d79d30a" dependencies = [ "bstr", "dunce", "gix-fs", "gix-hash", "gix-path", "gix-ref", "gix-sec", "thiserror", ] [[package]] name = "gix-features" version = "0.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a92748623c201568785ee69a561f4eec06f745b4fac67dab1d44ca9891a57ee" dependencies = [ "bytes", "crc32fast", "crossbeam-channel", "flate2", "gix-path", "gix-trace", "gix-utils", "libc", "once_cell", "parking_lot", "prodash", "thiserror", "walkdir", ] [[package]] name = "gix-filter" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa6571a3927e7ab10f64279a088e0dae08e8da05547771796d7389bbe28ad9ff" dependencies = [ "bstr", "encoding_rs", "gix-attributes", "gix-command", "gix-hash", "gix-object", "gix-packetline-blocking", "gix-path", "gix-quote", "gix-trace", "gix-utils", "smallvec", "thiserror", ] [[package]] name = "gix-fs" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d793f71e955d18f228d20ec433dcce6d0e8577efcdfd11d72d09d7cc2758dfd1" dependencies = [ "bstr", "fastrand", "gix-features", "gix-path", "gix-utils", "thiserror", ] [[package]] name = "gix-glob" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b947db8366823e7a750c254f6bb29e27e17f27e457bf336ba79b32423db62cd5" dependencies = [ "bitflags", "bstr", "gix-features", "gix-path", ] [[package]] name = "gix-hash" version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "251fad79796a731a2a7664d9ea95ee29a9e99474de2769e152238d4fdb69d50e" dependencies = [ "faster-hex", "gix-features", "sha1-checked", "thiserror", ] [[package]] name = "gix-hashtable" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c35300b54896153e55d53f4180460931ccd69b7e8d2f6b9d6401122cdedc4f07" dependencies = [ "gix-hash", "hashbrown 0.15.4", "parking_lot", ] [[package]] name = "gix-ignore" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "564d6fddf46e2c981f571b23d6ad40cb08bddcaf6fc7458b1d49727ad23c2870" dependencies = [ "bstr", "gix-glob", "gix-path", "gix-trace", "unicode-bom", ] [[package]] name = "gix-index" version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af39fde3ce4ce11371d9ce826f2936ec347318f2d1972fe98c2e7134e267e25" dependencies = [ "bitflags", "bstr", "filetime", "fnv", "gix-bitmap", "gix-features", "gix-fs", "gix-hash", "gix-lock", "gix-object", "gix-traverse", "gix-utils", "gix-validate", "hashbrown 0.15.4", "itoa", "libc", "memmap2", "rustix", "smallvec", "thiserror", ] [[package]] name = "gix-lock" version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9fa71da90365668a621e184eb5b979904471af1b3b09b943a84bc50e8ad42ed" dependencies = [ "gix-tempfile", "gix-utils", "thiserror", ] [[package]] name = "gix-negotiate" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d58d4c9118885233be971e0d7a589f5cfb1a8bd6cb6e2ecfb0fc6b1b293c83b" dependencies = [ "bitflags", "gix-commitgraph", "gix-date", "gix-hash", "gix-object", "gix-revwalk", "smallvec", "thiserror", ] [[package]] name = "gix-object" version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49664e3e212bc34f7060f5738ce7022247e4afd959b68a4f666b1fd29c00b23c" dependencies = [ "bstr", "gix-actor", "gix-date", "gix-features", "gix-hash", "gix-hashtable", "gix-path", "gix-utils", "gix-validate", "itoa", "smallvec", "thiserror", "winnow", ] [[package]] name = "gix-odb" version = "0.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c9d7af10fda9df0bb4f7f9bd507963560b3c66cb15a5b825caf752e0eb109ac" dependencies = [ "arc-swap", "gix-date", "gix-features", "gix-fs", "gix-hash", "gix-hashtable", "gix-object", "gix-pack", "gix-path", "gix-quote", "parking_lot", "tempfile", "thiserror", ] [[package]] name = "gix-pack" version = "0.60.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8571df89bfca5abb49c3e3372393f7af7e6f8b8dbe2b96303593cef5b263019" dependencies = [ "clru", "gix-chunk", "gix-features", "gix-hash", "gix-hashtable", "gix-object", "gix-path", "gix-tempfile", "memmap2", "parking_lot", "smallvec", "thiserror", ] [[package]] name = "gix-packetline" version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2592fbd36249a2fea11056f7055cc376301ef38d903d157de41998335bbf1f93" dependencies = [ "bstr", "faster-hex", "gix-trace", "thiserror", ] [[package]] name = "gix-packetline-blocking" version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc4e706f328cd494cc8f932172e123a72b9a4711b0db5e411681432a89bd4c94" dependencies = [ "bstr", "faster-hex", "gix-trace", "thiserror", ] [[package]] name = "gix-path" version = "0.10.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6279d323d925ad4790602105ae27df4b915e7a7d81e4cdba2603121c03ad111" dependencies = [ "bstr", "gix-trace", "gix-validate", "home", "once_cell", "thiserror", ] [[package]] name = "gix-pathspec" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daedead611c9bd1f3640dc90a9012b45f790201788af4d659f28d94071da7fba" dependencies = [ "bitflags", "bstr", "gix-attributes", "gix-config-value", "gix-glob", "gix-path", "thiserror", ] [[package]] name = "gix-prompt" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ffa1a7a34c81710aaa666a428c142b6c5d640492fcd41267db0740d923c7906" dependencies = [ "gix-command", "gix-config-value", "parking_lot", "rustix", "thiserror", ] [[package]] name = "gix-protocol" version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12b4b807c47ffcf7c1e5b8119585368a56449f3493da93b931e1d4239364e922" dependencies = [ "bstr", "gix-credentials", "gix-date", "gix-features", "gix-hash", "gix-lock", "gix-negotiate", "gix-object", "gix-ref", "gix-refspec", "gix-revwalk", "gix-shallow", "gix-trace", "gix-transport", "gix-utils", "maybe-async", "thiserror", "winnow", ] [[package]] name = "gix-quote" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a375a75b4d663e8bafe3bf4940a18a23755644c13582fa326e99f8f987d83fd" dependencies = [ "bstr", "gix-utils", "thiserror", ] [[package]] name = "gix-ref" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b7a23209d4e4cbdc2086d294f5f3f8707ac6286768847024d952d8cd3278c5b" dependencies = [ "gix-actor", "gix-features", "gix-fs", "gix-hash", "gix-lock", "gix-object", "gix-path", "gix-tempfile", "gix-utils", "gix-validate", "memmap2", "thiserror", "winnow", ] [[package]] name = "gix-refspec" version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d29cae1ae31108826e7156a5e60bffacab405f4413f5bc0375e19772cce0055" dependencies = [ "bstr", "gix-hash", "gix-revision", "gix-validate", "smallvec", "thiserror", ] [[package]] name = "gix-revision" version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f651f2b1742f760bb8161d6743229206e962b73d9c33c41f4e4aefa6586cbd3d" dependencies = [ "bitflags", "bstr", "gix-commitgraph", "gix-date", "gix-hash", "gix-hashtable", "gix-object", "gix-revwalk", "gix-trace", "thiserror", ] [[package]] name = "gix-revwalk" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06e74f91709729e099af6721bd0fa7d62f243f2005085152301ca5cdd86ec02c" dependencies = [ "gix-commitgraph", "gix-date", "gix-hash", "gix-hashtable", "gix-object", "smallvec", "thiserror", ] [[package]] name = "gix-sec" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09f7053ed7c66633b56c57bc6ed3377be3166eaf3dc2df9f1c5ec446df6fdf2c" dependencies = [ "bitflags", "gix-path", "libc", "windows-sys 0.59.0", ] [[package]] name = "gix-shallow" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d936745103243ae4c510f19e0760ce73fb0f08096588fdbe0f0d7fb7ce8944b7" dependencies = [ "bstr", "gix-hash", "gix-lock", "thiserror", ] [[package]] name = "gix-status" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a4afff9b34eeececa8bdc32b42fb318434b6b1391d9f8d45fe455af08dc2d35" dependencies = [ "bstr", "filetime", "gix-diff", "gix-dir", "gix-features", "gix-filter", "gix-fs", "gix-hash", "gix-index", "gix-object", "gix-path", "gix-pathspec", "gix-worktree", "portable-atomic", "thiserror", ] [[package]] name = "gix-submodule" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "657cc5dd43cbc7a14d9c5aaf02cfbe9c2a15d077cded3f304adb30ef78852d3e" dependencies = [ "bstr", "gix-config", "gix-path", "gix-pathspec", "gix-refspec", "gix-url", "thiserror", ] [[package]] name = "gix-tempfile" version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "666c0041bcdedf5fa05e9bef663c897debab24b7dc1741605742412d1d47da57" dependencies = [ "dashmap", "gix-fs", "libc", "once_cell", "parking_lot", "tempfile", ] [[package]] name = "gix-trace" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2ccaf54b0b1743a695b482ca0ab9d7603744d8d10b2e5d1a332fef337bee658" [[package]] name = "gix-transport" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12f7cc0179fc89d53c54e1f9ce51229494864ab4bf136132d69db1b011741ca3" dependencies = [ "base64", "bstr", "curl", "gix-command", "gix-credentials", "gix-features", "gix-packetline", "gix-quote", "gix-sec", "gix-url", "reqwest", "thiserror", ] [[package]] name = "gix-traverse" version = "0.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7cdc82509d792ba0ad815f86f6b469c7afe10f94362e96c4494525a6601bdd5" dependencies = [ "bitflags", "gix-commitgraph", "gix-date", "gix-hash", "gix-hashtable", "gix-object", "gix-revwalk", "smallvec", "thiserror", ] [[package]] name = "gix-url" version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b76a9d266254ad287ffd44467cd88e7868799b08f4d52e02d942b93e514d16f" dependencies = [ "bstr", "gix-features", "gix-path", "percent-encoding", "thiserror", "url", ] [[package]] name = "gix-utils" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5351af2b172caf41a3728eb4455326d84e0d70fe26fc4de74ab0bd37df4191c5" dependencies = [ "bstr", "fastrand", "unicode-normalization", ] [[package]] name = "gix-validate" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77b9e00cacde5b51388d28ed746c493b18a6add1f19b5e01d686b3b9ece66d4d" dependencies = [ "bstr", "thiserror", ] [[package]] name = "gix-worktree" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55f625ac9126c19bef06dbc6d2703cdd7987e21e35b497bb265ac37d383877b1" dependencies = [ "bstr", "gix-attributes", "gix-features", "gix-fs", "gix-glob", "gix-hash", "gix-ignore", "gix-index", "gix-object", "gix-path", "gix-validate", ] [[package]] name = "glob" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "globset" version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" dependencies = [ "aho-corasick", "bstr", "log", "regex-automata 0.4.9", "regex-syntax 0.8.5", ] [[package]] name = "group" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", "rand_core 0.6.4", "subtle", ] [[package]] name = "h2" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", "http", "indexmap", "slab", "tokio", "tokio-util", "tracing", ] [[package]] name = "hash32" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" dependencies = [ "byteorder", ] [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" dependencies = [ "allocator-api2", "equivalent", "foldhash", ] [[package]] name = "hashlink" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ "hashbrown 0.15.4", ] [[package]] name = "heapless" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" dependencies = [ "hash32", "stable_deref_trait", ] [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hkdf" version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ "hmac", ] [[package]] name = "hmac" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ "digest", ] [[package]] name = "home" version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "html-escape" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476" dependencies = [ "utf8-width", ] [[package]] name = "http" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", "itoa", ] [[package]] name = "http-auth" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "150fa4a9462ef926824cf4519c84ed652ca8f4fbae34cb8af045b5cbcaf98822" dependencies = [ "memchr", ] [[package]] name = "http-body" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", "http", ] [[package]] name = "http-body-util" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", "http", "http-body", "pin-project-lite", ] [[package]] name = "httparse" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hyper" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", "futures-channel", "futures-util", "h2", "http", "http-body", "httparse", "itoa", "pin-project-lite", "smallvec", "tokio", "want", ] [[package]] name = "hyper-rustls" version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ "http", "hyper", "hyper-util", "rustls", "rustls-pki-types", "tokio", "tokio-rustls", "tower-service", ] [[package]] name = "hyper-util" version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f66d5bd4c6f02bf0542fad85d626775bab9258cf795a4256dcaf3161114d1df" dependencies = [ "base64", "bytes", "futures-channel", "futures-core", "futures-util", "http", "http-body", "hyper", "ipnet", "libc", "percent-encoding", "pin-project-lite", "socket2", "system-configuration", "tokio", "tower-service", "tracing", "windows-registry", ] [[package]] name = "icu_collections" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] name = "icu_locale_core" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ "displaydoc", "litemap", "tinystr", "writeable", "zerovec", ] [[package]] name = "icu_normalizer" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", "icu_provider", "smallvec", "zerovec", ] [[package]] name = "icu_normalizer_data" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", "potential_utf", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] name = "icu_provider" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ "displaydoc", "icu_locale_core", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", "zerotrie", "zerovec", ] [[package]] name = "idna" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ "idna_adapter", "smallvec", "utf8_iter", ] [[package]] name = "idna_adapter" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", ] [[package]] name = "ignore" version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" dependencies = [ "crossbeam-deque", "globset", "log", "memchr", "regex-automata 0.4.9", "same-file", "walkdir", "winapi-util", ] [[package]] name = "im-rc" version = "15.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af1955a75fa080c677d3972822ec4bad316169ab1cfc6c257a942c2265dbe5fe" dependencies = [ "bitmaps", "rand_core 0.6.4", "rand_xoshiro", "sized-chunks", "typenum", "version_check", ] [[package]] name = "imara-diff" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17d34b7d42178945f775e84bc4c36dde7c1c6cdfea656d3354d009056f2bb3d2" dependencies = [ "hashbrown 0.15.4", ] [[package]] name = "indexmap" version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", "hashbrown 0.15.4", ] [[package]] name = "io-uring" version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" dependencies = [ "bitflags", "cfg-if", "libc", ] [[package]] name = "ipnet" version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "iri-string" version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" dependencies = [ "memchr", "serde", ] [[package]] name = "is_executable" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4a1b5bad6f9072935961dfbf1cced2f3d129963d091b6f69f007fe04e758ae2" dependencies = [ "winapi", ] [[package]] name = "is_terminal_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itertools" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" dependencies = [ "either", ] [[package]] name = "itoa" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" dependencies = [ "jiff-static", "jiff-tzdb-platform", "log", "portable-atomic", "portable-atomic-util", "serde", "windows-sys 0.59.0", ] [[package]] name = "jiff-static" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "jiff-tzdb" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1283705eb0a21404d2bfd6eef2a7593d240bc42a0bdb39db0ad6fa2ec026524" [[package]] name = "jiff-tzdb-platform" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" dependencies = [ "jiff-tzdb", ] [[package]] name = "jobserver" version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ "getrandom 0.3.3", "libc", ] [[package]] name = "js-sys" version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", ] [[package]] name = "kstring" version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "558bf9508a558512042d3095138b1f7b8fe90c5467d94f9f1da28b3731c5dbd1" dependencies = [ "static_assertions", ] [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lazycell" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "libgit2-sys" version = "0.18.2+1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c42fe03df2bd3c53a3a9c7317ad91d80c81cd1fb0caec8d7cc4cd2bfa10c222" dependencies = [ "cc", "libc", "libssh2-sys", "libz-sys", "openssl-sys", "pkg-config", ] [[package]] name = "libloading" version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", "windows-targets 0.53.2", ] [[package]] name = "libnghttp2-sys" version = "0.1.11+1.64.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b6c24e48a7167cffa7119da39d577fa482e66c688a4aac016bee862e1a713c4" dependencies = [ "cc", "libc", ] [[package]] name = "libredox" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags", "libc", "redox_syscall", ] [[package]] name = "libsqlite3-sys" version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91632f3b4fb6bd1d72aa3d78f41ffecfcf2b1a6648d8c241dbe7dbfaf4875e15" dependencies = [ "cc", "pkg-config", "vcpkg", ] [[package]] name = "libssh2-sys" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "220e4f05ad4a218192533b300327f5150e809b54c4ec83b5a1d91833601811b9" dependencies = [ "cc", "libc", "libz-sys", "openssl-sys", "pkg-config", "vcpkg", ] [[package]] name = "libz-rs-sys" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "172a788537a2221661b480fee8dc5f96c580eb34fa88764d3205dc356c7e4221" dependencies = [ "zlib-rs", ] [[package]] name = "libz-sys" version = "1.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d" dependencies = [ "cc", "libc", "pkg-config", "vcpkg", ] [[package]] name = "linux-raw-sys" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", ] [[package]] name = "log" version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "matchers" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ "regex-automata 0.1.10", ] [[package]] name = "maybe-async" version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "memchr" version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "memmap2" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" dependencies = [ "libc", ] [[package]] name = "mime" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", ] [[package]] name = "mio" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.59.0", ] [[package]] name = "miow" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "359f76430b20a79f9e20e115b3428614e654f04fab314482fc0fda0ebd3c6044" dependencies = [ "windows-sys 0.48.0", ] [[package]] name = "nom" version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", ] [[package]] name = "normalize-line-endings" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "normpath" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8911957c4b1549ac0dc74e30db9c8b0e66ddcd6d7acc33098f4c63a64a6d7ed" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "nu-ansi-term" version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" dependencies = [ "overload", "winapi", ] [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "object" version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "once_cell_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" [[package]] name = "opener" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "771b9704f8cd8b424ec747a320b30b47517a6966ba2c7da90047c16f4a962223" dependencies = [ "bstr", "normpath", "windows-sys 0.59.0", ] [[package]] name = "openssl" version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ "bitflags", "cfg-if", "foreign-types", "libc", "once_cell", "openssl-macros", "openssl-sys", ] [[package]] name = "openssl-macros" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "openssl-probe" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-src" version = "300.5.0+3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8ce546f549326b0e6052b649198487d91320875da901e7bd11a06d1ee3f9c2f" dependencies = [ "cc", ] [[package]] name = "openssl-sys" version = "0.9.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" dependencies = [ "cc", "libc", "openssl-src", "pkg-config", "vcpkg", ] [[package]] name = "ordered-float" version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" dependencies = [ "num-traits", ] [[package]] name = "orion" version = "0.17.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21b3da83b2b4cdc74ab6a556b2e7b473da046d5aa4008c0a7a3ae96b1b4aabb4" dependencies = [ "fiat-crypto", "subtle", "zeroize", ] [[package]] name = "os_info" version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0e1ac5fde8d43c34139135df8ea9ee9465394b2d8d20f032d38998f64afffc3" dependencies = [ "log", "plist", "windows-sys 0.52.0", ] [[package]] name = "overload" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "p384" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" dependencies = [ "ecdsa", "elliptic-curve", "primeorder", "sha2", ] [[package]] name = "parking_lot" version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", "windows-targets 0.52.6", ] [[package]] name = "pasetors" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c1c6d47c07a08cb59ddad44d53cb5729855e35f7d993184cec3aa37ccdb7008" dependencies = [ "ct-codecs", "ed25519-compact", "getrandom 0.3.3", "orion", "p384", "rand_core 0.6.4", "regex", "serde", "serde_derive", "serde_json", "sha2", "subtle", "time", "zeroize", ] [[package]] name = "pathdiff" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" [[package]] name = "pem-rfc7468" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" dependencies = [ "base64ct", ] [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project-lite" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkcs8" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ "der", "spki", ] [[package]] name = "pkg-config" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "plist" version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d77244ce2d584cd84f6a15f86195b8c9b2a0dfbfd817c09e0464244091a58ed" dependencies = [ "base64", "indexmap", "quick-xml", "serde", "time", ] [[package]] name = "portable-atomic" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "portable-atomic-util" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" dependencies = [ "portable-atomic", ] [[package]] name = "potential_utf" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" dependencies = [ "zerovec", ] [[package]] name = "powerfmt" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ "zerocopy", ] [[package]] name = "primeorder" version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" dependencies = [ "elliptic-curve", ] [[package]] name = "proc-macro2" version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] name = "prodash" version = "30.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a6efc566849d3d9d737c5cb06cc50e48950ebe3d3f9d70631490fff3a07b139" dependencies = [ "parking_lot", ] [[package]] name = "quick-xml" version = "0.37.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" dependencies = [ "memchr", ] [[package]] name = "quote" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" [[package]] name = "rand" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" dependencies = [ "rand_chacha", "rand_core 0.9.3", ] [[package]] name = "rand_chacha" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", "rand_core 0.9.3", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom 0.2.16", ] [[package]] name = "rand_core" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ "getrandom 0.3.3", ] [[package]] name = "rand_xoshiro" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" dependencies = [ "rand_core 0.6.4", ] [[package]] name = "redox_syscall" version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" dependencies = [ "bitflags", ] [[package]] name = "regex" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", "regex-automata 0.4.9", "regex-syntax 0.8.5", ] [[package]] name = "regex-automata" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ "regex-syntax 0.6.29", ] [[package]] name = "regex-automata" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", "regex-syntax 0.8.5", ] [[package]] name = "regex-syntax" version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" version = "0.12.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" dependencies = [ "base64", "bytes", "encoding_rs", "futures-channel", "futures-core", "futures-util", "h2", "http", "http-body", "http-body-util", "hyper", "hyper-rustls", "hyper-util", "js-sys", "log", "mime", "percent-encoding", "pin-project-lite", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", "tokio", "tower", "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", ] [[package]] name = "rfc6979" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ "hmac", "subtle", ] [[package]] name = "ring" version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", "getrandom 0.2.16", "libc", "untrusted", "windows-sys 0.52.0", ] [[package]] name = "rusqlite" version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3de23c3319433716cf134eed225fe9986bc24f63bed9be9f20c329029e672dc7" dependencies = [ "bitflags", "fallible-iterator", "fallible-streaming-iterator", "hashlink", "libsqlite3-sys", "smallvec", ] [[package]] name = "rustc-demangle" version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" [[package]] name = "rustc-hash" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustc-stable-hash" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "781442f29170c5c93b7185ad559492601acdc71d5bb0706f5868094f45cfcd08" [[package]] name = "rustfix" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "662decac6b0486a3bfb3723a0621eb12f5ae199488ddf87582d09f8c7b3e9889" dependencies = [ "serde", "serde_json", "thiserror", "tracing", ] [[package]] name = "rustix" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys 0.59.0", ] [[package]] name = "rustls" version = "0.23.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2491382039b29b9b11ff08b76ff6c97cf287671dbb74f0be44bda389fffe9bd1" dependencies = [ "once_cell", "rustls-pki-types", "rustls-webpki", "subtle", "zeroize", ] [[package]] name = "rustls-pki-types" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" dependencies = [ "zeroize", ] [[package]] name = "rustls-webpki" version = "0.103.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" dependencies = [ "ring", "rustls-pki-types", "untrusted", ] [[package]] name = "rustversion" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "ryu" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "schannel" version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sec1" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct", "der", "generic-array", "pkcs8", "subtle", "zeroize", ] [[package]] name = "security-framework" version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" dependencies = [ "bitflags", "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", ] [[package]] name = "security-framework-sys" version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", ] [[package]] name = "semver" version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" dependencies = [ "serde", ] [[package]] name = "serde" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde-untagged" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "299d9c19d7d466db4ab10addd5703e4c615dec2a5a16dbbafe191045e87ee66e" dependencies = [ "erased-serde", "serde", "typeid", ] [[package]] name = "serde-value" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" dependencies = [ "ordered-float", "serde", ] [[package]] name = "serde_derive" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_ignored" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b516445dac1e3535b6d658a7b528d771153dfb272ed4180ca4617a20550365ff" dependencies = [ "serde", ] [[package]] name = "serde_json" version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", "memchr", "ryu", "serde", ] [[package]] name = "serde_spanned" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" dependencies = [ "serde", ] [[package]] name = "serde_urlencoded" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", "itoa", "ryu", "serde", ] [[package]] name = "sha1" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", "digest", ] [[package]] name = "sha1-checked" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89f599ac0c323ebb1c6082821a54962b839832b03984598375bff3975b804423" dependencies = [ "digest", "sha1", ] [[package]] name = "sha2" version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", "digest", ] [[package]] name = "sharded-slab" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] [[package]] name = "shell-escape" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" [[package]] name = "shell-words" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signature" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest", "rand_core 0.6.4", ] [[package]] name = "similar" version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" [[package]] name = "sized-chunks" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" dependencies = [ "bitmaps", "typenum", ] [[package]] name = "slab" version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" [[package]] name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "snapbox" version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96dcfc4581e3355d70ac2ee14cfdf81dce3d85c85f1ed9e2c1d3013f53b3436b" dependencies = [ "anstream", "anstyle", "anstyle-svg", "content_inspector", "dunce", "filetime", "normalize-line-endings", "regex", "serde", "serde_json", "similar", "snapbox-macros", "tempfile", "walkdir", ] [[package]] name = "snapbox-macros" version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16569f53ca23a41bb6f62e0a5084aa1661f4814a67fa33696a79073e03a664af" dependencies = [ "anstream", ] [[package]] name = "socket2" version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] name = "spki" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", "der", ] [[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "supports-hyperlinks" version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "804f44ed3c63152de6a9f90acbea1a110441de43006ea51bcce8f436196a288b" [[package]] name = "supports-unicode" version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" [[package]] name = "syn" version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "sync_wrapper" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ "futures-core", ] [[package]] name = "synstructure" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "system-configuration" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ "bitflags", "core-foundation 0.9.4", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" dependencies = [ "core-foundation-sys", "libc", ] [[package]] name = "tar" version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" dependencies = [ "filetime", "libc", ] [[package]] name = "tempfile" version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", "rustix", "windows-sys 0.59.0", ] [[package]] name = "terminal_size" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" dependencies = [ "rustix", "windows-sys 0.59.0", ] [[package]] name = "thiserror" version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "thread_local" version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", ] [[package]] name = "time" version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", "itoa", "num-conv", "powerfmt", "serde", "time-core", "time-macros", ] [[package]] name = "time-core" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ "num-conv", "time-core", ] [[package]] name = "tinystr" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", "zerovec", ] [[package]] name = "tinyvec" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" dependencies = [ "tinyvec_macros", ] [[package]] name = "tinyvec_macros" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" version = "1.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" dependencies = [ "backtrace", "bytes", "io-uring", "libc", "mio", "pin-project-lite", "slab", "socket2", "windows-sys 0.52.0", ] [[package]] name = "tokio-rustls" version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ "rustls", "tokio", ] [[package]] name = "tokio-util" version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", ] [[package]] name = "toml" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed0aee96c12fa71097902e0bb061a5e1ebd766a6636bb605ba401c45c1650eac" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", "toml_parser", "toml_writer", "winnow", ] [[package]] name = "toml_datetime" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" dependencies = [ "serde", ] [[package]] name = "toml_edit" version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1dee9dc43ac2aaf7d3b774e2fba5148212bf2bd9374f4e50152ebe9afd03d42" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", "toml_parser", "toml_writer", "winnow", ] [[package]] name = "toml_parser" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97200572db069e74c512a14117b296ba0a80a30123fbbb5aa1f4a348f639ca30" dependencies = [ "winnow", ] [[package]] name = "toml_writer" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" [[package]] name = "tower" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", "pin-project-lite", "sync_wrapper", "tokio", "tower-layer", "tower-service", ] [[package]] name = "tower-http" version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ "bitflags", "bytes", "futures-util", "http", "http-body", "iri-string", "pin-project-lite", "tower", "tower-layer", "tower-service", ] [[package]] name = "tower-layer" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tracing-chrome" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf0a738ed5d6450a9fb96e86a23ad808de2b727fd1394585da5cdd6788ffe724" dependencies = [ "serde_json", "tracing-core", "tracing-subscriber", ] [[package]] name = "tracing-core" version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", ] [[package]] name = "tracing-log" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ "log", "once_cell", "tracing-core", ] [[package]] name = "tracing-subscriber" version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", "nu-ansi-term", "once_cell", "regex", "sharded-slab", "smallvec", "thread_local", "tracing", "tracing-core", "tracing-log", ] [[package]] name = "try-lock" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typeid" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "typenum" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "unicase" version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-bom" version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7eec5d1121208364f6793f7d2e222bf75a915c19557537745b195b253dd64217" [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-normalization" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] [[package]] name = "unicode-width" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" [[package]] name = "unicode-xid" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "untrusted" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", "percent-encoding", ] [[package]] name = "utf8-width" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" [[package]] name = "utf8_iter" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "valuable" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "walkdir" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", ] [[package]] name = "want" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ "try-lock", ] [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ "wit-bindgen-rt", ] [[package]] name = "wasm-bindgen" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-link" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-registry" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" dependencies = [ "windows-link", "windows-result", "windows-strings", ] [[package]] name = "windows-result" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ "windows-link", ] [[package]] name = "windows-strings" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ "windows-link", ] [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets 0.48.5", ] [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets 0.52.6", ] [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets 0.52.6", ] [[package]] name = "windows-sys" version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ "windows-targets 0.53.2", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm 0.48.5", "windows_aarch64_msvc 0.48.5", "windows_i686_gnu 0.48.5", "windows_i686_msvc 0.48.5", "windows_x86_64_gnu 0.48.5", "windows_x86_64_gnullvm 0.48.5", "windows_x86_64_msvc 0.48.5", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] [[package]] name = "windows-targets" version = "0.53.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" dependencies = [ "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", "windows_i686_gnullvm 0.53.0", "windows_i686_msvc 0.53.0", "windows_x86_64_gnu 0.53.0", "windows_x86_64_gnullvm 0.53.0", "windows_x86_64_msvc 0.53.0", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" dependencies = [ "memchr", ] [[package]] name = "wit-bindgen-rt" version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ "bitflags", ] [[package]] name = "writeable" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "yoke" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" dependencies = [ "serde", "stable_deref_trait", "yoke-derive", "zerofrom", ] [[package]] name = "yoke-derive" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", "syn", "synstructure", ] [[package]] name = "zerocopy" version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "zerofrom" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", "syn", "synstructure", ] [[package]] name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" [[package]] name = "zerotrie" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" dependencies = [ "displaydoc", "yoke", "zerofrom", ] [[package]] name = "zerovec" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" dependencies = [ "yoke", "zerofrom", "zerovec-derive", ] [[package]] name = "zerovec-derive" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "zlib-rs" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "626bd9fa9734751fc50d6060752170984d7053f5a39061f524cda68023d4db8a" cargo-0.91.0/Cargo.toml0000644000000160650000000000100102240ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2024" rust-version = "1.88" name = "cargo" version = "0.91.0" build = "build.rs" autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = """ Cargo, a package manager for Rust. """ homepage = "https://doc.rust-lang.org/cargo/index.html" documentation = "https://docs.rs/cargo" readme = "README.md" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/cargo" resolver = "2" [features] all-static = [ "vendored-openssl", "curl/static-curl", "curl/force-system-lib-on-osx", "vendored-libgit2", ] default = ["http-transport-curl"] http-transport-curl = ["gix/blocking-http-transport-curl"] http-transport-reqwest = ["gix/blocking-http-transport-reqwest"] vendored-libgit2 = ["libgit2-sys/vendored"] vendored-openssl = ["openssl/vendored"] [lib] name = "cargo" path = "src/cargo/lib.rs" [[bin]] name = "cargo" path = "src/bin/cargo/main.rs" test = false doc = false [[test]] name = "build-std" path = "tests/build-std/main.rs" [[test]] name = "testsuite" path = "tests/testsuite/main.rs" [dependencies.annotate-snippets] version = "0.11.5" [dependencies.anstream] version = "0.6.19" [dependencies.anstyle] version = "1.0.11" [dependencies.anyhow] version = "1.0.98" [dependencies.base64] version = "0.22.1" [dependencies.blake3] version = "1.8.2" [dependencies.cargo-credential] version = "0.4.2" [dependencies.cargo-platform] version = "0.3.0" [dependencies.cargo-util] version = "0.2.22" [dependencies.cargo-util-schemas] version = "0.10.0" [dependencies.clap] version = "4.5.40" features = ["wrap_help"] [dependencies.clap_complete] version = "4.5.54" features = ["unstable-dynamic"] [dependencies.color-print] version = "0.3.7" [dependencies.crates-io] version = "0.40.12" [dependencies.curl] version = "0.4.48" features = ["http2"] [dependencies.curl-sys] version = "0.4.82" [dependencies.filetime] version = "0.2.25" [dependencies.flate2] version = "1.1.2" features = ["zlib-rs"] default-features = false [dependencies.git2] version = "0.20.2" [dependencies.git2-curl] version = "0.21.0" [dependencies.gix] version = "0.73.0" features = [ "progress-tree", "parallel", "dirwalk", "status", ] default-features = false [dependencies.glob] version = "0.3.2" [dependencies.hex] version = "0.4.3" [dependencies.hmac] version = "0.12.1" [dependencies.home] version = "0.5.11" [dependencies.http-auth] version = "0.1.10" default-features = false [dependencies.ignore] version = "0.4.23" [dependencies.im-rc] version = "15.1.0" [dependencies.indexmap] version = "2.10.0" [dependencies.itertools] version = "0.14.0" [dependencies.jiff] version = "0.2.15" features = ["std"] default-features = false [dependencies.jobserver] version = "0.1.33" [dependencies.lazycell] version = "1.3.0" [dependencies.libgit2-sys] version = "0.18.2" [dependencies.memchr] version = "2.7.5" [dependencies.opener] version = "0.8.2" [dependencies.os_info] version = "3.12.0" default-features = false [dependencies.pasetors] version = "0.7.6" features = [ "v3", "paserk", "std", "serde", ] [dependencies.pathdiff] version = "0.2.3" [dependencies.rand] version = "0.9.1" [dependencies.regex] version = "1.11.1" [dependencies.rusqlite] version = "0.36.0" features = ["bundled"] [dependencies.rustc-hash] version = "2.1.1" [dependencies.rustc-stable-hash] version = "0.1.2" [dependencies.rustfix] version = "0.9.2" [dependencies.same-file] version = "1.0.6" [dependencies.semver] version = "1.0.26" features = ["serde"] [dependencies.serde] version = "1.0.219" features = ["derive"] [dependencies.serde-untagged] version = "0.1.7" [dependencies.serde_ignored] version = "0.1.12" [dependencies.serde_json] version = "1.0.140" features = ["raw_value"] [dependencies.sha1] version = "0.10.6" [dependencies.shell-escape] version = "0.1.5" [dependencies.supports-hyperlinks] version = "3.1.0" [dependencies.supports-unicode] version = "3.0.0" [dependencies.tar] version = "0.4.44" default-features = false [dependencies.tempfile] version = "3.20.0" [dependencies.thiserror] version = "2.0.12" [dependencies.time] version = "0.3.41" features = [ "parsing", "formatting", "serde", ] [dependencies.toml] version = "0.9.0" features = [ "std", "serde", "parse", "display", "preserve_order", ] default-features = false [dependencies.toml_edit] version = "0.23.0" features = ["serde"] [dependencies.tracing] version = "0.1.41" features = [ "std", "attributes", ] default-features = false [dependencies.tracing-subscriber] version = "0.3.19" features = ["env-filter"] [dependencies.unicase] version = "2.8.1" [dependencies.unicode-width] version = "0.2.1" [dependencies.unicode-xid] version = "0.2.6" [dependencies.url] version = "2.5.4" [dependencies.walkdir] version = "2.5.0" [dev-dependencies.annotate-snippets] version = "0.11.5" features = ["testing-colors"] [dev-dependencies.cargo-test-support] version = "0.8.0" [dev-dependencies.gix] version = "0.73.0" features = [ "progress-tree", "parallel", "dirwalk", "status", "revision", ] default-features = false [dev-dependencies.gix-transport] version = "0.48.0" features = ["http-client-insecure-credentials"] [dev-dependencies.same-file] version = "1.0.6" [dev-dependencies.snapbox] version = "0.6.21" features = [ "diff", "dir", "term-svg", "regex", "json", ] [build-dependencies.flate2] version = "1.1.2" features = ["zlib-rs"] default-features = false [build-dependencies.tar] version = "0.4.44" default-features = false [target."cfg(not(windows))".dependencies.openssl] version = "0.10.73" optional = true [target.'cfg(target_has_atomic = "64")'.dependencies.tracing-chrome] version = "0.7.2" [target.'cfg(target_os = "linux")'.dependencies.cargo-credential-libsecret] version = "0.5.0" [target.'cfg(target_os = "macos")'.dependencies.cargo-credential-macos-keychain] version = "0.4.15" [target."cfg(unix)".dependencies.libc] version = "0.2.174" [target."cfg(windows)".dependencies.cargo-credential-wincred] version = "0.4.15" [target."cfg(windows)".dependencies.windows-sys] version = "0.60" features = [ "Win32_Foundation", "Win32_Security", "Win32_Storage_FileSystem", "Win32_System_IO", "Win32_System_Console", "Win32_System_JobObjects", "Win32_System_Threading", ] [lints.clippy] dbg_macro = "warn" disallowed_methods = "warn" print_stderr = "warn" print_stdout = "warn" self_named_module_files = "warn" [lints.clippy.all] level = "allow" priority = -2 [lints.clippy.correctness] level = "warn" priority = -1 [lints.rust] rust_2018_idioms = "warn" [lints.rustdoc] private_intra_doc_links = "allow" cargo-0.91.0/Cargo.toml.orig000064400000000000000000000216451046102023000137050ustar 00000000000000[workspace] resolver = "2" members = [ "crates/*", "credential/*", "benches/benchsuite", "benches/capture", ] exclude = [ "target/", # exclude bench testing ] [workspace.package] rust-version = "1.86" # MSRV:3 edition = "2024" license = "MIT OR Apache-2.0" homepage = "https://github.com/rust-lang/cargo" repository = "https://github.com/rust-lang/cargo" [workspace.dependencies] annotate-snippets = "0.11.5" anstream = "0.6.19" anstyle = "1.0.11" anyhow = "1.0.98" base64 = "0.22.1" blake3 = "1.8.2" build-rs = { version = "0.3.1", path = "crates/build-rs" } cargo = { path = "" } cargo-credential = { version = "0.4.2", path = "credential/cargo-credential" } cargo-credential-libsecret = { version = "0.5.0", path = "credential/cargo-credential-libsecret" } cargo-credential-macos-keychain = { version = "0.4.15", path = "credential/cargo-credential-macos-keychain" } cargo-credential-wincred = { version = "0.4.15", path = "credential/cargo-credential-wincred" } cargo-platform = { path = "crates/cargo-platform", version = "0.3.0" } cargo-test-macro = { version = "0.4.4", path = "crates/cargo-test-macro" } cargo-test-support = { version = "0.8.0", path = "crates/cargo-test-support" } cargo-util = { version = "0.2.22", path = "crates/cargo-util" } cargo-util-schemas = { version = "0.10.0", path = "crates/cargo-util-schemas" } cargo_metadata = "0.20.0" clap = "4.5.40" clap_complete = { version = "4.5.54", features = ["unstable-dynamic"] } color-print = "0.3.7" core-foundation = { version = "0.10.1", features = ["mac_os_10_7_support"] } crates-io = { version = "0.40.12", path = "crates/crates-io" } criterion = { version = "0.6.0", features = ["html_reports"] } curl = "0.4.48" curl-sys = "0.4.82" filetime = "0.2.25" flate2 = { version = "1.1.2", default-features = false, features = ["zlib-rs"] } git2 = "0.20.2" git2-curl = "0.21.0" # When updating this, also see if `gix-transport` further down needs updating or some auth-related tests will fail. gix = { version = "0.73.0", default-features = false, features = ["progress-tree", "parallel", "dirwalk", "status"] } glob = "0.3.2" # Pinned due to https://github.com/sunng87/handlebars-rust/issues/711 handlebars = { version = "=6.3.1", features = ["dir_source"] } hex = "0.4.3" hmac = "0.12.1" home = "0.5.11" http-auth = { version = "0.1.10", default-features = false } ignore = "0.4.23" im-rc = "15.1.0" indexmap = "2.10.0" itertools = "0.14.0" jiff = { version = "0.2.15", default-features = false, features = [ "std" ] } jobserver = "0.1.33" lazycell = "1.3.0" libc = "0.2.174" libgit2-sys = "0.18.2" libloading = "0.8.8" memchr = "2.7.5" miow = "0.6.0" opener = "0.8.2" openssl = "0.10.73" openssl-sys = "0.9.109" os_info = { version = "3.12.0", default-features = false } pasetors = { version = "0.7.6", features = ["v3", "paserk", "std", "serde"] } pathdiff = "0.2.3" percent-encoding = "2.3.1" pkg-config = "0.3.32" proptest = "1.7.0" pulldown-cmark = { version = "0.13.0", default-features = false, features = ["html"] } rand = "0.9.1" regex = "1.11.1" rusqlite = { version = "0.36.0", features = ["bundled"] } rustc-hash = "2.1.1" rustc-stable-hash = "0.1.2" rustfix = { version = "0.9.2", path = "crates/rustfix" } same-file = "1.0.6" schemars = "1.0.3" security-framework = "3.2.0" semver = { version = "1.0.26", features = ["serde"] } serde = "1.0.219" serde-untagged = "0.1.7" serde-value = "0.7.0" serde_ignored = "0.1.12" serde_json = "1.0.140" sha1 = "0.10.6" sha2 = "0.10.9" shell-escape = "0.1.5" similar = "2.7.0" supports-hyperlinks = "3.1.0" supports-unicode = "3.0.0" snapbox = { version = "0.6.21", features = ["diff", "dir", "term-svg", "regex", "json"] } tar = { version = "0.4.44", default-features = false } tempfile = "3.20.0" thiserror = "2.0.12" time = { version = "0.3.41", features = ["parsing", "formatting", "serde"] } toml = { version = "0.9.0", default-features = false } toml_edit = { version = "0.23.0", features = ["serde"] } tracing = { version = "0.1.41", default-features = false, features = ["std"] } # be compatible with rustc_log: https://github.com/rust-lang/rust/blob/e51e98dde6a/compiler/rustc_log/Cargo.toml#L9 tracing-chrome = "0.7.2" tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } unicase = "2.8.1" unicode-ident = "1.0.18" unicode-width = "0.2.1" unicode-xid = "0.2.6" url = "2.5.4" varisat = "0.2.2" walkdir = "2.5.0" windows-sys = "0.60" [workspace.lints.rust] rust_2018_idioms = "warn" # TODO: could this be removed? [workspace.lints.rustdoc] private_intra_doc_links = "allow" [workspace.lints.clippy] all = { level = "allow", priority = -2 } correctness = { level = "warn", priority = -1 } dbg_macro = "warn" disallowed_methods = "warn" print_stderr = "warn" print_stdout = "warn" self_named_module_files = "warn" [package] name = "cargo" version = "0.91.0" edition.workspace = true license.workspace = true rust-version = "1.88" # MSRV:1 homepage = "https://doc.rust-lang.org/cargo/index.html" repository.workspace = true documentation = "https://docs.rs/cargo" description = """ Cargo, a package manager for Rust. """ [lib] name = "cargo" path = "src/cargo/lib.rs" [dependencies] annotate-snippets.workspace = true anstream.workspace = true anstyle.workspace = true anyhow.workspace = true base64.workspace = true blake3.workspace = true cargo-credential.workspace = true cargo-platform.workspace = true cargo-util-schemas.workspace = true cargo-util.workspace = true clap = { workspace = true, features = ["wrap_help"] } clap_complete.workspace = true color-print.workspace = true crates-io.workspace = true curl = { workspace = true, features = ["http2"] } curl-sys.workspace = true filetime.workspace = true flate2.workspace = true git2.workspace = true git2-curl.workspace = true gix.workspace = true glob.workspace = true hex.workspace = true hmac.workspace = true home.workspace = true http-auth.workspace = true ignore.workspace = true im-rc.workspace = true indexmap.workspace = true itertools.workspace = true jiff.workspace = true jobserver.workspace = true lazycell.workspace = true libgit2-sys.workspace = true memchr.workspace = true opener.workspace = true os_info.workspace = true pasetors.workspace = true pathdiff.workspace = true rand.workspace = true regex.workspace = true rusqlite.workspace = true rustc-hash.workspace = true rustc-stable-hash.workspace = true rustfix.workspace = true same-file.workspace = true semver.workspace = true serde = { workspace = true, features = ["derive"] } serde-untagged.workspace = true serde_ignored.workspace = true serde_json = { workspace = true, features = ["raw_value"] } sha1.workspace = true shell-escape.workspace = true supports-hyperlinks.workspace = true supports-unicode.workspace = true tar.workspace = true tempfile.workspace = true thiserror.workspace = true time.workspace = true toml = { workspace = true, features = ["std", "serde", "parse", "display", "preserve_order"] } toml_edit.workspace = true tracing = { workspace = true, features = ["attributes"] } tracing-subscriber.workspace = true unicase.workspace = true unicode-width.workspace = true unicode-xid.workspace = true url.workspace = true walkdir.workspace = true [target.'cfg(target_has_atomic = "64")'.dependencies] tracing-chrome.workspace = true [target.'cfg(unix)'.dependencies] libc.workspace = true [target.'cfg(target_os = "linux")'.dependencies] cargo-credential-libsecret.workspace = true [target.'cfg(target_os = "macos")'.dependencies] cargo-credential-macos-keychain.workspace = true [target.'cfg(not(windows))'.dependencies] openssl = { workspace = true, optional = true } [target.'cfg(windows)'.dependencies] cargo-credential-wincred.workspace = true [target.'cfg(windows)'.dependencies.windows-sys] workspace = true features = [ "Win32_Foundation", "Win32_Security", "Win32_Storage_FileSystem", "Win32_System_IO", "Win32_System_Console", "Win32_System_JobObjects", "Win32_System_Threading", ] [dev-dependencies] annotate-snippets = { workspace = true, features = ["testing-colors"] } cargo-test-support.workspace = true gix = { workspace = true, features = ["revision"] } # When building Cargo for tests, a safety-measure in `gix` needs to be disabled # to allow sending credentials over HTTP connections. gix-transport = { version = "0.48.0", features = ["http-client-insecure-credentials"] } same-file.workspace = true snapbox.workspace = true [build-dependencies] flate2.workspace = true tar.workspace = true [[bin]] name = "cargo" test = false doc = false [features] default = ["http-transport-curl"] vendored-openssl = ["openssl/vendored"] vendored-libgit2 = ["libgit2-sys/vendored"] # This is primarily used by rust-lang/rust distributing cargo the executable. all-static = ['vendored-openssl', 'curl/static-curl', 'curl/force-system-lib-on-osx', 'vendored-libgit2'] # Exactly one of 'http-transport-curl' or 'http-transport-reqwest' must be enabled # when using Cargo as a library. By default, it is 'http-transport-curl'. http-transport-curl = ["gix/blocking-http-transport-curl"] http-transport-reqwest = ["gix/blocking-http-transport-reqwest"] [lints] workspace = true cargo-0.91.0/LICENSE-APACHE000064400000000000000000000251541046102023000127410ustar 00000000000000 Apache License Version 2.0, January 2004 https://www.apache.org/licenses/LICENSE-2.0 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. cargo-0.91.0/LICENSE-MIT000064400000000000000000000017771046102023000124560ustar 00000000000000Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. cargo-0.91.0/LICENSE-THIRD-PARTY000064400000000000000000002055161046102023000135710ustar 00000000000000The Cargo source code itself does not bundle any third party libraries, but it depends on a number of libraries which carry their own copyright notices and license terms. These libraries are normally all linked static into the binary distributions of Cargo: * OpenSSL - https://www.openssl.org/source/license.html Copyright (c) 1998-2011 The OpenSSL Project. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. All advertising materials mentioning features or use of this software must display the following acknowledgment: "This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. (https://www.openssl.org/)" 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact openssl-core@openssl.org. 5. Products derived from this software may not be called "OpenSSL" nor may "OpenSSL" appear in their names without prior written permission of the OpenSSL Project. 6. Redistributions of any form whatsoever must retain the following acknowledgment: "This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (https://www.openssl.org/)" THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================== This product includes cryptographic software written by Eric Young (eay@cryptsoft.com). This product includes software written by Tim Hudson (tjh@cryptsoft.com). --- Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) All rights reserved. This package is an SSL implementation written by Eric Young (eay@cryptsoft.com). The implementation was written so as to conform with Netscapes SSL. This library is free for commercial and non-commercial use as long as the following conditions are aheared to. The following conditions apply to all code found in this distribution, be it the RC4, RSA, lhash, DES, etc., code; not just the SSL code. The SSL documentation included with this distribution is covered by the same copyright terms except that the holder is Tim Hudson (tjh@cryptsoft.com). Copyright remains Eric Young's, and as such any Copyright notices in the code are not to be removed. If this package is used in a product, Eric Young should be given attribution as the author of the parts of the library used. This can be in the form of a textual message at program startup or in documentation (online or textual) provided with the package. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. All advertising materials mentioning features or use of this software must display the following acknowledgement: "This product includes cryptographic software written by Eric Young (eay@cryptsoft.com)" The word 'cryptographic' can be left out if the rouines from the library being used are not cryptographic related :-). 4. If you include any Windows specific code (or a derivative thereof) from the apps directory (application code) you must include an acknowledgement: "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The licence and distribution terms for any publically available version or derivative of this code cannot be changed. i.e. this code cannot simply be copied and put under another distribution licence [including the GNU Public Licence.] * libgit2 - https://github.com/libgit2/libgit2/blob/master/COPYING libgit2 is Copyright (C) the libgit2 contributors, unless otherwise stated. See the AUTHORS file for details. Note that the only valid version of the GPL as far as this project is concerned is _this_ particular version of the license (ie v2, not v2.2 or v3.x or whatever), unless explicitly otherwise stated. ---------------------------------------------------------------------- LINKING EXCEPTION In addition to the permissions in the GNU General Public License, the authors give you unlimited permission to link the compiled version of this library into combinations with other programs, and to distribute those combinations without any restriction coming from the use of this file. (The General Public License restrictions do apply in other respects; for example, they cover modification of the file, and distribution when not linked into a combined executable.) ---------------------------------------------------------------------- GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. ---------------------------------------------------------------------- The bundled ZLib code is licensed under the ZLib license: Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Jean-loup Gailly Mark Adler jloup@gzip.org madler@alumni.caltech.edu ---------------------------------------------------------------------- The Clar framework is licensed under the MIT license: Copyright (C) 2011 by Vicent Marti Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------- The regex library (deps/regex/) is licensed under the GNU LGPL GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! ---------------------------------------------------------------------- * libssh2 - https://www.libssh2.org/license.html Copyright (c) 2004-2007 Sara Golemon Copyright (c) 2005,2006 Mikhail Gusarov Copyright (c) 2006-2007 The Written Word, Inc. Copyright (c) 2007 Eli Fant Copyright (c) 2009 Daniel Stenberg Copyright (C) 2008, 2009 Simon Josefsson All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the copyright holder nor the names of any other contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * libcurl - https://curl.haxx.se/docs/copyright.html COPYRIGHT AND PERMISSION NOTICE Copyright (c) 1996 - 2014, Daniel Stenberg, daniel@haxx.se. All rights reserved. Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization of the copyright holder. * flate2-rs - https://github.com/alexcrichton/flate2-rs/blob/master/LICENSE-MIT * link-config - https://github.com/alexcrichton/link-config/blob/master/LICENSE-MIT * openssl-static-sys - https://github.com/alexcrichton/openssl-static-sys/blob/master/LICENSE-MIT * toml-rs - https://github.com/alexcrichton/toml-rs/blob/master/LICENSE-MIT * libssh2-static-sys - https://github.com/alexcrichton/libssh2-static-sys/blob/master/LICENSE-MIT * git2-rs - https://github.com/alexcrichton/git2-rs/blob/master/LICENSE-MIT * tar-rs - https://github.com/alexcrichton/tar-rs/blob/master/LICENSE-MIT Copyright (c) 2014 Alex Crichton Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * glob - https://github.com/rust-lang/glob/blob/master/LICENSE-MIT * semver - https://github.com/rust-lang/semver/blob/master/LICENSE-MIT Copyright (c) 2014 The Rust Project Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * rust-url - https://github.com/servo/rust-url/blob/master/LICENSE-MIT Copyright (c) 2006-2009 Graydon Hoare Copyright (c) 2009-2013 Mozilla Foundation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * rust-encoding - https://github.com/lifthrasiir/rust-encoding/blob/master/LICENSE.txt The MIT License (MIT) Copyright (c) 2013, Kang Seonghoon. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * curl-rust - https://github.com/carllerche/curl-rust/blob/master/LICENSE Copyright (c) 2014 Carl Lerche Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * docopt.rs - https://github.com/docopt/docopt.rs/blob/master/UNLICENSE This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to cargo-0.91.0/README.md000064400000000000000000000113671046102023000122750ustar 00000000000000# Cargo Cargo downloads your Rust project’s dependencies and compiles your project. **To start using Cargo**, learn more at [The Cargo Book]. **To start developing Cargo itself**, read the [Cargo Contributor Guide]. [The Cargo Book]: https://doc.rust-lang.org/cargo/ [Cargo Contributor Guide]: https://rust-lang.github.io/cargo/contrib/ > The Cargo binary distributed through with Rust is maintained by the Cargo > team for use by the wider ecosystem. > For all other uses of this crate (as a binary or library) this is maintained > by the Cargo team, primarily for use by Cargo and not intended for external > use (except as a transitive dependency). This crate may make major changes to > its APIs. ## Code Status [![CI](https://github.com/rust-lang/cargo/actions/workflows/main.yml/badge.svg?branch=auto-cargo)](https://github.com/rust-lang/cargo/actions/workflows/main.yml) Code documentation: ## Compiling from Source ### Requirements Cargo requires the following tools and packages to build: * `cargo` and `rustc` * A C compiler [for your platform](https://github.com/rust-lang/cc-rs#compile-time-requirements) * `git` (to clone this repository) **Other requirements:** The following are optional based on your platform and needs. * `pkg-config` — This is used to help locate system packages, such as `libssl` headers/libraries. This may not be required in all cases, such as using vendored OpenSSL, or on Windows. * OpenSSL — Only needed on Unix-like systems and only if the `vendored-openssl` Cargo feature is not used. This requires the development headers, which can be obtained from the `libssl-dev` package on Ubuntu or `openssl-devel` with apk or yum or the `openssl` package from Homebrew on macOS. If using the `vendored-openssl` Cargo feature, then a static copy of OpenSSL will be built from source instead of using the system OpenSSL. This may require additional tools such as `perl` and `make`. On macOS, common installation directories from Homebrew, MacPorts, or pkgsrc will be checked. Otherwise it will fall back to `pkg-config`. On Windows, the system-provided Schannel will be used instead. LibreSSL is also supported. **Optional system libraries:** The build will automatically use vendored versions of the following libraries. However, if they are provided by the system and can be found with `pkg-config`, then the system libraries will be used instead: * [`libcurl`](https://curl.se/libcurl/) — Used for network transfers. * [`libgit2`](https://libgit2.org/) — Used for fetching git dependencies. * [`libssh2`](https://www.libssh2.org/) — Used for SSH access to git repositories. * [`libz`](https://zlib.net/) (AKA zlib) — Used by the above C libraries for data compression. (Rust code uses [`zlib-rs`](https://github.com/trifectatechfoundation/zlib-rs) instead.) It is recommended to use the vendored versions as they are the versions that are tested to work with Cargo. ### Compiling First, you'll want to check out this repository ``` git clone https://github.com/rust-lang/cargo.git cd cargo ``` With `cargo` already installed, you can simply run: ``` cargo build --release ``` ## Adding new subcommands to Cargo Cargo is designed to be extensible with new subcommands without having to modify Cargo itself. See [the Wiki page][third-party-subcommands] for more details and a list of known community-developed subcommands. [third-party-subcommands]: https://github.com/rust-lang/cargo/wiki/Third-party-cargo-subcommands ## Releases Cargo releases coincide with Rust releases. High level release notes are available as part of [Rust's release notes][rel]. Detailed release notes are available in the [changelog]. [rel]: https://github.com/rust-lang/rust/blob/master/RELEASES.md [changelog]: https://doc.rust-lang.org/nightly/cargo/CHANGELOG.html ## Reporting issues Found a bug? We'd love to know about it! Please report all issues on the GitHub [issue tracker][issues]. [issues]: https://github.com/rust-lang/cargo/issues ## Contributing See the **[Cargo Contributor Guide]** for a complete introduction to contributing to Cargo. ## License Cargo is primarily distributed under the terms of both the MIT license and the Apache License (Version 2.0). See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT) for details. ### Third party software This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (https://www.openssl.org/). In binary form, this product includes software that is licensed under the terms of the GNU General Public License, version 2, with a linking exception, which can be obtained from the [upstream repository][1]. See [LICENSE-THIRD-PARTY](LICENSE-THIRD-PARTY) for details. [1]: https://github.com/libgit2/libgit2 cargo-0.91.0/benches/README.md000064400000000000000000000143601046102023000137000ustar 00000000000000# Cargo Benchmarking This directory contains some benchmarks for cargo itself. This uses [Criterion] for running benchmarks. It is recommended to read the Criterion book to get familiar with how to use it. A basic usage would be: ```sh cd benches/benchsuite cargo bench ``` However, running all benchmarks would take many minutes, so in most cases it is recommended to just run the benchmarks relevant to whatever section of code you are working on. ## Benchmarks There are several different kinds of benchmarks in the `benchsuite/benches` directory: * `global_cache_tracker` — Benchmarks saving data to the global cache tracker database using samples of real-world data. * `resolve` — Benchmarks the resolver against simulations of real-world workspaces. * `workspace_initialization` — Benchmarks initialization of a workspace against simulations of real-world workspaces. ### Resolve benchmarks The resolve benchmarks involve downloading the index and benchmarking against some real-world and artificial workspaces located in the [`workspaces`](workspaces) directory. **Beware** that the initial download can take a fairly long amount of time (10 minutes minimum on an extremely fast network) and require significant disk space (around 4.5GB). The benchsuite will cache the index and downloaded crates in the `target/tmp/bench` directory, so subsequent runs should be faster. You can (and probably should) specify individual benchmarks to run to narrow it down to a more reasonable set, for example: ```sh cargo bench -p benchsuite --bench resolve -- resolve_ws/rust ``` This will only download what's necessary for the rust-lang/rust workspace (which is about 330MB) and run the benchmarks against it (which should take about a minute). To get a list of all the benchmarks, run: ```sh cargo bench -p benchsuite --bench resolve -- --list ``` ### Global cache tracker The `global_cache_tracker` benchmark tests saving data to the global cache tracker database using samples of real-world data. This benchmark should run relatively quickly. The real-world data is based on a capture of my personal development environment which has accumulated a large cache. So it is somewhat arbitrary, but hopefully representative of a challenging environment. Capturing of the data is done with the `capture-last-use` binary, which you can run if you need to rebuild the database. Just try to run on a system with a relatively full cache in your cargo home directory. ```sh cargo bench -p benchsuite --bench global_cache_tracker ``` ## Viewing reports The benchmarks display some basic information on the command-line while they run. A more complete HTML report can be found at `target/criterion/report/index.html` which contains links to all the benchmarks and summaries. Check out the Criterion book for more information on the extensive reporting capabilities. ## Comparing implementations Knowing the raw numbers can be useful, but what you're probably most interested in is checking if your changes help or hurt performance. To do that, you need to run the benchmarks multiple times. First, run the benchmarks from the master branch of cargo without any changes. To make it easier to compare, Criterion supports naming the baseline so that you can iterate on your code and compare against it multiple times. ```sh cargo bench -- --save-baseline master ``` Now you can switch to your branch with your changes. Re-run the benchmarks compared against the baseline: ```sh cargo bench -- --baseline master ``` You can repeat the last command as you make changes to re-compare against the master baseline. Without the baseline arguments, it will compare against the last run, which can be helpful for comparing incremental changes. ## Capturing workspaces The [`workspaces`](workspaces) directory contains several workspaces that provide a variety of different workspaces intended to provide good exercises for benchmarks. Some of these are shadow copies of real-world workspaces. This is done with the tool in the [`capture`](capture) directory. The tool will copy `Cargo.lock` and all of the `Cargo.toml` files of the workspace members. It also adds an empty `lib.rs` so Cargo won't error, and sanitizes the `Cargo.toml` to some degree, removing unwanted elements. Finally, it compresses everything into a `tgz`. To run it, do: ```sh cd benches/capture cargo run -- /path/to/workspace/foo ``` The resolver benchmarks also support the `CARGO_BENCH_WORKSPACES` environment variable, which you can point to a Cargo workspace if you want to try different workspaces. For example: ```sh CARGO_BENCH_WORKSPACES=/path/to/some/workspace cargo bench ``` ## TODO This is just a start for establishing a benchmarking suite for Cargo. There's a lot that can be added. Some ideas: * Fix the benchmarks so that the resolver setup doesn't run every iteration. * Benchmark [this section of code](https://github.com/rust-lang/cargo/blob/a821e2cb24d7b6013433f069ab3bad53d160e100/src/cargo/ops/cargo_compile.rs#L470-L549) which builds the unit graph. The performance there isn't great, and it would be good to keep an eye on it. Unfortunately that would mean doing a bit of work to make `generate_targets` publicly visible, and there is a bunch of setup code that may need to be duplicated. * Benchmark the fingerprinting code. * Benchmark running the `cargo` executable. Running something like `cargo build` or `cargo check` with everything "Fresh" would be a good end-to-end exercise to measure the overall overhead of Cargo. * Benchmark pathological resolver scenarios. There might be some cases where the resolver can spend a significant amount of time. It would be good to identify if these exist, and create benchmarks for them. This may require creating an artificial index, similar to the `resolver-tests`. This should also consider scenarios where the resolver ultimately fails. * Benchmark without `Cargo.lock`. I'm not sure if this is particularly valuable, since we are mostly concerned with incremental builds which will always have a lock file. * Benchmark just [`resolve::resolve`](https://github.com/rust-lang/cargo/blob/a821e2cb24d7b6013433f069ab3bad53d160e100/src/cargo/core/resolver/mod.rs#L122) without anything else. This can help focus on just the resolver. [Criterion]: https://bheisler.github.io/criterion.rs/book/ cargo-0.91.0/benches/workspaces/cargo.tgz000064400000000000000000000233571046102023000164310ustar 00000000000000cargo.tar}ko#K|_!h?ƽc16f끍|1fB>"%NSq{_EL_U%ŊȈs""%f?s,n6b6`/3d;8y㌲8;4n+>>I]$bkهp5)EV4m6n٥]]a$r~}M=4G?&izSj7ܿ~o~.n4~nLk|Džo6_xk?*=ҴҴiq&iIJ-=MFi4jiw; y2}by]8mgqoPH\=ܖǧv\i>xR|M4y:ǣUF^cG'=$m}`wY<=>sY_w<412Sا/pna>GcuմEp}M\0s.=-gU__~Rr񻻇40o?^]M=]~i&t{'50%: uBtN[{:HUZ?RbYah|:+O0i {Lxs9=mw>1â{ǫ9iYiZ?->"i*{sX˟Asz̳\T4%|_wS"j|2]n?-q>+X@AwΖ{M7/r>mi>2C Ʈż +?~?awhrw7mf:rq hS3#M|W-5ڨK%z8 |/wP^?PogN54^' $'wۿv翃UXse4bfҟ+n;isF}|7MB0rBoWu,lpath|w](u*D%8Ct[4lSB핇~^JdT('|!a*Y2|ZEeef/ گ`_f[5:FmЊKߨ#eq9ݧ~Ey.ia|\|"jJnQNd)w%S٫Ы/0&MiZ[+zZP_V7t<~139팻/@D_3e6@͞b|n@An080^i#%2.fts0kɚf'f%+) bW^߆3'h_RLKdA>g^m [)s/Ϋ_gZ _96_ɬ|x8?x{j|?ݏaL/,i2r;.ׇM9ꟗ|t,(zC>Ug7w7;LÈoLuBú ~t7^,_~8o=7v{Ġ3=ES SϒUZZ9HKPFVJlrQ'm]NE Ф$ޔgYGdLM >)NG[=2}qI:#zn?@E XIB3|lyB5-'_u6_r֞|>9F}nt4nb];ܮVӮ@^;YKr&:z ͹;Clu6[yjzsjqϮ6 6iJY$owƮgw*Y۝n#+ƣ%e݌88vFWׇzp{Y%qNepPkeZ]a':a%ؽClFuy'Ĵ{ւ.\þh܎ =2ZG@qB.U r&[0@j(+}S]>Ӛ }xP XT5WbZă+ta6]M#892 `I$T'Kd"Botچ7RV-Ș.';lW_~ݕ-lTAIi}@5;V@_2;S*E s2TSI%2Q ZZ[A٤(.K45V1Hd fnF$ztOg508uVuƄwio0T.Z(VW):e_`p;9\KCPSҺFУy*~,YNVY*tTƘa6CtZVxPkv0Ҋ|SED͇TwNktAgRsu(B"^aYaFL2& Y餛*HrZEÿ9>uȷ."9JR95 7pUY!C[5z& ĆR(}%9]kGYͥrSZY@kV:a[͍?t?!ь]@k(|AX$J.7 YLu\&M/XmQ|;W')ٮkW(]Рz H2L_ڪR*%iA ljVgykúXk[8 n>a]<qNB&y\{ E<.R{t3ҫ쁂/,`, \ LY`AE[^ `j|.9*z:I =4c&Dž̀)/U1]fXWډ}m_t-/m)% )K`$$ɪ6 f⌻}m4ϘB-\0NB5VA>a1}DPәh=?Q^ +)?%Jߴx?t Ȯe5Bj\}Tm`(̐^\+>E mM% UI@×"oC5*c+N7b4s:4r^ TX2KUSQ8 ;Wm1L,U^7R2u͠d}ihm7I&JTtI ^1_)uK-xQ h@8,hR6iChATTƴ's{: `pm`~pIIf Fg>Y1B ]>>@D /0>(8Qnk VW٪0UwG1C@-e ߕcitaIg!ȯυup!QfobTp^[r~Trf 08啕rTS 4}Rnk/asC0x0EhI+eU&aV ,v2 p 8 9Ap;vN8+q.vUѵVK~":ҩ҅8Z@z"+jr5W 0K^j!/Qj`9s0deP,Hri՜~CgsM!/JxCdH2#D\2{UX n6}|_kЗ6xK#jd]t)̒],p(-'/E0E]D[-~u:: PW9ѥUم"ګYgx VuR-v7JywP&-o&| NEm#g1UfwU >'qIju^8ȣ_DF Ң&g_QDHr?6k_midga]=:|}NU RP\~Ri `Je`@RjGlsj. 1 kB9 4W3Ux9{fE "1.d4Lz(]^{sV<BKIwR+ -i" 3jcԊ;I&hu,qԡ >g8_ΣM3`<ḍs P%Wf]F- Fk`e52x2NPR0TFDg؅W/wM;1 9*/5Q4ఞb]U6QcE[^+ێ6:͙WrʆsYTAI5_q>-> Qh s7M>ZiRUndq <$NO[ld` Vr_qsKyVشgi(VKS T֪"R&*,680qA~Ac%7 ؐoE:E_upK`vd*k_8$j߬+grdn.E06W*+LzC|WєΖQ^^;H pDv`cTΕcTf8@=-U_`-`l d -؊JkO\5m2<@ |VNLƒ B+/nٷ=O4rg`ZEY޾{DWAn.|Ҵ|<ޖסKHVU} 4ड़/4DT2Es= {ƊVȫNJwJWL`S`"J`jeDw_}C[WwN3VQ1c`d**M_}U^SŮY+yuf?;Y䠋zr[Skw偪8'{xocBRw%կAd*K)Z8ƙ'-T1at? J|/˜v 8C 7K& %1sH/xNzڬ!mۉvWu0^ם4+ ּK(&D 8c`D7gQ]}с5WZ@@7 o[L Y&рu8Yz[='xzK" Z]I-oweQڕ8gyx?0U+kԭ3ًBn;C?I^#l`M. KBY$DZ`c}Z ރ0:_K)7x\c$~Ii*&!ŀ6Tӷ =cç< I99a-Z褴>wOvR*^L}0s_K'>:kQH8{p W,zୂESqMmg`$mm:j.ҷ-3 ^{ʿr;RI$lXC*r0b I5 Jɠx@Aij$9\ JZ&TN+|` G.0eedZk$ X5ת=mox+yoe iW߉f{]oGnO1~g*gTd&'hYçH3~d | 7cEToXAI7@Ikvi{bx4&X^[|op30]Enr=~)>hh+[1IGZ?-yOz_V;^fn)iBik/yd1@N X '/;73dкtפa D1|~p^14oP?>b/^tx3*g\(6'yKKg`y`9m)Lbv]ߤ>Vg>| D+fcZO_/?jP@;5E7}<3 +Dcnd0MH)>--M =&|-p$㠘k>yk:6h[yA@6\.^.N39c~ؘ~BbZ)^g@o"Nmz-Ў. JGT~Pt@*ß龝*5C33_.&\zt,39jU=J.AxAS6[94CFxz8 g ldG h9%p&Q~}?GfXӯ;ٰV㣫'~P"ҷLc\}9VuFM_:x#=:Ҷѫ\ w>›Ox}We%#py >R8',GI#}J^Y58-($wTDi ysA;lYz뿵[ˋum%a*M!79~Qw(U5jh3 9BdgV"| Ӗcok9:;o!}`J!(-^?? E Y5q9*0ş":E/?2=R~&o)NB7 ֎й<)4 x[ƿbjsVb]2NP@m^$_Ľe?Yzv s_$iӸu7+?!hCUm;6QX3< ,LI˿J}c;AEOw?sr~Az>vY[¥g$^@AgKkt[XW_̺;i/auÉY:b$s\~ôRL{4_!)ek YՏ\n6oC\ŭ ?C!\>Jk*C%ij^Ũ%<߽vPl unlUjPtfWIlJ\`dvRwB3F[wX'8/;! @_)=a@+˞Y_OϮMD;uv7ş ۭ<x/ҝޥ5<89]զk%.>])Ǥ y"3;|nllpxN8t1X6 q0[sWhm6 ?WK ֚I!&:xtcz$uCew9-%@>׿ _/f^?mt"zs4[I`?i80/P?5}}MlX.M4xwwOVΰLқVaMLe-a f`k ^w52qp'kB]ޙWQҧck%?:6mkΡmsk*&7[yh\w R,o"4GtBӍŅ 3?ˑ]pAwoCP[gGX{~1{O7¡4-DCNw%6c.2ُh)>W_k A{g~1֟Re3m_WFtl_gRUNo֟}mާgwc[{m-BS_$ 3+<dc> Px~eHyCk35sK_N36&~Bz&ۅ^16"&O-=Ȟ.B7 h  $|DZf17eѕ`~ӵd#S~'mdzAHn>xmxH>oHT`ݒ7W?8W^Ma:59*_Cex ?Uy΂sAP,u lE@J%}O{_h'j8(\q Uq+69GjE;f~S_7y-;wa ?Ơ5Kr=1­3=yks iOPƩeݡ^bٜnx {lSӒonb+D7v 9J)fWԁ,본Gg޻9;!ԇO5EF!|@|yAe\[\n$Cn "5>l?Vⱂsq!'nY^U&y$.g?~ߜO^p,՛y;Q!_$ o+ CP?MP9dRK7v)p3`aaO ج'玷(w|N7_^n֓F؛(T l@f~Z&Sy|9m|pK^^^}#wg'UqL_8m;9Rg,Eis&7m^\Վ_ zsT#ybF[n~&_#WKJsvLe5G ]q B:uioף k{9pB߳Ar%b WSdR-b9KK{J N0჋I# ;'x΄< a( 8EkX> G7Yvi B32AA$B*w4 x~2~ûnmZCu] -쌔%{'+ eMxY #Vc3Rn?zYv{lF_HBY|@KV 0 ߲б| M GlSeg!I 3*%{Ϝ;HT_Vv43Bx(,kVNE$)g =\kK$$7@zIR,H>EB:ͣ-GW->%=xXJ>UMRs!q>y˝!DVf}'I߿%s1ꎦTc/1 \ݷ=swG΃Yf~r-Y՟qwcSNB<8:wn*s,f=[!Sys$m*9^aMؒT|FU%R7 2L1d5/KcπhAIwV!єсsxa%bFXld7W !7˄nY!3Eho7,vhTzLM0ԻOҩHvJ %-Ӗ^_ȣGʖw^ԠEf9(,zPoTiǫ\ Vb Bc3.{I0FE1+V4WP?#fO݄iOf _obLȚ,ni>#_I+W`KD?8%18ʼnĒ_7yXR0N_W0{fQ@+*$ΣӔXEh#gecr]sqt\ Y; Q5m݄V**@\TLQ}Z(zyQ` 'Mޕ}(MG-2"Zi(XA/r, ñpGIz~Z-<~bA[Rtֻ$PZG$D_zGza,&`;3]oZi Լ$q|RZ6l]k!&y(/:J*ZT>\`PHcb|"_!a ן݀Нɲ-sK7Nqܪ¼3 pf4PLYF4\ձ GO:VӔ˻;p`@ qx%AdᇗG#\IsP@Τ@ol(@?hD@r 0)͟ ]\<)^- p9\3I30b4s- ވ[XͫaoOfuXc<3D~5i~h>'1kf 6^J8X7C9>۽n_.=5,_h?{a|jӵRG9? j;bDWagnr:/Dӳk+:^ꩱLBuvk}1n^d@jDGiҬKǞ7`rWdoi@)zBqY Go <gwSWWcR HeW]SkZY:@xv⃶ɩUʒ!5/uXzoqrN_#ɾ|+?J t9O 4k!UpRIwnl%⫥v~MOAԟxKnoaS׾c_3.ͧact ]/5@ y"IĻ-V\<ĈmiiOEUziiͮƒq4ub@i_Fs}8uJ0fB:.3xYcK;рC.+3O5d.~&ioF5 ײfꚏ,U;d60s7oPA[zt W7x9I^©@ />挟d, L$UvfR5*`/VD j7|/_p6?/GB3FƇ; U ~o jdg73Kb}"iSGA=hCLOňOƿfkڵ;~ikߢogyן4R2(Fzwnc3Y"kЙ Amn\=[VxK] ~:Ұ0E5/^efkOFq*Ev(3zzXuPsO`J8԰.΀k翝XEQɳpZ8Q]x>9 X W4q nCDhFj7w&Slq |Uowڲ5[BWYGx{!ΪjG!1ɧ'w1ց&I^Gt(ʇfG䏘s1:'"H4V Leǐ1o!  :7euۂMT+v#W]CbөtMm,_#?3Irm,*t=C,)_׿ڙx??Kyrx(0|/A_EBZ(پ!q bll>&05IO]x.`7L9?)& zpUdtUNR5QSxb|!\哇U|lc,-gZnh97CFw qKkHnʶ\v8dlJlYyxNUi5얛;\eft\zxsO/ɋ d"%Ⱥ&cJv3. I!85M-Rzg1J_2C~.w* g켂o.hJ]t[gh  yO I󣻵#h)a9a. 1D$x^̂ik |yLu$s;_c/>5-;i&f,Bb64VHI D(P(r ~A\9?~E?ey}vlڅJ)s@k8^2?R3hfG:F%L_6șG&ֹY;ntcgxfit5kk'>zSXqSe[cI8ڰu'x|kxh cd z(yizEq `7:nd3?%6@j7S>b!Mk9qvqV.IM<DTO;@7BzjSPk k`>ZCc9bdNZ/?o?LhD8Fbȋd/[3%s ,'ĔsqPOzb\"#j׿L38־mYh ןbJR*me_zt+nkq_6K߿R>O{[?Ng 2s6Ͻ7kgJr|'oQa)gQjYt "E`C6fpPOm ɣ=yjM/Ǘ]Wyt䥨gˡ3ldjJSAk3nf$ںѱegNG/U[\}\7t# ,,٫nAۨY+G@׊ݝTFQ2ea&_ cێ S%Zpe⿓EZ )r%\$I$[,nX\{ypv D~٨uv4Df5*?Uj%m!Uޫ rzH4 6и gF^ne2l]b Y>"ֆ9zTvl(nlulJ ͌I3Ar$^=*IUƽ Wam'fy+%d<_H7r$.Re^{6ɚualspkjȯwmW6|`M/4w4 SyWSG-& OQvBM;jghԬNBe!SWU,5"pQࣩ( [?ZMAnvL _C}g<&&Ds:?? o꺝oU+}6 Q6[3?6 뿴o=!o`aV)䂓r%xwPeB/o:GL3xyE_ ؇imi?u I) mwœ)X{u/At~,Cߎvk}1d\\^Zs$ϮGzF0_HDOmw[9^%=hxIlW^cR.6|飋ۼ_oe[Ydٵs+.Jy߿: s$maۖC{$L &O!Y!6~pPq6dmh-yP/)DnjpaM`4dgpزy`UDYD QϺϧOZLtvJg{pu [/Qon^bk?) fʌ<j;fi)G pFlhٹޑKjPO BˢP!WvM<s|}I([l Fq?[{;@YWi)%(ANg J|Ow_Gc6 o@ve\a4զ o{2Ty+g (s#k9C٪@";G y|\D}*) E/W$x[;`4Ғ!QM(Ŗ>}Md޹ۇN4LMHR3`,W=]\ 6\f |h }Um)~%[1v+^?9PñYF8&|_Ҿ0,LVwi1N~`\.J^}}67(7Bzi5P&Z*?eyr0OuWF+=C쵭n vN.UPIq%Z*guE$eeMY<C T[)m '΅ >]w?s'}|dvk1_B P_/.MH.bUH6Q (tfJU||T |rdp}$#Pq{kcֆI&M\}1W0tIH5T@yܴNUzw+HR˾$lktI OdS=%N20”mjϔ'WW@u^M'_yDP;HkiKeG (G˭?E%oy[Q =uwM:sX>GDmC}eڮPOÑUC&Ɣ7JX^')sTf7[-#-"ўn(j:Hm"5u9hޗWEE襭*x%𓄐_ K+(")+ja qs".`jd mg#^8 @* ښ}=@zYYbCA4=74kՄzkQߏ^Hw]7l9u*Ox;9t/Iu3*(>"!fU|ZWjOCוּMz;Cз뎺zp.f pҼ?W !KM.Q*3C'읥!9ќ!w`Q! @5 `/鑡[(ʰ*H >;_h"]ZNE};oK.w 3D;\Npzb)XI+^qL[>VOL?ӯmp2o>xhfIT?rL_W׎P j_Fm8o8QdO݈߶7azm_^xuT-pO@:9ckYE5dբKYL'dJNʦ,IpVyW3XBZ7% g i;x4CNr9"\)9"^1x(7*V?_1(J˪҆z+o3z?@6 h: 9?VGϴ+O{gnzWu;/SnCj[9mOC!U5V$kc>?ZSUNoZ;6y#ir2*bfwbߎeIkf#'v?$f㿶s[sGqg}yb2aUư]uP"a]o1O o]EM9]T_҉ ȏio#Co=4wOXG?̉!֒Qy2ZZO%;>E J1`g࿍e\o4ͷ]5 Po@T|JɃ)?,?Y/:w:3QzPq8@x9""~kKzP,Zzjߟ3 ik'h 'wok<WK..^ӫ1}uP!hXZgEzGóDP˒kngܩr&J711Hݻ"jp~}϶G:H%$q+5v:9tOIj @mݳ[=O,ۿ@Fф?2~G%=lݖQ6tfI,DMVJgql(5ZT Uw7Z/jj/.=fߟ٠\15O@J:C:EfBk6SMYGl-OB4W#S=6=򠎌(珤8zRW"#;L aD1d Zrd` R\qܔ%:&0㿓n3!ޱO[]3g2Vq/K.:\g\d}K"Ml(x:jTKnb߇nϛoY^rL4VϯSW>@{9]3nPp[w[K9~P`u]1q=Inf!lEK`o켭`dٟkkd| 7,͗fGqd{ *E@ }DV(b.e4}[N/TuoS/]P!rjQOclM?^[7]8y ^ _vo!HI*csze֠b\/ǓMs81 Atvd :ݰnVK;Z_̣l 3 yNCBj3XtXVSf"FA]y%@f&\ ظefo?p-^#h1, ۿ7d_?v^.-yGLU1:k*Ҡg^|ooOi{lsa`Z~.m )Bꝉ:g-O@=R'~7ӷoϏߞnrZ? bPeizRp}ύ (u+h^4gҬe彝&qfn}I/73sȘs|/>Gϳ+,Yf;USӉͼN (nZ^n"9N%\!qpNLMCTUu_oOHBgo_/QU;C %S^ګͦky0qNjii.XrtC+\hu>=Un]zkO;ezY](J|Ͱ3^r{=^VB">5Ose&EL3a$fu1W(DxA1"#, pIqp^MԘ'COoDߞkY"ݨ?_4?W {s}~$:pPYW>ImqPǻeOAMR2YpZc8,e=Nzs^Sx@D.pϗ 2..fOȚ O^VH,T*a7)~xXbM*ըis`@H7KRWx B_!)!ySwޢ!gV.v#lT9FM}RAg 9r^OnNQgT&YsTտ=7f-Եđ_CTx^D@;s uǗ'6`nct 0 ! @P~ <}>:#`q[lw^/.@^Ky9F7z<l\勅,f35z=n&4lmiƎcOk䠄 9`OBM ZSO劏)kuA*"b]^>sghe$8{#\_2/\du9=z=' /n&ܑ'sϵmb`ӓ)ZAe"=\ٗ)p>lVgp}Qc37tFG5M;Ә>Cc܌2&ǘ)cy/&λXLε<,&@(7/1,)`sdL?0UlYGG*/#HI'Fr9{\2g ]$2i(㵩w<8vPk:a.9eu)w:|3w:wyjξΗ;}+P:H~s;EB( :ir2nd 3 und R\fքO@!wM:k.s1E| ɻ}r78rޅEL!Ydc]hr%GCAn?ۃԝMˑ;雡#|'J֑Ay;r#˕#u H쑺is|3K^ϻ1gY<2g̝sv#E,"#f0e̜2as^2S˔.3謖k|2S˜+̓2gY* O#3eY9) elwl :c o%g]̜oLX̖]1K^Ťvc+ƾq%Fg(x}ԟL8ZB`^\`*MxI:ksw{g[S<Faľk?dȅC++S{߶^?_>tnf8xi>!L M Ԧ*Nsޡu5K^_Bç 2MD=<(\Vр9^vbz0iڦ(9rn+; TD.V-)2vG$99ᴭy U&V\(Z̸0}9# n"Hp!cj܁pUDKs'p?BNx<%v}Gm?!(̙jUS{B_Ē9rm#oJ\r=~z._J}zg?;_L";btq6Mu- + zu϶^%7սT!uLeD2koFF݅EMfgnLJm~5pq1CEW ~|Ӑ] 40zI5CP;S3kW';C% F{C/ B;b p\R9aKZh̒, Z`y9nnQ&Z!q¹kjnt֡6KFFh uX\t嶨\.Jel>КSt7R fg o#p' olMV?ҲBp?Iq a?є;{(6rb3 WSlqބCj[(,?cۖϴs&/,'k]_Ƕz`:"kAgf+ R?+ z :Vw/*d43!#꾺~qA A(CC|*DCB}t|nGkNp0*h X]?W{?r4e:вfa𿳷/n=JNS]g z@G\P¹@ #7x׀W |>x@Mc>gqS7MAx@ gN"?fǢJS& eb F_|q-XQʼ#*b!ZDn&s(8?i8Fbcɷ2Z,~zIЗ:3sv{Y'DUHU't?t?L7Q#)6CxQU1+7-$ddT KԜ]cEu⊉7N߲JϤwwA ЦmZcEz?VKgQhK !iw)\ _ zfT܏XV8D%PB58O8 0x-so{9?(y%.T9eW_$a߼cY|e\> EEQ&b/4Deӫ3mBaG/p a[t^R So9å򬗟,vBDjĨU87+=;GۂX㥪s_8zty0}W<؃a-OeL:3o)e״4BTq+jK茈WFFb{MՐo0*d 7!`ta9PO< ;/께Uh>MF*y7fO]y&[D*ŝMVkUpcVjǗ簞֑۸'1y}Uh%QXKCcj=Rowj7%`mo;؎^ ?~В/k1A6 i!U `KQ)|q/PKӯU&§_ЎAv>#GWVC)[*",G@}KJ6[XZ7DvQ spy#31;זּYUc9?p?gktoCIa@V悛r>%]{yك úPxy89.JGGe8 rlÑ`ek7'#؎e?YB?2\Za(lfpD?8"Zq:`{g5u5Ik0fi08?a~`Qaf|IdoX47ke]r7aOΚ՗ٛZUpPl^ Nqm\SG L|n οIPmpdW8pb?c\oA_:'^>&uŗ-YO֖'W\QH{}sLe+/t9ZRɓ;RGÏPͮ5{Kg[Uhl4v"bqAmW64W4_`rѰ]O?M10뉻:]#t 53#*7"{";p1jJ3׭ 3B?q}Ӎ8#Dl&1 SJ $j0ىWJ\MUuuܽgq)Gu%/_5Sz@4,D l@o=[m&~Yb1HN4uQO>}ײF9uҴz=9?$BbPl//kTd $<*%ߛ^d#un; *^urWӎ>(njZGʀCϋ@3*A(9p@P%^>&[E/mz-_YU'Poƀh=~3f`ge@A+oZ.GA K-;`i_Ua"FU]}Ök~$?5]jۺ{td ->FVdgT?̱>!#ez7o2O[ԜI+HAW׬68ޘ!nj2:)g^qCkl6[lU?;M !0,uShMC>8Y#<3WpZO1 v=?sXDJ&/,aR/.T&e|8-xV_!нQ}C_ﳑ10c3Y\&fS#;6Vت{MN}?ۭY'oo[lF-B {T`4e@dLe'^o]OY1e[!2a/mvk_>-2#oG17bGf*Xb"\?5S2jNxxo]::?O$bq4476,4#m?&4?_ !6#Ukg?ןƏ6~YX~hh忺>isB6%n{aCuTjf~*]Sk{YIȈCIǢϑ " jN#. MZ:v-\VJ3 \<P8Q:.TCzN_^ lxl"Ŧ@6֡"Dx{F̍iٗĬ^.m1kx%'ᔗK's!읂ٚa˃Ϩ % n+k ZU+fG|,=?<\O&їE AK([ Defg:zqm6 eY:;GpB8PpԂ ˻LM/~L}'>~HѶHq ʧ+~?Q 3 { 2 @U垁ǯ\Ud6aߑzZlVhTyo ZkY?oQX!r?/q($ pP_G0OK%;?co-'heCD ~5 RxgA{-O~D^7П|j Ij% aSX2=/k1ư_g{e"O M;,~?sګLNUu=YEa8Kl!Ic3(1D92eD#~fJ:V U̡oxLϽ4uߍ'/FYp|TiE/K_,2kмq֝FЩX^\֠Bur%P\+"mc|L~{Tc7*Dw>~ S8 =m[Z/B_@sfjd+U< (ן7t߹5ukCCD)V} ٣<σ DoE;5w"oyR?3GAǬvkg& w_Z_l#!3SP.<"`ZۧJ[|Q A,GQO]ja50C⢏Emw:30Kitw[TY M_lXE`˼qT.LuH~4AョOv"Pkؖ^sC%TQ*i(giې)4[go9խR[(Stk`<838"#i›TU{d~ /m@ d{;Y1܇vXoERNΨ a"ńP H!Iw$JH?x5a,vBgރCbrsBj?){aWG{2M9L Cd/.*ذG'snuLΚem^G|dɿA>!YxE ږǿ ZW7LR-co8YdsO;eAqį&>_}eGȔrO;T#m8ao IQh >[QÜ,*5s1*~?\5H#i?&r ZSO"cΕ$q ܷ$ĕDYl\STcf$PhϘ2`\:XZ_JW.'m^_2?sLx/5sls13d%|݃BOx_͉b{ |\™={f,BSl#dSLoSlot\EPs6O -`=tnjMPwsDn(4]K [J@@FM*aAgl \}+T X_z5g)M-Eֶ(, [_wIxزj @NQG.ȑk36YmzwOPՒR$m|[e(|vWK~&pyUZ*;pܕo`/`aw jΖlmK,z#wٷZ-}Ll *dj$U j??'j7#nosLζuwIL5KI x-^,C8{5w~#&Ug4L$J.Xp>ݖUf~c<|cq%@ꎱ{dOMQ6£[«T Nw20QKkڪbV+Udc \ AWmg_ޗ\~֚y/(|L]zzoEw$qRlDd[b%[,nX5g7!us4PUsz,pV9&ynQ5]|MÓKǛF.~TG}/4p =*/良a. r\# ̉3C?Mu!0ku`zG:H}Nī\ч^C&e!ı vQS.h:K{J0 i?x5VB>H0VvFAP1'wZY-x\^n6SwS{{l5VɊlN=ޒ)HJ y&V&/ ):WF134 `l\6Vf+4s6wZ=T&l/9eW0 {- m-`i ?vZۺ.O'ۻ͎[:3{ǬzgKآQ*0(ٔJI˲A$5>t>K +Ѝ AcJ8Z>?ٻ5 >E33{$y((DF  %猖K S+eCVu,` vCqƏIb_%R  g j`o,۬kVDȭ77 h)XXӰNwPV…|;$)YN;9v"ϥ\|RLnvv&Kj@rǯc֊kb4?@n9m@;]k Ğ^,_q g@S]ӕ4F| 5@o;oO/tN6ۚa>hw؅!w)+NXfZVha$ɹ$(Q}[U-{?'۷vp$=y q7J `m(/q)N ѹ$>?l~ȼ^&9[yx$B[Bf>|(2N;?kA:VzwR? z)xoH$x"h#PӝQɨDɧۈ.Ae8{\9:IE3/km0|h"3aV"wA_ve=g(=fe?UђEnrb_X7R>j/zWz x@/ ?.˫?o HY?k Ỳfw_(iW{Gp#JtS@}ٺ_Cmޱ.C[^7/^X>|"V,|||2wM 2yMoMc? ?z9_4ϼNsc:W/fR??w'{n %-cuvC%]_Lqbr ^YHd=Ki#;iVq/=2l$%RQJ;V1+Pf@uZ [Ιۧ*Mk"Op2dyRL:w]f6SeZM>8 W3OT;^x;"mD,S(P=Wl ]8`~E1!O pmʄN\)kwڳVWU J"p+pja'NTi WqwO)C:Bw,{煄Al^'yG6g= } EٳO T/~X,fবK_0?yUɋL.?Pm ?Dt[{j?#U1}G˲3*[<"I^yҒ-GG@/_ӯnmpGY7 x 7AOAȩ ZѨF/_/ D!,b圠c M3MEhTdˠt /+dmZ,Чs&[\s3J@t@j=, ΁U~=pQ'Aɟ4;H 3zUԑ&Q8=Z$변IRG54ӓueHPބIjy>Q,}|,7 x3SL+G b]V5~U&k`W7 _4u%77OTVed-i9a0HF5( jBtjX)J&q1d2Ծ$DTQekLS\S(9,?]yOȰu  ki?{ozpD SU䓪 /2_ Ex7śxƇ~yHYXՅA5O>]9`h_ʎ"3 "C5VNZL0! '}ʯRjYq}RoYe_\?(||'ۘ Fd|% ٍ"oo6Jř=¬ULp8B2|M Jp͢1Y{l^27 ]l3\dYXd7?Lsmv  $haSvn :,6qM/͢_\ad4?J- w> #k%r<;;*v˪:Uylǫles|n``9#Pfx '容GR?jb}oO[o-[k ?*]Rc忣'\:&*?p7wA`GZ콆;,5DdI,iv%rSvV%75wUH俪 >{KζP eHxT+mb|JibH1@=f\nV5y+l{y\$RAB】 iJ @6T9Zl Z2 Y}9 8 ̿V(q|nq6&$vpZ >4yꞤ*QiOhWעKjb9m50#2cWN=P_WG㿙+ Y ;^@(| `)؋\LN'y4vO'qw˻OXni]3YR<,c#f?l&-D?K`[_XlAHvoph71,cSUg6p4oF&c)juA=v/^45_" x?k3>17l.;GTiohy!Z؇4V#J"'FNG!0仁@џ JYY'7޾38\?J UC)U㬻ЕHl'w?[{:>q_6b( ?:w%q 3aZ8,B@~&,QH戉wp N9.ʀW_v&3:v3(ek YX0j)T:,1~&o&ԝ$+Gt*mG҅;{w ![w cgS #ē6V$_;u% $L+FQB;> {q@yL}=t]7cA$]wU"Yc.REMHNP(TX(:6[q-X,KE:"佔>AKd/HK= 8J)9e>|&lm,-;ÄbA>'?7n}=7zqyXE?tף꼼w_6x,/o=Rp KֹF%OսEл?sbҹ~zI9l*.>ʛ M Tyq1-m'j5vSVeߔ CD3;.FyhWp^E;]¯V'.Բ[m_[w/R..7;_ؐ4rLM>?ߒ*n6{򾵺 0\?-iUٍ=َYE,1Yz=]9x+ɕo^H!O ~ϑg}uI3A0??V/7{:W_o8npr1l^t{)?ƈ6璏{{އ;Eӷ/=\ucea8$&Ƕ%5Qϙ(}:;E:NܺCKbO(@zwۉS<* OPzys*vnB8z<-N#| oA`>5[~ q=tڏ@~[c~s~tLp'KrHl[u Ƶǯmo<8yt&jqA>v7ptGuSkoJݽK;~)F~,ާM~Kiyhi;k#ʠ GwdZ?GF)X`T< լs~Ffm:_6L ~z zy;]A/Cs{`Ld9Z*-:ך6P) ۶Nͩ{@q[z־5}oX#;`Bw<xu}qoKFnN{_k},RgOivM O @l}n?o0ҥu5}po{q;r_ j]8zZcPnz}9Ӗdt\T}7C{y{)i]<>u?{o*i3ޝN#w{X;ak5..td xݝc~4x)ybG>G/rW^[6?H @O[ /?˩)"5=c>˗neS4f޺ݰSWz}kB"/_wX_N_y}υgLu[ޖ/js|7o{?ʗ? ? ϲX֏{:4cAHEܹehJ[bxJ kioA|?ht_yETNC/Art]i_޿69u|kM9GlHԏw-u9Icºk1|)*GM\{\ak/ag\u9ykVb!R5E59!2$$u0V[wwFL.\'2 VNm|yQSa2`y+~B: ͢L\) Q]qpX?u#'4kmI,UTҚ;-1c@Kg( N[E9U )l6<,K.h|I*uv*5پ] bd"_J ڊX ,#b'xξh"*ma& tH,~2l$ƃ/ڲ ǮB#3 g6Te >gI+G|_hեDLpF&l'joBv)XsV !f0Kۦ84O6U!u!N(5<; I2rJM?U]^܊΢&3N*jaD-"d Jj1v3.nWB4_}ґ D%׀hΕ6JY#t((X(8;nE>x O֪Z8(9So/->cYHVJY(tc+s.H-NfWkU_J? H\t&_]q@@KYNi8 )zL_.B̰)F|i8n&sɪk3BNMc+A%#K%tRg Km!0'cyZC;#+BW@LYhL.cƸSP&Ud/`y멝 .> 7вQX h {e9KXk  ZUp78D_x:=ceMz`iHqYW>ϰ`&Wb`Vi`;$]-"˘Va ʨ0"gwꥱNJYXUEnT-MuBBȕR- !D`4߽qourZsOc0b{Via?wmǜS+^:4>\y"p理~  F1)_L_G+/-O:H4ʥj4iNCz9~=?gGqz=5z8z;ajS x_0?=&Ry^^X񷗓GbEGfp1tgOō*-85bDi2;Ld8Lmf_ߞXo(j}}И`7YJvxt00k-ƨ\^t1 {۲[Ʒ<#x-!iX F:,Ȫm)huUdå2@<,5@cb:~Q<%!lP^)9E%p"O}}>rFףr۟~ѥWXbS&]Ng3ep֐rB=P0w^J6Eu} GBvVQaLb*09a~=/8!XB+V8NZU1UgB#:? o@iؤ %V,-|A% Dl| &,lxze1h3΍D{@Ra!b}[;KSI|vLx&@5IM$N%[YUQ(c5IxbnK!4őPzPL:!jBդ)5P\0$ i5K$  =HEXd/9O"}-ʛsd2Xt!^$h>gs>{.mٟCr Wj]K(X[GjsގK䔽SwQ.tOEXL.`h\Ryl Q)\)Agsbu, "a["[}lJ||=ƗgYu o׏أC6 -non{Cxzdb ) a"ztPj 2aY]cQ3n*z%R*j9J!F!v!*UY|g8RJ=dd|dhWYBqÕ "@ZgAah_,H96&[&c1KB/ mdû'.I, AyIJ$RN*恡-)8O,z#t5IFE˭SSƗD`mn7Va)<>b~{B^?iR4}]x6E >ZnwQ&ky,Y:+i\Z;UQDF˥E 3G(U'QvA]6vC.iʭkx)GCۮu@+Ovbޟpu iÞ{'{^[>;"{, :}ɋOg0?l`t~2#2Oyo[Ƅ)eG4N e_!ҊEF6K1nϣVS lj40RW0|J)[<ӲTA11GyV^÷fLh?zoLc8,/W/04(Fw`Q؏go:'3,H=q1!Wsk. ̆XۥkՇ$)6Gheǁ^\g3ORدĬmlJ6Q**nm<pe'4Jj5SHN0(  l&Hs`,>\"vUXdFH"dM9"TU#trJiH]ʥ}(T)8'p] 2"h:md[Y'X"% oe"Õ&5Ez0l_X0#=.quz/~BG,@1LHlJ]),1Q]osoܟmx;bn@CG!8!PYj4BlKm#58A*1{K'X3v4% N{.׶ Ƈ QK%!¸Q>U_towYXo]*Q`6Z$J_D և" O SqDw-˯+qva\a)a[9cDDz^8G3^#ÿCB [%I]R$$:ZX(?qJ";E$%K0dmLI@bs80SNJGI> 5kPC- \ȢNZ2OA `ujY7Ċ/߽ED_z%~.Eg\&grԐjq9kdL}`q&-wmZUTz cIm)HnR2[Fh5S/N8i$(Z\ a\ŏ4XN(-&Bx` ǽm[E%KX4X!K9P22.u0ŚJ%r`V'gw]%on#ihվЮC2?vs7_]R٤K-NJKfqg cx}dx/$a{EwCD9JN\3ܻ;Ta0+l9ҫJuӅ NHIEK/%-X&{6UwQ&gRfܫ~%cIye3W6le gQ>Ys-<P$)*)dt~{}yywv2lY_Q;as9>TtoGHA%'XX[^җ+1M?lJORL_9x2#J?{62n/\`=hhF{>F;[}=g-M0g;ȩmg5f|g6n,zE]$uc_>ܩ7<_wOet/;%g^eX/r;~mm/P_vmH nafxWK)p`a].R'U|/ Ԟљ[VA %dE j;yz?r!QՒv ZȀȓ=Tá;It|ߚ¸ρy_NYRN7"o+nCWm8qk_ږXYM𳀞@j;=ؑ2TI;)`CZ*2٭j٧wjt Pj* W v̩(lg>;}n4Iyk+g풾f6jE_WܱIx /Owaq%g]hG=z﷞B kJN<)2WRn+ ^NNۄq sxZ~̗ #yߋ@G/.'rsyWh}O?}gO{!q%DB/c=} yxczQ=@lҭ я6!~$“Q;|]家]=(2nS*z+D U 'Eyf5~]q] `nL_Ov8s  3MM/Nʼnj{;W¸¾>I)N0i:a>i(X3o` ociIkhrieipGa]7)C v@]:pܭ)ndNyH焹_w%޷nt%zMǟW%njq, [`>R{%3d%N.Z~tv!2-6`(\ݼ,olKQ>s@NW T[qZQ!w225kOf&Ƥ::pF>moNH~.dxGesiʤ__.NDyDحOL_8(? 'oD~ǥ5^dnː/0w$ԑы͆M/ ||s| \I/7ő/aK qM/4F~,-B~{VTa:o)Xzƭ~Zoq(~jiNN ym# HP5Éq/ɋ纏}8a;)l1χ{[1$XK6vҽ~m^#z5/v8=>[fr3Y뒛[VpwJ g's`\-v:_愥w/< Ϙb5%zKzȼC矏Mdq Cskxj&o9`cE'a,1&#pM[J!УaÌ$aa""7Z;s`J{hHO~-U3zP ~N[Y9c藭gN l--y~6l'h 9gEv:e0a`:σ1O.-).W/[]>o't I Ԯ+#8)"mtD [grtRҘZSύ cD87TitnɯFXC|/Wҹ?b\O'̉JG*mkurLsSCySƕp Ln".aк{{އnxe?@̢E~•XpycS.6ǔl]ٷP:xO}F!#2ޘ~5l9_8} )p3P ]>Y@5ҽjfxwɃ>_߇wԒMO_O}vOQ2p[-c:{\ b5ۨ^Z$K20]U͸m)sbR)_}A/U`e<{|R :%JSdLn<5O'"aqkU*àEfW2&35m)pK`YuAEr1=H"{*3K.9zeYb-+QG_ XHnpXLyr-#wٓ99s,h"VK6C)L{"JppRLQz0atG nsxQ˜Qp4g!1$tJثSÀlytQ:+3v$ƨkrBqBrR5iƳ{;izSW*ZȼxţX|6TQ[P?ㄎ*߲'x)>~Rd''WӗE(NI͕ԻD}bbޘwUm5{iҌĊU)GRDy(^R|ɱUiMFÐs- "o`kòk3sruJpS mГN ]qNE} L:K-ص7XT[ylŵ$ '؋9Gj+Hm '*e2z:j".p{xY'!ud]1AKݨ N$uv}Ix<@K@5m>MnѲߞl1萸%D J-.d&lGVy4nr -dr rFe+| \;,niHbquAcbV2p!]&w+=s0B.,hgJV UpU:~yLz h?*L.e̩ jcK;,(e*{Akn\ڒ)_Bd,9'FPWL,^rCXb%g.be.d>aaflM\(SLdϒz'7(aWslR&rQNLVr֢*׬2v82 S!AxW!Ţ,;bAyP*`6B1NɂWsEDTSXSPpV7fG]%r[E2ƈcU2ȅCcfR6IQXO)A}7 vd_ ˲bw>TB9VDB\t`t"E(_{' US%"eu뚃t+5HBFi6Y-p6VZ ='ȄpsÄ:`kd˰3Eeq>K# t!Cʼn&i˜d6=iqЖvD|=>#^w\rMhWˏ]n{-wv}`Dq^)2s=zߥrETTvYcՀILMJZe/α%Xçj } \69M~E䉐J^+B.x"M+`dh}oK@JPBr@'48VCj#q$(٨m(R.g[q!Bk`[)f'CW"hr#DR$ "zJi4{-DeW:ܘ3B1!`6KM"~2ԸɇuԋJo&/JURV)lI$2sx qmGϯOZ|qeLI22Zȅ/qIVΥ*[;n a6yoj0L>{ F~>Ltw+4`3G. gؘܪ`\J F1LIZ7Bzx /# 1Qme1y|aQxT4@D\vh' EH:D5 쎃?HN*S ZGFL614>PD.@~2/pa>7qos)}REM^; :g~č)kREa>6'DLsP x-d-Q-6A*Y:ϒCghB& [_&tZVMXGaqm [h&kJhRP @6S \"׬VKHz-^;=^ ֧;Z `BrES!;vFSCi&}(hI$QX!~A Cf(V$1:z3(e UZ6> rw."/ c1Jt4E$>G6Ucй w* u*1|;|{~'fmoj{GDdU@d(q,K xF^B3LOQ+]H:ELkI'[z% EZP*6# ;?Y,OP?95R|D6Oe>bhƻ[=W`FKk7;ڤO/ړdW$8]XٯЕEl gsª#c QJcN*@WQ@,X KrZ5wWqwHKTخdR8, d+yK$:f(j(#te V g1D2׏.)^k칞\BaubGB^l؛ݞ~)#&LBi,uqC%K5`%`U EjMf)yb^x#{Zc .<䈲XoN5tXW:P'eh£A[4Ҏ=i&Fvw1$Oף٩KœpK":D),VcfD2뮯 rU&KaSw9Ʊ{-NwFc0 ܮ5j*\]1cB+T )%쵣0(ȹWľ hMe!05@jjā1†T6:y%:i&DPRs"vrγ<7작YYp3hT`{$Wmk=)\b"+ؓ!3=w8j{Gei(Qi XNʨ!z"m.), 0#K#V_)@>fd^>} iIBHɸR ($3ı]*sN*t9R|d!.PRPf,^v=4bO6CU>?~Ny,_,\(DI9O uᤆylMwxS(;a8R5p}} #e*< `%GpSnR1:b*bs9 -nZx5L+()$qJ+֓ŹXG) +HU (rs$1z35c_^BԔT`;C)h.euZs!wV!SdJRx 2آjQvE4F,ۇmsқt^EK,U@nR}Pc*: .YDPCUӕ^^wʪzQkOvO9R#&ʼnty艵@f@(ON6,0)|z%QUW FC 2^$`;DD-?8z7#5fc TuɍRi g X{ tJ`i ))h+^7/PHb{&gKBdc#S!rnyqTLe]r9+J% 89>^~hFbWR߬.wZ%s#{{ˁkzɍxv.K6Y8ίMK :<LЉ&f?,_\SLiW <ڈт`*(uDkhSY+c率Ƿ|WTXм΋#v(MH@Gd&<0%s_*y*.:;D}2rqXc:,SЉTɘ h1tYhFȈ%k%: L +j2ؗt%skSx|/[>ye=ŷU]Ӏr704qbtXP`+OFNG<%}HT40WM^9Jl0 mHƕ7Z()'_!>=pNbѲaPQ1/a(ed*D-,!| p0=Gl!$bޓha;GE9 $g)<3կT a@ug]_m(a}I /mc {W<8-#:*ꑸ)#K06f)~:k{{J }1Y n~%pAPPURotM%8d<|uͱ2Si*)wjl\[ѴSaR>Tik4l+9]Ԩ `CH,IRHץ!k߾uF_VGmNLgsORˮGOL4Uϧ;l_t1|zkNźLWOw RԞrѱkv٦EC][ٯGުp3m9Γ֪eC5/78'Oe'K9ovAb́8]7x2#$2VEI+g*"W]氪pQc^ Bs$ ^b"LR+Dmd\RcDE>O>s͋s7kNK^vBfS "dT"gKsUE#yɄ x矔\YûSiO{%(xJ6D4uN5M&ԏk]⸻:K)D=rTr5 XD` j9dGlلyBI0oco/b|X 6.U"Ze֌3.ZMFjEĔHmrS{]Fz|H`Z-(]Kb;B8hN|GjjfyoVc֓i2p{XV;_s*WGV)U(jp[6`+J{:65ʾ5XY0, 5\(e},gD1蘒Te6vRlD)bDJ5رz˄孄M6 HTTxUp'qȁW5׊kpi7ǃk?^\/ekP)Xp"DT6:>ʋX=oMXa$^ Od3RZgC8>C\:|ʋw{Z?MDh?{ƚ#I~3刲ܗy-23 ]D`@@fnX,Vrpόp"kH;&_'ap THtdÇptG&HK5 V*(JL,eZO:D ȩVPEZahy9B'9i 2bZIɋeI:"C&h2?qdгUSZ V;Z mgۚ,t$ߑ"1I b*Ίq::c9{S R ="he5-C0WKp`;:7^t}zȽx&ճ);w]K^v_5hƽYի|@N uզwA/Ky~,lǯݺw`Mr_7o rjgͩתJ˖' W7Q^MNJ:VOÇ }U4|n{? *Mq68gsdB}Qzrg "G;O]K"zsak}:85O1(h_<`AWD8L@8L[^]?~Z[Nzs6\^g__&/n=? |&u!']<%ʇg}vv/w]^-B8~ +d|dC?%/XUc#x:3͋GrqϤb?KíӧԦ4awLh2ݶ/l&~N+^>d:LF/_7\9eE@c :^94urY_E-}lUk;~}?kmIſ`yz\/G=۳̩?,~mpz.rܮޕwaözěm]'6@潣Zt K+̯c`F^ļo&n,=gΒ՟\?צ}}aV~2ļvXNyV{5ܙcc]u_8OthG/ sv6^yQL]P_XskbE dWn㽿~滓 ڨAhTko#6umd5?F ]μy쁈-ᝩA?`N:xbd 98Ԕ.DI딜D<+:yeTqN|Myi5eFVtو`zaGlǒ]D_{G}}*;+a]' \*nu~\f_ϿϙZk@𾾼9}6{Vj9úFbf|JV06s\ED_\O2'k 믅>Fg߅xst1F'u.2*L: =,l=G#i6'jÞS_ڽގ:9wv<ؚmj[.^8>Ҝ>pK.s9/\?2y >+)&jVX&` ,n*X{i y? gGZ.ֳ7O; N|_3wK& ʏp?wgkQ%_ߺ+5F-s+u%" Wɩun"64_&|SEtAWR V{SP|Ac}UNe1fՉTvkIFOϏGr {Ixkit^:nE>ЅR}tؚ,ec3y6Sc`>w{;Wì};w[8+i[ lE6C0FZ[k|щjQs ;skT"%΅t=ܺVT556ZG3ZS丌ٲxZG-ĕj$w{@x;sz8 l쬨7̌jŞIS1jE]HsYuƛ(*\̓ڹ3W#\'6L }$RB6+,zZAҹE9oX5&k1q*UTʎ+sR5~JpQn ୪}*)B"BtїBPF]0gdž vN9Xu*x'G[Sorl^ r}+pwƀ{ih: D^w$ո{wb H2Ҍ` 5DeT%X tE'T&)Up>h? 4ӚbOV3T. Y$tp7bx< U'NRű'ZVf+^ TvW(qZ|Ҵ(AQ@$t5d=c;EElQM9\tʹivLl 0$ "Fۣ*|t+Mlמ ˫#Q8"q6z$bYY\@Bj![B.owT4 ]XTwňtw#~FwNXciM/goj&47O=֭ d|V1јc"#N| !puiZq'>sPOHT| .i| BNrPn(ԤoS*m4Gsn[cI˱1-W~{Zxgtw#ͯq|Oui 熃ei`>nUB`x1ba̭H\q~|\ߞ&hcyvU5'T?J4 bӘA. XtoDb5`\*&N>5ϯZvG{ (QdMަދy+ϕ1{\@H!Vv"vGϊoYPdWnwᙎZ/U xG7Ԓd0UI_PMv>)Z0:}yyx{vŹu,pI+/Av瘒[c쉦0Hu;.^1BTAܙ3 Ӳ)!$`Q ǿF`eMե'PE&dӈ^+(bwAٌ#&8(*+ vDnfde'v_;~EQ8ucڃMue|8GY[D'I it-~1p.< #iӁ3:d9uD?JAt V8G*Ҁ$:)o)-Zp4Xskq@u4`A?VoD`ObN}򣉿ꑰaHA$SE, nmS#@/ou2rժwjq6%.%&": bq=+pXx:"`LEѥdA$VCT#41gVqHX: "Ԝ@Ar[+oU^&W{zb'skPl`Lj:M*:vɩwW^km= riRE۶'oyʙ^@-шv%+b;N9C ۨ\i|jRδ80G[,Oib΋Ҝ)(3Z#]Jn۔b@)!Z_l e$pF[k%"j`P$$M=b]TZW 0'xf ۿ]NqU7w)j `R'j`*Ul/k-yk8ό{sb=57s!6Uk*X*i^+8w27x[_+WLu%)RdIɤp:u z/f|kcƺbQ ߹=np4a 8s">N%HIIQU*J~:'V/`QΚ#7Tdo]?)̦fuBu(㉙SqU,,W WSEk:6Pae3@(ok +kY:Mv`+ߔabX8{lO(6 Oh#\Ic^m,耛z&{yx~{;ury#Qd@skXnQ r`Uhmm/.矖N[OVQ)jҍ[0֕ȳbKk!y:?&¡tś2&`.ec)WiT%|8m Oß#ps1ZqfB+RA!vTu'T-EQlz\q*}y__ZH(dK yPgGu#{RC 65b% $mNQW1HN4/ pCϱJ5ty) sK fpҼ$Ԏ+F^NC4HqJ] Ƶ+GF _2c,w-($cQTZJ>[rve@L5$cC(/)p ꯯gN=[R3K;vrXz)LbP^rښC~*~Z.e/yT{B>b4!?||9{hoJ';_toTSs`Ea㓲9^[S ׊|/uíhZsq*>lVu8 P)[ @={-P+T/ľ(0uLt . 2) _굤 xFPRpC.@|6p2d_J 'WKsVj'1-iј&٣,(I2v/ƯvqGKՔ4h0`@T .S,}``7koJŁag4Jc}NPdp] ll3sզ[#d|U(NdMdnc#W *[""ͯ#͟7/O| qg/kN8;PwIl`D5&&K.س܅:(9*HAHղ-N4Q3t#X24rҮTd0vjj(RN-R[b8hQ൑X䪫h g*ФVjO܋Y{(A~|GnP,W( g@ @ܥ =b*d Яm`6'-7I@ fU^L+0ЭI{ p}0!Fzs8Wl &Tƥ{_+ߢ1ATp(uD Z @Ftb'r~JJ XECYpu_Yk5&&F0? P%{7JNVs2%$|*.=F`(AFW ^Ъw!1=b'w ss*.X):¬-('̌Q{o+ASZZ mI5YQ`GA;R b W-n37Xǚ9dm%5G GnCCh!o K?~lO{s61{9٦8^=?XTZmKҺWsn,t؞>xb/(ܕtۢU)7D)6 W G.$n)\g!Nq҉qMէpc6 HMl)6WmەA0Rb-{/p+MYp_ylCbCV[MYTM9BRsH )s.A'U{5r'Yo.?a6d X YEFoTs#EIytL=M\\'Uٽ :ݤLj곮re@;d+%X)OKj?e|*<߿:nN-G1w幭F-'Ǟ{/,i-Baֲ2VVGF ww&aa_ZӦn>~hAK!hDٯXy C! W1#E~I#jXw(D>Q~,{8puӛ|Jnc}M@Y#2:՜EtSך0]S.=H(7k PSQEV+@xDqȻ;е7DɽoOM e;CRIGL mRp]9 $y(uU Z;EY|Z-R U`>"jRY RPcs$9MVԯ뢸 g!{BNc_N*ju2]%!XR"ta.0 A#u[=]Ϸx?#+?>ZVȻ LQYTs"(J-`+WA W6^MDS_+>t\&,L =PȂ;?2͉M.فkq)nIS*q\zZk[)t%ORW-^!NOndʐRnfͯϋũ)0IdA8-%%8*/YyMeוby q)K,%e7.=?kK{IRmA*)kϺ$ &iKyyVHwջVx{nM뉺Mڕtn>9`聇Z0K2r,I#HN"Np @ƃ[ڑ(wMFKAv%} Fטo?LV)ϪZMW$6 P^JM7H *]2\G^ߺLQ-;WMEy@Qu{> B^1`%},`LZ=@?ǶnY ƺM+!Dٺx<ѫ:TLBO֭7- $Zkqu"d&O^V0+b:~/ ȱ|'Qk#4lCB;RNOYsH ,$Ȍ*jķ0N4 8u)e'2636?vu&L]D: +k1#ϛfiyaELK dƎt""qHiʸe^t\cq~ֲ7(ULNJIH{HXl3 &ŋZ]Q'-9Fܽ4vaBO6ݠI /^f,h!dX_Z ydIORkqE"N&JXgIb$ى x!Gx}M?-ߗ{o#OawȽn0g%`Bwxx،jl썝뭶w~xٴ=,Um8,ߞ_1[K+lg`Ozx ~z; uUʹu"m?6^!ѳcۓ[d۫n`p".ڥ"SRDD Рtw É&Upx`g,hR$lk]K]b++]{Б} 9o\qwloMTT GP\L!rFJQL.5h#8de QL;1 k7k[ZwlX8 c(LLٜ6п0Βs{q3wH'{cw. da crolCoH_5:=S}(>kaEx3 {TGPq$TVD F ۯdMȺ(ʼnز`%##B>__ ܠ\Umh(dVuk{V@`KJ^F@ToW[&*=GumhL/[(7f$&txUTSNjmorDK,ՠP??93z7nBc~)KpLQ[?0خEI삚-\r7U ;Vhc=SN2HLQ^")7w#c0A]+ыKhcw嘁Xkj8()IE(᪑" >$J8>Y+5>XMKց($j7*#q!t"uQU5#=q0'Z BDu-WRɋU5ˊa!;7!KsWia2Oİ3_.#s;R%y+-A^''J,\*bH Dt&IVxU66E*QήaT1Gli!vvz _>~jX`*=IThd*悳gهJIAk=gMr+mN >au߻s'<.{~qP{$*W_zp&^~usk1@ĕ$dJuږ@U'&F+_ ,BU,ZI$KR1UJS*vIANsPŽwD|ϓ57 i5Vd[kH{A[1!vbd8H7gxk^ >Bb[3͆еG0 &*OWwMuQp!?e7N>sUUc+caqVJ,?=}}޶?ϟ 2#PwX_M(.2^9׈YzIC!Tc$y)XDv<; zW_0UMH9v,ſ/VlidkAj|eoװ㟽C;vn8%R\HAݐ`++֔VtZuC5ſע/*O4<^L61?9hƖ]Sgc ET[GGRsNHSN5Mq}mLwN?n[|ri>YLN߃s|e1MslZ{SA ׍8! EY}HI؁;^א|Oof|&]IVbWZ 4&:tRq޳R24ϒ%y,10~uű)H ysooͻ{:g'.A{rr;Luz& _ҏL @D[=A1ר3Eu(IU3{RK]k&fgvlgɢK|5vW(q*h|.YX8G~l3@O $4򴉟WO}IFaE|5 c1"-&"[SmN6XhפXL ~O뒁ɢR"CW&TED_IJ3W[TF9[JeH 1,^l u`z~1*eo_[+-yU|<Ń{q%kns#P%MFf “N[wp/y+חz()`8LdRn'zv@HuQC$( 59R/B,EGi~_)jw@δ>,居v}i7COzSﳔa' W_Gj$w2kY%_eGdTu@²A$DKmM/ M[V},6*Y>{[!ihI:@Ӝm*#3}rbfH*f{-c+FsPr.JDžr(bS}ʅ Z("yMȲ,.1G`u"|EޥJj{vd/Spu$CI>\+p<5IN;sS2t Jj| S|*XAEhL^.0ųI~+̶~O@n][A"ۢ/ǮF3״"Fi͌ydx^p#i;>A>Hwrt~mY^ȿ~Ʈ>& /KW_MVǴZ=wi2ciOKKp"hBS, hZI^8I~f K1x'O!;"搻7LL.DYqVZޤZ_|z]2CBqYYM֥t!JS-XLwԹ9n=枭AR(keTM ܿ^g8*Bʿo?m|8&!/.$Qz5@XvKe$'uE!(&_S)9 cnq:5rjwJhr-Ęs1Xf%]V Bk5v=FO.d 8jӤMF"r`ŶUEYa:Vk}E+dӘde_q~ɏ;Q!}Ηt`Imˇ 'wSpkYlxxR!iulьNc{;O~|}wtܗ89>ouA^\/Kڒr&RсQ0:ƚ@|}#xzÐfnZ;>њ.^*T,;*r Im {js Zܺ|5F-2;b/IE*@ mCRLn7w̄vW8BVVie&c+}=c͘L=s)fs%$ƷVnaحڃ{C 1}~ryr eiȑzc^nǽeT(k8E(' յsq꽼hހJ@f^k1:!7u  r,w6b-Ix5ӛT i&:8HF4צF%=XЭ 80p8 Ȫ@Lm-Z =r^8'0ݏE'^2.좜7N.,OX=3ۚl"e,a_C,GͩyXp94 H혰 +$ 9+ОZ@2s&Klng(~96;ZS8"$[i+Е15eJO>( 6+=^&dyNT'%7X>8ZƁoJGg721'"eZ=`wVb:&+PnK/zx.Rl!+{QmÙi"`97/J`Fk]эUw@ ֮35EA ;f2Ғ"ݯˏan  $/k*PDLIHU?YZ s33-.Vcu"e=x<$MT)MjH;3!*?tR#m9nB_rw\/_&ȹf-b1%F \jj \AA] ?y9`_mn A@uUB}љ DEn);]= vMd]<[ |o`l]qi˞W7ٷ>0bUPqBA uk]Z*jA,S,LgڄT]vk\D T,/H-**l5ӼRnb'z[[‹pGv) WjK#[*L9@:[}7+ b'$Z ($ 0%#8 7i)[%4/ fRbc[ )].K#cKD8b=aG\^~,.{vk=*^+:iT9L=]V-WFTv,0"c10!ܿ*6~>~I}5W@`J2h 001@=v&8⥡p)qOgܼ?}[lLq iE\4S91”K7S\c(]n#N8rMn  t b < Q$Qʵ3xזiH5YUt@Zd̍^/?Vؚ"eGeg*(!J ]Lh M <=q6Xӏ@:1ZQ%#kcS 1c\dD8&3YoϿNf=jϝ*!45lJ#qak[u92&Y8ޯѷs&rWZz:[4 ;q(<ǫ *.uZ[G>`vnk7DP7 >n+Mp U,~ֈ& +9D:[U u RjKhZI,DDH1;|sÐͮ 'ӢAkJLoo򾉌o?N>b" ȁka\i7%laQL ޜsNfcx{H.V] 092ǹtwHc ՛$lƹ<~љ{ݳ}RX}NBЖY!q. Kթ)t8q^Qʪ_E!NW5=2"sFlD{ H6U{ڪ:ɷs#jc:ԛˡQ!:L)o ·sbrcCޕܼ{oKW)YʵБ mDtVMYPT "R.;Twxޫ}AryuM1ǞxTiGpA-$zVk}t6i>1KF'E=WyNN^^J[bnh;6t򍻪Vlb=01IgXZ75vQQ,:9CY`S ëYo@Xl(]u~!b٧W8.6V6;7Y[KyVO #p'o=^b (/݉V2 O 9vHDP,qNpȶgooq^15PF|y%=i|w8%ifl\ (WoEb.(3lfzaS/=$<o䩴Do(VY;U'.w1U/cVm1o]^xM܇$݀;8{#(Je #Pcֺ bS-pM(#r!( e Mp[aĖ|[^h[G0E8R).9XVt([ŝ;[ҹDU<\dǴbd..'" /5A^BA? 35Pf+utTED[`dOt*@뇇0J$9-[+R3fZj/*A l@*I:w %`Y` ']E̺NXIz , FĬ/\*[Br)="ͰENU#L[ =nI:дʑv5b.IVrD7|g`3ABq< σUȷuFpXcd QNCqSbSJ2ΨJRV[(I)()M r~x9;*w>&y/_]Mmׇ#H6"bɊ-W BM$ϢȂoЛ"GpO?JQzўhn9%{35M^b NN\vEQ ԋC ^|#SsM ;;M~b=*s !riE?5QDH4H R*X'׺q$#ٷ"洂$I/BrIiA6!b7vD7Clu%~'.>?t_]8.,VpcJ?JXݜ EO<|g},`vI':+ 3HZ ?HZpUZ7,o̽LU74us椬EGh \Ȉ sBۂg6Jg= a(xVEUmePQ*J;6%-w*nSj{D.Su]&Z6 fH_gLgJgIsk?'x E7Sk]gLu!F4jk~m)& șr^Js~%Q`Ѧw `r궇OolXR{I `B`B o.u ԪjiUBC[bwBN+~ue >J$l퇤 m5ho])䲢w_mۺ__n>Fۨ7ET #eGhVIVJcBo;mnxAuuM79jH+@|8$@/r`ogD?*|Y7sqt%Hwm7񝴔z3{d8}\홹8vg1ɤ¡%`&iO^O9 G`Pc̖{[o#0%3y(1.'NӴ?UYQ?{ [\TIXE}$&6;1N^rhܨ?xy+S"$õ53:ޯIRh'J#d|֢;V & ׎HnPt RTr[YV߼fbV Kz(q:>umJ,d$yf y/{V͖.-Nv|yy~}dl'V-&޵!_>0$MIZ7_c;q23n}ȼCF)A)PFAZE *WD]mg:yAzIw*wy\nQwߍA{'(`=iuE#Ճ,g5/&cN&?C ܃U0{[%D8'T`"Fĉ\Uv^Ec1&P]yTaz.zot ܧxI;وcK3+sb ao q]dpP2G3 ~Uym5Pqrt%n-,*Źǿ幂) -s8d/ h m&cka^E* ^}~leTHa6wu5`ì1[x+QrJY|(qO X8*Y$|^SaYJ!0Ui='+l}c +lWG5OM:5!j[[/<(HE?[> v{ʑS;=MS\+t>L7TGb+Hep wuwy}/?^R/h έ]U+u7~[K'NWtŸu{ 䅌RrS`P3TWTK&xw{hjazΘiY{O{$$*7٧93}Ed,mo/:ِ| /O^~?OCʳ9l:dYQ\t9YkG1-c<77͓$>߆n0ʠ=MM>P[< $=oˆJIMy6yh˸ʼ@?a20\{JR&L^NZFaW|T/+}c0yrワ$Y'tŢT86yː#w~"kJbA|z51oo%^1~3[Kd H: jM68ei5GN b07 g|1Ӏ7`GXGd|&:14b”o,Ǵ EWYųI5Hm:F*8 3Xkε{“~=zd9 ?&d<iȱBb|^,,1'KVW{?e4Q{89}@+8Q)RfZf9^ =}ޭ@P*!AeM҇Lo$ mT`gR(dU|ԦGa %?MI=97By6O.YsMyhO+P-Al{o"~oi⿁8BYRɮA96 |Mb"hNy/JFR )V[h Gok jI7~ pӯF.PH­a/[zM6z#1L@D/xP.+/|g84]"q^o1$lK-ü'nL['" )ALp؁O󼀸mȮe&rܧ ˰"1C$6'/~ԼRN+/.1HkULc}ɵQEoFr:7[qil|7JȘ+@W S$~)e&O01E ٤>f"t;_B@ۿ|m9oIU#HD߲Ziw45_/"&9/`H&絩<#i$dہiG.O#iD"cCgvu{i\~T λ y8 L2$hc{^޻u8-݁:x= gB{pͦRI0Qܟx#";*WoʋY*6BT)ăO?Hǿ6@P7ir8 +MN6Qmœ*u ۫ ?.8dfjw߉$]d%~q)^d,r`KXs.$a_w#M%h}ij;jQuᒱ%en @THcBtI ooN#/"kQ]զiC&/Cn~;Su05 <嚡kI}3BUˡ<^ҵPcJ*7~{hDP _vws ?51v5Hke`>$|TndoWGG2 A=SpR7[_{߅!Ξt&qR ajcFỊ7}^/H>f=pې54/g9jq1bSbjb#LwZlk?|_J.>\Lٯ@ӣ@T?&ϊ?8-O[? Ha}4f~D2+_ksBMc_D.,u?itowj_A䩊{dj,cԥ,Ɇ><nMy'0+)Nnm;Zg j)cЯ]sraIykGQr mwe"MHbţM>Ӽ$)ы?->:y2`ztMͤuB-tNk&d<]'o PHaj7[OOsV>v-Y?ڰ&(*I՛%'j!wϝ_|^C 8>$BOekzֆ lSF0(̬ԭ5na)u55(QEQ> _t2ɀwZ ܇x5 y'irOwLWA=6ia+6 @2fIJ x/R&EA%ƕ):ޓ򨔯蓖TYVu Ь-2W7mj:b}qNUB BWl6I$}Xdׯdھ$%z\u 2nhw|,`n!nL$&"<&}& +fS E325=brnkhv ٪k^wTgXBj{Sn(dpkG=nYĴt#ǁzX^8 BP]֙7ynmkȴJQ#F=Η(jt2HTe⏍hj.Uai|3pUEpA?}k((<é4nWsȂ^b?a.:xtKo3!Aӝ>< N>J_M|7NB'EfkԎ pGxEk7ful~[cE,P7oڶ]L,JVr) y8ó;v֥ߦр#'pU. C]8t9gh^/1z7,1ej|줭rtzYN4K"xVM`q,;$Rf;dHeRus/\R7yĦy hPSFPgs]q4ϷVD0;h~gJY]u&O" v agl:n7IRFDNܬܴCveAB/uZm?{6wqN^$8NH$d}_:VCnaQ;lt2t? ύGixm72u I$|EbPofŇ:=.ܣ}iƘ_J/,ɔ{w1m}/dRK^E&(?1#*_ö/U_^E[I^'&;俑w=l= o9xΘ :^ p|J,FFJH9?wh}h1_&ŒbBc~ߓܱRȻEDtɧv +cZFO>}ߚ2HR]ʥ)hδfM|ڃ|GN;:9sN9^+Z֥"*q:K6 )L% O!nx cZD|0%qr$ސ+^B }ߞ'UKde󿨶!!LW!W)Y! E£[;6Iʗ$y5 7K~J(Y>OߑGJa p!0 5b "&e?%5zb W>A$Ѫ=A.#-/E?!o:%N6mVrRjUDlJS( ?,n`Y `!R 6ʉ#tQ{@v6y2tJ+@FSbg@|,Y%qb{3&ل^ZUă]}{vwBwT~CGޑh(ΠC >+Ok\Yo, Wr(bk[cdn&eD| ;:n[N%F]0:<`ʨڈ|VreAU4Q9|JݴwkLMjۣ>zs=D*RW$XS&H];YINru-T7V'&q/$óߍNO>Fy/X28} h7coe; h7Md=1_0y[H Z8׼n9_o0=0*Zn'_n8Ԉ'SJ"&Z= Tx+׻7>kO5v$7wG`NKm[BetU% S"q̣^/Kf?)ŏ?fn%sΤfv/g.?wY*So:-Xrߜ/dq`EtY$3~)RӖ)&' .KEq/L3-|NT;Vͧe&Q43֊T] fT:eݪdjuꬓ穗tlC/ej xi$6ݎ)`~q옦 (WpW,Q'p}r'|D#uj,gPRXg%97>֑O]|uڸBP>QMqH: "9Ŝa'ń;24$uYO^m~?X[kuL2{ek2ޟ x$'?hm?݃o|MvsjC?K]1=G۟gX."&x<7𑹬<ߧW3_@NYM|{G!&l.#fb{iU$RU] <!ی#45F 虥C ]'ߦ'B?V"5P|_nLWap:LnVGGchAӺށDiu6d)w,ECoMYD:*B 3jWE0%j{um0if]cY|D*,]3L;تW3R)]q;?K:},4,hXIl?޷r?q[xG@ȱ7PBf q9yJ|cCL勓WaL- Zo?%^J,ٟۿ|]F?0{>FN> :ִ .P[ pd,y>^$8N-]e!YݣE>SP&=wnIu-S)k js:=maa2nm0A?܇K"6jNw])z1Z't:stKOF'} +!l=+HGeRIlc/) 䗂mLxz: .>8ik5k~ω܀]D S#dfs^n9̏=h._Ϸ /l(Rr24jq'12&UdYA '=PzhIpʗԉѳ6IJY_pT(k04,h*0URǐ? զ\\2 H)2WY%i{=GP˜t(Gr711ߑF?CnCzEtQ괫4%"ҫ({0w+l'dɪp庛{FmVM 4WѽthD 2Fe|!(=}Mr&MtMhtF$ttӰoBwےLo& EqE/6s>}(4\oL1=>F4 HOf8a  ;qs`hx2 *ÑM]Ş'1 T*/RmJ8K3/ˌ=DdA}~>NÏ S=W!-Cm.+wåuKJGkP/,MW$fO1{3m Ѩ H :\8yœLkB|zS*y&U[qBB&N-{MIytxK7YYMy;ИMq7[E jN )~?+VF-=6~SK* &/VU'+U6%W" Ȃ|=N3=s [!qJ fEtk0y? Z{sVI =ڕ lvex%?D?dA%?8V=5wY^7= PGyb6'+iV YHYzh Ȉ痢"eu)ԃsք&Y+4$k4я]':ޗb~ uoAƟx0M-nhMK x2A\*|Kv#u +Ye[.DWsFa4Ƕ;?s> `\Ec{I7󄃿aRD(k*%5 #9m񍗒$7hR[{JU*Rua=pgyV+=?oDwmr º{Ztp]Fp'2fk!#Dys-zs/G3Gm!K?Mo(/RS?υ1}2}/9YYm#"d*W9=>3KM*O:øj?osə;/Ys狔,IL/-ǐ*+E75 kRآ.Qw++m5lͶҬssK$?y(*D|U\7Uq-!A~멃7k:_Kx2O}TW&v8ďH̔?By$rOF eJ*Kx*cv??̃,$2VM$ h!BV"(Hh(%xDO_FN-(} *h~V]nqo4ws^ _͓|֔yv)!8eM߱]Fg@n!sig"z_06u1Np^XiǚikhÿF$\hlif ZUoHeM6%0.]ΔjzTdUKX+yA;| i6l==}?SOMyQw)?]]ߝory;%NOg0 0dT|'LW ߰F;Xe?X=CvE:Ň :E]}( GƧBV!ק_H/)TK(ɳ/e_RGmm3Mʃ_QG]%?|S$uU# e I"_A ۏ7IN-wk^;<$^F Se-? B/QsG) g%oᕼΆ$+i?NHu8O~ʾVvP,`a{r6X~H͔rA^gt;*q3!tWF uN*kiD;(^G*yjf5 s dqNśDB<Ѧ]V~`E t<6}97/h'4Eᤧ`]qQN TE/ u M^n*~cjGBBmdLワ7`t#X??uvCMbb)]㸑b@Az TCJ)ݗ\|?6|#BR? x|iOCols i# *X`"䤻];:2,!&E:)Dxp3x7&U%5g{M#lnsΦ0ŚD#G*?& ߝ͓EZzd[yLҢt٨|`u?఻C-F]~ClT轂+L[0i ݪBߓdb:|a>;~đ@`!+ MX?-u׊is)zܤwv<7=(Id9lO?,P%K7R%T$w"i#HɃEi4AOF  b{wxAUo0{7أ}j@jQ(c _XVyA90yl$M{K7{<".j K AZX'o"orJM K\b3àf "h zC"&Qj!Xw]kS1WQ ;:X`6ɺj I;y_ˉ;{w``$rHҐyްLăD4d2+ov![G,㷤#OUT9$(܇ƀ^hcP]эv1o?Q&x+׻7hA raHEso]o`:5BE2'=Pv.rK~,Bo,jgmTʗ,“9?mKWAYbyL^4%]Ԡ'ѐ :E{W~#n|rcr~<i"0O~0V.J}g9f@̟]2:1goj9FzTItIU EI^&`O"R8}f" *:Cc^u%vMv,ϰeͣ_tȝDNÖ oyg'rm3Df;U >e(pїQ׿n#?7';k[wk9BQP]kznYc3E@#~f\[}e؝ +%ZM8/l6 ;\5{18}AC,ꃞ$x{*i(M8mn1PEr^TOr;*xR: $|oHzw*Y'R`5[}.+*TQ+%s1f$>| W ^rr[CEqq4zͧ:xnczɔΫQ?d}4%9iwH>2֓ߝ?AZD=8[DF#i$8a~Z+Ouh|Iwab>ȧ_‚eB@3;XM95f^deh dc{qHn;&h.nmD[2s]ل"$7)7Dߪl!I% Ο;~^jE oM]Ӿ*09~x/ymG$CP!Tk @ܡAuݶ}9.d|&EIy(" ؋ؗV/GJ>jA]JP3Ag Z]TbH_xG1׍<\Ӫ[ڸw%}Y(y2Pp~ϛ1Nhm,fWް3#;C)CB~Y8˱IHY3McŞϐާW;?1O2nㅳ}1:~uJb \y1r*>oJ_=Ƶ7M#j 4IB>Xh/?͘ȒA>Iz"4+JKd"\K1x~-Yaoi'6z|Az A3Oh 9ZVHH6AH(㯎دZͲCCKL^œ^ȇ zBƫ|fL}iLh<ܴQtVcZVK0[A^>;@]id^‰U)&g~bߢm'$Œ[*Abv1Wh;%9zHieۡiͮ'Mts4ͶtP`N6r\b9vsu598G㯀 wűA}snfnR F_YW<-v$h'6rLSi_]zbMj67?=,>ECdSf>xR:Y* kw>WyCG%V-luQ1|Rӑ0tEˠoYe|,XW)H<#/)ъP̎'fL0@OOlӕEm :"}XThUe ўㅥqo"M%2*S*bk&¢EJ6?&P($  +iIݿ&Usuer6|](SEv(rq`ۏ}ZlMj~t72y#N ܰ(_RZ9E!raJ?LJkxZP.g#aq^/ބq2H$uxs 3M!rX%s_(r(* xE+ot`+lBHHpU9&!':q%NF5Idv4=~nVPcX4ilcW [Y4;H}K0{3m2̰YiI=_WPA ӊz,9 {r|=,51ij Y ]S]~ZGۯ(GW }.i\9&͂]&bP".˓`xxJ}Д\E-Tǩ_S?怗T۱;=mK8cƪKs4t#;HʙX)*+QWШQ 0\I+ ;3 W?y/7۫?̱]Aگ/M)mI[)uQ,ʅ_ ^oM@TpCϕ^ކ.;+ ?!u}3D ~~@M#VyV(E$[mu<μ~ ?_KqTGܪxDbџ}M6/P8ͧ0:΁,̦k: L/1Ҁ'ZNL d›׶6k]EM5<|䉲$(5_NlƵ$G7??`"5b%~OUW„F>z%te8+eƋf@+w;RȋL(0\'üUB'c %ӣz?Dcʔ1zx$oJUdx!ڑk zT$om1_.sT:C"/R JPɔ8C*>  J:8i ۽$?Tb K迪m5nq~9ƼL$' żk'*++2kJ4B",L<%q#WL+(42( qavПx#fJy(>W01 XϊObb^~Bc[$e I R/zVE[M՚}.I+E"Qsl$[M><k7Hb< QH部1Gq"fn&1&gY4O2)OL+kw\5JlGi^ķ/azx/tݱSN*Y as:A3_+A&;t@xnS6644L)6KYy2 Jr8TsJptR M#]0V=Yˈ*X(-UET"1Jڨ 30>&Ū6a8Œ#/ yA )YOa<CWY= ĸ?ɱ!đ ^vē,`Pء{7cj[szcS)[NXPͪqԄ,u @\9fWml߿]= }:,#K%lX7 ӲR_a)y"! L2淯_y@1 !& lSHNDJɋMx.A!1 y䀻a64npV_|gW~2(~Io} .UÀkX$LIF.M@zDX֢WWi!~I.ʓ5v9%NEvHK* >PDGA(nIr13HWAT*Rߧ̕,Eո؜sm1=sSpj,L/v:'u;+8'fZFhBtPYN-ͧj-Z:Gƀ"`KPϔMO|m#δp`BY"j޲h } _蘒k]<̲-P[sWo(Mawq*Yk9E䆧%9m_69)r\0cH@/Gr3=Dz-tfpbt/ŸgjkfCno6(ŁK@}(|g߁%$pDR1(} 7 )eM QǷE8>]E:"ufO'ʲ pT*X ].QRVMx'"}m3ӴY{Rk]{GNLqj+[(7cj[[)ak* "龿!oBQH koox9X ⧽A8%&p+e ;P.09NĉCM^ [0R0-Qm㙦Omz.MCsT7Y@-]%mi3&3 ,j$ff*v붎*qqU<7p<] x ʙojA@ρ9 , cfY  L24s5 $ Z ,p1dDZeY'??,9rv<  r#8lyM׵\Շyp}n@MC̃]Q, dtJs~z ι^yٖoÆB ;k%J U`PZ2@zNle1dkNymq{stpfygF@g:ǀm|=νj'pǏguJNvJZübhAT`l#i>YT-Mwa,tX,MloO7EY`gcEB#Xj]r) / :;_Ҷݿec7\9á3A"uXuMaahAĬp9B0%N7j8 -jN6<]{ 5J%ph9SW5Yܵ= Pgڞ9٢DaG&]ʛ< ,]}p 5dy@ZE=W%2fIٮo6o]c jr;[V6v,̓"4@$Z*@yɨhp3 .H X I̛fPU2-<հKX':E-f0f.ܫz(2f횁 45M GAh°DФ!DT\BهY¤&<s4_q LG%yes)pz afh) ͣ=FGcEfj^Ĺ;7LñpWM40u# k1_w-).>2qQĽ_m30m`5FNRaiԥma(X .]8]|b9nT8% w|\簳.gVs- YǮWu}º2 PF=t3@➡z:ұ5314G'ԭx)!j Fu0>n H*MǁFeA7@,AcDwp d:Q'(7isFsk/(74jyU`,u̓;Hl C(h9[͉i(;*6۬%S0a [HK0/;ꂲpA&טO2H-tV- L33a'I"N{?"ckQ &3kF>TVz&Nu9_FjkC ).8nBU۲SMnPF<y<7ΝS">N}4GƬoW[Sn$mkcO-g l `7Bkzm' UV:pbT8m RP8*+|>lX rSw`[)7`aϨp\& '9[^pZub\b} !:P`luM 4&u,OG4rE5Ygp-ʧ%9O 45UuJsuķt@xDs4!{O)Go*$vRwr:-,*z:*qu5kjX&dn.s u脡yGbax:OAd u/|h"?1T xaS#Yj@<۞幁{ 8qDz]8@vLuxH0,jqX&AlORub )[epbӯ׷lTU95` bԢ6TہMqym>ryiWs50Tt9!qm ɷTOvaޖ>:dTգ촶wu.tAS!,!8"bq8NZeR.6&z>{L[jeQ)F `0hq̝Y=z2؏t0n I˧F@ekx*WfeZSj&U]sTAو<<292EڔA xT4 034ji6}NM@c8J`䩄]",hj NŖ}͵AU@  ^k> K8HSx0E^9w }L5IhO^*,Yr>`=z#R m:ȡEĀXG@^\]8j j6tc8lUM`:TqP^6aRXoHԖ5˄P T DV SǟC9,Ȁ̵\f{az,F2/{oQz.RfjYN4 |\}W Px+8i92ܻyu 7Z3-(lkh:|r-xؚzdYJ]aW \lUh8} {6ḿ߲!@A zRw0!4|97)F`Pn:2LhH},Ae PJ#jqր4G+ WfqgAA`8=]l=;Ĵ R烝%{J ,3EWUU;mG 8LF@ `΀Ae @u4MtlKƸaEI\;*:tFV ~O{~`HJs1S[ؽي轌DS`; f*kp&~`с;t=BQ; iDL`9_ |pN58DLwy ]@$jמA =g:qM ΁nb~4Qnh>ʰKƕA3u0,T5.48rA6ѷ 9>a^~Ad;=q<5-0 P橦i ]y9*yvPUj}~j@v0 sѶ<|*#~T<N#38$S8Է&;46xmU#dǡ. PY #L̤ۢ@ \0a}~<V]aU qBIWaavЮy|"6PF ӓ3Sz?Dzl-ӧGӆï㾭koe -#Af@`hխG>{!T] s_JM_ tM n*てRwcF]rwAqi<]tLSw 7'P-)Ŧ˽l4<5]Yx6 ͵MM~x}]d&tp5ua:WA75yj1j`N3U&|$ T&54b"aiӵ3,27ۚߡ~3<%[ F!.%>I2]j3L@mjNt̀lBа`hp|sd(N.?oyittO5tդژՈ Fn:ҸME'3t̙g'\&+EjW)ma~2-P|OuEܽrcɱ,B~k|6Zdf$ *`d ca  [HWN<+[`]5<&MJk{d;_y[jg_) J1ҭ+^W1WⰐ:' v8]#fr65$뢿4+?Ӳ#Cr5QMBI6jBU_18+-[ әܛ yQ}绗iǚc f֧|U/)5ЃRMa#fZsu..G)f)sp8KNLF2-KcYؗh4&'@*l9'.Yy,)ㄯMД~L0ir{ђ0Ra,:1V% i.P^0!dmⶡV4{to&L.wL5z&*0HPuش>BȺyFc{h{y[sQYCmh/JAp!Sٰe"$S ')T-SEsMpڌScp;T&@=Rp#=PL|0jȖ1D%9! УVRhcaFi[걁LXaꥴ&W5| 5S =T01g& kbsR9R2Ktix;*{mIڅZSd9n*Ig&ե8z*;"Bl(4e$<;!H* nRBkl[*\N& skCqoiAozc[c vo?l:sIc=b33rRtRK@ɼտ Gjփ}5}~2;+~v}ZN3w3ĭ5נ#o,L!`;ڲњk|GPd9LZbo|6i;{s`r @J."U.*P-sq'ܤ YLeF˧3Śӫ&J(A ;˹x6' aT]NmԳ"HC<Îo€ጛЅ>(bQi,u1-U IBamuJA~LƴdU\.tжu"H<<8. ch*d!.Tg\okr:pW_Jtu8Fr_1(D,=-BN*uNB6ۢhmK9.П|Ԗlîw&7;k1$qhMh|w&4?W1m"I D-gPBr9X A.G sΗe2>']V~X_鹻s揧5h4 ;EopzZ XQ1PK3(xCwmF6?ZBsϭ-ԕ?:"62\Z 5__dĵ۬g{+!>rlqi|mgVr`VWjbl3l& Ut"]޴Kn-ۂw<NEljq$%PՑ*j@A? ݆ <&$c"}-cB[`!!]W5_DÅ-,q#Қ#Sch\ayKEZ+`p2곗8>7V֚La:37*U*DMV.(] Ie^uele/őqN~ kؓB>h-ZQ 'C{f \iUzT(m-̘W+c'TTaz.ҽL Tz1or0(z =UkJu$5:o<;+=}5d2,Gls5"3dS)ZslM_H_c\]]ӭ,{" ,5|gPpFi.cP_u0!č ݠ$;u;$J%HSI_J8dž']֕EhRl5xuK^ )Bs$o<]ÞxIlh&'l0; B^)LC+Ld]=VaXRkl[]Y{8YmCd[ĶLk%]ѣl2xB2N%Xb]R?++UOʵ!U:;vkwչ2c]je5kU} op`\ ;UrI:Er\McԺ'jQiNR2f6i<e '>%EKmHKBXw`8zPkSR͇ vS~#$!YH?g ť~W%zl˽Sq @^kWV"\ n5Q;{hZc t祣֜!m]\>*I?!xxe-΢<$/[I*[9J1%mle!7E{*XSRRi )86学+A; al_xσtAa.V)%ӺB|! VNY8ᬻKfj-~DD]&:49V{BɥP[D1,UyT@:z_B#p\Qp%3ǚ{G./ 6䨫'*Ϗ8' %5c]kSYRa4Ceڔ$,W GGX[*[7\|m 7U<}+A/V~}}֮msemO ѤI hDGxPA6.ygcͤn],m,?v;lw_Lƚ3W;ѣ{^T.VMxFx'a3/R}a~[N~% :!Q\C5%Y+ H*Tٻ 4ܘa. sϧqky=ThIBp%h-do mUx᪢2FyeJy\iuc |ɼ.k{^LS3r93=}Y?¦/OuVt5'}J+%j/œ~a A2kyNQ{&u(\.~S>z٢;;no6 )~%FؽIyX5hj^Mo44s]izl.Wyn/nmbjX|L;wƆO[1 ^{U:\~?)Y5]xL|(tKZ{~}z߇yKՁ m''K9Am_a]};@MjÀIO_){ ddF84bTz#[pu:b܅UOrTƝ _z4+&yӒ=NMz}UJ̨V#Z[1\5pעE#B{vqi9)e*|1ޚCMR:V*V`Ze,YaߋmN?눳 c GAɡD®#8Bos{r65zQҀZ.YʽBb5כlk7ȩͧJCs(TL !tm2sjRn|RgiCtBxI,F _r1onh{XXfX˔S"=|JwZ+1x 6:̈'U(56t>iٿw .KKHF0r7Jixb`|UP?w^Vys#*-xSVO'mv?e:F H.F$Քs ]ɹ8HoEm1?~D\;+.;{ =k"dpHxzsThƎgSt<}95@@W4I(1WВjz$j&JcpU.r>I{ZXqޥxG<<ۭ|gw(׷[MKU7-?kxPyl-w濾v>WX=;7cq Բi1EݥnXEprz7A-)ϻ\S-ek&V!gJKY@IC!6 lM?=;uӢPLQ `5)IG/t$t3.sjО =g 1 uRݎi=7"@'T i"3MՅ6m)vW8aXr¶nDJ*s.oMxjNЄ-;M]'m`8|) _lZHn>])Emo7ϾaTbH•cuLtّ3Jb0{J mY]]kFepې xp9oZ.nMpcb}|iqZe36^L+aF](\mQrjiG%X)>LtxynT(]Q3p;HU-Pu0 uvx2&4/o̧89ܝ 5i;Wd:=(ExI#`mQ0.y_'/CR5k3-B̨kO0cA\^ \Tqhw%^*$0_9 8:mI*T]+ϢuƏ`Zv{L!r+cEtd}C6YqHBwdu7JQ}N$:#^r~Q1+p>UJR1dDyJ_禝Z(?˧?,0{n&Ogjm?_~xoxfioc/ jHޚ  r?y}in)t`9e^>2qL8VƇI7Eƺd>"KC{i ܳCdlvAh#Q5ﺲ$_75iKB#&w[wp(:)-cxVm~bs1+ ǸKOQrF\.T]Y*mDw HDL bդZCK}0gѕ)(EB8fl` #ۅV,)*/OTa~fʪD1w-r!]ҥU;ѥKeF/EcҀ@Ժ m=ZLfKdW8Ihit.ٱ1'G:N.6DB@ UOJzvaéCN1ճM<'dXp:ĊLjL^v0]\Iυr Ȥf:2ma/*?N>^Jz*>(?+޲H+<u8ðcxM]_LLcF `$)͈F/U /R. =X@kSSc"qRج\57X^ݔrX>fۘ]+&ti?z+=-3W]5?:H _ڠq5y:i^Zak&hw>seDzhtnkWWlս>{뉼nSo_ FGNӞ3Rݻ3տNBOmٶ{j>Bkbqg6_ !7i(ؽ9QWYɤ(ܴR(!o5MzLu_ [r1XG^?f{1i'-^tk^m'=vs·1mG쯗\Ɔ&!ǩN>Uy:S=΋{sv%||KA/rmzClZD[sLU!-0z&Q&S LAK^oMKM'7nc*QO|.W|$k{pӏ,X8y}ޑ=Ø޲Q!.ՙb&Jd4zÒ*SwW: aR?}]rWauNs(V祀*m[6LWgc)'.Z#,'{JBе thE _ NRԒWي/U$M3cI?{\}6-+*EhZ.âT1^÷~UGrGn$-l)'OZ ({_Q۬?_gubsa~qHiOTCs5.hzl)آ]zF9=38k>USBWEY:fc9XVIٴ@J05w=pJ 4ƠX˅L,rV2(D"Ji`Vő7vJo &4[ZwtĀc9| ĩ%%5[lC&TG|5Q\nCәG?gߗz6-X18E:F!TM5NV@azEKJ|ӊ:_nnnb=Oec/D=vHh~kHV0L1{`b:4㊼;sL=Rſ~Uꎅ_!f{F{Z`; 'tWNQPLtQQ5^XQTXGEXuX7QTU!bFzJQp|?&{PWb{QFSveIIh [Az"o:pB3R/%']*ՏﴼR84^ÔaWֽZ֔ L@>PDT-JTR…jVꗻxl1;lrfNdx Ds ӛ%MR@vGMvCoZ ԥU"re&je*Pb> t\Cb->-y\8VC=1#kp8;}.dzཷy.蕡T:-x8ب-TSsWѶыDW;xD&x] <}b/ME)o_Y?nK YK(#FF،FBk#NWU%BAsjɱ v,+ڒ|ŽR{nX`bbE5iGR&"0R.F_[[% tFM#rh=踐Lߐkc,6:X#*֦b7-Hvc8Fp^H!^qI_uGEt(yX}iwPP5VJרYY5WF0gKɱ䕂q ?W:0drU![`+J%wkZO:ȖS!T:ߎc*E8OKx,/(KSBUV!V*Bp]#&^/Aoa;:{kel=5]f??~=BYt9K| pTrK(Vr5M( R7CχԞ>xzđ먘/||}newH!OJzlgFbuq3Іqd ZIrF`@3]4ĮvⲒ ]n9pKS>翎>m8Vlx&'(W+KusRn}En=7{[le&{-! |hڻl*t$G]8y'e0>s0>Y(l'}-ٲ)j4e2'9'p̌PsWtC-ݯ 'puMW7 2F@%U9:dZ-yawkYcݦt{!ߑmOw,CIG}uঁ~<ҦCK|R}I@ |cRsGGLpAlsSm׎@{m'd9燋e\>7E͒/iё.}Hmhv{~LdϑCT~9oUӜDNKn}mJIcݢ*&~crHI8K қi[P [ɭ\*qj/`zQM.蚛\_HsO62•jb)+z|׍-z_/S Mj9*!F]Um 5M'8iɘJfxBTrVh\kLAGdHjyMzD MR,e V+;BnWQDD@G3yň BP<GR`mKZ<B7{Rx5Ҍ0bIaآLBrS>/ZҸq~ 76O z;6Ӌ)ZJnrwִr`7lRHA3Y6x4痷˷Xݐ]jFm% _Am{kׯu&"U×#ʙV-VLgl}׉SUF!V y I( Sp?!GYKݭBZv86,ՉԎ&c.&RUͶolƒZJlN\cb?份͔m6{l ] gÊ#?mm"rTu \j+j/}ݏ> 8`qAr?wԣ{ָg"S*;ÒA` k@ޤ\I,-w  / kE<[2&( 0ۊTa7xyc+-"?-Nr8KEU {SN9f[|*,3`D(#wkᑵ4chpF2A KrHZ|ᄕGaX)Ibn9=&^s:Ӊ9ClT8pHy jNE7"Y N" <1W#|Mӱ܀Kc^c+F#wqCcR2UCX[U9prH^_긞SGlwG_5'vO!R%#/G:#/~,5F8ݔ 8 &]%c^\C!5<:MʽiNb "|$+):X68"!+xg9X4gTpkSsK?8Єk׬j38!jW ,z!!KGS16us7ڀ/tx?}"ڦJG#0iVP]7QO䈸#`f1V${EtDW7):%`uT0oAIZԱri\Jh8D"k<@OXws͒VIo×nM\ [ n n8>vO{ xh%_`N 7kX@aHG3XLhD,bEʨ{(ZآkV2"چ" (Ċ~{2cAYx¦0*k\8D-(L.9j^E8\nTd &7QD3a($ǕB Ƃ׆kRgק^@ڣ(KV"U L[E֩t6Vkt!c9['R^i6H|Dg{ Sw\sI-#\J\#Qlivy.*NBcrάK#2/+I  wt?fJ^/NLl^hpDkQ|6,ɞ#~15› 1 oKfC3&g 4u( 2'NUHPda84z.Gbu&8+2V?k瀧uXc,$/JCQ1 vKJ[8E >F&5S=>a(f:3s/?lhvBXk%^̆qf1&gI@qn}~_S_p/OQW7&z|9JɯvU%\M YEbb _;$b["䲦 Md7أ7Z|c>_/7HZ2 t9b{Uʭ$!ۘ s8]^>&%L *pm YMҽ59=VR/%8s n@#K4PM[DssW &eD:f52| V[SPsMQ=^6ҽ /{|'gYvanFܒ&#` ӗnC=\)bG"HBx..O)Jc1㽦 А,wk=㠇 񂵩Ḇ6B).K^W%{,u4&ȍ`% lfds Aq+(-p(CVBD,9 /-s5K똥t8QAxh%N-QG*P_jJIH.b\ڕr&^D*=RӍx_v1uvms8)I#45y%V:#RZɄ`D(CK#q㖗]Tz?9im%->S5J%"V*QECYJ CzΫ JWRN%rY8nc ]LQt%&OV53٢x:-KoLMpQm"-0 ފ+RD: ꆠI")fǯi)@keсMcmք&!) ;тP7o| #&_gIk&cl~\]'<~r0-,>%%ұWHyTZ76-"80x|85C~zhMce+%ύq.%U{U;54, 㩨&p\9XH4X4mًsG3{K^l? E0"`%W-5ϫTE ʳe|kkw]|{@Q:$m7]8X[M@sLtWR'|1WۤtK~}wJvC TD L/v:G( 9p5.BnTL_4R7bְh%pcք)Dܽuō|ҙ.Ux ^$6,BM]}X B,!4@+K$ބWP§4(/t{|t]E*GRe-W#AK%kb&)X;` OZRM,vƆ}+jzѿФ"D_Js\ ix{*|o.:蔍"bDZ ʭʝʘꆧ\mRn3$6\Sphle43ؘm"$ږO{& ]gՕp? _%u]z>Ngoa{Aٽ/uIRUI.P^J:HAJ&nu@?UD+}eSK4%t`/KCac\8U\ 60jمRF#AmOqlF5" AHSO 0@|V8"LPA-,m}V= + _XtL 7@p+ӫTXla"%r|UGL՚C/*r,kpũ,Sw,bO&~yQ%IySA-jp[.j@NdܒψS7T:U^IcU!^/3]N9!s΄V@\.T6Fp^=e<"_,U^On ZE`R6<#~1.eF*.,Q7ts= wYa7aaSOal3s?MnE@b.KI]!*Nr"D2:p>ى& cխZ"88p/,pB[HVBTTiZҐ$ˈ@Fr닰/U]" {{Z rjE !d[&!,]L BEIy"؛o0yFLEՀл IMP1|l )- vB!z ԁ'Pm F@bN:TIr^& m"R0 c_|>wVxN&T 䜫HKU8S/,rk=%kG5݊t]'wZiۯy['cУ7Ďa׷%\Xׁ5l@ط-)IaE,7|َI&':(42:-KLTN'sWW)7R|~n/qvEg?֍'P#,mY ě &&*k3(.r$ M9 NʪלC]0ć 4uC,̎c:v $ 17&*Bę0Xb1YV՜D\$t’muka KOOB;}ഩd2|)[J!0_See/g)MloM]'p._w Ԕn?L.?P!J]2HXS_}u5}_Hf1RFuL8] V4X+I1$lvÞH|m{YuOer%Y4l)5^hXg43dY9-YT1kMe2f>G+tW(!&,'_ ;eI} I;'=?UN}ҵޱ4'MCҊ|g_26EߊT6r˛]9{>ibT4vE^Uvj.G8@:2jSk`䠇֐Yĝ֪Gh  ='F?}&( u E爸 I@ВK% r*w՟< !7@b+SТ(ځҁk)Ids0Cjm#)$aOlI:`43 LДzWJED NYxP1ER&%c^gܝA"zjQAȶЉ6H<4?sHNrkriKic;Z;X?9be`zv)84^*LVD*#Qi ${ XSq D;yeڦ)-&)e>8&j9Ta* 1n ~|O7ZGFP~石\~j,@n Es5\jBE7z3 @e&@ܲk%ޅ2A>}I=;EQgZvl )4w:+Qr(WԂLRDuiom^sv&(ǿ?/gmFߟ_鬆厑W$Vw`"ưRLC*i %`eFXWJF~hZBGУmRUW$2EThmS*F =P:noZ4[ϕhL ]=#Ϸ WieGgW&\qE次R6150kW3>La<)q)zxVK3v6 Ķ)nۼمby=mq3mvEeiQ4=R3mA >~ ~VɮSX*z"y Dt$2\w6JIqsrORdϧCސᅽ L+MoɢkϛE +~|fFHn;8Ou=UO+u\|b }ӣpۖń9ƿBݛj]W#?XapRy0qCi|ج|{|ow$Kikߝ6̆3`^Ix)dU,PxK3:[p^(?>7j~OLϛVOŬki24N~Vmmb@z4nESRt:'#7 bu(A@jX6 ϋ_c|8ګGnC\O;2%YM#b]o-[GԻS)3E:$/jft^Z'.ֽ#.V3WzvvHtA\c8 =}X-6[\6hXjilɵ0 !ZP"/g˦F! ()`c2U!NQlg㐈 :@^;VW ;c|SHi |ƨ1E^fی4?Q-㬋鹡Lf6kšbR MӚdΪ"gZJ0׫R7yI7Js*U?=`^*`^I:"jSR㸏oq%.ɽ4G+>%Wh}A {`a==;FW##7T`Je{4`}y իxU_̟6H׮F‟qܴ;SQ|M7(CGI=_X{-_)]+EQ{麈u%fH(j{ Nm*/KVDzb 4kA# t %4R NK)mSO:~vg'gwdW܍E[U\ Q oo5kxC.F8owr󗟫ee)(Giou )LeN#W)7/>n&Ӌ,ycKnql>q9ypߗ?mwmˍF ڸ_>ơ%[-2H[ߓ[UdkH{1zЈ[SysNȻːXLI*+U,W! hENw*v4}nTW}jȅq.8 (3Z|RA{m{/O}+W!^׃W_[3u]l&YK!kvlŞ["4ro*3IAd)&t#rubg'KvK..T bU!óמװ X35Ǧ,5E!&r \ZC)ӛgaqwE{!a D Yef|9\nΩdfZXf'Ȟ\mL1@;ڪ V+QfY*>n&s5_^aPLLYV %BǕ#92h 9>o(5M ז<T-NtU CNI4 jhLsU PĘ ŧ*ah{-/4OXgW!5{4 |Ue$u-/u6~2t!>10H[x< vA%|6&oSfR3 :5J&(} %@U5ot(,h;KU7,E A8z CCr(N sDdP1oK5Ż*`B 0 t, `(;{6~o

֕jzڷaEX1}9a>ox;Hm\)²0E1$xw=Fk|.~i@IYPcHl5F\؎E_ֈ ή-ߓC RNhDVG٫dSL ż#fh")_=E`1wGp6Í. ɸ X"4!⛅Cs`WX;8{sD sGF;#r0ݴL5RV/̩akEsf{Ƴx=2edLQT &;+'g$;'lPL77P=YI(5)S*WLJcEvPLHr0H V=r/%FzA: b@2MX ~Z'xQ+WM;%4@ ,2 xHf". usJ#xJ:[|4pꜚ5 u^uN{Q%`Rc <(e>hr?o\nɎq I,ҷ"U唵S-) -`bp1{eYfژ*eW+|j BmUTR KrZsFDt0A|E\Z5,Gc )DbQJp00n"Qg]q{m.%KI3fPX8B)FwB SCC"Ar|GOH^ThFo皘H724a楺}bUGHwxa&Z unVt1/6i8:^i6Z%ad)<3 6^ Q-JɞfGU0Ti@s9&x6nLkF /妇T6WBb$T8]]N:jgU gHH,Vz;7te~\'dQw1sxg}x\Vx4wtfVt}aFqu ~ڥ<>([ϮO~~v跳L*\_>A9}n`y7SMrY`W/s_/ -}>emd7\ P ,嬔1 z"ZmQ7ieYw]y8ccs萢7 ilCķg`2zƼ/La z;&.|~]nw!='?d'پ|b>5֏9ri1!v%1Ľy9e鮹ۿei1Ihbc#}Wy&.wK>e7ګoM3-J|cId u),fB5=BD⯇iH7o_a]/iBP0uoo~8rP=Su=Th_H/{j*_Uږr}hY""د_PYAWo;Aa_}+񴋞/o7yO<ѯF~L{r'd>9/N;T$[KH= Bl]Nk|2Ks֣ Ӽ i1;RuVSEeJ香Ȑ;$}Wlzh^*R#̞@TղR-yV4N& ߇m|bTiEFlnxC>Fw\k.?UY'ΤblxCs&7\v;@*d¤OZi"\RtcRQu:ؒ[$e&=(&(kaZ4Os;CG!w>BęmN |aXaЙW fp ^K%)D#M0WkT^ JUgBy$rɪKnUNiM>>m.)i Gup'勺#*ͣ"(VQX0'\JV}z;Awl/3W4G#Ń4-"cweoZ##;%;e(XÏP}0QZ ϊh)+y2ERB /<|.'&%둠/o3 L?a*oߵ|{J3ZeJ =qKa2=s-qp8jo` l@ -f {vM@m%`fe:{{*E6c9T k'T'{GdCbVdkdfHb.@Eοy}PZ]lǏknskU^f,^wq6=k0HmdWܶMmzJe+ݗPAe 05 ?V^<4%th~x(??U7ig`͊_&y0_(pNm@\Or+Moc"(`xyI7I9 b |ɔYl7rtw[o4zT2Ʉǥ8-zZrd}eV$ph:חQݐ!~I\E@d)yXTU -m̲4+?cˏb)%K&Y}%k_hhN) WL]%(]G˺2fߵP28bäy pC62;wlvk=:jL;r>˙=Y9;DͣvG:3:Om#q'tc՘GGd{g&-t`Ǔ,dd7ב1 2ث{/d֏˛G9E`L:G'\Ix]<JN*}d4L׉`J;0W͈1sgrh}e bc.#pq:{N4yi۾t?9_c SEڑ.7YDt ?49G;ݑ=:N-Y ڼd'oA%|fqnJڍPҋ;9enGԗj]UBf|Zۦ(s3[+wxV\7ϵ Tr^lngJؚuu,ߩJR fYG!bj)M9|3ozbj3{,B֜gv c)WCu/0VfeӲf{FϻV^yIӫ,U?ֺUdXVUYY\ -h]Ji?KG2l/5LҷK-[d~)'mXF no~?'R& pL%s~_c{QA_dv'2_h:o"s{}|}"OY?Ku'J`z2 pw;`'0jS@_Bz9㫵=cou_M]@.0۱zf2DYxu*D#eD6C,[d^*V_Q rY4+i(E\~NG $y?G}?DU>[G?tS:ۏ'>pc{5Ǖ/3Ս͢u]c}U??i]6ϵE7r;?ݟ˨mo:cH 3ΩʹU&/+҉|q!UFh[*^}#o^Vis7/Q]-e_&ϳ8#jUr*=Athv5)JNwi*)nj:]v$,D_KqW/- /$ V|U&~ :?O&>qLӎ'Od%y.72j7W9~o?u_tPnFgc {QyW5?1=.:/·@ߏ c?@cׯe5ߑScw_[lX?whMTϭӁ!A1Y-":bTae1MN".Y҈z4$> C!#AoZHsj"+z2 ,ẚ qy*$^936(EreSjXÇ"ZJ1Hm$UcT56k.7[ȟRjI=S;NնvkC>"~_ǥ2\jy=ŏ{akLdX="/Q3]<rfѦzcuwPGdA4D ѡJ,HE-s<?S>A&,m/5'J=%N)n%<0ߝu2x'mt1=EiDlESu-O~eW# ak2R0[a}(]V{ U%!>m Zךۻ%Jx(% W F& Q :|ZYQeմ\\>6Ȉ>ngXc0MmcN jb кW3;zݦЪ%ңm,2uVȦAw>;a_X/AC(Ewj;/t(e?vɡᖽg9NTjSh('?T ߽!IiI _LʶkPE9ƷR.JұpO{zOfWcwIZ͎MfTqȨWO=i>a#ZZ[ \FĒe| &׼Lx892YSe#}B*fdd<3x%`\&ӹ˻+Vh`/gU: 0"\bFȶt˱]K6*[G4:>VY+LtGQj|庛$:[޵ ˫ ÛnR.63f߯ēn7;!Pm v&4m /iʮPY|:IoYu9ǜef?^nVQ*  CrY䚄\&y̲l8JՕ qjYF%"7R ?N8\X9i=$iV^^FI^&,°q0nǗr͙S*(E\~)Tzi"jYO'#]1c%lc_T1ɨ=\M_V1d8A'uwd :fЎmt5e\PR}C^'1lsqslj\($lv?n՚XUk<*=}js GGtpg 20UQr$AYd~68$k9#.{dָ A,42YoƗAe:]S^"p<c^>蛧Aj lPg +ù pP]g?xQPγxNZP"]Q@?c_qtI[; ra(lߗtC?@Y?֨ ]6U!^&xQE/uU&_[C_F忌NOu*$ZṢ* c= $-f} w /ͫ7GI?ԭ3J$D܃<ƙ+ê(Db Kb~ y(?WЭ)i\:/wA댐a ,W3L".T N/έz|)2{ySUYŬ `%%I cE*kZfo3ezm;>ˋ~8:~C4\XFdbNDaX]Th~P.W cpb,yqJqLZ%~qe&_*/lZZe֩R9fD0W)xdTe֯r"=\kpn=)^kĮk xT<9m/*| v1kBmnx_;kEu{ wtЯӐ̻]h>n؅bx}\q<c!I7 ҾXC4 @+l0[=U ?勪2^ Psmi'uٚȑ&#eR$F;U"bcٚ$ݔDnqR[ eE곽mζښurܴVݻiWŶ΋}#,+[dN2}lЄ"J6C$ ˗&Coٲ\ΣrW%몙m I}}?X}jf8@VWpF7ttvgW E&7a]uC?I;58\Rf{ 2 54^!dp' x=Y\,Z:herQiw| )Vi[=Rb>#RUOlԳJ-Pv)G]͝e%K:峌thvo*-e(9~rq.[zX`=׭:Ge9k_ 73 /}0v }1c(ok!k=6_÷$IZBN$ZdQy{kg8ͶgVZ N"=ӗHh[Zת*&hyAR8tS-pQ/nr? 7Aߡ]㿆.Gb A59X\78bO8P0 _n?mґCWx+A:Z/ s 6_ҿ-9 pau?y[ouͩQ|?/hMk1tO;۞Xm+{Ev+BkӉ^[%EdG˗F<?ϪiQU3lzo#Y#O^W6^*ժa|v_.\7;Euk<1xީW/p^[U{]r]$DGq(R ![v+vgʼB٣sRJQB 9?A??<>vuZ+h>B]bT"AпXx8@t߶F7oxQeήkp2N_`P}u-.>~ \җ [`E+m:$oʳ+*1)u?ZcQ>X~IżC=#˓C2˸[v?CyVGfA:{N$gh©us66q7 eʼ/_9+|[R]#c6ϵҪ=_T!‚dT,dY]_sQT8UIΧ2GIe.0Um9O4nbOvUr_uɤ焕 sT[^従V9/oj`0,?uF _aq,{oTwTu8_Y8 PO>݁F? 7vgjks%ȽK :1zn.?+.5nυ'h7?Zq?*dVJֿX>1Dmۈ_kF\9<f-˷jTkt M8"Q39<7U?Xϯg /4#\{xsWn]Ю_c߽?+jp y,չm;L-Ь)WwB2cô7eR]vMa+a'c8#1{gt?Arz!PqC/:@m]A!o?cA#-ܺ){clUw7dq.?*jiGx  诋T_f8u?>? S25p?/2Sc>s#_JS];fi?򙽋Ŗ9|pwh 5/p+}q>*39\=ڸSn;+C31;-w[?Ww/?k=U2pNA)*c佷EG;GΞ%,~ˣF%Xs?iQc?TV,eAe&缪.ʷyAYwh9 i?{3x4-S`LRm߿)u/6A_M=.tU߱o^j/! ٺzϗ7;62OαJkwo%o=~i翌׶]p>*'+)E\=b /BeKaRɋMWJBh1,YҜ"6%]stEQ?_)0Y?;-4>Ac:yC٭U;%Z1r }7^'>jJFYPKF?kr[_G?tBנnGT)Os<{V5xVo:Kʩu,8ͦ1k2T9;o%ӕt8+<͢Rv,tI1Uk|yO*LhJf"e"3x͆E.8dGă+_V,8S?.Xpw۟',*]7[dB?}R44EQ==/#pV1iIo&kOjcAqnqGz?0;ߡnKz=I:ÛͼC7̮O5t,2`"b&KyR*RM\^ܜ3.,co2d븨0dq. Uzc$SdٗSxG̈́ rHM6l*6 l)N9nhm e[ s[FQ=Xw0;(QGkv.UL@U~莟kŤ+K1&U1-Bw) e?E1*lz\U_٪U-VC$4ù߲Qߋmns[d?]q6(7eMCywE}ZB./OW;H~ȋ?? _xB*ւA OŇ 2x ?\G0 ldp0'mq1=I=SEgum*suyKfpt+Q[N#5%*r*;qNBub=!S+ͷUx+$O*r'qEjn24/" m̆Tx]&ϳ8ϫ|{X4Olds%?/o3ea$o\2K_#q^iI}`1SӈqEz5,x)Tdo%%Ͽ|r읱 Z'|jjM b+lú~:KbsB"Hn7gBN$Ǭ7O&fSP>Rn-h+ak\yju5yF]iEv;*q!MH1Ex}N˞T{^!&M;˥)p $~|ڽW li~̢tXIjf&:oM_A?֗L0\l6O-ASC ,Ӝ%f7`7gQᲅzN%ĝwg:.21k\ t&:-4zs}u!jILץLP".Dt>N7,yF_! }xc,; ?nc} ۇrL7 :}]r; Vbߓfri/짂RUg2/a [0dEܢSٌOa DݿSs"uMf/ [7y&yMe뱝wrZ ~.e}9͑ CkV\55)Oߥ?1OhyMO EmW l~twDV]ׇ}QuU<=KïvdAw`߯X.Z9?!Զ'(R.?M&&&9xiV .޽|7uqilzPy#V?3![ tgިy骫(/T.nSMk 8J nn^ۺMvhdIv.KGU2m #-r=GXH㞻U*sIEb}}0%+x@7,O,J~ J5vU)q9\ *eE]tnVh.N^kѿX"olOa㉖fܜ;(࡞+@l}'PR-.1^Cwg@DQXm5IPjZk=c?oBe</'0w?i"'jWӜP aQkV)"<%%cxp(|$nj8jzeo(C/ |W[bn/zs*pJ3.1%DD9RX7mg_-~0Pd֊5e2+leF&SBe  wXT/i򛢼O&~8Ȏ߅,v {M5G2?ΔjtrtuUÖk,~ao9nxؿh@K';MIa0g?kfX8bܠVv("A_,M|rLz&|&)Py2Zb+EuxAd+0h|FFDI`*)(O`p^aH|7*yfa(Řt$۰L e޷\ÖjQQ2̔JGR?N2IzX߆i`{k'BqQQ)`=2?mM=j'\>P(վRMծ_=/+Zlz7ENk; V X-4$W+wG4={dħu CdT#6ժN<*j]%nV9z?Y)Ά+N)YNOWoHB|~V"~ݱ26F*.y_ ߴgNQG|UK,%ҽBv5bT𧊽re(`|ӱ|Zf0CBo ]Aã8c%g8X BM:Uէ=>O Wy婒2Kr2P[e2CmZ%QL&gyg6/,$Dr:vNR."3DB>\Ok90pfkrTӾ,i.ʏ|ɸg Sp[Z儇xRA `12[8d7$3A-K/Cd8*ʏٗJ5e\*m6@֖,PAgښW4>$3i%s ' ܰCi!ھox°,gv$XfPh4GCM<[!jzf&qF= ,Cءw/PxWüWM-M<`.tJ0%3aai]ypA ؁ (fwahc ,eI;#m `cX= ay`oHhKb3-y"pa0C%(O<[̠ h4%0*)F8Q:kzؔ'D/` 0A!loڮ- +(qJszZOW8A'!0X/mS-@١mڀy3솑;&b8mGEtfh͗VxЌs9Wg]CS'H _iX+ɸU&A ζNguU>~R#:Q c09LN[^f9 EB6BlILͳGfܤ!71(9`I:IR*WuYNKC3OC=]["[b4Uu%hK._0`:ZyhuH9hz#u:sBԨzpm eVEopea'  Aep^`S1st9 T!iKx\b% ;Iz6s-m*I1ڤ3NZ":0TBI჎ܴa̗ @1 ) rЀRX[pk ez5 4.ۮ:ϥhʐg}UG0ÎeSj Bh>,-Ήc F@ w(ůsjR^*vIE Uw/V)ȷ]}KqvEai^-ab $D"S 0ݼ|A4dʉ^VޅriY-^ ͪ?6Ql ahvl ~s>SSW v*kf]3Y˜&4% n #ʜUDEOXҷ-uȵSY{k$Z^h:Qp~~hd监uPTM ~ ݨ"/bXg;˚3 aZUE[w8iv]F-8s'gmR<,g@Na(Cpi \´]JMQ%%yg$p|1|ô9@=B€\-%avc ۓkߜz}Ǡ ɢO VxPϴ ǣIv\ٞ'Y9bp9 okǿDQpPwL]o seq )aS|ߗA7˭o¾PL׳Aq (@p97@L(t%r)3ڏGM9:&>S2Muy+Rݵ , e}>@Fi }&3orƅGB#x8 KҀ"Aw0`d^ncCr-yoI/t|u^Bɼu0lF\¥@6BPԝRπmQ5p95?o.bOӸF  ;AQva6mwt ^ITfd:<&a& B ^?w.;zo> \e ؤe; Ì\=K2ftj b q\~ k ?d1jl>,[Prɽ{0K&|uq q@^2ƨt|fˆ) %ta= bR1 + $c^84ni^ݗj4\2p>H R=åmnE(eQlm-oϣe_2BYb5"}W*7*ftm=j^-vAYJga?6MK[~ڷdtޡ4AOێ2ayʋo$Tرvfm}>L:(c椬 c3/RjNhMTtmPfKPv 52y9_uus“.ܲ}CEy \A @IpX˻zS>3J@" `^ CA3r=[幜{>5,[vL}.&Ukt?ZGӹ]놋0M S4 q~n1PZCK61c20@GhĄ'D3e YCj` C6 ,XhkYL !&j+04}J:EEЭݕtKQV/$l.gԑ{O "FizLaO]0`)1,a j'F]\8 lt@'Om8\su1ܫ/3nBrMF>-P> ˴8H3 -اJev3c$k:a`h+Q[v+`0ɃzCikg$$a&hgâ3mH]bxG<.@$'p@1al+n @SP[,B-a`.ߴ}/5a2to!kӏf^Ձ+; iZdN 61`y!$ xsj. !&=Qu<JrJ4`/ ;lΗ7u}tţ"2tji=TBQ@|Cd s~<'4= ϦUNGZ_6_s2Qs0K-է̀Ab8!L4%(iXiH#4p:^ukj%4.Z} ~, t6e@ڙhM1ə>hDgEoسe`W{ؠ8l\Hn}J`)~˱t;W8Z*Dǀ? 6j0@7``ہg1b{tْe 盻ljTKa !1].9̰C+cj= eX`޶{Wg) qeP '2e(L C̜gCYGKaɹ B=9L)c@L@2Pa&60M0 B}-0 Gg v/WW;K5o>PuCys,\*l,\s݇"=lr1d(@95L7 MAx!TpehYn _;}Px{)H@)MBgVk$뚦oܣ=gFǴm8h>:WO!0%Aqڠr#m[& [:!Ly Iֹ,c"ٹߒhuUL_F  9 d̠ *D+aqˮuO-ch C )8AK A-j.!P0#Dː 8-a\S "\m { 3"q۷=ɐCo.C&-p {Y^D<^'ٴŮͫZ ]ðܖQAS1 i`(tK:#?*D-h1Ƽ/*_?Փֹ ,©VkRPL_p-?~  0MXݳ qJP3״3yF>$Ӱ]ib?7{Ԭ>ͤ^=C9,<7C)8(/b0}ϣ&&̳`[x&: fefҰ d@PT]φ/\:TDm䮂RQU 7*6]2ʵP3о,i Iny!vlB-ädM+ {6ߵju zU/0 CPe>pH @&4lyt Btl+K{H<&Ž6IATME2z(` w9\p<" d ֗3sAkhW7y-F;}p]}h.0i "AX6' 3]ӂX\=߰, 0}*9cƒ'L?L2Zr{vZokI(1`3!ڜ@\pr:o^-r O،:N ATX(-LX.4O>kU. X濪.y|ٹP|Bkr kT nxX!p ۥhx!x=>8L d3i TZSXNϡCHn0r 'HOo$0= <г:O4$<TDyjr^i!VgVft2lxZ(곹)~נ} z'O\V`FGa|a-0Nih 8,m6JxQpP/ ]z~0B;W%$ 1 \ IpP qp}|gc~6XhsGk(FMcV(pBT8hA ă3ҲlЏCRJC{F\i>Jh,adD_i;Z#IJ$3*Ys̾ӳDQT#GuEAH-9zQkJjV,C:<Hk '}rLIiW?6韗zgy}?%uX:]ܯ6D^8~nv{_51_)hea uǀT%H  dh#Zz[I[ug 6\N=w+uRhF%MUW`x#{@ u9:H .(V&IT8/YK"V-hЉA 20l iG0'{%5iALmu2h0A:%B&FLݤHXxXjKDy*`gkZy gEe zf8Xy]]>ps+ t &WM}MR+؀.:fPk->^xQkRSHoZ&N%fI3*і :ԛ ;[_k|iVҥbz*CQz%J3 i9bWz`-.~cmO``O1rTE[DODILk"'4:CEȵ.މ$Y/}F,N'R20olcr,5i(-3[13x9 Ԭs3Jl*0\S&e _ı08haو<$.(8*E4ls-lWnHM w@&g=vpR:?'h\%[E\guE & XpiSsD(j*nNI<^E: XbbGsIAIv'wR|P :&L$wMQ:R (ZU/ pWDxS[q]bKT-p8 +b7M߱@rח>/JB)I$?NXR h -}c"@S\Zg\Z ~]D &bNcC=V7tv GxqfS]2Bq.J`2N.jكZ \IeTPc_9&"RډUEn ''CUh9#BA")?[t>Uo))&i@Ha1Rs Os8C#m8CÑn8Pz"#&TMK`FgKm 3%<o9\|?GhEǧ)NrT Pa]U}LX^Ul=ڙB=趗"/܏QWka fd+U|.$;IgEDۧ8S.vzPypmMܵҦjtׁzd,ΊLA)f%K:XKhIttB G:_ &TrOfm}m]jϞqJԨ"<+ L3u*E!וTJ+]J,leƯ݉"3R15B%e)+MWTLU V=kbQ~5Zc2RL-7pacm`R>wsq vDHY7/ ö$K7 0bJ$ꤢ 0:+x8Qw9PݾV玢ڏ׻Pto:\~ я{X+9XWd(m u!G+HLM0@ؒ%E4O}RPb;}wLFb Q-|TM<ϑNHV:t+:mѤ&lᔣKshՕ@;!U[O:ʎ$N(3Izv..MCM oDSO>xYk`pI")mpt.TÕEG._{&T`2%WUZ>@-ٕf\TF:,.`-ᗵ.`*9{"04 PE燬huE˝8 .}y]@X{A}"L c(hio`сD|?z. N $ۇ6IzVx`Xk/.^آ]L^m(OOm7cN1S5I6,?vٱc2>m& VD#NT);EQuYjcc3$ m6ewqe!qsi+A,W(ou$oR l'ީeh#<cG{y|Cv C޾g{}}{/ N=E:=}=SovUoO-!*ha Ǧl]28a T` faKٓ~#{n_yw 7[3 I+JZb5C3n*z^dVRpLٓ1!r0³DffB /JgtWS@pvBVgtW2*XI@o\R onA f_b.3fxƆ_X5>=czYYrQN52M0a S[7L1zJ/\\pzkuIj%|TuAW)*:aE.,aBwZfg9SF[‡0ly=2ϻb?HzM-dllO&:j``PY *7LJd6OBPE&CSvC*8E4pNVtV̫d*xEJ>t7 p8-QՕ)iu.93[o,@yG2_(4U*p>ǚt6r N|Y&' /*hU*\׻;|@1lR( ԝ]jg}\#,o]( %56H3kޗ,,5NlJDnav \_:ccGbGy< ,\Rcd`g`,,~&Oy47Gb.|q{43rΗ a.5U5ӔZJg/M\D6xv[ 漉t/Kx55g/p`wgj'K6_  R9U ^j\S;WIX͓3O.k[`{(–awJc*&)pԤvsZ4xV KD\sZb6 KDΑ\$KN&UWb(K=_i)ƩEVYҸZlYqLf'Ou+!7iN{FQ,g;9l 4eǝN ߾˹?I>]7GdD3=&H$|rZSQi9_ef,s.hk9JT{ E:vT`Tғ\Wu" Vpd0C:2W|^Mu[(A?}0=8kJ NA C2[ICx_Hܶ,2HE# DIQiJ*ǞE`d sf[nx,r 'Cg,f!^%v5Ei*G߷JjHEd%plϩcJ(MYH5$u3"=QWenn~A٬3Y%,MKӱKm=6)[ %1X-HY{^eW@+%J6zIHK=L=K'y|5#Caϟ܎հFlyroJIga5f8ߓ+,@z2ʐbM\U v;(b5GEtl oqJDtӺyR- LB/}6R?u;T m@"#MW2tbP)ͳ{WGZ$ cvxjs\Lg\a\k}I':KQnK)TUYDP)D+-dͫ˳|_M䭻ռ}v^ ҠG0ƱL,-"l E%-M[ۡGǸ?'">`M=4p|,,bO`q3^ŀR)ř3L&{Fڝ5Gw ҨodMn+Y%3p4>i%>j߶5jg_9.^V**"wh5ז;.w뒳NZ l Ao#_t v n‡?\<ۍi?TnDZSt73 ަclK_e0|9x0:p:(M.DTH+Gu|\sǯ݆v, XĖVE҉[gD<Ŗt_+hz=kM ypr]Gí@0דeP9 X)A+_D24²0Q:`)̤DUmߪ)rAk [3gB.mn,c1DI=bE@vTLNk$[@FSNxbv"_cX~T,'sJp9ۯgkSj@sj.z0aM]pRnͥ/a,/)ZX2hdD;ކ$% Ɛ@&f/i*6 ~|Wj; F*{%80jtI $XdeN$x&Yu/bp 3F<>UYkk |u R?Jk`EUeWpU#9Ir]#M%qtZ2J;РxZdn4؈E#uʥ|/ǻϧXqLWX^ 4fIΔF, ݒVn Go/N$EGI3W8 ~Y$z|tm/tàV F8 ș-E >Nϻבs)q X ٥KgR-Vt}q>?k4Ej'ߤYgۻOճd+uw4[;8DK/Q'$ -z3ASVZNWv EaxM~a m4^.Mh.Onr#-_/H{U:Rnx{y[^_EtG3x ,r+/%B#6X8-RqFfuu+}yشZ"[-~/lzzf>RMca1S͜؁dŏMYb3+i,~Dsv"xn$)Y'P +klXExv+^(dI]tcߣe?k'B!e{N NaY8Ժ$jԭ(P055ͫ)oo%ɟ-s?'=[EW#ۖt@ٺ @j0M1(bΫxVWru8'Ő;"KQR)7b}al#K+LU}KXX^CF^ҮNNbIˣ<] ˾$=g [ ^->Ĝk 6`.OOe)GW%JhT3]eUE1C{f{6К.Z+;M3A I( ,3Tlt]Z£]8k(,$Mr5tsA-<UxH*7ݿ~W-)!A{Z.}Z"J!U-t2[Pm8xzWUoEnulN%LZ77I$ wI?)x<9/-Ep-zdu C?^2ɴ`CBUUPn 9#Sup`Zauc:/7q'To֕l˷k=S2!qg-ٟsyyi)7O?FIYqqKJZaZ\ ܿsN~jΔ{>TݲE-,C; jwmqx}hF G,N)s&ZkE:U|iRp㕶dw;i_k#hsa-(DQ 7t&3&3;W.ohtjYwU5Gދ>4'݋=JŠ.x95qJYU8@Q$xX+$K")CJ=Is^Py`KRib`Crd(bF9Q3ރSŀGe3@NԜDoI941*I(FxxsRժNT}& 6܌D{?րYTfJ_@i ~ƉgLNͪEL6tLMkxeڶV(H9Slp6c&4RS.b#SM<*mGG:]y+m0$5L Ly)a;ْ(7!&` v&[̎]#_FWŒwҢHzHO8%4MIϊr*xł)'9uQ:l81OYѵ⮠(^dz caDԈ;IP4{6yCݼk3 :Gl}73ڄ W[Zc߫%NIXLKcnOo|#SSm$7iy'um_i~&?,0&zLUB8FE-֗övH'ԗoO_}{% Ukgtz~y+V^U+|?4h(N$zGI}v~MqU"3u+`I<K8iD29#CXٯ~݉w_}jGkvî\[8S=k_>4{$})kQc $%EפAP^BpVZk)7:>81W/`W}Wgqȳ'&.IA}NAK8>-U89+%וZ亝q{ZtBJl]%E`Be+=!dR*!*5bq0,  2 _hm@;e˺WM#[Ι LPUM2\Z9dMGzB6]yx ?I+kVMQ #69i @䕃—Y|AFU]eJ)'>06R)-{[$+`~Ba~ V^m4K.' .ŒeɐY4*J#ߥl\ΰG>ا$8{RIǿ %o H&UE?AW.u dtcb3wxKY2i^ 4 SgiK,Nuu5eT*`{eNzqyU=YǸ.lgZZ.wMp ؍ob 8C 3MNU]-fſw\(~%D-8W$DHMЮRlE&v)*<Ɲ/ Ƃs==3ݼݿiMߛUg]X:.""/ /FeK)ˋ>-V5^T$1_w>.+F7g,+ -@H^ĝNijTSR-t ԛu=Ywbm @ Sd|D Q\r +\pθ .aias>Q׻}}ݱ4$W7uƿ{܍[0HgŝZ'V|&+Y[+&;ufO%َ㰧gf ӑr Oߥa{qk ڝB5d\vdRM3#  j8 R"H%Dt0%鞤B+" Rf<Ƴsͽ }iݳt9ÏʉLf-7~zmkOUÜ͓ՙۗ ?Ⱦ:'[S99pMrp>d?s0'`!z:#5~vz[?}vl}8924nrdFэK;:)Ϣx0TJXv,8aE3uNߎ$.s:G^8ƿ?w#,_q2cz{s8=2<__ꉍyA {{׬ɆM,n=syvy'~yK_qmf2hFyg:X84Lii;Q;/?K:}jӯy/iFqb=s]&m6>fS=qN?uG~˒^Kw]%ߙ r,oH $ yY,9y'~k6 zlƧӹhd}Yu!(?snG^Y=]h5G@hv;'. X>o"w7^Vu6/0_qvbbͳGvĵ&n/BuD7FY<A\ٿR} IMyC{y,3?_ͮAv”yM=чg>AE ta8}9 1/~ پzH΃r`h)OFL&Ыv_z|zxzyPpOg~X0OOo \݂uI|N \l7AޑyoێwЙQ]eWk j*|8ŒK2.)Ks_<3t{8g&o=ǢVBwwE_jL5JHd4wVsEc#fZ/% zeh\jӗn+P\d4ۂd\7ߓ:LvV@6|мDtxkTie@?޵ݡˋwYKVg~/ HNYbOS@ײA_& /8ԝy 7SXgcvxmؙm2az+_˗]ȃn#_'߿5R 'Fi"gt3g^Ncst |,M*)fGٹ.SDT|L2#r~Щ+ÏqUG= .Ll:?a}-lxKm.%!b%tOZI^7.c>>3EykQA\[ v5rX6k0Xuu6*usA)|iq>rEߘLZmXwG(-":3 DB0>gHohV{Q݋^ ! mվ&[sGC|V(TSJa~ԽqW~OO=޴uKˮ7<ލA*Kb}/.lB3ow0'VITC՛IwӼBkdm윪ٛzbW)E݂!깵qBGfү%zu&(a J[|]WJ^YQݚr?ݴǾv+Ck5D.6|g s7\@-;kSH"k qiSE4gp_ѣ&49-)K%_C*GP뢽c=DOa7<@XZaJ)vaâ_ae(uU湣X Ɣi̸e7^GJʑ]Z9+.vή9S֒n9?Rmq̡u8ZV8MV8%PVC!aÝQZNsi#^^"ndU d1h=hkmZ1{H[R"׀"bA5Zx<|lw]Uebj([t:KD6Zh`26Tst~#I3-bۄ,Gh-Ls-JƘ53AjGkih[T h WaL@1HpÁ8p&h@ ԋ5ә$ͥ? ?"nb(б*H4U<oUgS9]Ű趖Q᥷s 8g]:[ 2sf vOM}}& fzmQ|zXg==~m:o/[k`)\"! b|}!Ful&zy>Jp{oq0_eϐs2Z\L JR΅%% bLS 6n^KGSxuFz.Tqrf8 ˚vloqË!U]^6!.2% CL9YE0>:>^@r-I őIؚ-h׀:|>A 8٦@-)b7-`jA"&K+\ |@Ayf"N\QըR;mճnԥhtp6p˹R.( X_/j=I 7A{:sKG=OBmf=ֺ0 b#*|8uUJ{|KFŹئ[Oz[~j+|4gp.}w@ZB+ 6#N ܉?GqVA/MyU+5䯵8tIۤQ9*=Tď(D kKjST1v+7sIItXI3Hb4_ofuww/'7YLQ1k_s!z*qx|/BN^<5mRDjb$'JbJ*Jq@({K ͆~6#I*${:赥I:ZwLGOrua n/&p悞b94ptG?|vzy+7~ܔ^Y_Z?܉?iWo~6{ӏv[*>|Gmfar9.lĿis^М8XVۤUO0e,hO>] i5,9|1=yOS eTrzSɰbBR6E 1m3±aӹuʍ-SӱNY~ &^VAd\R-lc20%K/B̫0X$rZUOm|1_~YA䋏KoR³sT=h 0Zì0-tm `G+)ӂTû MG?3TZCYnu%:yJUhWgu#, fן;u[xNd+,겮JTS3\d^V &PJXw|mבk_`BP}pC,~*Z9#!7ɚ2V8wPr32rv4v(ZGL֙ `Scrg!0K3R'RQk&(Ⱑx{0{ȡ*:'3KTlOfv)޴.H%ׁe]`Ui$#,v…nZ坌 d B )XWV|U bjJU'*Cm;'nGcdg/7$]>=̨b@AsyG`<Ø d+bP|D&>xóL)=v'_oa[7s3aw_@`S`>*HJEMTm=%它/Z\exY a))au J.mەBX$3᪷QhElExzy !A?Ʀ*j2!b=kGA`5wn1K]u!^sQě﫧|l3An#$o1:1%sUjs7 C{\>cƛI.woW[4>v֦YaG`X"<`d41\Jp="KEGl"O Z05 ]Q.Rjə & %.(ř'y:Fӱ}u}kp?]|Eu(=Q Jfi&LRкڦT_q!%UgRX&j7w5bf{AkV~AE+p8K4`l1*I=)͙ȍ˨%- X\Aq:zo֠c6{y[4`Tyrznj$⽪m}x/B"K憹Y`Wfb]&eR|N7VI7khvg03 Bu6m>vp8%ޥĭc[.rU#J؉BC:YM DYoT)_&H71>뎃n4cNPj=űwWp.hNu Lmrm"h4XecS@t<>ztфJ&ubK]+ >gDW-{A¤V[  #:_\{F3`uKҘS5V'Ptr2;'m{aރ~tβۣsA fUzEEZ*$ 't[U+kewc4E"TYDu!?Hfh"'\+TUP\3߶Mп(QJtfhszqJ  D% M՘:ΩF.[OAIj|h9=!7Vkt`Y.r nfɞq.jޖ}rJӖ|$;9Xϣ QqV91P7R &f%қԇV@HGذض=6]\!&¯쎑K-x("QR"X5Skԏ^vU8NM8։e ;:^g0Fە( 6@ubMחvkD$5%$]ڤc q(XL p(p1?sgH޾&R\&)-UT3BEt%$1V;| \eJM^;fnƉ ]8Hh2&C ][;8PǠ0:"l{P,YW*)oJ֢~aqŪj%C8 D92I>Qg7ۺDPߌ`NP|bQ҇bSzaD8{+ "6g[Q9[ĢuRetUԊ+6!ӌg| s``PWasJ% 7LXɮ>R䤊%[b)T5me{St^̿ywlå3=6DF*l'xӳ-A&[$iū&"5\Gurų$wC1Vkdt.9sFvqEoFiTPkS[07E-*g80)|vP5Jg=/_CUJT QRf䊵d=|,)9@nD0nrd6gjfI./ %onDR믋IjWX2""+ v}HyN\؋残P1Ho`ReN+;g2NmS}ڦ`;oIc:*58DM1zTAN F#a3sazvK83z(k%|#rwOo7/Z<5X&kgo h86ISÁNȉ :"4cRIK7Lg8J Oy1'L6%دB 7F_7w͏nL~iQ\s+k2>przkQHsbQq ppI{ {@j1[ R=H ~Bmxɛ+֒TIOل5YoK|<ac(C42ٕ],J^*J3*κ:>3 Q;1Ƒ_]޲[$0Th.&ir.i!+95[^ Ml@[G ;2M2rDPU5}#sV4](Q{B.Uz4¨j}@\cq|F,> u)?_vcargo-0.91.0/benches/workspaces/rust.tgz000064400000000000000000001300101046102023000163140ustar 00000000000000rust.tari븕 ڟW(?S$kܞz({bދ +)R&\j; )qђJ^Kg_m^ -ţQwigf}CmǴMj5!&o`<+72*n7K8_-ifC OCFcF{j7:-$6]&=w"]g'Kuu M nxM{[?LChǑq*Hu??1+ȍLHxR7Q,"JO76g?nsh2Oy^neGio;rg77N3:tww7[7?d< tOέxN|KY5O7ppB 3{qQ,y-"ׇuo$P#/aH}Xf\{̋ݝ@"6E G {6WpER|VDUcj&Lr7~qi%KT/IHy!in{p?޹+'w=#4-"?1t&_BzKXYQv RUDkYS,ҍ"A@h Qw;=濾,`!#~2[_BҲ]/fI!BƱ`F.3ʺeʮh =]R72kp+:7[LR Q6Y#l2Y/ C_ڷ >P[Ě9Oru v;Vo$H5&&j AwoF)\_R~P\_Q}D(bU&ه|m֑MA)&4sP.B-bN46MfLe>U c^HZ!@VM"CۡpkũSguqB ec߶5_HzC .&5'u;x mOU*aEKAכ9R?#m3G5G`Wf5v4ݵX|UWmݹ3ޤe#:WLՎ6ә?WZo[tSj{xW;4#h!H 4lQM ZKmE_ GFa;FyKo(.--&r%>l=<.t,Lv2[FE"L|G6 e:ʵڮyR}jMӢ5Z3mJ7 \~R%p>V0{lqʓkcjVzCe5,xk iQCȤl JooS=@|ԼY0*WVDŕ xM<ڼY$g^B7gl-8Jg&L@Ο@ 'kw9 ݕ;/X'?QjE>[w/{ɷM)_iBW/'%ZT{Z@tہZ$@4/7zv\ViWmg~"xaQt-ų?]08V?W1ĪE=-⧶Y9%sD خYrx"pQCqay&yo?ݭWy#4-)@JR.y.iS~wqA{g]6KkH?:>0d')͂3();G9 v]qnRZTDbŋYR@fQpgfNթiJpgdEX(9NN6;75Q23UhٳC OFAu/jśgD:G:?Q :t:q[mZ8zA.U6k6 H:5:x?Gt5:ʢrOtGLƐ~5Ƨ^e,ҎV,/h)rm?(?ΟUuHfHKb( Ǟc})xIa:'UA7808X/d|\x9-8ao_U:z%|9˦H+*[<̓L M>@9Nȼ W)7CQi3>r(<6hpQm.;|97K㪯).nnۑXOd$P{K_  gQ1-wuw#n;-"M9Ld[1Rbt WLW : s_0%? hynoj3{EhO*EO1q^jmbUbѬ]j.䤋(eA.U@3OS(գpijoG~{-`=R *wJb؟'K/JLYVh޴+Wi6{Ity.\ +wJ(2W-]5CjimMzj 疕-Rx~6~sw( XU5iUFkxw!c*a6 FJS1ԑn6[?JJޚ)L]Uxa vw P ;9@WLQ67WgY!G?BkD:`OuYdCEM^ e˖r6}[U29wSL:H%d|38%2 ɰɧQ"k6~nIV皮hu PdS[ШYeSOlyO+/ѢVG c>%sùW[kT}D ״Z!-;nnR 䥫"Z^_nJn-ڞ̬9Kf$J56ɳ߻7s9GF&$oi'.e?J_%|[[;Ѯ;kEj_ %/sC8+]A -Q֞@ 4߅5y#ۚ^e18%كTF2FCZA|PXpD:, F1AUJg JtS" jykjGv|xV7-uο`_ e{ 9ݡԘk[Ɠ`_k_E&òT,BI$z 1<-mjp=y]oWWF0au.0/WI{YG$~{%B/x2N_jO54{±t8gKS:!5󿗕(Ӊl6_O2*Vx= J=ƫs2oν)Qo\e&Tv*.TaRU.@/4C ]w&\bfv1K}ߪ:ڃWO-J72HEç*cMx34 MMi4`+08EgޟauFR/o]w_1l{ eXl$(5mY i;,x!iյOz4W< [q cE w6G^E kҎKOE黔ˌs _ϐ `ڹ._$@l#L2/yIƂ>iD<zSZ'd/XD<땊3 !Ųyq+q}9k9:3* p2=|6ЫBar39= ,D%fL}%5([_ޟuߧKݿW:)#>hW`V^h_\tZAIg^Z]xi7{ ̳K)f9pRkŗ1ļ 7n$Zog1x2Q*K؅"VǽFORGgZI:pO&1/F?4e, ]İc]BD椉&gFJϩ6m+-HeBQڣ\`TT!xEzfyݳ|gtQQ_ۓ߮Oy?7mCqsj6豜~a$-hSVrqN~ZV=:|Q+C++58¥'M4se+8vq,;I&|a JlRx _R|U_)dpb+NïxR'C쥼Y9d/&˧<%/ԒPȥofܪ/UeԬL~L~YU%|fE Z=u TWyy%uحX.x٭2f^5MlsBp)4eSU".q rZh 5F\"XM|On/}"RgFF!ox.LWjNj.N~*ֳc3t3_ ƊR#un1":8# yqObОQeYR o빧M,{m)oV"GʞCm7JjJ|D%sl`4d5W_R31:4x6VD?G?IcOc>+*w7'bs;UޤqD|.eTUa|AB>7tCr ˌoVѯ=G#ڄ$I!(>vD?8*S `//b?%gc䇚_|(.Kۓ".k| k|=%~ˬPxj  \gZg,W)#=l@P2!&YEc-yR7H`Dy KPxWso6+_`F>C?QwhIZ}Ѡoe){?WWҤE)p&lxsh7ñ&?A*1I} ^Uph,HL( {+{] .188f\1 nj$VJZfVu~c,Q 1CjS{rt7UBׯ($E}ye]> lxdP }o|3C/9׈_s=_k/UMsh(-Ϛ? |D.QʄŊKۮvxUUnX.Pz\P<4!2*5t:=Ԋ΅x!g-Jnx#]i+{/S˧W:cyC jn92:ѣ˞u TXk}ѿݶ+}sJoGW- ^TTI싣cVq4:L_FxQռd}:}T5ڃjECx&AαYȅk9 }pe/ Lj(YDMߦ{zrQߦi<q_6R$]fϥ?2!s53&(w೻ZKA\iZ>*_ZP?Bۮ|4P67 2`Z*O?`662$loɼvFg?r]g=_:JU2Q=.̋:V $7TGXWm.2S{n KEqT̖[X!q77Y2P#!\?~}_ɹs i2IH3tUw˨Xm;8c -S+>#72TW)? bţd@\mU ;CZ=^@mƒ%Pk^ґRTJZ7O'oz g*u쿆z?!=_Hք2xS;?~\_Q7ul-ݮgwԭJƛEJnFwkZk%ß>|H̡I^bw_fo$OFfVǠNWL_b칂?U$۴uL|=ΚZ]!qU5rƸuǭBoRgJ-:7oJ\~n˜e!ov:X~1;]Xᰨ€VJƒ9N1O_ҫ4z"2dB byi-t.Ug;x/CEn9G& fv=OXy:')v8ocy˟`NV2ȥ|>8W;LuXaI*? pS ٓg2+45rމԿ^J@~\bȍ#=tSi:F0{o˨9q[7;x Ǧ/5?SkIr7RaOc3Uµ+#DGM#L9/r Y ?OoYuN]p+&2%?O/^A7hO)[(р;XߤX_j7 c),xxB#9Gؤ "08]Ϙq?~ƪ,x%q.OҖJ ̩?$˙꿍uSobM/Fc?PeO>[jO*ޏ/L௃6NtLۙZF՞FoN7Rv/b/2@\6c&N"? p '\#Dz{c /K`m>STI??F?Nɵ05aHɀo@7 ׉G;$7̜Ǡ"\Σ @.ҍ EcTA@cs`u`!WXZs] b0>4nIžMQݞWKyQsƀ%7'Wlko o]_ v5kpw,Sxra"։[YK6Xj_ L_؃AUtzLb{sҪ󽈣xKYF NґlfLOgϧ2Y_9|mrfQGL7A.hS@=4[x#YΖ22_x WeTo S<ȗ4 ՎE$8 o\|#!\?~}Y9E 9X@?O[xsQf/`UwֿN<NZ_BtD&ZpTg<?Ǡ]ϞyPVuH_PNנJ'huhY?M+bݭ42YFIi,D2~uP_ a?GH~]b9œw4(`)On/:Ct~5a7o4&8KxE]+װh_ #|NgNqMc_<\MK c=SG -Sൔ\{njyf%ut/kNRFj/!A*Lq߲;&?FBfN(dV!v ɥ|.SDA!%٣'\oH@{^d0Y_M dȷq1ߣm!s٤BN6)5eHW#E .I]6e#.}eo@fcw0͝\Gʃ5WL` /xW$BwyDUyU {sE4"K3q}M*=:Wy+]`g xțRO9¿s= 6]1p7 4 x K&Dmj+?Ȧ#g'&3caU' 2 ֧Td⾺y* J1Y>Tt oI?5Z0Z.6YZZBN_XS88ߦJkK_(*cGEڨ[Z Ev9ռܸZ_"^w`X\+fLq1KٮLFz!6I#4w!:"\"_imϓUT#["&F-PY5'DaX(S.vLq\VnqXLҬCNQ7q(e+@I qwVe3J6fP0Ҝ ٺwr+^J.Hgxeno]Eb]\$)t HFyJI6;i5[v]yE!QZ"㦻2v |Dh6FtF1vs. 1a%y#|}?]?LNC_-$rU/$,l3s&KdQdzw< "L#yd4yğ/U$ǕG[h:S {\[li8 fdH+S/`a;mLQ,輅I,}Jx<9.CQ =󺎴"}m98{,ChJ  =,?~5xhMa~Ӊ~KxAu5W]xo5~fopAu/0w~jmb۷,Cyؽ|`%Y-uu  ƭS޶}Ϭ+<35צ0{Ѡ G7K ׽tM+(\5XKq,cD8/|k` YJ}|T.;n'.XʂoIvnP ˢ@cf8Ei[T}imˤ+Eпpx9#i\VƼTg9؃ J";0ǡMB‚P'yw 77g^pGz:*oo z0~jLR7 AQp4&2};kTzV+7iإ-l+%J&=fxg(S{8!ds[1}}ߟÿu{?2;MIs_ρjy?1*z/:{Ahxk'?Y VՃLx"$MHSP.aO#bX+Ƴg(*UMEܣ{81鱚hU+'wBp\̐5 MT jvccg9e;d EgW"`qUNWͷ*4],g"*`-Dy w~|+ 'upM_yG^F~~8♜FKO-g~l䳼J1˶ɼ8Y n=t-76>*Q̫uYܩu@Zuomncg?F?οX7۰cL)}~^RpSy}[V.5]|[~L2|}爎߻\C| k |e׹jNڻOhՋ即od_~Z59 $k $˔c" ^͠3W>w0%%_U҇Xy[|xqI} 5v0@tC2? AӿbuL2F" i8*p?/#G+˹E ϶L#ufv^jA0/_g\Obtcd߇Le!hأ~CgM#ĀK?ړ_@kbw W*EˡSTl{{qC xM:JD--u&zKda[jr>BkiN:Z}=of[D9oVP<ܼ\sz)Q?Ӽ=7hy[c=ؾU.]-uQ+9/QTQ1t怳Dֹ}+ܴ^PRT^mJi=D{͓(]&װ G&Ge؝7:<dYyL/"ݍ9~gm cN#b t$g@T4?FmUK b5s^e |>C=R8wl 7vOw]&csyW艪Ӑɮ0ʢLsVދ0I S(|Ĝ8O5K6zIBoeUxIìĸ\5^\}^-9eD9N^Smdr`@*` 5"fgLSJU u]7E [&y8ɞByk:C^,I_{U[so ;hx_r#B0bk^ňCeKzo]˨?F?X}YcCo)̆Sãf"ݼ  ,-;mb_e`t:>focSqQ%Z紇[sz@Nˑhg{TU `;:;#Vq +oݴXj{rjAb8MۙJ~qY84Q pG3<U6oԷpHݓzPW:y߼DK S`AG~ 6T1MQuV{ٌvw?jO3cyWRKx :,3e$Kw{)}_5[wۗD8>Cl7rEߎnE\uooUIH}'X@Pp͸}&c-l6a0Т " {l%UWzS(MHzmd/R0ϦؖgJY&u8B,Cӥ3 [|R] ;D2G%rxM+tL <-u--aQ/) ? j[vػk;JZ.!,22xN3`E4xcL[]]ד\qY@X4 ".ߕR^lFN< V3wEPQmҠq~$2{nԫL麼 ְ"j@m(f8l2,-/`ฒc8aDV("''S 70LçY؎#ջ/<ɣ996n=@=-9[b4U+<ÒE ]8%|._qݰ>ڥ=>hC N.G讵摍x|.q04 o pt=/i!>H7S>(Ե>[1m 8 dڦAN#̕8Vj%$Z1i '$']pѱp&0 W(觻0ߔ-,̕~R͛dY7Gօ,Wvst4ugkmxΗ3v@ E0mu=L&jzpgt{1kbˣ\<{Ӷt\@eSs,>]&=jVT!*?Nc]vpm D;6YpӘBҲ)Pp.Pkrጹ@U=\Li!(@{~/q@MLKHCX],$ںJ٢p=@W0 "*Pc6$K[: `_}NŖf`ǐgyhwPXV8zϷ琬XϠ4U1W{ϗ8дصaAѭ_9@a{&Pt'јIz9<ǖ9Ck\Tؖewl8 J1hfnv e©kGԋDL9ĵ(°STZ&/жU9t`^U*&>i{.&߰ yi3jP aw%?^}" &f`]?`)'~ p:> Y%cp&Sv42yln_̷dJDaZ[$~ " AaSv.7'8uVqJ@" an(|]s;\v]+Bup=jV>pkR}f- .¥77X^PPǩAh%cFl !}?^v =1 yvZ p` Cbl41Mx`*K 8Va<JڮI_+閺#_@HJë-an1*7 ?ցa> O x 5 ऀѷV+y\@}+;rh)CbB@Ma"&ph@1CmRc#' דTStŗ(sFvWtc m UnZ&aKHfcXk16cK l,J0W.ҍ\nJ1yr=au6Q׎c6e JX+@DKOSlN]>Mvtͣc[f}j gm&l$q8m2Z@,4auO*)2lG>(ߑ^y!IA 1ؕ-sF}Wkۮ̎"yCg./-n8)EP΃-ۄYҷlYhy@\]f>3Pu!m'bKCsaw<)7Ek @<W3\#y6 .Hru\:6p%_e>4Q-[7w[.υ 0}[LDMx.`bYž`گjYֶ Kj1SE nHM | j1sqTȶk}<Hqz5J#t;tz9n(91;epчP7ϐ"9㞏˹v܀Nk߿:YZha9I'M &a.;̀bؖ.LdG,&0*  Mtl_RזJ>4.~ ,p6Ʋ@?@\ 9r> `>bUyZ\<'"} D0 L˥ĥJrMG ls7m n{ЉC1$pt;|btB &=&'C sFhܒĀ-ܬQX~wSEMu(l0$R["B +4C;z" k kgjECu@ ݉ 9a|/t-08ut(l90= u^ ޙ3 XF`@4,้H2 ԯmSLd .TI۳ c`g9*1Hu]KḰq `!R]E{F =7¸.Ppb3ǧ2'x!&‘>pˁ%ٙry\|`)eX$3 츧.b WF؏|8S8Ӵ rD=DsX}V@I0{^43|*B%;DJ[ئ`ėp`gx,i9 C )uCOGv9.[ނgz ʝ+v^Եjr\aT 8*gAV)ݩ9hc g } &Fla!e[[Ie U+@ BS'>jH]/E7 CP ñzCu2xݵَV䲳p:䱳( j,a-|IJLP\C p7 !-jmX&<<EE@t=bwmː3X W,ظ,3dS+?1Y} 2 k>wBK8ӖŤ6@vEh^lXy3Tт[uBn5<C"\ Df3I"Yv,r,`{a`)ʰe}TaM I)@X<8aQ=ԇ!L~La>/)!HdMHr(r X 0 P=BD %JK2[V8|.2çWBMդ#Nlb(dp`=i3S\a;kpG p@e.:PfX 8mzzĵnRۢkKAsHr0c'tc1E<4-yT8rŁRk^Fl˧*&b鎜-J-th"=@&!k;0gv(` ^` Ag6zφ: NX{>}AATt`G:0+IX~S߫rV7t2UuLX̣qyFv8Ҁ,YE Osm<`01 ]Sqem/a<&u!y;X8AF|ρ\`L_QU@I+=ny\iy!V㦏fd4t$RֿiklܷsE/.(RZp(z6pM)!3~]FAg#c|:⬪MD }εZ]NxR!5U*aBSQ=hRJ"L9rZlYqqRBz' 0`M9Cn>;^N&|{FA6`Is):{z%%۝JK]Be.蘌J3 pRZ\4E2W@ңRK-jU4R=^MouM.QH2\n24~Ǟ_?GjeJR.__z~l+ IN$@\8jHt8&bI=(jA$0kMryjos1j( Aw :&DŚ;yPo[q~4$ťMu!Ņ(k8fSvh '8s%5SAq'}lzgHi'RVɺe (t# EW뢍 ^8{pnGT X ;ħrf#qֆuH&>] ˷ GPC艰TS]7S,)瞁CJ/1̔{{b#Gr}:Q5@uU{r,3ayUIQRdjg ^zD@p?G])R4H^T;"$e!lN ءiRӻA‘^C6]HrZJ+c(y[ X|j]e8+2)9+,-N?l`ER/}'I #*~14b^S9>uE>{)Q؇JPX60io@|ԩ | l^WR*Vt (v'`|JoC icT5U\aWS}3U1X^^Q닥F.WՠjMJj^SSJH1K xÆ9rMAIqϝ:ǽnow/\P|q$N|"Igݼtos+J>2v,A߀gj +ZD}uXCyv~[;k?^V/OC̆ҭfjx;e"Y tЭ(D GS.͡WW@Tm <9+;[:flTr7΄'aصʺ4 7E33 AN=ugɺuW'X7)ә S W^^]J|Sɐ\E'VeT|;6,(߸@ D)|`k1`)h=4ľ]f78 ,j|zf7*"UEk)7ea~,RMMob_չ*:I'86K W@"RJUtJ&]XlîR8 Ͷ|s»Ӎ*Fa?"azdw~hy0Z^3؞JuMubhM1T>u=!H_ nl|dL4jUpqYi )WMU𚟋 |oAqZ+1xg-R&`=\sf"X$.=;؏d鿮QjaiT@$}5m@bMNA^TЪUZw_jewb2ZP@;|E$ՖD5FfOYnһQ`Jjlv[9fR/)YXlk%ftyaJ@t:ϯĎ%y.X~6n6Y,XMi0o]Fù>hf/A\_kj,)K^R+4Xym>Lys_19LCkkd$"_&-Rk% nKOؗFl r 0ռvܱF'g[].7. &P -MdUj5TLRI7%@ w?h1 nB7]s WY\ *l`Z#67I.hWϽʝL¯P|Ηz R| Sqz Hٲ@LKO>0;q.c W C qoVX(ƣ Xb 5w$is%5hʎ;NY}sK1~(Xcq}n\Ήf"6Y{ lM֧I| HеpCr Y2]@st>ܥ'ʳ E.l`ted\<*P~# 9`4&+Q+(1I{2p0֦ df|k.}mYd.FH鉒\KU+ݕU=|KX٧̶.oEY N"JX'&Cѽ8K(*\ kT>o)|˕%N yg!^Yi}iSWZg bᠺ(Rsj` hD!PLgnTf,o|7^[ʀRCntƮUD8{6k;Y65t!$"a(lc] ɅL,B  2Xh*M* 9$"PNk pfTqq#(!: ]4ȪSeiF `kR=Ѝ%,bD\-q£:J5R" XxebJrSE u Ѻy; Rw܆}L&vKٞSSDu.Q@0CkIg"Ez0ܼǧ4v!Y;ug&JcYǛ&pczlR4X+1RJ6c8]["ʮ-"WJᙻmJGM9A֑zPz~Ov#jZGI?aJ9͕$EHj%̞q'W:Xd!ŚBvI9Pk`+ 㔈u:[/^[ _Hl~w:Ω*XI%ف3_1WEFd(TŠF'Rg򍯎I*Ǡڃ%YW < [8Wg[wyɥA `cX[[Eb9"0GvJZ6'?C돎q+OOEb%|{hYXXNi ֳg3Rt3g7Lԡ?;jKQ{aGȚl "=W$JVgLk@i|J ;} =mk Nr]*w)UTD)Qj-w\%ge( 6KUՙFpݲ? +9<8s'a' mys~,?ݎcog>M+yؖ`r`tfuP.ݛ\'"IWlD랅 z_ 8Y(<-c##/ҷΈxʙ--[V>{|oךnKGU1&3.;(e[o!|ga*ⷣ'k);us%%SNVdti6_1-eau2SI; ?+t&Otv[XsM(cuP )#tp wUÖl };-*+5YX0h"1&MbhM,;vc#˵JS9ߵ81 lUN9]ى|mna+RJT*lZeOE;y8rYz?^=66@mxtA&̩M„5u!MK5dd#jaɠ n{b$ C*w:qj8$~|3]]:4"fð)' 7p4cQ:EdսQ_6p{`?m?TmdE1(-1HpN+EV &_UD'w4qANh(ZFG tBkm`#)c>ZrbƉ3_ay%ИUdG'9S( wKZI1Qh @P*T h:hvͧz2Y!Å\s( !YM"5e!J΢|z{~M˕镏" +>5_X^ WqZDN+IKG^afLKG I$>AD*KWĩRtzٱKtFRؐWԧsM>Di&Q~tCDYiG렆9'͠^5*e!FYmJЕ f[t"gj8n;(, ;}n>^G{PΕLq70b'1X@-*hhEUoqӱ>V=\G<¸D:Be:XG6҈9: mDƞ٣zofUb/owҾ2u)bZ .=HR "dn\=R}Ӽ )uDE{6z\Vpb U_:zҠ51ل -{mzt ~k-u `lɀgz_d;2qOqr=${*qu,&s{Q"&i6`Kq^u|Ef>\"M~w !%ywbjEjP"Q$a]p)~:e6uk]pta,xKP'ӀURrRr;6K!S MC}}ITy;,}^לkL`iq_tR!` V &Rb‡. 'oW>?ړ]mpubNCѣ_$`;%Lj%A3 C]EꎌKyY.DҰBFsedʐtkLhR(4Luع)]K{ XY"ké7'8DA'}K{Y[|~p)d~1hxc* d}ÿeF\.^rj$L `|?icv rKB7d|R }0n TvHnV$=V W W f8؞!N0k>Ej'ߤYgۻOճd+uw4[;8DK/Q'$ -z3ASVZNWv EaxM~a m4^.Mh.Onr#-_/H{U:Rnx{y[^_EtG3x ,r+/%B#6X8-RqFfuu+}yشZ"[-~/lzzf>RMca1S͜؁dŏMYb3+i,~Dsv"xn$)Y'P +klXExv+^(dI]tcߣe?k'B!e{N NaY8Ժ$jԭ(P055ͫ)oo%ɟ-s?'=[EW#ۖt@ٺ @j0M1(bΫxVWru8'Ő;"KQR)7b}al#K+LU}KXX^CF^ҮNNbIˣ<] ˾$=g [ ^->Ĝk 6`.OOe)GW%JhT3]eUE1C{f{6К.Z+;M3A I( ,3Tlt]Z£]8k(,$Mr5tsA-<UxH*7ݿ~W-)!A{Z.}Z"J!U-t2[Pm8xzWUoEnulN%LZ77I$ wI?)x<9/-Ep-zdu C?^2ɴ`CBUUPn 9#Sup`Zauc:/7q'To֕l˷k=S2!qg-ٟsyyi)7O?FIYqqKJZaZ\ ܿsN~jΔ{>TݲE-,C; jwmqx}hF G,N)s&ZkE:U|iRp㕶dw;i_k#hsa-(DQ 7t&3&3;W.ohtjYwU5Gދ>4'݋=JŠ.x95qJYU8@Q$xX+$K")CJ=Is^Py`KRib`Crd(bF9Q3ރSŀGe3@NԜDoI941*I(FxxsRժNT}& 6܌D{?րYTfJ_@i ~ƉgLNͪEL6tLMkxeڶV(H9Slp6c&4RS.b#SM<*mGG:]y+m0$5L Ly)a;ْ(7!&` v&[̎]#_FWŒwҢHzHO8%4MIϊr*xł)'9uQ:l81OYѵ⮠(^dz caDԈ;IP4{6yCݼk3 :Gl}73ڄ W[Zc߫%NIXLKcnOo|#SSm$7iy'um_i~&?,0&zLUB8FE-֗övH'ԗoO_}{% Ukgtz~y+V^U+|?4h(N$zGI}v~MqU"3u+`I<K8iD29#CXٯ~݉w_}jGkvî\[8S=k_>4{$})kQc $%EפAP^BpVZk)7:>81W/`W}Wgqȳ'&.IA}NAK8>-U89+%וZ亝q{ZtBJl]%E`Be+=!dR*!*5bq0,  2 _hm@;e˺WM#[Ι LPUM2\Z9dMGzB6]yx ?I+kVMQ #69i @䕃—Y|AFU]eJ)'>06R)-{W$+`~Ba~ V^m4K.' .ŒeɐY4*J#ߥl\ΰG>ا$ߏ8{RIǿ %o H&UE?AW.u dtcb3wxKY2i^ 4 SgiK,Nuu5eT*`{eNzqyU=YǸ.lgZZ.wMp ؍ob 8C 3MNU]-fſw\(~%D-8W_p"$&hWBE`h)M"P`zNmmc޹ڞn_&Iybno3K.,\dKc2إE{`z/*/G ;R# ś3sW t/N\T4c5)p:xno:s՞;KW)E2U]"(i .9.jg\0 Z9Jb~٨ݾX[M|iϛ:G߽WKs-GZ[KgR3SuC`+DS 㬭mtR ݺ3ilq3q3QrNZHK9K]'Ұ5N!iJ.;p2kP\E$ :J HU tOC )m|Yɹ^>ُ?ghGD&s A?жЧNaITLˆd_-DbI&N_m]90=Nt;=->~h6> Cx{z92MGۥf_g׼Η4#81՞tg~xPoc8['LJ#~{FoeI/s};.9y]iL,Aq<5q=seS\42,9|7~#B/m.# t4}zxko?ub{}m/@^/8k1{N #;fURP:" i ._S_ɧgO$ӿ&f=<x/f y;{aʼ&3^:03q|&&=<'q. WBcH뼷mGK;ۨ.ʲ5ԅIv5VV r{Y Stra%%u/m~Bۍ3scbO+{;zrV? 5&̓}%Wlf2;yFܢ Qc-r ˌ×2utKo7( n.2mA~с]S}u?Λtxv |+O KP`Ox>mh^y":A5r4_z?j2 qEY;CH׊ %?z3be_'Ӭ~1`'ĩB kY{ /l |e<섎ΛT3B1vCh6L60=GЯ.A̯ГIsߚyb)~~m#4 3/'9:D>s&}wFR)y\"`I{r*Gs@ \&TsgE9vTٕG8gmy]uBJEb&6Sh<\\6Is\'wR/C1_G[P|oh4C'wKM/K~9|{6'좼y[qֵ .rƭv_9,5 R,ߺ:b󺹠8U9oL^6#oz " !Pzd74EW/Tzնj_-pҹTW>+^lF*r@}%e0fa~oCyǺ%eAFNDs|1}>y6;~ +rݤS!}ͤi^jF5uiK6vNM =q픢nAMTO|!ƣFSUYy=w]:t调Z-+%/],(nMnc_}䡵b"e}>3`(ڿls:D =obpLMWj)1 ۤWn* twXlKذ1R/Sj@v&_ҧ?xC)#w<ܙ9L17kp-[=Ec,a ElgVkV*øbMi:]G[t42da[ Y0a7C@t|NqrzBKN%:+dXk'v1$C gY |kƾv;\nY(B4l-pY$}tI1Q)J:+jW@MWզT4u-74=hl*kWԽ YT bX~U5&x ˝nN@WB(@6DJZ#:4z^GhfIVp{uEd6yUk-RMk@1TYgz<G>6?Ҫ2@B1d:V d"OTHAV@_9:mn]ImqdpI^xXtWU@AcL #4-l*R+0ICd X$@sZu8Z4XŇLtUKs7C~1Xp$*lt骳)Ү{bXt[KFŨ۹Gʎ` `]K.xFb-H9e miz;GvG3=ă6wy]=͞ q}6tM-˵xM0Z `VXDEXmFp>G@>:gr:>^@r-I őIؚ-h׀:|>A 8٦@-)b7-`jA"&K+\ |@Ayf"N\QըR;mճnԥhtp6p˹R.( X_/j=I 7A{:sKG=OBmf=ֺ0 b#*|8uUJ{|KFŹئ[Oz[~j+|4gp.}w@ZB+ 6#N ܉?GqVA/MyU+5䯵8tIۤQ9*=Tď(D kKjST1v+7sIItXI3Hb4_ofuww/'7YLQ1k_s!z*qx|/BN^<5mRDjb$'JbJ*Jq@({K ͆~6#I*${:赥I:ZwLGOrua n/&p悞b94ptG?|vzy+7~ܔ^Y_Z?܉?iWo~6{ӏv[*>|Gmfar9.lĿis^М8XVۤUO0e,hO>] i5,9|1=yOS eTrzSɰbBR6E 1m3±aӹuʍ-SӱNY~ &^VAd\R-lc20%K/B̫0X$rZUOm|1_~YA䋏KoR³sT=h 0Zì0-tm `G+)ӂTû MG?3TZCYnu%:yJUhWgu#, fן;u[xNd+,겮JTS3\d^V &PJXw|mבk_`BP}pC,~*Z9#!7ɚ2V8wPr32rv4v(ZGL֙ `Scrg!0K3R'RQk&(Ⱑx{0{ȡ*:'3KTlOfv)޴.H%ׁe]`Ui$#,v…nZ坌 d B )XWV|U bjJU'*Cm;'nGcdg/7$]>=̨b@AsyG`<Ø d+bP|D&>xóL)=v'_oa[7s3aw_@`S`>*HJEMTm=%它/Z\exY a))au J.mەBX$3᪷QhElExzy !A?Ʀ*j2!b=kGA`5wn1K]u!^sQě﫧|l3An#$o1:1%sUjs7 C{\>cƛI.woW[4>v֦YaG`X"<`d41\Jp="KEGl"O Z05 ]Q.Rjə & %.(ř'y:Fӱ}u}kp?]|Eu(=Q Jfi&LRкڦT_q!%UgRX&j7w5bf{AkV~AE+p8K4`l1*I=)͙ȍ˨%- X\Aq:zo֠c6{y[4`Tyrznj$⽪m}x/B"K憹Y`Wfb]&eR|N7VI7khvg03 Bu6m>vp8%ޥĭc[.rU#J؉BC:YM DYoT)_&H71>뎃n4cNPj=űwWp.hNu Lmrm"h4XecS@t<>ztфJ&ubK]+ >gDW-{A¤V[  #:_\{F3`uKҘS5V'Ptr2;'m{aރ~tβۣsA fUzEEZ*$ 't[U+kewc4E"TYDu!?Hfh"'\+TUP\3߶Mп(QJtfhszqJ  D% M՘:ΩF.[OAIj|h9=!7Vkt`Y.r nfɞq.jޖ}rJӖ|$;9Xϣ QqV91P7R &f%қԇV@HGذض=6]\!&¯쎑K-x("QR"X5Skԏ^vU8NM8։e ;:^g0Fە( 6@ubMחvkD$5%$]ڤc q(XL p(p1?sgH޾&R\&)-UT3BEt%$1V;| \eJM^;fnƉ ]8Hh2&C ][;8PǠ0:"l{P,YW*)oJ֢~aqŪj%C8 D92I>Qg7ۺDPߌ`NP|bQ҇bSzaD8{+ "6g[Q9[ĢuRetUԊ+6!ӌg| s``PWasJ% 7LXɮ>R䤊%[b)T5me{St^̿ywlå3=6DF*l'xӳ-A&[$iū&"5\Gurų$wC1Vkdt.9sFvqEoFiTPkS[07E-*g80)|vP5Jg=/_CUJT QRf䊵d=|,)9@nD0nrd6gjfI./ %onDR믋IjWX2""+ v}HyN\؋残P1Ho`ReN+;g2NmS}ڦ`;oIc:*58DM1zTAN F#a3sazvK83z(k%|#rwOo7/Z<5X&kgo h86ISÁNȉ :"4cRIK7Lg8J Oy1'L6%دB 7F_7w͏nL~iQ\s+k2>przkQHsbQq ppI{ {@E[ꥫXwMU^37 %BT'msޥPnzDxkosu}ԦcN{Lp1*lQ7)W%$E 3kC9<<SyK,Qid+), Y'T fTʝuu}f<2 wb.#e IDa.$є] M&\CRM Wrkz17(jH9"v$6f]d e }F%(jN%FfhM)2YoQB|LiREԟ?re;?=<<'?gϾ,}VO|ŮaG??O.B/=-J>>/SVx#<1>@_yL۷]RnĽuL&_oʟ ? ߟ @]ao?azYNZ|ɷ 𮫗JB6 ՛~$?t)9P}-8a1@>5'/0/Y3\bzp;|I'<@^޾?"YaKX#Xڛłb`r3k(I t /ϗ8Z7pk](>=[̻^=L˻#6Ǹ+JWGKM{Ե6 Mh˴j'S[Q&->mv&~YhKAtx>Jսq^=SL_up M[?wt?qۘ/zfDba",1 <:[Ka.I_XɿB)ۯۼbdOJ|M`Î{*J8 7 O__=2N$ܴnjUWi-T48?:z҃{G%Rۭ?.ARoXssGŝS7k:=AFݔ ,r?YYW|tbZ;%COaێ&!8 ;&aqֿ(|2mqm}H @p2^ 5Oc=Zx}+96C@T+dиI=w]#OMcC;FN] ;/;1_<1%\6p߇糧Rӛx/ ߃k~Z䍏$Kt~ "d?n#nad}215- ?kkj_]}OZ"ޚh `-y9_5Jw=@`v+޴A_[].2ܶl:ϿϟoU=62}KͮXYo}^{-qz9eNaoMTr~lz+lɭuWhXx6"ڇw8*sal:UW66Y$YbBSrZucP.wyxƑcpWa +EG~TH^cPm + gb%7 u;쉧VޫvꚀ"Gx/?l%hHQg%3}f_)Q֣5wu6m\wTnat R jh"`Wۅ̪¾:fw>@0H~JC`dIV浍Ov1WM Ou#G.+59C?_Sɾ<0-MF{PxO!4pr߀O߾-9ц_o,`c7/˶r]V&ґߛp_߅,i-;# ;9>W)/a@_M"&ӭ*}fl*:oj`nƪ@jZlyINf҂ ?^߲T&pFUd2_EV\~O9kvoZǻiL-5ϱ_Vi^ %dMuu\<3KO%6{?YFnUY&r_.Qe ^Q>LKMVa1г_WNrOW Gg;Q|Xk:9[/[NB߯3׃zFm?S#/ } N|JɃW7tƋH e`MEKrE/ _zd$R:`OT᪵ 8 (@~+]…AW _~?Ѥ%OV5x> ~@WG|L =8rٔ{ F^ ?FpT7C C o/~fӲ 'FNŠ?[>@Afx6+M&s[!oQ>/y;vj`EZ 88㉟Fjר˱]ȿ? 0].qad̞a-@Ɩ ~ =ݞkx ?ߞ7ƺp1.GȻ*FC Lif]&>idOcY> 45]U޴vߦ_98;[Iq)Áie{g[UNZuJMG6wQXU0y5;4h|#翬N=gk>hTWl:QګTҾ{lvȋS/q%||w_lpmo=Me쭺c ԇpx-~K-a:z<=k}ܩ!;itW Pۄgg>޳%RWP9oqEœo_|Z})[Kuf9]iVƲm_`Z5./y(h,^ϟtdCR0%|w5+W6 TG]4ýpʪ _]kU?l,ΦVZup[ӿΚ!?=4N+,W?kI , ӨT2FyN>o(]t@7^(Of;gy),n3LdWϓh\g/ⷼ||+l"'4$-7c|5ZF^LVozDmG_/^_יz%Uu8*b폕 N= >Tܞx^~~Ŏ>aMn<8k0Ɲb[@z98XX'p ktj>ԡdZL1JQ`_\[l,C*GOyio_~U.Z.0n&a3}(\QCO!. '5h:mmR~luuValWC;LhŚ?9As,yùƻ dslgr zHqNm.@i~RiS0*({/owoIao~ Ŭc%)HSxfOV6xˍg';7ןI9#5t|ǥ\ϊ(i,E߫"$]|Ƹw"#o^Ҋ&>fv04=_Ff;O#gxew:lYf 8:~azQ֎]WJw;2C7 ^O._^U'㿴j`b*uȈMX^Sp?iK-M?6;VqgQ&;3zH@wPgk 9n:_08 |m+ ;?w;~Կj2mh{tϿln oK{Sns_86`E ?w=-6G3< tu*]OWhnhB7q:ӽ8;7l[>E ۦzvoUYY!f{%JΖ0*Mx%.-ȴh W۸>m_*.Ъ뱔8g޴UE^`;4KV1 \0mU-ngK3mC*<,&25᯴9EwzVt30O3?5^6 ]q*u=f.Įy3]mY[fyJ#=9p ׃SVGB>_RWkvDz_rA!ZaZ]sC[s{u4W8-^7Vk͞_ oYBpռji`ۦyOs$0:7_o(/x2o͢X]1E%sJNfۄ«fR<ޯ8rZd- ḵH}kVc>2?`ڏa&׈tyokoc%Y/1tU\[fbxzOS czZ-sYW9ϟ:WvC-twSy({nv>\4WVm8m6˼[3 o\`E_K"qŋnZCVӁ5u^ i ٨%$5Wv [?/jv߈dR4?` H(IAZށN?^?g;^?fkVnnXn%'T?9o?nߺOUwыgmumg S?%҃f_}u*Q2Z&+lUn!.vv&!s=y=[v8|8lF+dD}vP3ݜ lA͕m?u=z E/hxD7ڝp%Ź.&OZ7x9dwQXWJYJ%CivqskrMk575jG7pO\"QH"TTxvq_"fCDܸ?*vAdׇ<=,WX 2n_aJzˑLVo wh>@a>.?ۗn>V`Ñg?Z?8ew7r|zա#loSWTO d>p_I._re?&g=--.,{z_˽6-exvņΰr&9c֘>ۨO9d1 T盄d؏jb[O OC{M h?݁7+hf"g:_ŎGomh /)ld2-"0tȖJ7WtAs߀%S/E3[w߬Q'-ˏ]@Mߟp jem ײ\{c MjYL0BPZ`W ?wZ8[k,$'M_!_?/7@w{d~׋i)N oԃCڝSfO|>nGZA>௰Gj]jsdhɝ[>CWq`ß.s K?/1 l/6R n[?Lq+Woql/~ TF :ExGoCk$ g'CF2v9LѷZMvyyJ1]+'Ow7AC" r_%D|xbNvy -Cqa 5m(c/K+ wJcWK1/(yئ+/\ͮϾ'ȡǽ%֌n; ۘO*Z4"Lͼ'~ Zʵ:>mâYA1ԻrvzفF{ 7> 9LYwcw>uH{|Gl䋳N{.p;@bD/;/I?df|}̑c4&%ț5 ؒ6WtWA8t"}] S)q|* _26qd=9ZENa/!wIn ^Fߏ#% } ` / ݄$VCj` w?f444$G/~ӯa(himseC>HBxY.8&O; }qZl)voߢ>t'2n#idv*5*PHjdm5P4gY3AKz},hzٗ{)&nWc{Yfbgx4VAL˶VlcZimװ8M-%,"Zb^lXm%fZ([VM; m=]/y>D#7 P68B?P p}׬nw㤌;}ڼ5J@lb黝ӫpW,AlAki8E.mfJcfo)rAi$Ʀֽ`i84O/N{u=nlz_޴?]2X%%w8M(}u&T+59zt|Q ׮_l> aWjXė}+"'왥KNG/v=t>@/E J7֢Gj,wro݂!F>{-$׺)GOINq5$?j%戕[f.y,˂|'ǟt= *1Ҹg6-o.ŧhPtq{e^Ugp)p?U)M|="r4!=y_@Hv1=`+8}NC9KY^UMvn. O͠p濁* ~ʿ??zcw@/ko_-ba%oe=y|pB Pd^N>>&Hxr5N褳Ox )}~p ;i#2Bc=?0v%pLl0nNqG$@Ml_1C>l:b"0!Z)a>Ea2,3F%AU` 4:~MQGX;&Iq  `83M H%q &Kz v9_kKHѥdzCj*d&.x @'(Y:)e!?/l/br@9%+R%γI1'Jd% IBq-0Zƀ1L$)f>Rb:%@W^ǒ)Xf5qօ/D"~ >HM(AK]'&r2xJ3˄vH,,4IHVNΫ-(H?bG)RU(>ql:fQE.weHaD&6(#ELNH Lhl 0,ksh >(ee2s׏MLȫhg,HxA0 J +ddR$l_,)[ wmG|\EsŴ" %Ch2Sʢy))0y.[6ԘlӺMoWxxl{G/RƊU0cόFՓŝlߛ=$"o% < Cڥ5*FMPm&`>ԝ2@b;#:@$A҈hJ@ 3 H._@@x: %DL /ŨS m}70ZTx"!2< ԂTޛb$v*T d <,&lr:p\XĞO|\'h-ǎ ŵ8vY\~X C±"8ѭ.~k:‘}Ϙ6p*Sc  ep@TNf(G̚%Nwzbx4I寓hP|m~ 1& 딨M±e$f02a`8g4I%g')H_ʨi97;pRl@+қ, v'X:_AЩ]gHmT8w^bU]]a5 TyI@Td u E<`-eh0g^HtF6k_Cz r\4H(8<ȹ`@S9[e,qd*l'P_n*ɸڂodPBP-Z`lLUyeҜql;1Y;g/1`Wk|N>Qۤ19Ev08mXy(E$1mY9;r N׋ vu H3R!0 ,MpN :bb΍9fZMEc=uz/EzҝYh2P`JA6,H{cvgsK3kM}SIlQm8K`\Xm %Q'igy%=*p=~m>&I16 'JA#@&F` NcK43N븂[G4 N8; @". +,%]q, Dȓ:"_M'WX" =DK!#`7\+ 'R@L*Q3mj>D(8 2e~L;r3U.sg A5@&Td觓eSŎkE$Pljbp{v(r-U0,%C Ȁ5,L Jdkǚ;0X~]_zbχR xLXV*t$)K<KPAV?ͬ&unYE݇ S [0M` YrU.@A\@ ~hj" h0JxmQnՖ Ek3J)tFM:b2, d`4##k<^QH>m$lkF٥`O7-E%h P, l C ]+ٱaf68ӷGΊŒ#2@4HL6=,zpJ `F4_3|t1R(_jcٻ-!uiau0Z;2`JtʵJGxl [h@"M(߳(8f,)ItL+2@qJx o+5q%ztTk2}O5GM"jKqT[ uiVFl=}O4JWGT0]FOmY[zݧQE>o\ N9NԲf66/k QQm<)Jl.v#(޽M6qINZr X6o18eo%o_ʍ}liwe6܎f/ic\G7T9Z2.70I)%0IDž l*QMjzvM5\wTAp, dݒl3fPSpAG˹La<жw%-#f "0^8Rbh EA^ZdS=50<{V{i zw ́`',GpAP$O~ }kS16 7ZQiC.(#1Jâs 0  )tN'v=iqKҲP /!:@l)((pQ]*?6$>c RbX*٦)05Aqp)$kSA=5XAgIXNVߢٗc5y)4 +>͈PA- BL8Y3DJ2D݁cv}CbaЇڮ6nTAWJGџwV)xP))M)2D>y#2)70OB4_Zt&,఩"4_& c` eʀT;tDҀndFh ЈQ(fRX ϟ`qP0V:{hK)'cXGȒp`K7_Uѷ< Z(@*& W7\M1eFVx}nw=f Zh8],Pe#:e-aN`HrZy-&9&Q,G.2JV|Q#ȵD9-Q Y+#*bƨ} ?h;j1TFA]{>dfHh0ISD, 2(K=hwV@pY20gpZzul(@Q t"#2Qof)a"@^{{@6"zD`S7Q3t7;U?_05 8ID . y 8f DF攄V cYUL>M`j1в}Yր*,5:%&Y-W4E,C%Ss)1F@7 CaXn ψ,1InҲ $'&aCMɇ n8hu:EL{3CN:3ɹ Hqv}u@;eCJnWBDs!(r!l?RIvVڑJNU9tvUUS'aArF!mu }f=p^G <1Đe.h8"Lq[M6ewñ.+5 H40`#+:)Gf\@mH3Cu%웋v407`4AH9r@"`C0*NMSu5rdl- 9<`, 7M@ABa1.-WϓNlrrKQq4 }i2/ {gump+İ^).k,b_;pM"  Ftr01hMH"J[4Zey5 x)i^h(#yza7$0r$K,@rP GɃ(Y1S.-IH7hPD's22AO XіB !3n;lUPSRʪ'G]>w'3R#0!7hܓϬfk:9LtB`ƀu6h^=yC>LF; JqL׎% @ }z4VMH::"?SMc96w%y'@ $ 'rS@"e )汜 i;s[!}Z/7H?Y p\rƝ3@_u3敨B2C%IDu:{bm ,iT<P%qq b@sԾs4K>1s)UIT`VO Dq<^d0=ȶ#Ɛg ©Z}hwZ:-Z8 1=rcPè`1a'{t0:x4KgO%>9 [YYo { 9>tӣŲS]@F9 YEԠM OA]IދR+X1P`~&̵ym*ջKcHѳ,I,Nd cfWbTBQL)-?t,+*33NE -8!F I C%>jXKVW-QU;5@6"`RC$VušEUr@,u"%PhȎ]%HR2txDk]兮'}MQ 'ZNBsK! fEG(s*ψ )vmںq1rü]ZwLuQrMo[1B.8U6s),5Qu%Vd"4S:uڶ_,,o+ ]Q캨- 3Nbqi'*&x̀"H]4]-=E.J ip` N O6rH pɃsuăB"Y޹e)P&qޖ(UmjgI{ dpL4 UEN1Vt}&OY 6_fю, c*Vx5h T[BcM}SsOSF:!tnaぜzq#51©] F"U ɼ?t^ĔJ=MݪŷE(KkavQٳ~ӯЩl#ΏR8jޯ,6ф$%FEsjò\0p×yl6O$y Xf,6nHg0/p8 hl19.J#JiF}ɰHVĚ&s$[:)b BqP5^J谴 &Zd@,Z2س>AtA#F AA3V2nSĈgQ z20Jȱ0 Yie+ ʤ d/.OnzfIk{ŁpRO2J¯\Hep4 pyXb:sY x^ʎL X0AbuG}*SaPe ϼ]iퟗ;u ߻-+,fZqqSB'\bu خѿ"&+$Ua Еp8y'oG=5$,x.PuSkj{זX!!i2Q 4tR̔A$p yф1" nez=ӝ<ǒv )UøVbɭunN2Ƶj6غYt-g7:mٲ{ )zHn>>Tu09h(˷t8NF}tt&e˭k:}p~IlˑTcuS<ܤj/[IH` zy PNQu)%['%{osV sRu!eFDԒk ڌ:[|ʮ]$wHRɝwE̓B>Bh3$$'J$&e$@mFjCZ_`m>MM7ڋoR4sas |K{[-\VB˸~*'-,^D!T^ G U[5Vpta-7n wց_~ݜOe+z9pC2(L=\o@jt:'xήg~sH%51XG-dzFCd@v@ a &|h^!K:KIXXgLCo=[Z"c ored ܃V!glv"Vsc_ॲ *Ғr "eDZ^^fwli+y]@Q>}amFUNdirYZGIFOnV]3y+1mYxkWŻ.f{gxjVbo.F:΁_GI,|ċ_Gk?9[@y6dD0.,I =a*Wͩ+?6m?wROqʙӏ FsSj<Qu؀FX}PZW5ޙ|<z:7١ye;k t1 H3N:c3l%T5HlPJ3+֫dt8tv=V*, 1RI){fum3=ҝcEӂv~OQLY{VK4QUqD` 4Kk8x* pڛqo7؂6)Zg.F-|Nt*k6+R[`Xro @px#yvGevْ} ĬJij6段eT#KrpbVgŔG IyeyiӽWܼVG\ip]$ڐ:d=}< GSykSE}{dURpwEHE6Z?B jY!2cy|{~OIv+-x6=`%#,XEܔe&U1,5 >gZ|{]7sdhwP*jFvH)&X(3(Z* cTu#͍IʌY%#BCKTa  ~w%/"YW-rg, {W)<3Jt&a.,RL df V匥0 `n^Tsޛ|Ȱlڕ|9{Y%%彟Z\'?}/RT` )Pr.r!Ъ[Tx21w(9ԢȲjֈ4%&'6k @$Z)D;G_Džpxۏ\KGbs.ZB=ýVJԄQ dC͗qp0TsZ{E7 9w#L}:?h6滽~󍐦~wP/6Gu?` wsLrts Oz(ѸK'2%Ί(sz9iَmsZ[1o%tmf܉ZUIy_5uMIJW]MɓEEçB)AU\UVBU?-9˻Y-@)c9lζ|U8r+ aoz/+=]y/i 7G;H(|$5`V3&9X``"*R%hz~_ FVnW~k,Sؖߩr>YhFal,鿱q\C`Uj*߾v`@TPVQV?"oOeM "!#Z&UJ}x f}խWm[9~HcYb]SJ5q]2FQf]jlȩ$ynVHcY \Qw˺)?CbZ*-W9x:XJ] >[ܼZ \xC}&t.*Uiɷٸl4%7F8(G. o~ P)TU]l{$v 9=4儡AvGēZKc夬WISˆPOIi52ʷ"DN7KAz ɎHe"biFz+ kȄPЖlNIKBeeFrV^K&_m>f}8V]:|Sr@+$uEػ- z%#r(4<;a5/Pz|(GUaQQ3t-bhPWZnp,i#RU%s h/rwSY%5ku,Jl2+=Dž4Ou":D<-k',\ =oep"DE8IGM/=!9UctVWr_|{?9-Ie髏(L@1Bo+R/a "f_a@+\ZU| ΂$լ&$Q0vmpT-= ^/tdq#ֺ:垰ZdXyV ~z:.򝖻\ė-^;&S4rӌ%TBx 설c>S=^sޮ } xq,Ex<ĺ\Y>-Lп>_bF t">6ZqJ\g冮ˆtnwR7v.ǝVKX·~ӾfuK=.ڧֹMN-6yo޿>ic!=g=[8\kxNF_=Kڿr7TIE/ՙ"lmiurd baT/y_Tkp! kSJni)VydF%Ŷ-Y_ƘIZu?&$SC6+ݵeљT5v^Pu6l{ўyí d{óQn \KGdd|햻wuل~}!^R"M:l&F1)]i/ ӹ0Qrh*9J+hkULq+GnKRuJhyݻA#aȃ-{ctL/1c7+i{~oUuO 딨.=gUYrp:;Ri]LxdpKZ }@ƘS dzMbU k2<ǔ]=B\d:VYJ*M_b|]:o}W 1 |ВKnuQ:jz!Qs6;υXI*p |}t+ojE&&Twnro":}1{u:_ڃb 9m!=5xpW%SfoSZ}nȱo2u6Ru׊W-钶dR”.pCxTقS`%Dj9GdnPaw0RJ??8;,$uQEmtm-9kω3WH*1|4W_V;B7IᄚNlﶘ=U"rX_Ʌ\L:62zೆCʹ2KHѐ`4ę 5P fЍYc0.`UVYSDN, ʬƊWSE>k?\!׹#K`-.V4;z`na:I;&MFtNBrdZįH}93nRL!R-[;"s$vgC5s* !닭ceOL>2HL"lMd*R ݜ1L-;IVJ>WLGLMܨbWhEc0Se>sHԘ?[Rw8`xB0Hnb@u҄^yrFw-&m l;sfg2m/<mpk Xt:)J-)k>K:!2٘sE^I@`>vgX>^ @iU.R:~Uj0(j֏AVa5<#L䮫/(CYs`XbV܀"a`CT2V[md &[Z[z"&ʭ>t%Mn>@|o.\YJB o=<-a!;iV菾KxpenySiHGΡa}KOm HXbaa0đIB'YD:D#XNW)d3*3Bt>H$w; ʱ+Ʀp죫nȾJbtkxGw㖌߆òJl~uc; wnsFESly9Gv޵25 +7Kd@Na"VŽg)Zw+w}1_|y'kl۷3 1:sXyLB6jS8=1rIHTJ\Nv0y}xzB *IJkV +<@T+70c6ֳ*:k&fn)&̬~kث)XN17_X%}4^նIZ/$}ֺi Q2p7ۮBot9]eǪz}SDk+IV=_؀s31*Aثg?i9N0,e3m@sXbA aOXQՕpt8;"U}P֒D09,Yz  1{^+;RRcXW#\NO̽}HkkQ#Hv:(~9o ?ܿǣTiL]0Î6yTP.^`K*#ۭPc[=dMf?o_j4Fvb)xBT7^kI9{vm/Z-.Ӑn@j砝iMٜ۪duT#Qp+6wHY j,:RH]*"~Tj9c&ZA`S\0ώm1b4,T!%WSya)0KW)O)Fq}(]` ܒ%YK, >hMزQ5R" %bi 8}W*47TqnC,24Xq٨zPr2T1zn6f"Xp6i=!G{Qn~\`Hmf%2, d1I tJBSNַoY 60B:LTnmIy"T.LKos*矕_voT5]lC59 %U*6p {S#exmY~=sgtPl먺%dXjc)m:1:"t2=6|PJn07ϦVQsJԋzOQrIp0% G.v u~.5[`iϵXHősfo 捻+9'\V!fd &7R ʽ-Grf1w!l,7=*#7>4<'"&5–RW#aLX ԟ&{u `zo;*EX̽+㋱%옪mc8%!1gh1Ye"*,"k鞴"W2&WWr6t5]qwxwjsqv&V4)RC4˞Qi9#g̈́_FQ ҄E}>=ϓT Dd1hZvr,c!7ބy2Osc8Vs <@ů33K*`,x .t$zEJZE5Jek?.w~(O_9ucNI(IսHRENcO)J YI"4qH[_lL"ճp'*w@!/Pؤ+nJ.'mv!j|:GmId1:Ri]pys x1eʝ0rD V   qIc\m̸ǺS{b'JK`K. zDdIɓP&nHdfҪ8oFHKVԄ掠{AXo9q|enB牏}qYBL*6JH꾢!lEZuMuwI\#n@0\lbBP$H,a} 8<V??ҹk߻5@&gmt m5SG.ME9 Jk9 _:_߯gCܖ]֗%r,eQLR1pZsl ZJds|!t9wk \oBݔH޴dmxOVIRbp1tcvSi՝7rv-LHPNU :lQ08,vlcCH;=x`+9Y0Tl9 ޲~r+%ݺϫsg=}y%-5č}0X8pTT')&]mVn Uj9sWl%Q?jz `7Y'%>dƸMa ub!f#tR:uH˟D;7O,JS '_str"^CefλSm q~t̰?~G¤PS}Rj^c;qdsP)i'MΊLΌwm-lx%QIc( ( T=S)~e>>;if%3yH 5@Ĝ|dQ၁+S~.gu"' G6pIfOUqMox|cb)qA|`q\ЪԘ[g''02jJJKFV;}Bod"uOz z;_@yx܀mlB h3);i6BPЪçꆾ4XlܻD640竵<.[MA~JG $' 0eE"&Da*Hxp]HD.iysTj"RG%[j,M] 7kX@mH,fYntC ?кiH#.<5E-ֹ\HS>Au\\T[1]rmln4{@\Z$"M E5E }UHucc~mw$7ز)0I@#ߣXa/H̫0K,J}򤾻js.6+J$*"Uy;"' TAuIK{=rIaKPsH?б0 KO1;#7*j }h l}s+yaBvEnwar.~gOXUMJyRD@\oMD8\"ҝZ)|J:&]#XgeRڊqDTwN{$ǹZK;Čð^%yQm7JލܤO],iច9; >Ͽ}-ݧ A,:YA ¯g}f9RK 3tlHq|lKBj'Nȇ[oJEI;U0:rLp/V$k8'gEcc#qeg4!IFϢ]uhF'69dKc"e@_=}W</ѭt[<{9*RB-2JǞ\itFЩ~a؛O {'jD`ګ([P~4B[^/j;,:j[bDo@~h@#xl+Ь|JijKdGY%J X 1I5g#-T`D#r1$McLBaZԜrT[mB@Q$ᙬ3rθRa!!ԦgG;u& gYBͽ%ZlU*,ZzP[^pATK~>\s:DGFX=u|iU5FE$ VԱ^3֗F*57fk9n9#UEZ@cTIZM'f;ӯt.fZEqu7ZX*rdDs7C)$hKHrNY4A+L*_7pv_SVc%^--3ײ5W40ĿwG2sl{EDKsV@L"1l }鍓,I Al¶tGq+O^!|y|0]\v r5 ;r` \S:KH|Yr{(NkX i1vI]uh텫I4[.^@F;:V#{\:^˓y}lU^">!k%_"<ҹ<Ӣp4. :ش{Tї?P!#jbz[n}G/+'Z,׊he#¹mpJ/iOt>"OVm ;R|{u^}Om9*Vͷ+ SS[Y54j:DYV}4W!S k9,k3o̱+v)#/jLJی_-fuy^nW~gtdN+ VzƿMF:wǜv_So6gOK{7Oby^oS߻Xk9彽 rd'd[/W{;XӁٺ4l%? @Z*{JnC(&iHKl~";e[Uwzo1=E5.4CQcjup!@]cUd"(-.I+ 5S4:Pqp;0_\dؐϛJ躥Xm'1?(d:9enKnN! :gl]bڌwh-jzFLeiKɁހz[kelu).P"\KkMB9t %\5R}aGDDu-WR{Uͦvdv3Ѻ7!xB8~z7Ma^nα^/ɇ+G3|W ^^;~xys*2rM.sK:.5s@Y%.6#$X N:&NL_ٯ\߉p*MlagߕT#7C&+nYY6^Ws)k:C-l()VBgWW(Yɭ+u}tR"UMՅ+muY=1&X?p;Q ~g~w'WX/=<Njǯ|59+|Y-={nP:YrG aZzĈ?N+P zu8mޅ+`>G/zyae96zWM9R?谭N1OwuG4$'O[+5kS7C]Sއaٖw$~:<=y_f>?|<8~^__NYǿ;~~]bas9c]}<-v/6ۏ^?iƛGy*?l}aۅώw:=j0?O~tbNDkEs7a4X_^'ohX3+f6L/^_Kqwx#OHt^_YLJq>^ع<O 6[\W}sd8;V_{7VQԆ9]}zǮd۫RqUk+>A66cF|oTZ7[s߀"i;#{R3Hw潥=줫q6t]c勇b,N^R2ֶZ+ッkVg+màRM.եç&gz-wIt҃#Jḧ́6BJg)` '7>GN&g[.u +IJ7{8/{䑎'ԉ9jB'SͨfMdթ`7(.Ks̖wmz w*l,LTK6K"U> 7A.5tطrM뺼65}:"~ΐwS^k&e*#lYirKfGMɇ&($I5%3>O͐J{&evdr7a ~x?m?O;~-]x,W(ν)zh(gя+Arl?<}WsiB}|4'i{cxrl=^CͤR0k*MOmu`%y+]&Iz-F)_}kc䙱ès]„7_5ݕN@~~{^f?%po~/lrݯyu1N$HB߭t|v>ұ{ȩ"S(J68a*Iܪ{68.6[y'>V, FW.F#%q٫dUL \d U7 P!uX;8} JpՙNY:uQwr@"3ǐ4Ģ˧w8Vǹooc8IܺHt |@ wXS/w]v$brFBWDԊ& tf7ɨ`}Y2t>|Qqt.~eёTxEtu^3qέS)ׄ8 bLjZXc-BHU=n6p^1`@8-:URCo#f;;/XP]Ћ4"K4z;Y#,%! RV'.1JU'E/~8"ANlOwRnMٍH, {nD0L„|gC. EV!|Bm{e|f\M;1yExlkH,)M.Et!U+]sn0);>Oae!/+(Oأm0McUI<*I}1 Fݳ}bSsg}٥<=i^ 춱fJ!dԟ82Am0mPjQR3 1FZO'ڧ6:&$x*O .tOo.}Inʚ ZrE;r^koh7y?h<#ĩt{0Vuq`:y Xհ4hܒC̕kŷKW}Cv{8B݆q,Bsr*JT,-E}rZP=FLR30[ݏ7B=a\eXB_8f1NSp M`d}t㸊oO\xLj_皒 )#RXD\D`(vO @FeCy o`JlF$utt::w7v)t2)  7]*H|H3Xx]ŖSϯ'%8 ^tVHhLI*~!6[ RMtRX eY*Nޙ|84 0GZaRZ. WAc:"MIt38ṵ`b82mؤ)ڻ6$?nabdBQU%s uFhӖD d9UyzIMqu87!@ja`yb^;lZ@Aا ;{ȡ|fKdI:ύ% fWu7l:+q? +v3.8BvlMb.Y .'$7Ì>}D]Kh| kVٚI[MUVJ^zy1%k}v\UPKϢVf۽_ǻ??<U/Ի%j֥b@t{UG Ϩ%2K>L RB**ga]|QOw/7@BuJypq"(ܐ7}0a۫v[1 $PrM d~:?bzL,E5 ȹZ aUs9]F:(\>҃΁7bAjъ#J./ZL I3 .3{t|ywQ$p9upZu"kzoZB yJ.ܦܝ_I.δ;]~ŋOg:/*7U@zDJj *(ZLN*=\$36 v1,=M/pAǯC tuh}Wl-k業B~ѣ WtL9*A+TJqF \u$Gܭи7tݹ]"aRio]8ۓ^ AE2S%NcW.U3} \$ hJ`q'Mllmc.BPBkTU]4"6V陸Ā|s ^cp:+J"ڦY)cl%Aa 4U͠jm0e4IؿnbwtrѥjLb`$ ܬRbP6!$X{Monr 0g`L '؈6fшD0[u.x]a_ي9X"SJK[dPD]_apQQ 9 a=NkhG|X03Y]&EƶP,uE[?m6?pDf`rݭ.>f?m%W)R.ܡtk`7 `u ƮR8̄1Dtk-9CQѹ𜝃ɀ;Aډ/.#!j&bϮc O+j`'<dzr k_;yhe` ůÑ^s:K_3unڒAo@ƔuIr O,8ZliF7|]V(|p,D]'@6b<;5%Kab1wĕDE±zCy83,Dͫ aЪ)Jl= rVc qPQ`X`5MW~8T޾_OE~”y6à,ݐ{rǤr\bQ>*mLJ%T!-;%y#rqS⠇Z$.OK7SV)i J'1&9W.1Jt9 :Bb=tn`P0T,,`Oj7͠'aԬ>Oؖއ]HJ}4s+_IArߤKIͧ`@7˗oh[*F)IN|VO N0&0! 뫝k[wO];-cCD.gg 5!I3X7WHkn? n "ݵ%pm1:0@1 ?9}@HJ*^Oua( >'PF+!*㥖*.ǃ\K1W RjefUԴ\pcC!25|n|_tyӳO\jpc[ 0hXtPWrXfwliiCs[4=q KH@#'pt8:(wjFtX+Fx]\bݴ˄ؙŻՎ% 8vx0Jj"WQH_e dKuLsϖx*^E$ u[u^ 0тt;[`E9I8mRO:e[tBG(n7XA=} ˸A;: ZKxQpR`}R Ca! W% ¦ e [0Jߝ#30X1HB d,3E^#y)ܠ8g%?"F;NQS3@?3XOZ$Cn?. ,98Cc ޜ1Ǘծ*H0c!}^@]i e mݕN.tY#rJq[N;ݲӢߥ9@Oc"JF]&}h lEJ˶{p1NF ~Dɳ ɞ>{D UI] 7xy}nΝwN]-)Aޚ%2^j8iД 'DG9tt^^BW"0$6#KMYK^,d||OۓN / %! zMb1̹rT-O6?^Vg|_?i by\րO'Sd*PuJp>%gb9h;=\^"EԺ,B%1X%U\"pP۴ċkZGD4+qPȵ'RTkr#4Ѵ.ER<.OTޯ9}kfo;7;#x9I>3ٻAqz&Ϻ("0os>o0j^?=|4ӑ@4xoLITiVn"dtȬ|D4۽?o>c%f2VǒeJ/UDUPNfrRgNMKYGrG'H(II* Ȳп&!JO4G(Nl-NO_E ~5qV]jqOPVxܦti;;̛nlRM]F99 M:|QED+lN3y'&R uϔ m(n>A; 3{Tx7SkM:x*Ȑlq^3Ag$76ޙ7OE"Nc X=37!r[")/]{EWX%푋V$^x) ?)B Z>&fA,&0)0#̐tzT0;ׇzom|SoW* K5Iq Z20j,+H@>O/|_qR(ًk.؋nք7Ov٢ͥQ?kp%z?n\Bn Oe]}sQ Ł`ւtY% _ Fg ^+1>}]OlG]>p |> |tێxF[q6ɳ4 w1__ۙbS>UgOʇ;Ҕ\^z`&o>h}8uu>|`_0^{{x[YG~^ldze _|H+WgN7<ͅ׻{L.'2 SI@Ikc ^/×]=llm?_Nس  k8j@+an^=2vE><6j?/Ӧ|8yz[ci~gW|8>{n]Bs*΋J|?k~w9+%?ZCx}_Ks@`xhtɱޖҭ`bw \ r+5{ WZϧqU';9/74Y87:4mL6XN1]Uؤ׹+[j>'Ms o뢸r|_g&W4rʶhB/O-0ү׾ZzYi `=^;\{E[ɓf$?_M<&~~4Z>|MCp\?MYJ5MfQ9k52&붲^cK>9?+L+nqޛ>i[aس/s$Ӕ4V[ul{bӅu~w/-j ʳ"9u## :Ib ,Dk57 6a7*o:>– H7[7{H)a)\˅z uN>?푪HoINX2^bp"֍ij7mٴ\W6DjYE[x3YuvT}Mݽ:F,BkJnuV<Ugt Od%pC|RiV:U§(yBp|FsLRυ>cNM|]5.y["|1 ҈t(ewEIQ;eR/[}ۛ 8)]>g,qpY$Bk{k%3/o <͹Y~K#ʇN\ AƝE@| VI!~uJUm~N04}ye^IU()^ ]Ȓӕ"iU.U@9K-nIދ+zoo.DBCU5M]r >&kp=I"ᠲF=KQ&|,ު !J(8 Ys$IR%uha8]# ۧ22'oQuvq>U1ԳL:BTI2|*c-O7agޭo"_5-g+Vge lV]'ltWԊXqm|z|u'?>[cargo-0.91.0/benches/workspaces/substrate.tgz000064400000000000000000002654541046102023000173600ustar 00000000000000substrate.tar풤8&:*396{dgWwٮ޴m;֛rw:pc{A{{cGHGȭf:H}ޯ/8<,;:N`8Eo ՊzLˀޚj2߆ژ~8#Mw4&?mðÇw{tV.>nV9#p.=%It-ڑ&9=/t}h''CQ9~d<8 a3 Ip-sm~r9~؃{;.ghb.%1:\Xw^pL I]rrcEAurݎzA2ڨ 0rn>@jX<3꼿{Q>j )qu>ZH2x٨s;mu'z fc:>:];yl4F;5XX!}Q;.GWGY=>mk>qB[D.?A$Z:ȶow%9Qz j@pkaϮ /ػI1.P&c˟u >v#?'g1zB8 +慨}ƗAPő๻EϠmk:t7B6_!dj ?m~<ө$ һgUGHD]3>%J j,<Ċ@ i|PficpZ U׍b_ -,?"1D}4_o,%gxv%zEfl/wl*'떤ȆO izGp^417P~o osfл7ow9-a_cqG'pK6ǃ'跱bnb'r\}@}?55'8C<>W- QD۹>:vlD@%=?>hLQJ˿$ k- vg;54PƭO/C~r=\P/g|\jd}4}TzMB@rs:= @_ zeK5'͛jǗ0 i6fԩ Ό㶌C#)ޒ;+e+uHr!CD.pԖ$ :ΘQN&pGq5"/[tīt՝z[UV+,hLc3l;QU\l_?=b?uu错jRh?A*2'*@~IIm CI?8I(9E!ѶƢZq?~~ô5/# Gɝ)bڦxOȵpv"d]{G8Vڻ[pt ȳ+9rE;l Ȗ} ,'3_ojv/V3]4M˅AdIRwp. *P}RөtXJS+r?֦1=6Dzj%8UH;z:6i ғK0!^|Nz`cԑ&gE=|c% =ֶWi- ,Cٜ5ddHGx0VcNϼpkͤӗ*՞E7z6i~]:8Jb {;Cxa ma1=܉ٴOB. a9B EUA)sfנ'!L،q=~naϗ{lYm\WOm@iU"t"˪am@kz z8N̐l'HYloR翲{6?ze/3W̏w^BIc= Zm|Y pHe-҂& US7d ?ZZocP@'7k52)urU̘K*dCPvC/piY@v DH2ӓ_7b]˃`@7\/Uׄ|Cbȯ$% +#S~&kӐɅߠM-_ )sB4so & j8 WpI絪%EKDU5.IZTBV?)Ye]QFY?7St3͔"7_XzcFyU͹ dfʱ}>L'SEv${7wp Fc\yb}cė)&h MVDoS?5vv45lJh,?)6? Z9n"ӿ{k2X*/%]qm=̊0: x-|fB!bY[y @YdPŒI\VhraڐVd r,gk&kg43rbYy@Z VJ$ W$ }ԒSlEVLV vQ(oYXs?؃'27Ii9@*?~\w3eվNv| FQx2[).v#wqhpbe(k@;ƒTP"`RH~{= YZKQìa0]Uz٤VAqU4|]Qm5&)rE"-v=z'7iC%3ˉζ9^~-}|&Z'Տ/(ȁfTOS௜G慓LAn^{jeu_A٫ t<*_sH\s3%4**Y[D{˟Ϟ!u<`]kĢXGU;3@Z*}ocY/Ṿ@7"(mj# KVBmI¶(pNcήp'} !g"ޙȎTm=%@^J-9~?Y+0z՗=}u#Pfɧ`rmË ~Eq*n4b38%w'. ?$`&".-5Om xl%8rOqlIsD* e`'!~Lj3w!O7;6TYYȁP\Wܾ?d 82 |W\c<λ͍y -&1%'_ͯV!K+I,r0_c7vdf5Ņ(HqmW6I|QF vOXmS "Yv礈S_Q1֛r濲֊[땚Y9G686+z EpE2UodTAE%@!ߖ?~cZdv?$P<*d@C{䄈%K/|:> ‡sp%zdG@cu=%ssLJ{'6=xlх9A('JWҤS&2HKk|mrԹ0W|;#&롕' M o9d>7zSID 5ơU\[ iKUIyiV?H(׍!f˄Քvņ!}bR {",KeAV˳+k4k2sN[ޮ[QM i͟ǔ6\(2|HL~a3Ldjll鏽E5y(L#_pxgu(, ܲB g;޼dbu/ʕ/V@nV%"BtZu0LڛsTDS)"S68bccq16;dR^s~)!׭;hJÂs+i!ٹ3ϻ=aM@2(=8h>U\7" pTWOj.`mp RO6b,T@vpiq*U[pB?Nx"gB⻿^ BpQ]7sN;3YNZz;8-2*L#w nLfU1fJ)A 1Hvlz!6~ĥic;amYa˯&ٚ d9:9ZԋT)a2pC^(k7pj"}a`;)dӎ@% #1*wG6a1\Z! ;uvISӺ[d~f7UnUj^f'm*J@w{diu5M=rwO\(2w7*-=QO fQnjT}žN.H5V(ںeOw#mN)fHDTWINR/+B(j|,U]qP)6g`p#֔x =6Oaq2hMFr/l5z"qD]~dz|ho{}q5;Y ~ܮ㍐@CᵩNJ:P rW3+ym)HF?_Ubmp*e v(0 ۖBy^TNd G-9E >PfΥCi [4,&z# '|U?@v?Kz!WX zZ0'TbUh~{-[$umR|Kwn8CM"}53gkRnijT*fi:zʢj'w ϵ86O?|V/,Hغu7 qx#쏆 +[weCXJ4HQߥϿ("7 sCW-^OkTt.sbqDEHddC sf##U)ꆅl_CԣHOIq4֥ƍQAUުyZS0ܨ] ̻Yw~cb>ܱ^'vJng\!3F aNFlSZ/R1|sJ/k3TG9G'@}φI?OeYTA2B 5|Uefw&yO--匩Z//tlCmƇ&8`o5iԖ8뺍WOVKE,E~N@UV@rjUZMT B-V k``VIPo&O`HjVP` H?A̿e)m ڸ"+Y`V'pQvr^sR$A JxJ"C S3^?_11?dz_+fʏp(mzFUD{*[a-UUԕ!?9yE$05ܞdS&G#}3mtj/6lHP??|Z(Vk- A$L}xU0` t4C'4כ' ;>#R ۫YOYpd;X uXݪ ϵ5owpk=7R X5BpERSycQe^DX}!OP!Xv[Wc '?~{n. ׆Kp^uL]/Rw5M3Um(OŎ/ykU4Safre<ԑغ]x/2H1iS1 b$X*[;)m g+ ?W6vB)vžME2rZKߵyׁ4[!Gp봟~kC`7i@z5W(O`\vxᵞ>@='-ڞlDѝeR7ke3>~H6%Qvn&1Z`Tg R W3hՋm'ZG'mΉbK _*ptQ%(!f} ƇIc9w#΢:Ǟ 5Ԣ_Ets̾K!|87TZv 6r8Y3y/skF1\kYqh!na1mOT,<%uE#!R5<bc3S^Uh{FMja<Da!-"n2E֗1?Nh #z R3ϚCdjןq(S ;$ ` rI,ϤMS)jm3#$ٙzAmb,}yj *` F|t~Z'=oG@m?)7b8WSU (#1`o-$r z ;~[jR]0|dwk5VICFҧ[k9+k%N<֬V!خ~B;3t@gT@E!GO;sЭP9̪GwY, )6} '\ӲOxp#t NFGO#)x? \G)؂N)" mn;e@x_h[YKɟ{?us72?o.7ijޒ8nh^O`gPrid }1EV_pO_~xzq#v^0/ Das4kztJp,8m֖1{p^5e)Al.FG!>es䳨udZ+Syk%`&xQWD, ,VƸ R 1RD+dg/eCeم_?mÓX_JH×N#XHIN]@aa]BmA8E>A׷ IFP 4ikǖ^ǧӭ+ٓ#Hw5l&ub9&[#ٓ*t oYY4TJӻYM,wȂ&WۚL޴%T˾ӝX3X LQ t 80VOAQ@7 ܪotntD\Uyld 'g?KY>' BbTt0A㸥\j 7E7𿙊Kï*?)"ULJzbO{vw\'ƾĮJъg۹uSqU࿣%B?+0צ^S ONgws1}A:d\U"mfq *E<0i(;}Od0oGa=g2dRV_y3qrC{,WA2u !ʱ11 #:˵\e8CFln]ks3͒~eɄcMxv~9K}i'.?8PD;|LOuYݹDπC0胇=ZQ>Cxnڼ4U$ jI:y (u'wrutvǯ/jG{e"eyOK˘u^,ڟ"?+Cne~a$dRtn_>ffa*эU߈+]ζ{>ÿDtJ/xO0[k'ez/WceU*V;R޸ϲ ڱiu+8a'sikg-GsX9S:͏Y-_{dZQ۵)OR`K#x".C9noT.̞̄tP\5$ߤW +/5P[l\ebqGk̛7 Ypۅu;p?Rg_wdT2 ;z&+^K(x {"":*X3GW J<ɾW S(i'1(*ql.H_/ v?/?#(vlG ReRg|fU1;)A ύY`_at zrwTsgfxFx|Pv{z^w.3hj9 t9lv_p$^Z8;$5Cu@}o0/ulj hnrbJjzB=Dn@g)_Mu?s;/:^=s:MrTqI\~U޲co2xDbjnĿ!Q _ .?< eO~v?2T 2~5U?w3>쾰sW|kfr6nJ*ܕ_KQDCdD.(!ەJpIΘAV🲽*۫y/ i-8sl(t?H ?km)w7C2@`#xfH' Ncb"L8^(G)_U0xϡD. ,Zg!U_C i_"wSr8 w*t4$t)L_+,VdXCGMK۝{=J ғKa|\ ‹әr[W8X@_1tVg.$H" 4E AJ~U )HwXQr327 &7{$m(G\֑=H{<Ne$MQTK?o ]Jk20BMhʇP/QtZ?˜80V7-5s G?N@WE=.;?(Xo xA"é€RGfXYa`g1aq.sS^ۖQ9^WeUAo^0oMCJn&Yu6?gE=|NTbOX"' v~D0fcMPhe=z zpJOR׶UmoM9@%Jr9Wh s)ggS,*ܐɭ6xjX*G؟X%ze$TЯF] ӎ-fKNxHXy<0^6JEl$ζB@ ۤ~W"KHc;_ue:6O01 g0yJ>_>\Ӆq9p1%ҲjMD\PK-j|VʄMƪ? ebo,UIGN7x|Hxx.\)hkcՁvaͣvVC0;ܫA+mΓs]Ӳ{㫀6@u~=m_>k`;i@r -fu:~ ۩1pXKyM_XmnRvȓnIp\Pn")=9?}~҃,}S(j:q0mϋ忹\[UpG俊A>' ou,מK`*Pyi)A5àö x[`T0g;Qery| \8* [eU(>- ׃d&o1")s0<5§_4;!Oqx% }J$ I2R?gI͍+<_(=fde+6WڂkF̠@I]C! ;Ȝߠ2WJ>r*=X k檴bLWHRHLNӋ!NWO6748[8 K"aJZ?F;(snTBG?Ny1ByQMԸOkT~3BVXOn`am|푡 ,hf{qjo)g@'ݗȦ֝q#U ]DF~ՓOM XpC&P\DOV|5={F~%';׼knଥzA@vp=0 ;5M`LCƢ0$KKT`>=9ply3cm_kBQL FC92V47 R6u&~|0f?$wc'_`~޾Q| *q3)PƯREd*y9xΊG,?.%WMcqDGV- }|ϲI}{+*hyY!iU/#* 9KÃa"3VVOVoT+6Tpf<#j_p;z6ccޯ+<嚈A.<'Dm!_ۘ#&w{ӆezR\tkŊ֊=YDGOgώcVt|/nBHM`VəP_IC:*̛|$AZ W=Ì`kP$HN=yA IGVA"<fӇė!NK ڳ6_sÁލt|aX5&t'Wd}7N/,|QC=:2U&Pn!X+/gFL @_6Z)ۧI}>* ?*Vk}$/ζsr}wVi.'s\TbʃiAa+Ā 俔ǢHC 2ϵ`A,6Y^,Њ~Ȁ ]e+T4'-=^"V)WE_O70O/U?2;xdr,A>z fDZ;oʊӄ$ qUi:~W?[!g=W(_C]2!P?wNQE (Yt ;GQMphdրa!xL;7ΣV &l $A%5qW},фb@#_x~CM`H w?.7&u'Td-dg: n ^;ֿ֮E7DNla'itIݣo{6sXcҗq7,8(X,?pv0?iETEsoJ"sVR?3jEZe\ fYQIOv|;f_A xL 羬1J昩;;yNkDq=7_M/1)0׋ jD}w[r3i%rlLT""0N+uV$WY*nB1]+ oD$i)_5Uuo,]?'qg;sQZ4!~j1%؊V7)b`>)DwPdyxt.h֜CW49~`Us2Z/j,PTv~Zz}z>kT:Ep'T.J \bxWt?kTmO  on0`eV?X$`-@6@隣 )-Ϸ~dK;3h]^teس}:u&L5AޔZ ^+Ct%>҉֊y%iU+~RO?D-+T~#/ 9%z6_+UZzQcV`(KPHpn =xJ~'`a^%  /c5GF' a!Iy NinCWoà!u(|+~BmT~63459,ydo<ΰ#ֺ~h{ 1$1NhH7Sխ(* ;Տs\CO`eʘW Y+_ClFo_?#Ԝs6 +=a6gSv(]ϗy ]EJS1_`>S.ɹ 8 !I1TV x #l,n-u.TY]޾ aPk*7xCF"&n G*$șpsF(e*ܧb*bE0/7K}4?, {emoa3P{Cpq`պЩt]9ӥJ(}ak*yjYܦNΧ So#9=4 y:/iYn.h:!WsY!ON@kSJF@ByXpHj?sA%>in5`JHAuc xEkEQA-* WJ(#c(Ŕ\?k'GLs&驴:5F*Š. uMF3PH^F?!-$?6@za׃I79ni*[KMY 0rRY9WʍUhDsm|x@8_i)fj'@l;+G aGCpn4dW yb6}?*_\7-2k[ =G[C9 ?8<鮽ag˗vJm+z^vO Eʼn]8'?G: 69!>"4q<779l2}O0Jv,y]>v.і#.َ^1>]v(:=%H^i! @< /̩qЎP$TD^_wc)1!Uwsqxv23x"hƋY=P9+PT~ݡF Yw3@m7d67<ȼd&]=M/}Vз5րrel)a1ܹvKz!!'\Dk,]B}]FspE0 ]@;ۏQ}~ػHEv zbb'f 񂍜4& xy JV?9П^o LʝaUkd=&n 俴O?J {RI$?pCKJvhdGPF7J$,tS<ŠN C\$pZ0kV6xoc/P]J㊄տGF@i{~ђ "u@4HBh8g/9 QО>kXex_FG"hGK0/o,3XT c6BYC\q׵WgJ+Rg0_#}dW YS IH4 >2d:*ضLƘ2]($fqx>IUmB)~sbs#*O RDѦWZ{"5#,;nHͻh&pgaHrO,맿'-_mC-: /l:+f ooRס l}%Qm( /薟 ኼKFrr#@]Oe'8P[oZ/ܐF AuT(^V8kI; }}5?f%`pt g 1N$@':^s@jՇCTO>60>$+A,0UoSSHa=y ix\[Ϧ0Z E\VlQ2_R)?thl, \})eB[,-1alL3v}F,MP%.L̮+–冏هP97 >b?QNQXzyD1jȼ?kҸ < g2Unnu϶='_W+/h[~Qb05V{fJ l 1Nٱrx͍zy]͋GpQ[_`ARUH 2޴P~h=)_S]D\b*:/j4'X̮tzKv 2Vh5QkDg# ( <6D #2Q܁b 9,Y{U2?CC=j,.^J E!ץЉ(4PA oQh)+K"7IM*G 'PoIV*Wcɨ1K<^c=I2v%O9.A:<͝iY_xZV-Z]`O2>܀VXjrp̸_BuAU-<7>;]S)5Fc--g AM'W{H,Q~ s)GR5NѦI 脗$0eS4KW91'g),sߕH? kI U*?wQZ.X(lnbsgtqJe4- bpœ>ke3Qm3[Ten#.EJٱhٲ~2lGA[ dI6)c>*?r[/;0זO_]122V3CoִTowն{Fj+dtǨVC=@ga @\=A=<(G:G "?7M)ia3GiXRB.>{Y.B S}=ov(W#ƱY|̟Ӫ_|OZJo'qo6O#9\פg⿯K/ANcHѬS'?s U[_)\_zvG[\aZAdm €(w%0Ru-A'E4q[.nYz'aꃤX{L{"V"dO n< 0$X+/e*Y 'Xa@fd'v%Rڨ)+"ݴ!  Fw~ .'c%{?׶EbGw.e;=^;?rfy${#ɾtŃ[ dWe? 1Q|?0,j$Z>n)i(+^\((]Ʀ'6L6 k6(,U> le?ŤEVi/#]~gV;kf"Pn~r̦{Y1AtAQpk}d0-;^5Ő#xe\!; r,t'z fc<6T3*Tuƾ{/ pE_ͺ DPt]dqC51F+? >\W]jԮh?=wzX u4JLr| +, Sy2kBkbGIZ؉C?.! aiА%X۸3&5$/~QusPP7W6˝^##s!-j?CɅ1Ô䝆)SZ-oVv: 0#.i3c6d9-MWW1fk QfMlN˗WhCB?AãU5 aѓ*i]4i}][cn:n/%I~7_7ctfZ?2[T!sXY&7gŵ㟄 o]C6bmQg9 CÑ<&^ 6[8.h\iEE@t?!W/C '{bckӷ@$$77w;{W% b֡sNqKI~.pG.ڵ~'9=j`?1c"q'LŐ$P5קP>@ > H&a^j/׊S! @}ՖnT眢 ފsL.pH]ֱE( L0;o<$xI.0$M;T)?4)?a-a6ZIfé܃"j 3 l {c#ܳϙ,!vV[iwaTO+g]wpePOq, ~IQA"eܫ~yQꦝRUoEh'X-4|KH8hT)ۈMC[ە%=7i!/s$?ksOĕm:] azf|d4Z8F1g(T(ăZЀ&"L؇4ӗ Ebea!VR?bFvM0T|ǖ=f[`퓍`g B:O2O͌3躬߿a?K:_%R̜ݡތse߾xIti\ @ɐX8}qCӰt=DZ9 82 \`陳rEgw,\:X>jz6Չnl'1s=~y3áR"č ŔƵyOe>b2<+!Ŏ[=jC֪t Tfh\7ceFJqw{4γG CPx~=-م;t[0xh)s,[Ba ;:Pp?qj'*2WHֆV Q&~Cb+=,| ; 5T!#T3Q%XRl_ޖb|v Y4]'j%#͂s⤥1 gzY[݉^d fTMS#m{:V'qėNs^݀puad{LhpϜHn𐹲qYǼq:SգUv8rI|U_#:mҀw>,݃h+e2t6@r$>$8wM?_ܮ ϏՉkg-bϴZOUe>`v޴F/D 8F)PATh  +UK;O7^* "{:H wv]!t] ɿ0mڃUc2+jԢEsPtYYWQ49g*KKltpT}m~v} ྀyr D:x pAWC% VsKUpb r˟ #LKU/=u 6aۨ{o#|xASUG/nUѭ{ 7ӝ7GoR/hH2w_\q~9jj+vf/e>韠*xwb@#)PMdY+kS *[FgAQd:ѸGG)yU΢K10vLH[ߺm˨.6 }Qi:`Tc|}JSM? O5Lr*k~OnP΄v'#F301:6bVjm#@oIld#_tx{>ZVQ6S%y{z7WI#ϥ{ 󏌳֊+PrDXz7  8pbu٬wf`͜]9= k5<6S6mvoE0rK0̿ܮ /kc*?GCo)wL=:8 z|Yow =[̗ qiؿ7@;HOSJ(!$=i';>k]FcMBԇ 䡣.e":sϡg=/S19쎌Ŋ֭Wdv0wO^"C!cR7ȳ~Jf$6ϿK ȿdZC*OU8.8Wډ JK2Xt9rp [by%v{>eQ9K9։&=Le%/3yc*U_ʹ%F QoW3oh2 _OC7VJ9;;h$+~J9Vy,od&DMT eXH0My@1Xxyn|*fW"KTdfP?gk,5}Z7?rd]wpAf A@jOJW!Q2$'7.m$@+%xqI>wesrC8D W}/؄3xm&~/o(d&nt`,GM%jK4*m]`(zq 5j? ɎTV*&t"6^)7%YK-{+ڣ4+nB/}c(qp`%@nZ" YC[ҋiL_2]1pAq [ϸԙ0uM{rŠtJ.F^NM +)-}SD,/RHHۜ*EkՈ:ohֽ Q4c*KWb_m7JdJ&3"Z+o '~p xu?2)rߓ1^j?{3HtAbkSy%``65-|D>8\^҂(r0쮍$S 6?.W&`U_9,XVK&֋R6B^ M"2h`CނjVvhρޛ59dgbϺVzɹ}LfC㌤6h3Vk%XH @2w}'@bȼF}D?; 0Nte<-jw2l9vҕ ;\~ohnW莱5w7 + <_#5.Fx7W.TF^'`ś+]U#2Z ^~õ6 V>)Z߿nu5x?&*w18+a3MBBW!nf9οQ0#sk2a)oؼ,L*61 9.V;=qH9 ѝ vGnQIZtmY=vfj> |,N2=I~78uu?of߬âI; H?>~O Qޭzo,߽G>e׿,&a:?/X{wigmzF˼a/?y_x9so=q\SIuqbY?ETomp!a+~sˤQzKCk@iϻWk6[ձo7l2/gh4kŷIzhdۃlzQXizۅrf7c4>YY*u2]eJ*`_GiX>*>GeQn%$O)_2nKڣOսtͩxhuK.>jf:Bn [ k&x{[DGJںyFpښ+tqw64݇eDKOGu}G߶cG{ǷȮ9tP^jNZ:K:wvMu1z@Pd EBp(nO_m.3"x+ ᄏ;SvɻR),Woqaӗd YCvu`.ɞ`[.޵lz~&}U>s)j@׺_:w}7 /,g㿥=Z.qw=ݠ+-R_Y*o[!Cng| ]ޗU ְ1KluA [LmePg}mpwI?5o<'#g=w_5xexSqK J?$XBD웨p$B^ &;?miy|t(c=ögGo7TjO̒uJ Lf4[]jY_}1$6v7vm#L:n64jk qSBpmxՆ߮w_[6$=SaY=~ vE CkLFۂgn:`g3ڽuWcsc;qSQwm.kv㜽Fi77"vIQ؛ЃիjƛLA\:LG z[;IW[o[tp$ЎKt'ku[{;^wFJo,-Bůx.Ŀ}k=l3O's_^͌XQy~0#0LBATO0ps@{sAO7{ˢD•.['&ta{eYw4eǺwK'.N]3Q1o8zWW?k; Xl_i?⮏2*k*<&^TO/?Tm9gsZȏ鐎ޠ>ɇ,i8Π˃"[nL=P)?: U\cX]jIcγ`noMoQ{7XkDa9g]oÁç%Zב X r;fE(vMCy#CCg=y~ںLvCܺB;(x@,فC h}ݮ v )&̹qؘO^'hdn*ot gk=֟?k:'^=6t 4^=?Mr ~rƄ//b p"ze|jz+{s[çܵw?kxM/-ۮ~G%KoVҲ?ebd:\&4Ln?'꯿g%XUlb/kӰ\`{*<=D~*xqD(:ߵwmy2XC?-έ1=]l|h?4Ǧ'brl?6Q[թ\=>)|oߧu wPl{?!qm_8z1wh-_h{[rEnٜ4觔{L^Yg-/: 1Mmjm-md_%ímY缁1= xO~xJ>lOZ?_,Ż_Fst3╠J\ǭo˃p"BtBVK+:Ǻ}yA/8dwᝃc#B|˲pM?;e? 4_- >7m78 gl!M";qZvn~vπeR4$pMg^+U6 UHG >(U@{oaӗxOz+dvKmxCY2.uUr9hR0/5]7}5q^͈mk×F;dɣ9;ڃokטׁyM5wX=q?{{z\c}Ca>: ۄٞg8=`>ކD&LeWO/'/6Y>#(KGjsǀe{oorpfXVdZj2Ty`=K#]L8eYe:2|495r*Ps wOy0 >kP".zi2w@fw=i<)CσrZ%aΛm|vŭWEϝ7tas<Ө7$p)d!׷‹o,i3aY _I*VWW\uKr|l.b=1?'S7޻7*uF7hc=NʀP kL&_`_J ?=.@%+X%I>5Eb/)-2Hu&t+ʨ=e&PՕn>L08X<¡Bt哶q F?$=E9"?chz/v511 Iŏu<˫r+u8\ר,@M*kw+W'͏~?/,?jYq}E ɫ6d if3Cwe=4l6'[bRYEj6dZ^]qW =b7v5-}' yжmۇ)x|wf:?8up +ZOƭj'KCL$̀Nwp^c,םlu @sboe/lӖw#ƻMvdZW_ί?v)-;Wl[1lۑl%Ŵa/JKv9V}YV~C4()O=hȦ(ч7plɍ*W_=8?!$f\`LWEaA:^`՗mS}YmixovW֝xJu@<]fH H.!w4#,dtc!ݯJz˸%=|9QRfesS_4 B[-,2/̑Gv_ps7.h\G]w7}G~(HOtnEᅇ0pRqa%N)C=d~ ?Զƽ_,_huxo[3cEG85rg- &^NQ?ep@vXvjVso%B(4HE>pSW[/TstpNwe:ϩQ[ڳwk/ rfo%9^MT xN!0k]|e_O~fɧUY< n["un'O>!̪<>ɴkM%*} /yh}]\ }nlG$dy5ߤgё 7-Q3:/q'3s;l6 8jykϞۢ_B> 7:î K;t+/;,0fa:^0=|K"Ekݻ#!,'9-x'k\R*~sAH|QykМ7s`/)rEM5%SEjg[x6zL?!y_'>}x|\< V"=<=y"벨;]HۻB#*?RwuI>+Z~v֩Q_r3JuVxbxm]{vRA΋HA7rxܳPg?wEP+at1)Ns߄6G?߬mxNj!8 -9֌'7re}wp>|Qrxu5Uȸ3^>J~k]Օ=lW Sgrgd>:s+K{wHaK-|W=7}i;=B<81m)W䢞RiSdKsBs`t|hy\G9Txe(fi1D\L:m GsxۓѻJ/Z9xUzѽ 6c^R:8wRK:,WY~ OG7{x1r._a_X!'I^ڻ2!j6g4eױ-9urM.vGweS:A_'hy²nӏtwd4YW@#a>;Ef>OOe3ڝx>:sl'޻ڧ50٧Dz}Ŧ>s}hc9٥$}ٽMu{MrwIS8rB:m%<[% X]pJy5;=7y{rp=9AqFq'On./d_{A z竻s?|ǾgH犾7ع$~5E =ZMvy6Vvu!ox:YACQxpvHۘFtۓw^o z%[^Lq]'_Tw޳[1}*$?w'GO#`H%>l^#N|x`=Î@峙̀_,!W|n;ǟ34=цtΞ>k{v_Մ^q)U(hχZbRa"cwK5N /G >ԩU;5$T:Nt>A&tv'"iNJsT.QC;99=Q]:/t=fݷ^޶6ӓ ټch1^> LсA =`hKD8}w;V r?u72ͷIz0ÁQFy.j|t4_v(f-x!#OjN7 Ζ_m}NLХgu8=JuzӃnM=V$g#/,we}p5..1R,QŪ R&wS$˅[kV똘R;ͪNt:[Y-^GXftmg,RXcX hWU$.콶z[~Rs}Yu3cm},iX-yR|?&K8xR^5E,TM̵0]B<nSa-Ii_e=U3p "M|叇#,@\m%$r՚9 ]c J!%/U%#$ 9D\~X҃!t*`9^2j I 3eX b8/%r!/ R,,~C4E7|,Bv' 4}U5;T j)j\(%Pc&t5/ϩ:D6%SH"|cm`c2ΟW'v];k@7^>J JIs>h9Jtx`CU .b"߻V$>!IJIjs !S*;[qrL|A7=`ij:?f 0O6 |kP PmSLEkƱ v[yAZc(jlnQ-v+Y lTAP_Kq3jņ%V w; i߮ ""{S3iPQ$Jsy:a={/)GjiNi #JO]jMBU1 ׉l_9CM#{fنGu-WmݚHO)ILoͣ ٞĢ!.Vb ?xxq~G 6褁8ImkɒTʠ6!&Ї +9|-,#ةţU3<[Lʙ laWP}m- K @JX 3gVN.-pN*a\jCRr\ ^L0"rf}R-NE6)#tN;fQy.+ Y#kk6T37-UN{.=VԅۣdwxO/y'AFBUF I(nN(pk<2aL&ԕ@-L _pdRĒgEgQ!*3/݌Pg;U'8nP= h` \m))97,%2r(cPKxsr+9 W40yO^e`ѼH*,_BeJvA?d5q3/;֕ϞjJEҮ8IZ9t5S/U zkS.\@kX$>̆b0i#3=a@(mp`IYL/Ef\QLt@9GA.6cvߏxJL XpcJ̇+Y2xkYo]Wka}qL(R0ʠB%֛q`D"xނESx(Nx{nM2$8%ɣIت)x ʮ胈VC5?-Z?\1T>p5m)h5vdå2B9,1A/Gec%y˵R.;h٠,loT>A 栶}vȚ:'Ȫ9eW޶`eCC}TF.ݻ&#kE^o͑ rc__J] h혬^@;jgv~4Gk4Gf̛/IZ<7a|5^td*1OڏOp~15f;vBޕ?^ְG^'ߦݿz\<+m~\jZyj .2 ,Fmg_t}DGcZMf?mṗm0t>w;_).&Qo$,GϡǙ[k0 zJdcUΚs 5GaHL8<`q f9z[XCeԃB" 9 (V$7ܑHZ1Ų5Ż &2UU\Oh{zJcm) Ji*|)=L匚ɡ(d/SUW5MRːD *@RT 5數(^}|:Б΃4o  X$D<R(. >E%Ebh-I#͵ `'xbx^2tsk:b@&UdƌӡIzGqG )\Ru%UUH xP ڍ2H.l(Р菗lEa|ڑפ(o86mNrg-Zx,^mF-p,bԬ"ٱ9 m߄ZU*VHB~*:7>ő.O) {qPA*hØ *kP Yu:?:l華M&2Qp ^oKE)<"ls)AJ&ӂVLo1Zk*sstAR'DN(AA襩gY_7 i|K72;mZ;bׇ$ZHC<㌉ \zVdLIcDB%:Վ ji4Ve*oԣYq*m$ 8+k&^Vh3EQYYUQu$ύG) p=JJhYr%~W(Ck3pixPM,*@1C\ID#Z ;Ϩ&ZqhW )|QɐF Mȇ2}:b|s*Xe ʃm2*aJ L҄u1\?~mh~~| yy#%%)A/fY'*W*3U9c/o"&lm{XL*0KZU\6(-ZK-¡=o=19~j,9C/DxNP1e3JTJ >b A9D'FЂC#6%r.2&GJEB*2!7P.HXOQ)Պ ׆q 8un"Md1E,XQ$*-^NZg5P &5p)P0*deAWqAPX*JZjSp[3c&A,S8sV TC3S}Ih.|,X] ɁbnR$pB̲^d6?Zc #:!`ޘv`J^b`Uܦ똚Q˦ }&* $ Mp0`ti]\PL\(Նr6-B r9DGx+58yUJg"<\`c__waz#㦤isM#;.Nܦn:BƃGE$tAVG(qEC2@AA9a7%6 #0ZU VABjU*bv19'[7vbla(9jf5 -֔rvxwDK&K|BF,d ;āE'bZ1>iAEEpPdIJIT 0cUP-Z* Z%*Pf} !3pMpƄ\Rāw'`^NŖrP7iSu6 69QI#Aea+8I5cAJDJ3D֥sOA)qg7Jn"αX&+*?`a /GEbo<0Cqٶvee;K'WankSml0k[;FStGy}\#TjVթPб6`\l*l28V!tq[()pVLjLduhU d GޱK~+mA‹,K^SMYk ~JM&FmǢ+vn:8 | [ZS28BT@7Tє¢ Y80ڲ?\4#VNh-A5xPTIRWTI `]KdPiʼn8ޗW>m ȁbV´1 JxDdjU\:D&X˞#k-nG3&yT Z>CZ mDv*kYEШlaK=7HG7jP"TȁɜLN Tx%5X 8pɍAP0,KKXώfxckA'sQduR18*`c."l8DE)r]n_ \"x+6b2Ŋ$`#>A\:p.UWK\@X&m5!ǨrW<-j@2D~h9_ccr&b\5$#Dl|0a:vTm6.|AYZhub^q)iXy?Y 8B$LuJ59C/kkxv\s[Y/%)ڑn-U!5N}3 '@ŕmhT+P;ΉOX& [=I?3C7 sEg)!$-)IIf4}AU]fh]On3Tdl2IPb,>+NV =C"ow~j@AGCH%VP(ra <)0VWK&}޷_6 cT*\W-uI0&JbQ6 hW#T"tL\kIFyD\,YoFj3`יBCJzQżiwդ-u:a,u/i[8`+ bhT/dᨩ?W`N-:䞨@2Ĥ" i&{4hraig\}xڬi<~҂ɹ, P0#g^3*R-ei Oګ$~BJ&.sEdh4 v]q&7D)j;WЉr ݳT mC$=C Q^w>27QԒ%੐vylP0)K.j jZB;Hc!RCl%9R,pjJBT"?,**@&`8d2`vԷ&ll&O :k8Wm64w\m*/ 0-NO;EDbHv&/y`qF ^M QK%Ɓ!c|QtqBX!oBf8wa}O ?M]AnoF5 K 1KaERiS2JpPU%Bح¸jg!?=GyIup@}0qh< J}jhh},5Y<_; i~nb[ܺNU#}^c{d=4o#&5ؖS')$"͂j LM⨱ZYYZeS?jT>?EdRSqv!dAd^|N%tFIe$']\ȠQ swQݮzl*)z +Jh]'R{J EtZY$ xUfceF,*yd1DL:ZAפ)*x N%VTFꙪMa*Ŵ ޵M,,(8S O4`̡R-8ʏAzls߭ǭVol\.=ES8͈L).$Y@I {_ t1էɃ8Q5̝!3y9^Y.Y-OQk&f屙(Bj 59Dty\(?)x\7ΔeRzLbSA1a(X)>loe)lM c .&SShCLh W}>r۳/ckR0pYu85~QQ0!46, ^!R%(P_̜:y?|DSdߒP-@cJ'4T0[ 2cB٥![I/ nL) DTJ>Tu$@9+P2 V/_ҕy9lSR>(^&m20Mp4[p_@| ;)]>պ %KR[hMe#VҌNC[/BoףZ|WO ˜9*md3´**!$-P'#9y(JOo7.V۲ +kZtFa ڨBMt8@&BEZ#rxr~Y#EAMy`4"jR霪M=U,+TH:50֑iz3vLFHZm3Kq[\Nԍjꨣ$hh:DՍ!:v:]zQƫ|YZ:CDՒ^EfS&2` 9/:tXB_L3k٪"(QL , 0y0 qaranr{+R╕TꪝP𰔁ʔ$á6㕪!Pc5lIOsjH-V7G R~ F" <-t I[/qݮ.NJ sTvC *sJK<@;qg!ݠVrLx H/5p@T|Hjv %OlZɻ#OJG?"iҦ25 "iZY͚*XmЦeI'˽$vn]f3„Rz3 p&tܕ6E͠cDքF+uI Whr\{m >7EF!!24"!鰺4Q+,2^%޹0v` JSCk7R=i ''xt|P)wmMʛ{TJC &^˃,ꨢ>d5'50IiN%< LYN|⇖Cx(Dϡd xlz9q",K<h-ޒ69& M$PSK <; <^Ub>k٫k ,HFBG%E` x[fF_-ĩ|J88/N2{x[Wz犪ՀoCgׅ4BRa3cV̴l3aCʑ1)]P|tS̎ VKN,, T\)n>YNLGD;x zVd6X2Y^(2[XV!3DNУs%}gWCQKwdcR9HM)՗ArhZ6zPT.ȳb5Lf7} MMC%p%VA ܲ$mP26>\O@MeMjdT`ymRg%/8BmӚ*n%Yn4ZT>=hfҖj%&U]E(iC@\SEe&1pURTYe *b] Srű7x XDIZx)P[H9CmIً:rA=& ]"qXrϟ' KE?)fzhiz.tvfFHeٸߎyO:7Ndq=xm||mCkHU~8_`KsY63M[_;zZbXlJϾqq]zi>23G"77/ukʡsL LV5E9ZB=jy)4M+b;SRq!{x,ȉy!|}eoS3}_V҆ӴH{^"M,US7A _-n=7H۴z\gsDnJ)s&,*yR`FB'i'9]8ߧq9I]A_t0G|o*yCoGIqr7Sm?a*z1OZQyFl[ѭgR;g W|Clg_\ڻKZiwuǞ+э;uvޚn1qV)8;"*zj}槶7ϚL`uu6"DjԶWeʷ0Uq,P2 Ūk."wHa?׆>$,v@Z6ka):%E\*p5Mqʄ7=M& ϓE{HeyT/S6EO-5Q}R,QvXw7ӁÛȝAuL2xO!^;Ce A(H$>{~J,e &Ʃo[lKuKGMħ<*5t%{LyiHsQ`>Xͬ nӇ2g㨔rY0!.6+]TeׅHB,WQR#x,ma :;֌m[t~4N+TS1jby2vuJTTEloGU.ISĮ(XfTLDxamCo \u0 hBB1+%#Ȏ⬤/ZRO |6kBZL7'jt5djg: 2u̩f |t±I(&r!RJߓ=~dkxI[x?rsr_?)b  m$հxdfgZ#2,ɿ-ŇCi#ޜrOXGܽ'47IQ yY60Zlİc!rh-OOeK|x)A鏯6ݶcxE>U%4`e t 뜵ԙ8pjt_ICt>Zz4|&YTxOMϕS1|j4p*fPKA kqNRQ2lщ>g( gjBQBUJTӓVYQgPg@OSh\bhL.eNMDCMD#F|;`{,`p +@NA_PAkJzaϋH4 xs2#t:xP"e{)6Z@ЮAD j%(ZxW8G N>'gɢZODjIzshdEu"|K ~ҝ@4UmMG`2λymҀ{W/X%h9LRjΒ7LZo`@Zz̎?ݍ!Lw +FDAd# ?{߶Fd~«^O6ǒ&)\_v$/-IJNVUw*+EK7#vimFT"L>Բ( sCߍn{]! _J؜$aQz[H]b,`IS)*M[-ɲƋdc6~{ UU@@$pصrL>+س]|*q @ <ٌϏݻ1Zj;(C #k_ ~u@% Cbt r9TVg/z#g Z'LwywFFG_ q5%=,5[EHLψUEl#%W}˼ -ʗk)$kժU>Di[eʂ9\,LZMS,#Cv]Ƙ~(Y\55TH)`=pMNf[}bJ}΄}n%c̟|q9A1kwe&J/b=Ai.VrUX:E.lϱ/W!Z+wvPZ_T:!sh݅B ԋSՇϵyG⺌"ۢs" XGE,*>Iႍ.&}*s*p*8*8ܿ|޶f\w0އ2o?OԨH"2rZ*c-ys3%`J sko0}//A(MR,Aq?g[FtٕcW@g as$ 5Cuf[K^ 0%NQf*ιQػP=ѧ 'у3'yi.n;R:|ز!Hx%mqΏ!?%_dyl5X&@o(L_7b`,]rEnH7i8zFMH/9t2q=I@mIrLxQ`(!"]j%P^ _/jw0 )|ۮnq:a?6|BNd/\矵KGkr=_-Үw.-TOy+3ЮjmG==} !k5w;,zl s$=l UD3 "\̓S+{\Lv yK]DAb#°oikO%NI˰ ~]`u9ms AmD^KdN򷡕knDZ=bVi&!pdPbMG=J9ۨ(Iml'RF(g}oA\$P^;l}*V@=rD{ fXIIH8TU 4rOkfMqk.Uo@\׃@IJ!hpے&Stl zbK7o&]&6z}-8icI~xÈT\9Wף\sCI?Vg>= 7.pZKDɏq"2,e7=n<\MV/lVG+0֑( gJ*UwK1T4q nT0_?W׽s\PN-ZU/ri..z)c]o ⷖRRR.Re7n<'9)G~et$.J5iB'%BݭU bq[cvZ̥`T>n֣(jdT(:im/|S!P-Vw{ҩRz(KOܭ5*EikPN%CiKÊx@(uNIuZaD%ujn;1)c1&+X~PIjd78֌71̿;HSzXGTvKzqKJrG5>[5Yψ?owXKt,HW2pBE;aňl pr8? cZ/ug_3I qUVRN&Tʩjp4duPyv 3oRc: 0KS|_kpbsb?JR\dA&L"'){Y?6p4hflvp~;k D FL-*&k|9pqnX˩nޕm*}&XWR 8d@=Q(F +LRXI+*?898+_[3^ ]/UE 0V&CQFl7ACkA_IL_|?!fj_Z =7*{p!] r!9lq$qOSG,'bO.pEMW[t5}`4ƈi2<KEpS>NK}u3vTE|҈vYي mc)55ۡl]֟A:~c\ײ|-HApཎb7̱j]&C{ 6FS Lĺv s267.Iĩދ ϊNw/4}KnJnP&M5J+-I'Vŷ-ʷ0ՠ=H#^+)\8ARώA v2P3PZr°^_Pٚeo<b$Ip`d8wҋo9tj;1c@ߺLNި[}O#oD F~m/?åS~-}ƊHRL)4@%z8 eQkl(|m7[?X撝﫯yncbƮ|U*="I@p*TuM@ Ў6Am|{b -&-yIFL ."50NTJ`KCV4snT>omG}| 0==w?]  r%VT;1 hQlsŸ.~,/ˢ=_d~x ˻wqaC AϘJ?CQ ,EZ m3 ݫěw>'%FDp tkZÆ"|W+Bw2]^n$lFrֳ )6X_g!cAzb+c2ӆk4=U35<\TЀN۬Mu oKtV5냳s1ث_dƵҷ/Gb5Aǂbvzՠ1 ڿ=F(%zaNk"N6p~Ѻk^]8d#|ԅ\UXsl;Q 6UA2rɐ**f߻k]V>*|3]!<\M$MQ|GW9-VY#iJRk sDpQ*4^EauQ L'=h ШJd c倞@1LpȗK0lK?c9uiC;^ݸ?nk _RϾ̗ák?Fm˧Ae^mۯIrL_<ܫ*H?uh˗o:^<u;nm@Qi,"I"vk{kI؈j:?4z.qajݤ߁vHD=U pt9ipl[spyEVL z%P#}kLQ?2P9$!`'OuHU D#ltI3rӄ(!f`zhP>m˛hV(:wr@o9_BB.* Mud +9B bs~-x=E՚Qt+ä́BS̈́]Ym[f!5QM)7{T(Is7"\o2}ƄYؾy4:Sb݁K[c^jhoJ>r;=>w:ݾ9rV,Ui,*&Ïߏy$#-7 f'}#!Qpvրnbd!%98AMO-4yG{vvwϤmq1Y" m/U!-5)0nRK@=+^+ D.NƝ]. XNMG_"_t5Gw½)y㒌z {d{.*m0ܪuh 8 +iSaWt]T Q{.t8NE?NO'Ps%$#=)Գ$!fSǶ lW]5 U Q<{qm#uF?j&V"'g)5P ѹ46L RJnUy]s7wIlJ-Tb/|zI˿nտ>o%P+f8pܗɩR.wְEű:qBkfwWgmȻEo3砪1/aI5BxO}xpix]p,.MU489)~P.%I ̚\̾`町0>0>/yuw]i:E#+֧.ެc,Jv*u ?"r<& cXry~D÷+.ma".2x2VF$nG:?. K,. a 9ԩǪD4+''Z.I0ȥkibUF=(Ȥw]ƸЉTԣ^l0thFiOZY+#r%IJٷZ$b7;"܈,ta3N۳:0U'͚l}iؒѰ>g`) BZd=%!K=/,^/#oj @[,9ZvTLƽpɶDY.m\!_i+,er: -cKc#"vp;9 I:fMSN`wF;=CKzrx4uȄgIHmx[0c=[B4AŅ&6N"]>T ݭZuM ~SPVTv%̇]s{ _C$]ЫQ5[#C$foڔ@!JFֽXz"!mnx]kh*Ԝ=7+&-]w0KlxD'ΦE@R;g`jr+ RuFw4> s5dG ,K$EB_]MENJ܍V¥Aeip~{m{kuDmN:e\bxQ*Vz~Q UMI.f5I;vsL^6=|q^z0GW٪&™{6Z,}U~(zY]ZdǓ&g{X\Șb3!wJLkCrO[ʹ2ܤU"'i1}({~yyv|1: vnBgε޽ DH"đx.MϳqK+ Kq)uˁmU` p 66 &tPek)I$qd 0?k vՁ߫3\$DޔWK.j{bU$oџ7 ~8pitz<5.L(rҫ>=Ҿ$k=/p\ 0*X/\یA*ba9+Ӓ(U57NJ+O4#2Xߥ<ܾѕaOl+Xu*r<- 8]Kö`#5JK"& ?1YMيWﳓfnrD&qNU\`T5'%W!o[cO:c'`[r4E?lӵ8GuM_ȩ6ֲTp Y{":\jr3V'øHؔӺn`y`$ ?ׇaEץ;\ǂs+_]{L>}%l[X{9ADX, )H슧s;;lN~1D|b=Wl}lWK]sх=Uh/[ : )GcbC75{o p_] p/xҥt( DѺtHaKhN5QNZoJ[hś-~]$jwr:\0wvJ:aoouzU%ͲU3)zQ6Rчh6v/4ry[Tk6FN{gxX9NXd= Bҽ/"؆DfI^+H9^(X3G'=:./{U*SA spաZPu YS{!%3, V%M Έت{[Lޢbca_%Bϔ-fK;{1\.,-]7\r}=F1_|y+~z QHq4/i'/P6upb<3'VjsZ v6Jk}Dxgn<6`9;8^av>"Y6P Ro<&DKE*%#>KZUMxDϳc}=}o i&ZV b![REAemwQ󮴠u+z?ݼ|jKd7W:0ca"e|a{T%?chWTvx^m-W^ᄍ?j|)vc _gϻ|e`AuktDeu3kGt|s0# ~۷W:  :{0,}8wmym 1^̫qkÃe@?yu;S:wǏvm {sLϫ+Hrg%)i/xf 7 6ܾYq:+)xv,M}‮sV;|.?_zR'|-x s50b3-f/>oXrnv \xUy[/Ͷ'~nǽG <ڑI[-;y;fӭ-ߟ=n<j~Űgqݞ5VW6z|Eꢇ>vHY{Hڙs2VNjxtYqŕ߾_7osxOOGg~9C+O~H(j$h—?t7w~#+(wqEne^Y>u՟ |[T6D/f/h8fn1>̜ Mkﳽ%=AyX 7+t0Ѝᮃo.>oՉe.8'ըznpM'|ӼS<+- _ڞ6|~=`lh*ژ=ّ!VG>_d}fPh;OJ|@ ~o<>YZ֯^EA[xhslCs/|joahq6f/ۈ7.P 8sx<IG`G-Xdv5;ɮ#5ЯmmbDcpXޏܞ\`gr!-Erw!`= ̓u6tVD+T9gGbëhrkϝ#]&oȐ^g.')ǷvFr'nF'ggùcY bIdEG+aelƉ{9rt _;AYӹIIC.uͽ$0?X+[+kE -ֹ93YzG͝/ܺ1U[|=^P,o%tc136&Z0VT|="QqYxM;.H|ۆ%;"vAʃbԚ5u#9M]VJ٪J#7Rniu1tјjRv&acܕs](ENu9!K Z#otyۥp9uLil.ST*m+ |(r$JES4Zye'zrA+KƥЬ&_:8W0Jj*z1]>b@WHW5{oT ;&؋;Bu/\ v 6yQ;{gN^Y4cM42$ #){lX^+T&8+OH4ߡ;&~6z8x V ^΅CvT%k1uơcQy2!o^VL>S6(kcҲKs#ձI\QmܵSƎTvEe vi%s .i6l`QV vķBlRkB7ۘןi-|tQc݁`ܵr؟ zىQSM"4s\P} .p(wѪz(X#QQIpEA pL{#gn u>:4!tN˽ [ID[Ȅ,[%,2`5W2nDÃj w5fg}ذ ;`,MU@ROA2O닱AdUJNZ6>* 5;u2*{PLl>JtJ$Ytr SМڤQelx4JՏExAꝎDA|i5\4զz2B\ieʖ%/d.rKb0Fdo.XVN)S)fM[:[H`6mZhUK#!~u}EOγm{z)iD~ UT  2+[74X*9dzDF8Wa}!iu]L 2,PR,W5& HFaoՒr+*1Fy{d8u]xiuV[3h6i>iV˻G0ףu|77A.{_3i=r*V2iI-2{zy\i\D&sh^4ձ(,ksYsޟv}~}}77dU][ 4ѿr{(-m p [-=dq][~FF%I/=JF9Gk>IEk +6φɼI6۪m%jw۳{ ]nKnN|ۈG7Ǖj7"ۋ0Tnޟx6%0|5W{ڟfD\Zs$z_;Sݨ~MH.!ywz]/ļ4 }lrPHl)$}m|OoRtAg8_rHj՗}L^?ߪQ9Mr6ߟʝVgkn!G듄[g; [6Ç93}$CBГ䱣ސKv=!h_]")%i]nۀq {A^?_V] /CFԌ_o -od}$=gxI_?T/fɍG3w<Ⱦ=]{n=#~FW%iuXijt7U*n2ctזuSYNJUkyqُ$=76{7djӱ~ڏ}rնx ʂ9z=RjT1PhIkH-.I(_VڜGiZwvP؇L[G~]uҹǮDV_T`IJ&&6QUjꮰפz)v9RSy#g k7=X e)o]ܚ'"(PjQƮM)e],k]jM~h_5M,f8=+.u/lU,2Ęqf[b0:5gJݘ3::OͺOJh-tM9{+zxaz^pr٘s* /,?^V8cg.iOR -$N؂(34X.3"msfIq I&֪Jd׎gw⺫c1wkYks5.M*#dD׆y6_\l2>Hg&+l5!xe0 gyKn|ܝ/I=a \\?˴uc̥)crpSk"2h*ŊAWir_^ޖ^Gi&?~0t9[Jz@H4OfS?NNn6-QE)n e3d[b&g6*TrĨrJCd5@.xλGKuNKu@Zɓb;wzIJ'0=Iyq4,dj7\^3jrb"jb 0HHhGD0^`/5|`=g $ucZK8dh3=(P9R[lm&|e 16{q4cI7GV Cj`-/46::bD ra@LƆfRk4VHR_޿)q8NWb3`]2ͧkc^kԳe!^'ڂoSct`)1uÓ[jg J o H#WME\m]o^~=ޝb+F#w@JV)Ȫ\ÎUT!]_!* o!3F1)t1D)N^&4kw:H;u|ϘzL&)`+@ZF\[Tk*`1p&;do(ouu.ҭ8j{*5*稣ufd*(.]%{k!x 1oth-}?q@j eolƾLO$ r1 ص8w5u c:l'+SUPD6J8uzh]8 x K p%, #sJ?kmҘ1gOXJs5:JUZ_aB;/,?@bHަ#tq/sלKj%V/IGL (/uV]ԂWh0G.+ː1Nc\u*f X717|nʭ:Ik'G<gFǽGqO_]Ma59@k=aT6 x"":Vbl[{d̆3]@hwCxFC a s![[ lcm0#mC_3=~ڼ-t79Yn7\];7풜8Tz8@2Ё :}뷿eлsI؇yp]{|Jda1TSHH' f{ "У+l{l1ET$MJpY{R D.ixC%N}ll+B}h n'~0j[Sd=2\7=uƱ.ɸ=g* %U5Я簜ŬvA)/@GXzy^s\<5'p\80HUst"ݪonoTAHnӧI nF::RYQg႕Bqs6MӚEV>ZCNBC בb \5¹#(X@nԱZnU =|eP@\.;6VL^[ĽK/LՖ1ҨVZui-rjK#S} 6a\I`mq=W 7/+;"Ý]3<6gswo5K 26;NdMRTIZQ1jƋ,,ߛ?rt35SɽNhEPl޹Tp W)f^cώ;nY_^Mͷީ+yxAr. /P(S{ުjX!FiWT݈p8 k"tcy>`NOX~?l-Ի(\5x*Q&-%l6;@C;Tt}Wsx@:]RqKj1{d>Y C/r໋`}p-6eɈq@Z%"ru9^l#^ԓ{ԭU&*JZ" FH U7b.iu-x-`V|kv~یiQ#+yʆZՆ.@η[ L{Z.1%_٦_O0\ć0xѶpz32J%DS׉:4xӬLX` @Na@DƥF\L@7;{ VEt ©O;[骺TnYM PR~9(`c;<Â]3/WNbԌT bBҒ(b]$8'9g1V7@$bp1b(z&3 "P % )I8<Q Qf[jxEuī'8qclQ/@]B!KҐ*mb`xwH\R}} FnuzlJ1Ÿɓس՚J6`aX8cHb\V| 79,M[~ZrmrVwiu>l}*n1bH sDt]c#BIeo5OZRƃ:a/wcg=;(UT~fݳӛ7`RG4&}v Qvknx"bt#HE(R`#ei ,}zM, plGdB,N1 =TRĒ(qu/]@R)Da!`xCɭL_cUiMAKo ~FQ3 3&2Ac1Իw3}Óe35=.ܖuIN(mB֜=("g$a:welq6=H Lɵ)5IΏQY=([ r\3$`O7E 8"")6VD f dMcJD,&X"\u5׃(`d5gA:j*BXꒀm @\R=IQrhiuV19:OwZ8dhi.9;Y4k$(57lr7ߵ 612 e;~G0v*J, Y;Ԅ@ yY7ྒRӍ~0R T Qo6Fh4 ѳ\n!\v&)xvr}1f1*@`lR\L6 gG A",11U<_q g_b#}<6wZ Tz.@KY. tkajswʹi'e} ]7{hHd(N>͵X$ Dbwd2 )m:8'p{{1">{I1`.9HS͈f}vf0_|?^CY'5Hgf1b= Tn$ d|Iڲ!RGm5wJP++9&Jh J XMD~@xy+ݨ|,L=ew? T T}6ZfK\j]['~d)%D+=?aޞ ;,B&[)%FB'q6Z+?Wvs dd%m7@ix1/0aʪ>~^|F-]O NcT\VpTF8OEG'~?ZƻxJ%l"ӔqF.gs+krO~hfXjJg*;4{G{f:$?lky$%ҜI v\Ķ*?j얯!eNwPZ tɈꪢj>%"]fԫXI`dƙ<#U"O,Qb}UQ {`ji1G.߸)zxkM#d"i^Lh!#,3Y)z;ZӼxޟiT! L* 㛀@yJ_Lh*.*7b<94^2kT~^ ,3#hto 2iCDrp<+eZ6X|uB_o c`m1ͬ<'HrA8k @.ez1&+w姵hxUN*Ꝧׁv#He6gaîEk}s.Ovu>*$RPD^&[F! gcՆ&# =˱Q]XIu*)ffBԎpieB õ )z!ڥk}^setarF@"JYw1=R~&ى5G 9N*im*j8V1c9pԯvi'kvoPzb4xÏfۿ?O oeM.'uYna9W?|ٝ^i? ߊi`,}N{X#B!OOh .teq\e~iq3lx=& y@̟KU:3ʺKp$7WG}rhv M9{mwX*}>[z07O _2CP<0`-d5cwıCfOA2u=g0<}:|',89h$r$$/ciMƜS,;>t|Ϣ9(q wvI )M%I߆jCB{;Lw|9|޽iv;}P'B7s 30/|=(먣3xK 1: ysVچ`zw% m~fX z1:"k^_O2]._BXvp [HK|}J&ю$OOv}?>*as GځWSiN~*Ϳ~ԝ Zm7oЍ8 Vkt/e :yjЬߗ#Qp؝݀[*C{^w7۞wfL6ؙz*ہs͍Xs9e#JӐuڻ^k]=Sr=<8nɟXכY ǫ)pJbJ=ovK4'Wս?;E|u?(ogx;6#mS߼ٟ)5O5m%"5!}Kޙze\_M5q~-~*\ LӘ=-,4\_w,}@ZfFp#"߅-w<śʦa:4n}f({2\UȉۡRڏ]l_o?:0߬*y/F;䱨 |$x7/l۾98 9zBwNHI@e~i; ʵ[w,Su ]BwkInj=<|Wu#Փg/< {Ne7's u&Hq!urawTЌjsxFC&w;9-\ݶ/uC[Po'ưtZ*D̍ -z%Ԩ̽m7;=e|OcCٵu<~?z4`eX&ʫ l!P//__]A^kk:wʩ]Μ?<\и2hV[D)PPWswæ{=7'W @jWA6\R#)C&j\V:JgY-ा]]j[wRu۬n_N^~ $z.?81Hݍ!Sݺ=&ȇ4YÜz/ ,-7l~ 'o4ЪvwɦYԅ\{nd>f$NZ{G ?t9,ѱL֥3/}H]:ھ7x߈/ey>̷r@ L.h~J-nv7H^v|,U`u7I`({92)wH{9ө'"p79laIhi'X)eaL[-Uѣ%4tl6@vWr=QDsډܾ11tL{QLJQq]~H!$,%YyMo9Kqt y,RXUP2HlJ'!lҼq׭%4łGd % /%eڹ&xU!-CͯcQ>?eϿDCc9:ۺؽ zXqruPGIQpl~blu[Ǖ. 욃 ƪnFsmdk9(JZG茇ĠL ,,kbCWX ಶ%춃Q`GgҴ./Dk kJB@qHb)2C.zȜOXMh0&h%mK|83VfRD#PP5{id.ȠLȒ֐GX6 nQ*uny(5&=MJMD`j , 9ZHyLG|&<ڙܣ߉ir?zlQFk(ƲhR)0Z]1۪JfB0!Ս݁_\;"*HiVRE^uLUV.Yj+[xkw.ϙʳ-xE$pDJ DBbSl7BZN.{3硅 E ƵҡE 9{jm&k. 㡟cz@'9&-;lIruhzܑ3c ƞ5x}]vw?Z6ߋAҏ`3 hXgCQ؄a2!7F\hoz׿M~z8gτXamMT,Lc)) 2'etNjʕbc!buzO46s\&o{psa\TWup\EDx0U3vFq%d)DJ:iG D/F)0Ί+''\l|iN j%Br|9F@Np Y$\)ɘ qTo w:K܆@dLͰ+ׇ4>hYIb\(N99R}.LOϗ`2Ҍ֎VZD`FѠS啯"X)CfF{72b\NWHfBtֆm͍ aaWSKca,"l (LgD^н>-樸'vI< sXe&܉1{!jRj'Y+[SH''n@AZyblxs"Gʝ;atVjY<*W }!k۰4YP|\LĘj5x8kPU2i4"I56wX^k"f-\]Zۑ|% ,B)U_mQ iƘUralUF\y_G` @c'1G;v9T\j#.g/x j]w)|xs(>z/3S` ; ai Ud_f̱_0z]&Fx"pM JA2׏\@1x{Ar*E,b4Z"DJ8HNJ?x7^D+q83LTuʪW*TFE ;NU'sfArfsTL$i-ȱ,BexУSkc,Vxa돷?)ʽ U8'dH,7ȍ94^]YV F'83<ӵD@~fϷC&nN 3TsY$ĵB8yr Qv;]͟M[q-"kixIEfݡ\^~6;h"UB8]wzKhkb!W0Pʤ9,] 3 HNNvS@vBM W_G ˀ gR oK/ivuIm9Lcr*c&e,}2?4ftPq'ӟ3By;l_{\+ٷW61g{A<aRߤMDJ x; xN)dT Zi\tLp%Av>E^kz9XP'O?HiMS[1SL~m~:lOᾳ+`F8]]:YၝE6FBfAB>` wU`7G9]?yf2rm>qzSGytUi2jwk9ÌuU02B7vRc^ 66 Xlk[N?+-ZK^dPX/#O$YI, [, Wٳm0JYO%!ЙX5q/_*6ϑEֻ^W{:,3,BVI VN&! K91DQfIZ5kF5i#(&S- P5)T("*N2 R[fBwe)׵:Q 7;{ k7Ty8=߲q D4hcZE"@q"Ġ Feh" ft^GK|n,ؓBtSUVв2nDx" D6 Cp,6GI+l*$$'`,l/ 0˵qJJ`c`&3ggh] o\eJ[-=0+OpU"d)Jcmf+}-1(7,?l7o0Jv"Mtr^ݩww-}S'o"i- ͱp"i#oLE0e1S<^1XCVp"FnF>B"anGWZXN5P@ Zi,3G<,_23B$ Æ[XH)$[b#j< VVv簿Un{qK;^fj#*6T4&lW@J 9e5wS58B)J4tUjtmGmy-^`dL 6c&i\J&:H4j{l1*;</rI &iZ|:ЫwYO鶏/O1Snֿj8$jzyH \::r L_N skiB9owiVWsv}xz)l\͎Q%oy68'*Dab%byڣTJx 3Q> Eal -d1{{=R~]FPʾ ১i~5c jL+=uݭ VKKJb").q5yÁA@ojf)@^b/켉d4HLgj[ϧnHű[K۫3f-MBFrL{`b}) |zFёe#,4,j?e5)C Ac[^^G<Xy@D}ɀpg>*,Dؔp09g1oy`bl`b((ӢȨ$Ձ[9;`pG96#svmr)4Βf[2˜ZȒb2)Qda(5WChɓ*¾R@/ӥ; P^3' 7 kB_#Be< 8zf>^CݡFTeY1hFW*"*jؐ=s 7V2D׸+3gƬV*`95RE@Lc)ŢuEjDŽ7V >R5]-}%|(H,$+SµFdLG2ZmTV Wlz?^6|&8%;jE%?B ' $c@ZZ$uY$%Ѕ*P* E, %<7e,ߤ֎øSEf@@]5iNOJICxi57IC,ZS}0n7f ,,?*pP0HƳ Dh, *4R8Mfmj2<%6hiuX?կž`Adh~}*8$[E *1J9SȲY:`+@lYZ.a*4SI8fW^osN|~ɧDw`K#Drv*%r`G!‘B7b#W4XY}u\p˯,mGJL40N%e:yϤBB6 ɢGIC!$ 'G'K}X|f*L@#D㡥W'hBU,ʈß{%ģ"WG` z9Q,0v>Ao89z6K5|i 9W<At\o ⱉXIHSD/<>ntBiGhY\E.kv* *&Q=:e9~a~y`Pmf=>,'S\d.?1 tJt4ͪ_@-0n!r1Abr =B$u jX{(G}-ȾRrf i*xBp2( c LMN~S}{8Ϛ&"H` (@Y9|ctDv$E`)a5Hc.Ua%H>6zLI[1 !2R($5=|4;ti9wx?=7y2t'u5<"l"P׷R,q!8>_N,u'Ǧ,|'PQAFyD報*qGd> "`˪ͮh1{ISM> lR*S7@Hl=_㄃PF)aD)=ɴr| eh uL>Z**:xbeYp< %sW^әq_~?~BaLKuHes]Yݒ4U9#"$laSq{@վPl37q^S,EI},Ѭ4$l=MQpY$ A!W h^oON7lL!% ])Y)9*2nI&̒8M g._Rd6 rD=d;2?_!PN^dDhoLZra9swk`}y YćJ4C9Jn&ES(ЌKB(N#ZK#A{O/EnF.8,¸ W1Aئ1X1ȌQ'7T, 㜟UcK$t"{}wY9NIuaBdUI(DYF%$F,gIߵL[Th=[M~="(!%ZŁVl0 )= +6d;69oM_t-yN=3u}e4I21ÅN@y/Zf(Y位(VYQD ɞw*r2"M)X!:4d4 =b?ĺYK}0bLKEromE[oMqihvA( eV4PBL\m;eϿ`Y.[ko(zXI\ O,^d) DKѩ}0LVfRG%oFOœZʅa2TX6 4MZfW9uKz8YqDX1Ybpr`.;e(GKcQ 1+_.Qt 3E )M rWT[p) I&V0wtY3cBrqŐeVNW"1ͥ7 r\:$!ޠMYz|l6& bxNJn<1*E&)S֎geJerv찘9 xoq d~iⴾ;(Nc.OkqL=442cI"Uo3(U)BZpcldaxDrN;Tuw9;}f{gh/ZrC|D 쩊$1:(D2Suv(u#{_dO0P.QgxѼ6{&\efQ:1K!S$Yr%-UMLG.~Z:)ŷM["!M?ݑS&Zx)ø+[e1[W Rw3&@Ƈ8wM/H,[h3D UbU輠WŹ`47aw37_l^&L5]'L#hX M? hkmPՊm*" e<9HOPo^r "|tT,*cFjPYHc"+Lui:,=,iބnEëy6U"UP{y8|TH&ft"FoWS J/ cargo-0.91.0/benches/workspaces/tikv.tgz000064400000000000000000001266741046102023000163210ustar 00000000000000tikv.tarYZr&a/)yvmnØl^C;k+Kf>Hb'H{*UϷwo߿~z~lV_]-a)!?F1 %) ;owzߧ>c)z6$eƌI*BB2'DDqL,sz/V;v7_ju xZ^aÓ?O0{WKmjۯe—j1gD%_:3޾-dѳӿ<=/y-_gl~$_'\Ǿ&Nvxi߮v]m|~M6o<k6\m竗ʅtϟQ̽ifRkx|e4 b ow<Ʉ87'IH"@#QzgZ{"30/?3Z}éTz— Eg~@uaCXo;9babW['JjeO?pvϳ8ڙzl<~Ҏ^+W.^~O~}n;5BOKHEhO뽙 :bi3~̗|3zHF6$nAoO#@fw_7留l߬6-UpϽE Tx ,~_o 7hÉlP~W-vf_vkNQ/~f0 |^mv=[9kvrA߶;w 8z>hFf+Y),0{)VS;ov>_hZݫor򺛿UCN|l`b_.FWBUæg_0['.+Λ6T.YYwA]5ÚRqQ\|gOi?D_m1{b ɰAusj0 7yzUTmd% T-鍪=߹MLG?FCg߄=@:)NQ+N2& >n#Ѹ3׭z_Ǐb\ͷe:_upl wo]"L25 ݠW*Bimi?N&JXdH[zBA*ļ %GsC[q_h}2~]me6pnf?* |;jk"p~ p}E/@Xm,vx*Ll nۿyR%MSw/X7+lx޼t ROG,rv M׋r:LiGQ1N75G|8v|!׼wUabk M\Fs_xד~5C$ƨ VпKY)ꩃ71]wx~y_U, c#{쇷?!#G-Al}mY^/AN4?6?OkFBVy$r,;SX:j+g?Wm*0TA3BEj[.Y`p#w.{ ` \>g]fnHklcmy3f~"~4ؘ`rU.2jrWWnYF?o3*/|ɷ'ڞ%ty¥}mgx2|uU7>vy.-{X;,˃O+7Ai )b8,wGI%Orz=8Wy7lOɱu?iGe}j9?%3NseG澍.= ނoTX?EEO$_ϋ/#fߪ$*-Di伿H7\.a?DRϧyVY_tO/^W?J跓zeiOxY\ikJdkSxvbjc%M^+}ro;nvpGX v]﷯= mܿ|'<3_Qݹcu iredlCo*:zx(q޲vX;j5ٹjJZG7@jwR`ge;v}dQ[e/s@$>Sݬף)cd.WzfH_@gtco_|[ dOWEroUoʁ9[˧OWs3U'91uS1˿d)u=d$ñ(Ar!?J]?;s32+6yY#ߦ#'BA2t\-R&K7p\mv'\r:_~3a>487RR=rb-ݪJ0p|ծ~1[ՏߞSGokgJsU~BeXY/Dg8/]&揷Ƀ_DPh{Èg 8ۡ!1'O0Xlz/ʏN:5/(!~E?PY%-w^XBy{8ApRj޾-d_x֛]- cnbTj RXsAH&bZ;F8'5Or?ߏH^j#ˇoRwwFnQ&j\^g4i|U`Ϙ߶CiO9 7t߳ NӫݰA<7zPx_r{P8sVG#AdXqB w:h:G+=@z0O\Sݘ@=@24YhY?o.ܾ-+հ"FOr0r4f/OQD\#_Xm޲i*n7HQ-FJ'60V%pME/q:[AkB_9/d{i]Q gc0k^ @Ǵ>J9F%X8#G~R8Q3јj~(?X)\H>G)Ei#:EŬYUjA`9GZG4~Ԯ2/s2 8m }5WQ*\(=zni݃?F⹀+r{XTTFڈHT#{ow/7G?3ۅ~ Em*U$Yq܏EC[" S82pH9μe1ԭr 17􉽛J2HR䚇'&s]'-,q^rkdX7c/] X_f6n-1lH;tuaf .Ao=IÉ &ݼީV"x]b[[ʀ 8L o'~~OY(y ddcŗɍb1+hI3Z~opvy4TZЩ*9qxTA'i3 gVmɤ___gO6>73kȫ&E7{߽kqfeD*Fu}gEX.-;\?[z ˮC~(bO߹80U^z}_W\ZHlsUyjQ̏naU69.\s/{9|1O8s SXd/ROia \Ť\!Qڌͪ8lA ڤS,yGH:YE ޮ.(ܴ8{JߢOs#pΟܹ-L[=v~+)d_e &_8Wr=W9o7 1esT?l0*Q=ߖZgz 9I^2yWUszO</l> _Jә _o0?\-F:2~;!r8pή|6HdTD$D0e%Q>rRQ5X4G?8j׻J[K7͵Ézum1{~Xz E*/!(3ò${}WƟnV`z[͍u]ܐOh9wЌKdc~ԫ)]ˑQ4Rou"J<w\|8OV{9diA?cYcE$*rĥH̽ =1D'j}"WJnVj>%C6>W jPZجd9KKx$b P<w]ڔAZBW V_s`UٵS/&J|u=x* SA͂++Bsq: Y]מviyn 岾õ;aSCǙdg.߀D2r큼?s9?>$J&owx3OXL@^X^\zXF7CU8&t~87SZ->)߇gqHʩAZ}sE0qo{[:v\?˯uzg>CLeՙuOOQ=sJR\Sh=bS߉H?٘_jZ?VRV#kH]5TmL?_B;U3tʹpz}-({O" 폧b c,x4{$ш5v~] ^$xmN>؝MhA`U(Fy0);z6dʦ!5 àyphw+?]CR؟وm ŗ0f4/k_#{ݠXC'F5b.W C+U@ԓg`~3%*70JdXFmxf wFG?`&i`6[M0Eys uXsdT7LuCY=U—ƴ2OWO75{cݵ5i0:+>d<=.0Z8ΏCa8+GKܗZλ"5d&O1\Y ?~1*vĕ/o+ h^'KP8ћVr-n7pu'?O{/&H#? g_&%g0P [|ŠNvYyVG*OqEpޯcN?` sO2QR|?lRζZ\·Eok:pͯ>D<21+/#+ldX_~lX`]~MT#û?v؞ǟJ*jDFc1 ߅ Cn{^c/c#t ۵*Kx x =^O^paU{v.m>I'^w}󡓏,^yv Ƿtu?XzzgOhyEOY9(; M]|1f-7:^Nm7Yon5jL2,4+昕e"qĮdJu?a.xBֶCeDsˌ rsJf|KR/fU sN]Е{^*xy 6e:K\4^.^yOiyjUOG4_ m[?o!ԢCbJqre1^_+z wOUS/tUxJoݧi_>ui߾zV?Oe;"Dl}vxnu#?oVO`Sdz/he|˷,,Ms~3ԟ'@'$ {$3=7^g4DtQ/zizGn>~Š\^^wZsP'#kO_R6N{f74p]L܃ká1_,t+8;uO3ۅxms͕4 )Zwf_n`U}ݬc`s=xW S,IA&(n,cuaJ 6%Xxh/dR+7  tt}iZMb*KL[ZlNlRMa@io5 _FZMe#KR ;ڭ]e`+'DQ-N+>V l 3/hT(2<=0sH{ ո;s]| G#pH1!oze~._2Uexzrxx,)$8~FK-P7̄- ,H1㏃o4=~R!BGPt NCl)3=^)@XogCĭ2]l:7B;۲/i>>rKP$'ifn_[/wFʚ>@vZwzjFΩNd!csq9M7]nuU²*DV~gMU9oQcϻ1?oK;Uyg)?zߠJBX[V%d 43D/'9(={g[wl 5%o?+펥T3C1//-WYH& +ބS "*Qӛo$eiD~:m[]e^$lQL}"ԯ:UbKn.]2[)X0 g/K?ΨlIUS:.gppjM ^|(k׳gm:t.dںléqqOg]/r'r*zѾt\mN}9-WӸ䚝~}n,ntZ lմmn5[2Hݦ!= Skk[ ΖtZByx:ӏ}skJu! HҾTO`["Ӵ1{JLf0kt}0 cZd .4)kg`b.ojӫN%y>[K۠- dfw6.lPl0Ptv ("$ݹ;4znCo5taj=N#PG]N&E}~S Z$mhxI{9w~uhR_;O }̫~~-;HؿA5>q|n03sԊ"-vl]VY{bȭUr7_%ZO|7P:v*ڠcv Cy!G1-o/z4DmG< q8Gx4<]>Zڵ|-o܇3mWܙyn?rrT]T ;"mGd0JH:r?Rb/ޣIKHlxRXc+yq6>ӖǥڵZiR^r|>KB6|) {Y%&jNj[թR^ _rn01ŵyY˘3/kCR^a?uolsC!mw{i4LO'|pbRzYvowO_| h_8_VOp >Lxe:w}4V}5 l~Y8W|з.3粷|A| Vȗ,ZT>~17ɼx$s5]q~ nӅ_^d'ڹ GostZOz4Cwom},@ּm=n:e5vJ[aF~;wÁ=2,*27LnIlu*%T-H#[sQo.d7 R {dwR>d߯_~3a^4p,xÖg\w&݀BN ;=f?w,>x]u5+!{sG8szSцgBϝϝ[vgv=+Ś}=8Ϊsa) a#j[kw僚mܿs4l[pl-l;g!D9Iܦϭi$}&LO;?d:][A".;t?VAi4OF`V1<߅5lk4/xpb9w$+>XoPY` swݭֻ3Փ9|T4B*(;{P%N9xv~-_Q_Q w7cmy?W=Tby]N\{?Mfsͻ\[rGﯛ_[{!l/ޜk{Al]+3Sk6̾__y 9[dj]`Xba{XjܐAsJwg{xλdx;-|b{KV]Ք̗ޕ~jg VgUs.ՅRJ:!q[Ѝ0)Pqs.5ed_:0_@$4edۛCzd]R? GBDtASz '| fX F?w`OTf 87?bA%w_exo7&eN懴ۋ2꾆Z~vS-?Xˏ4ǴlVS-a8OU ݶtܖ9^ݚNI oCjx4G?|,aSkis9s"0FnO0yzY7FpԹoq۷j c|녱wszwj9iFbNkXmҌZ_˱("Q+7gkT"0{{n^>p%Nژ.SS9+%CWQ?LL }qu猶ilyNKf'kz]]&;8{)81Zږx7Gn:tڲEE4Ӆ69uij[YWِmzg{*?fRҪN;Ӣv|$tK9[rv:8u.#'Ql_c_ZP3CL/~]62yb^(?~E ۂdSՅpOaҘEG\cK%G!$p.XW?SJov7) f*=T*5rkp7M;yt}щR \! F J\\(N7C8XS?fe^Rx.^ݭBќ)rRo33jọ ScEGn?S !/ 6<S{|=^cׅ4*%C+{ zTH-_ Ǝr췣F lTKOɴqƿ`nPiw^|D|&3x>sh`& wF3ŵZhE*&ʪXEBɸ Tk05P|zz~-ϟ~k{9 "A,0@aPOq%qt5&;#ho 2ː›㋁_:#.Mc!\ʓH K%qK:tjem5^ȡ=aD1EB;SjaIRRg nCD4(eV0*W8,^M0υ|%"ΉJ9ʢMS 0IXDZ$~D^(j^S{Qբ𮫥SWVӌ|I`Iu|@h *}"] !f16i0 411lt,m$׍dulCU!JދĀt>ֱK@D Fa%2>X @(JCh&6aD?! Ѧx f43ݲ-CϰԙOx]Ulxp'1-@E 1 UDLAp GZvCN &z^&7ٹ<\*V DGŹ`&-#& u?&LnVbjxso =x5+|1(R`S=Dfd@Y} @G"=*_,YUڂB$Ksܒ(VxAl#aj R8I! X?aaؠ Vغ5Kh/O-2ŵ*66H0]aẎ|9}M|OyeA\ّi.ymv0sWw K5`lRx UƅO,4'[/!֣özc7<3$u O &Jlu&fAH Hbla.$G4xtm6|,(5]bPCq%҉TBK ک=?F'fkSdxyf*8a r R) )ak8bIEKg JNRlZYP6hP9"1;^`[ biBdR rBY`,V0HF*+cnb* 8 VjhueE2A}[G4A0p$2 dJ&(zͶѹ%l͚J_rhq)`FfPژ@G ɘ^î8$!4VFUlWGͪX{-_};V`5@lH‹s28yE5[uUWT:^Е2a@(O=5$2Iu+P YIHD"[=Wu:ӍϽO^oyYMXUstpL9C OtI3XҎPLH N c FXˊ^c?eOG,עs?a yM`3"Ɽ^f3=Ln~j㍏*a11<`1 1Ƥ:0dl ٺYn6"`|]ڵ>&49 !1 |;2oSOiK-xϽ-8)*,54ɏ۽Iƺ{a>u^%l]&ܑ5hJE]ngFDt b#QX #nS'IE9HBl++KK.jJMe{AZ9jXo/WhyZx]p:ь'o|.LbkCl$K03۴':N]x1 `k(xA9zO z^A@F$2^M-/~n{5"i( KSo \ݴ-Bn`H?;4&Ek prv0%BSL$N,S-p{_>GIs1R%$| vA]Lɒr}sV #,Y휤0Vq4r:"k@($/9?[&4n\ `̪K!XZ, 1f* LcƁôq!Xx| 6meQT *+̔ \qwl[NiU5PZdԏzͺd>Zw22`dX#8$s4%jKԉu SoGZXQ %:#%>E.q6"jؚ"ʁכe*`[EC sDY \ƊTnDȁ46D: ;iN:zL:ZG/mU9FVpS]cݵ 1fqDKs8.Ը`ȵT2O8־+[s*ze_ş7 w97׾(( F# 2ѪDi ֡S+1 c/gĥN/յ0SBJK#&zA%5 i OIuKab_4,e1LO@yHɱ@=AaYp$r$䒜s`zԧU[-Xe7n/ m !#1KڮY~!OP+ZNĂƱ qpfc47}ȫo{0dr1cZ mqj @E"VTTPU?ؓ$doM5Εka[$XJ0^OboB$4a Y@u|wC<=%I %0O+ Fsh rhQOHfE; ZCzJsp VprĈ#i"Y V'0R] $>@kzeZߛ]W(znaաxGY!15s,5|DcVcS+j1Lt٘VYm'xrյ17h5&3iHŽe0\ BOE(`nhJث81|%b|%u+'1=g0mX L.DPW26˒i{$$X+ >><`ebϹ; }J1P\ ;am/xxE"' kg6ѿJ`-wsK K(e TFEcab V1mLQD̐T8Yc?j޽k*hu_HP*V*.rr^z[@ m 鵳윊bxW2ёh8(b129`n J{ͬ^| DWQ H jq"`cS 󜰘KL D6[?wwٵ9E@MKi@'H  Q0` t}fHMfCgɛq7$!-#jX8 1@d!q aEyMu%c6fA&qB -m3iW)nsbhl2{ Ɲ[ }#PfQX |$)`hma D{K14RaڕG:1)LJ 6"|&N7 M'jHn$1`%^&!K7:NTl0g9ܨ\B\֞syJJ:OsmxT&h|@[iLN;)r ޭ&r}sM\ܾi;XZ tow/-R>%>K<{b[O]GV\şVI.+k,+-83SAƞU}_Cf=viT'K_5ˇ/2(n/LXJ%B=.̪ZU±'HGEKd 61e`Z V0'lC0yb_߳%6~[Y*0 k5 ^ad +:nRó M|lݯa?x٩^Erev,kཽG>/E@m5WƒzHׁZT'ӻNp:tQr /逕a }m̓ĸq&Kxn2 &4HDSc8el >[X%ƴ%݀3ꪻ:'.u \Z8$ƪHBx#Ӿ+Q$)GBh碭Ҩ7x%pxJ8'\b2I4lՌHx7cx(/ !kx́ ƱJ,[2lY?ɲseɁ猿RyO!Rt ZC] |]_qa4='Sn3νlשߚu~Ζ";|V&Vac&bVE 3'Y xG%31f thDv =$B!`4ְc.Hh8͖ T+$`quD$mo(E Z,#l&t¢W֊1EbcPsi,UL $[aG>7Mx X=-! J&h&1y2 63bGgNWm+--IXtæq@b]ly oo}d0W&0"X=Όo:1 !9#%N< ]bIC<ᠡKdVwPDžԳJ8T dMt0K}Ba%alg^"/w @2tD!?/CJ ƠHx(Mh 5LKGh*@BC^ƬD,vGsm_thLS8QbO_b{gTRNnpe7}"C]u|yW.|[u8cEB=:qj2m7!>t\xAH@DH'I)36VL ~8-_Ϩj0IEY`i-20X+@D\*P+T&Ārӏq#X͵ݏ|I碗=}[^1ҟY%B)omq0 _6KR :N Wr1mV](ܖyԵUڶ.g5[") $Z jn`1R*"cOs 2YA. lp 6֕ %Vb+Irkdq6+P% atH~e93=.zg8W&%#v&;% 瘆ƒH1t1K@K"Δ2NN9WZL/żJ%DHXP υs0 O58]Y)3lI uZB9͓D 3 "_Zsg"ǧt1I90A yA6̔ hiVM$qBX +#leI3rsYnlݣK[wxoVo&@SZ0ԩcQgֺiVzNfzm|ߧzOk@>j"1 ƋsmskI("I"!uljSl UD/X^wT\35X`S $ xg,􍰇"Cr3uyEӬ<.A,_4HcN2cO%PIH{Zf,)+b0- S3﹌<$zWGc@9> 3Hsx&ĀO =It,6"/6Xҙ&ZL[ʹ)fFG6DQ FRn~iXpYZГ`6d"%#Jiڠ- ,[M$D؀A?i đ6Ԃw{<;A(v0ʥnx*ԡǠ%6֍LtIh__moD݉9IHxvN6X T+ jP&[P^YI*{ r.BHP͆]LNW9kxQ650ű' ri:3LXAB$Lɋf˭$9}>_ru)2*5(!.q CcV)Q> p`-%zwbA%v#ŹS[(Pz{R?6!WH& g닪,ܘuJ?Sq\ uTϲǨyPfEv'ff'*T!_bEbt#Z>4prA^L1]tVTkpߦ0y,G2I wM0>8`j~ ZİcW37*DF\?h!F&I5PU ᤌ Jm_;1RUׄ lC5JvŋDtͥESQ"uq\4)eJ$ZJ9p`Ȉ2;F|͔׌ qb =9i8-Tj#L0_7c9רujВT@瑸`wY+Pl&ܙta6 M\0q_paa7Tڷqg,n;6]y@Ba_{T-XCb9aN.q|s%>brVAyS89`ި]:Ԩt_Mĺ #bzUC$WR$AtDCٿ5&0/~[%}KE*r\#'/古ݷz(?m)0#^k9rJV+Rl )Rö0qS!fN[ߦ~rMe]\Wo-BܰtlDkϰ@b`MY,kE15U_~&OKA.՘r`jT^]jSE >2M178}Nڕ & zfִ5{RIYlu|uK]oh'D9t8ETx<\,i"y5W|ei-^!֫jKH8{CC1DFF}YC~؄UXQ3V Exm&R!b%ƕ3{y;(U&_M|&/}9JjL5tF6'0IZ0S֙P*<&wr9BҶRk J-&lZてJJ*xR 4 BRz̾zP ; <~s!%!`>,e2/kN0d wJ[)fV3H""܎%UvGk#Vy*$㮊=X.Iوq^Ѝ9%7ِ-&ҁ߄ؾLp]uрl?A'NBDͥ˖mh FU$cү8zRrRju358l'Y`/ke{³ݘ■om nkkO 4+!+%hq '%SaaHؤ>aWGl8:fu5#` )KtXFҋ ]`Tkuq6er><)+ ߛ6g2L/'g JQ` Ѕs8bǴ BQ!qe>'gw,NWvS)YɰCt(e-$FGCsɝ OtJM' }Z{BDkɁvM@U6p?ɜeH8k^koh3 %c]aF\ c2guFVcb% G0R덝 _NlyʱKl6"6wO"h} s﵅ Fj i XLL`H[r]"+,R!9Olʻ5=<o0[q}{{y_0kC5siWBB[UMH 0ի!xÜ #bЋHT 3fǻ~u/Gm )Igmw=B"lD `o<-ߞڇ#]y%A;p^Tw\.vG\;c,,_,I{G": ,-w7'2ҳ-FTbן4qF#a|u9#сHe{X8k4ksSHG u̵# :5kY5 Z=D}棻+Cʭ*%KMs*rIR䠣Kga.|Ǖ{|ҭ>Ty1,c1063H0 YѩFK-kiq49@[\eGHˤ#Ք׭T*&Ły䄰7W 0OݪLqeiIJ7{yC^n4ay/V='I4W"[kMv4HT19.$ i*Ldpg T,:#:yOTڢ,+!0KS. ߯I"oWm!_yi$rk_IӤq *:b]zDHo0gN RsW 0$ɲm76TUX-0^6`%:WM$egr2L"qawq/6a}g`v.Zst#Bit,xS@ZKwQKY%zho2&p.CuYġW8\}Җ歘ub˛.)7LHapڜm/BTꂆxY4T1+"L쪬d琀'MW{WV`;)Ӯ{lExNU?®sE;%r+Xm`m4{JpGr Y4K#ab'vȧ"E+<J3T X+x]E5 3vdEt00jp;cū89+.bf,{&M]eLAWUuw`S=wnyW?z}z?7#8a>$FT5*!f0l>A V,[FJ׍588(ŻƑVi;*Iu8r!}"K@ps mcҧG͢vѤREsE ,Y&)ٶC)͹(8I- "M4ah7|J4")JW*&P25ϯ@~xc\kX@,XX[F({D7.݁D( ZT:I8,nOdM"f 8{/H-p{A.gRZ1]m_'un7(WRԭ71frYu)n. }R,5p;=Lsa4 %&,b_0m"lFZ G'X ޮ⫳2ijejQ8૔$%WYx;,#UiwVɫ@sz&n <`@Ý)rkTh L$'ؾ<^+M;a'vD9-d8؎hW^Ce:P GaEbN(i^(Z~IXU)nw)ZqzJuWO]FW884_ [+8<ۊvNqzr]@Q5FD  .ԲMVoSRMo'SgY[յTq]N4EZ4QDT)T+uڮ}Uó(z)%UMyR dz.0#DZUu^Rj2z#Lۡ>s:hwnTu/yɪSr BSҟ~o7x6fi̹7\ific5"bCE> Tn I ZS)EDDɃj2JRu`N EVmO&9*P@:ʳ(b Z!yr$xsi7nJu"c.;H=x/t02UjÕP0Oדn99o `E{˥a)cl ΎݳpcjS43kžփE-0ljAMլ<]& $Lje]sy&׻T‰EQSS95HRLOֺ(aq~źa}2R#<4.u/YR1>@fL(t#5u#쿺:PO%E^ JyRA#W)Mk陪uDgf)֓tI[kM@4-+B46D ;Λܱ-{n-m*R1pzsu;@uM!lXcCY$;"l/,w&"];`xያo[ra^z0b-{S n:UĜUi3WZZyA9?_hlip10 pLĜDtLhnԚ=m9V u\>)G"Dm]Dɝ2LS칒X(a'+c9)=U?+ (gJmMÞDZit+WM%Fʀ'%#GJ*ovtE} iY>0K$oBX[Bp=RD!4l9FS].V/>Q 0Я~FL96aȔr~ 0>:. 䟁Aq.x ĥk}wN>1MK)̟L=K&3'Wۀp4/& ƵPc [,wMɰj% Dz׾qJKd#:؎ȰUk¾i9"Ppp(Bu%!NR~ ݷӞ.?t9#]!XV "HnVq p+b1{gf؂Q32ܔ7P骺Tص0쫭-Ii1M@VT#ƩTt;+#^OrWn D&. > 'qkZ[YzfRe.n:ӄw7J[}0cdR]LKE8J˽0 s]2̾:iI=*NWdD!_.ęXL@?}#4}q]57 pT:'ܼ ^Zv9` EJKD2a)q 6ۋu[&ĪVczFUgR//wX\EvV}- DIiRAhu ]XP-Z{i F"kv4Ø[29(m77~o_թRzVa*ޜ\T#H(^Y$7V )iʨjTfDSELOTfH38FX 0dQp .J^NΚ9dxea,PAhu.E2ل-\>bS¨jQrnö ЭUJXMv$G쌊:: @w$V< fr: kJ¼_K:ڌ&Ph6(i#o69Q%4^R$cs81"?LD?>}ۏݜ^{Z0IJn"OTU8ґ*pe6eao.e!;;޵:zH_gx =֯>wIp!B(pI5rXv=a '6 ۾ X: Mmfr E8 yXNқlrn$e<.ﶼǛ|$?4"-8<ZS nFp2]Ku\6>+Y.Hxi5*. _uAL\NS7ù^xyEMTekr_+ߢ1AJ{Tԡ֙^碁  DQf=Hyc+ #W kk`M=(8ڢDVS»Cܿ/+ Iê!Ik_z!6=auN )` @Tl%W-n}t !_b)Umi:_5<L .ȘMzV=XQJVJ#vܴ=~O>] ~Xヲ:3ޟ)( #;P~DΨq>&>#}q"Y>[yuXKlAnmnt'tnT楛=>>mc]3Bƃ1Ux@+ۺ?rDjzL`n _.0ZA jz{ {hqpEr{.VnX)0mFwƖmM-ϐ9;7/RrTL0-:Fy'4FX(f;"V/=Zֆ!x+bnN.cJjR֙L$G%rAݻ{>\Sד}]>/ErHHZ0ZX 7h&_1y^9D?NX؀Zl> e Vsr4IH{Dhd2{j!MjTsy-q/f-w+_3kX E ׼GɻXRL(Pud| )Z~,R_-BdL$tDQ`HoZY {i96r?Nq._dq+)1hTj( 8ICYQ-M JLakP- ITzNE=[p&azh)#+u/WEdȁ`3ixPNarjx/%gŁHXW,Ų8]gjDzN.Ϭ\*fesh63E!2 Az]6'\ިZ @ppNsAK3V*JC-&6F .[]ֆ3eTg$n`M=0W9& D0҂ltump.R-*K-J*kՍz.c W:Ʋ7QN]e$6`8]ڑІ`mQJ J7F'~G"8諉DL^8śc=>LvH'1 zhiNjo/39OGY+cS&|ɝ1th%*(ŖB3NmRdEh26tӤhڹۥ`Qޜ'{ϱd5TxϮWf)H1 з벪~.D&(ߧ`_flb_)O "6Xa1&x7.1v>i TK@c@i4M15}Nk,h&4F||V*h,.. R>1T޹Q,.LT ,u p`7D4p8/HHYљ葝aANߔ @7ݶ$Do57Z˕`;y-*B MA^*Lh=ʼn59_@j7U[riM^ǰy/ڨ2jjxK2Q I_:VwaX7c}˹׿&.^I& ` QIDUS,- DiPIPmeUgrqHDFxRF`S|Z%:i}qc66Oo|^nO-_| g4g+)l}Z e#d#T I.vX^ c6 $:ݫ3*$*,^ʆ%ku*Rza[NYW;C+^cW[NmF.dl/nS[n}|W߶WXKOܷ=l/k4ՐGL_#cVo//m7=lfJpnq=ӟ};y~:Mn;/^hoO&oO?exrq13[b{bc;::šGwǵPX1ne@W.ޔk0[mCT#hrt+p ڢzL$% [eJK9.\\UĨ4|ʶ,W풹YDp/7GëqؔvVK6D^,k4F,;PFS(h;W"6Fm @g7 զKxybOhzOn^FVkHsU{ſI/MVtOi;?-O'}]չܬ;fzx㜣طE_8>P#xȓJ]X9=ǩ[ e}ipjsλRo1{J)ת&SSN>$ޭzf{D'K} +8E`lv|rZ4a,d5 cJ6]gNr`X:Vo\rRYJ)r1d? 'öP[6/lz J *%&yBaq jՎ?-$=3+i1X.(/j/ڰ9Љ*7CʆVC3-Ϊ&-ߐD%O6OB,39535RcQFgx|o|s1s5g˰&^XsJNs!SG@`t>߶wvֶO^&ðZxtlm5^{4v>v ͶMapgaaM =Dk&Z<%ݲΘƓsnlc֍8hځEc5%߬7E#kjhœԄȼSmRD*E:h5q*͡ ^]|\ݜOî%3WG k0 Gm"C4U3\[21gp|,|_&l2l:)'΃H$SёR6U7[Pɪ4iWL']TJtP\""Θؐ!wQӈxI0ZrH 2]Hݬ\{\F]>M+7! %6DUt3 5c$)Jnl/-jj; .-͐u?=wu<|bVA cVy]Q.x;r$M΄@o KQ AgeG\KLF#OQU䔛h[?̀b^~SVȐ@IdJl&rs#EJu- Z%$VFiԵ2VL!JDC@4cvcNIj„VH:W#~Du-$Cf-Yh-BzOjR / sFuޚkKgaI B[ '^@ID-ИF`-vQ+1b4Kl?jĪ1y 񶖧W!~2=+N%ZKs pM1()6`&2' =x6+yvto:l `Tz ӢQI#ۤt3SK׬~eQyqijjlRL@YHG|jozXtWnNioLoK# ҆C3ذ{d t!RGH2V]ara{}tX|!,%qׄM lZdD;\x t)5[Xa$옥yٟebLmpeu"hEDjT+Z*o56J̳ k9F2b@ISR8C~_~{@C lya^}[}ObA`]T䤖HϨ9gWl:5 y/]c~񊬗zSћI:7 '᫹7FԻa DVՆFg _LSAW;! K/Oy%3jzk]~#sC"#$TV=7w-drva| n0L쎅 cGvT`+DkbB8 5)AJ`<޲hT;x$d>(z~Eۮ?jv- 6Ǭ\PCOET䋴KjcXiLVS XQuhZO#һ n?uAz;0IuT * sREcS{׋qK/Rp)f06auSb % } 4bj ά^;KMF$ s ֑Y0Ltb #.$%ɭqpOEIЂ=ϦD\;DMTEBx\*lcSՋqz &u}&qP) -RzqP"ގuɹƞ@y|5UYea9cs{%@D1W=^-RNFسEa)Y<%hBCj26sC$,عUґPT.ɱ9 *;1^lݳt.#*O?nv'WH\kے#]=}X0W*PĂ9uR{9kI'tuqh9Xq%tτ q4 !97ikݝ,۵+(譇YR6]tE59F 0P*i!(Mk"[t0N6A?h{$XkԭiS%eKKZcC͒OQqN?񷣒Suoc#5ۿ_^:,frS*X 8 -rPCO V>9G`z)x|'e=MM'.W#߳ܗ9C?Qyj0MA:^Vpn|.Isa1L ˍGiA4I`teq Er %џۤi!}f^ڍ.Qi`YxE~ac6~vcSXL\.rB8}Cz,Y}m˯/"ۼc~'bP7;-a fY,YsvoW -;߸M<l>x.׺nQe8/6e9ԓ,!9Ov~vy/_^>vo/VzWM7 Wهhք-HK^f]E%Bǚu&Er$S9c:Q2gljyU9gbJrApD]sn-(ZaZV@#yT>!@ԽvF$hv-|?c]}QXNhtg"fT4Qfq%&ulGnk5+EvɥF8̂ɢZ$p- h7xjjGvi~+6yMl[F/=6ر˃>Լ6zz_>-:RJۇ:'RwV8 ~Z7a,L}~h#雂zґ<|yy6ǤG z7#7$٫on Ou qsEF0I9Oe4ׇ.x7>˸b{~\^#n'ri1 _tnQwwu!\#7X]Gß3AQrhw6ܡYZf??}sPDҥfMǺoSɥKR; ?xS0uy=&դY銌!SPк  Z$vRP@׏dNߺyo.|V ꢼZ5:5SK'ŶrlbOs}{&"b}>&t"R梓*Uzugm6m|ك;Xz%%s2M~-Ǎӡݘұ t~Gx#4i}(wKz9]s)1s6R0p=q3qQ.$WrD m)=KCA ؾ}5'&+ygdYS[nvD;$qArb2t Ϲ|8s+jP%b2$*.A EgƮ^jYGsW';Ҷt@꼧V(9SF|e9[)c=/:Ĩ#"IJ!vıxCDwU>. &,FY@ ]!~sSan3sQmprX&РkOl8FT}8g+ŚDPD/VfD`eԱ=!go;ٍyT̳}hBO8]W(Ið.=N^թ 1uU ab !K:D}phk?4 "AEk6t2%}`0QգZmlPBCd Xz5+adrvU-}طjOm/%/-ڴ=&.DP>L8/SՅVJQu>'ej$ɺ`z}iX/ƋFpj,NCz19.xy;wVݩe\ИMTJݔEcQNN@`C|CT]!N9`LF#v q_k:Bw xBD=ae];K4Mip9{JVnE2^$Eϑ|CT F6tO72Z8̻5ZɥlVA"db}΂ꤛi"u^|a+"|TW:W$coQLx#./je2,#/X\r^TJX0 #m0lX*i`Ū :6 (#R,5 &+׈B +<0 7)"pFU`D9q"r1`qV'e@ ?NU=7E+M y`9p<[`SЀ=m–fE;)x |Hj) C|ݘ]qܛ'%E*V:l+w,"t4#f񝨼W>yo*d)00őXQ` ].Vɦxd yt9GfZ~wO`HgĘ7t"]]l0Pczd$Pu zAkkee DNXSS @ỀA XoGFP51=SG\zauHnRvlK6]xTKa]+jnJ*'(CdP*xR&z kkH,2M !3s椙)0gzpZ |氜dzQKZbrQ㠧RRv!MkRrCRej L-׻z$vW26!7=1cf,0ܱ'N湍U{S LK%"UM[絉8ZW۝3'gbﵲUV)NO/h~a@TE 0T[D=l bprJDJ}7T=. 6o mOW7_c)w˹)?Z::nV J.(PGD:h0DfE]D;y.좎N̵uSaO &pQ咳f1{N K%nuX9?Ѿ-K!*u#,-Ƚ)ΰ0UEl 铊s{R-{O`(i ŘN"=]Ģ%i@ s+-Kz,,Э%ҬJ@h7XRLj뚔>恏cYU[Uµ/CϕECG2X@ !>I %9_AAϏr}qd']z7M`*:+$NIec>r.^H'Zn'jo{*B߅nѾ\y.QrHH.D^<ͧIdpa!Ը&7Z'T1}yVﰶRܝ/+gc qς{?C#gդ?wx{a 89MdU; gPmݼK[Y]q=j!3mbc/OD{[<[n"?jͪ{V%O5U3^T_;)HF'l[vl=\C٘5V.@6q Vւ &tNS(]k;(Z*fqa:g#uVQm0 #}c->wxWMi>a*B_????0cargo-0.91.0/benches/workspaces/toml-rs.tgz000064400000000000000000000033661046102023000167310ustar 00000000000000toml-rs.tarY[o6γ~>nl~)E>؃]xؒWӓ.w(';MO ̃)iH z54n^݄nR;w6'jLiا~A.^mueym-ʲZCi/ŧDKke8 F+N*+C.qe ]æt6y[? zV!tuXm3vNWj%]v<*~*.5/sOx!i"9_}aM&ϋgeEdjuU5>f^nBBq]/L~oAp2盽 ܰmW~~^lt7p.58lµ[pav({ʿg&b2?}Ã`v8,MeYmqj0כ\ޅz8)َ7eoqT5뷩|Ϸ[aXzkg~iq͓a (wnU5W&Pj P{yq? # MU6lT>~&Gb}/5UlװA|#{bUhq߼A0ao~ۯ_nھr2S}qut5=ܹ05y UW3"?{//4?8SN=vcvEGdsMGyqG^MatBXdsAJOW9v]l~UyV=88 c3YMw62,t^NRO Sz0yb˸ўīq"&cargo-0.91.0/build.rs000064400000000000000000000123551046102023000124610ustar 00000000000000use flate2::{Compression, GzBuilder}; use std::ffi::OsStr; use std::fs; use std::path::Path; use std::process::Command; fn main() { commit_info(); compress_man(); windows_manifest(); // ALLOWED: Accessing environment during build time shouldn't be prohibited. #[allow(clippy::disallowed_methods)] let target = std::env::var("TARGET").unwrap(); println!("cargo:rustc-env=RUST_HOST_TARGET={target}"); } fn compress_man() { // ALLOWED: Accessing environment during build time shouldn't be prohibited. #[allow(clippy::disallowed_methods)] let out_path = Path::new(&std::env::var("OUT_DIR").unwrap()).join("man.tgz"); let dst = fs::File::create(out_path).unwrap(); let encoder = GzBuilder::new() .filename("man.tar") .write(dst, Compression::best()); let mut ar = tar::Builder::new(encoder); ar.mode(tar::HeaderMode::Deterministic); let mut add_files = |dir, extension| { let mut files = fs::read_dir(dir) .unwrap() .map(|e| e.unwrap().path()) .collect::>(); files.sort(); for path in files { if path.extension() != Some(extension) { continue; } println!("cargo:rerun-if-changed={}", path.display()); ar.append_path_with_name(&path, path.file_name().unwrap()) .unwrap(); } }; add_files(Path::new("src/etc/man"), OsStr::new("1")); add_files(Path::new("src/doc/man/generated_txt"), OsStr::new("txt")); let encoder = ar.into_inner().unwrap(); encoder.finish().unwrap(); } struct CommitInfo { hash: String, short_hash: String, date: String, } fn commit_info_from_git() -> Option { if !Path::new(".git").exists() { return None; } let output = match Command::new("git") .arg("log") .arg("-1") .arg("--date=short") .arg("--format=%H %h %cd") .arg("--abbrev=9") .output() { Ok(output) if output.status.success() => output, _ => return None, }; let stdout = String::from_utf8(output.stdout).unwrap(); let mut parts = stdout.split_whitespace().map(|s| s.to_string()); Some(CommitInfo { hash: parts.next()?, short_hash: parts.next()?, date: parts.next()?, }) } // The rustc source tarball is meant to contain all the source code to build an exact copy of the // toolchain, but it doesn't include the git repository itself. It wouldn't thus be possible to // populate the version information with the commit hash and the commit date. // // To work around this, the rustc build process obtains the git information when creating the // source tarball and writes it to the `git-commit-info` file. The build process actually creates // at least *two* of those files, one for Rust as a whole (in the root of the tarball) and one // specifically for Cargo (in src/tools/cargo). This function loads that file. // // The file is a newline-separated list of full commit hash, short commit hash, and commit date. fn commit_info_from_rustc_source_tarball() -> Option { let path = Path::new("git-commit-info"); if !path.exists() { return None; } // Dependency tracking is a nice to have for this (git doesn't do it), so if the path is not // valid UTF-8 just avoid doing it rather than erroring out. if let Some(utf8) = path.to_str() { println!("cargo:rerun-if-changed={utf8}"); } let content = std::fs::read_to_string(&path).ok()?; let mut parts = content.split('\n').map(|s| s.to_string()); Some(CommitInfo { hash: parts.next()?, short_hash: parts.next()?, date: parts.next()?, }) } fn commit_info() { // Var set by bootstrap whenever omit-git-hash is enabled in rust-lang/rust's config.toml. println!("cargo:rerun-if-env-changed=CFG_OMIT_GIT_HASH"); // ALLOWED: Accessing environment during build time shouldn't be prohibited. #[allow(clippy::disallowed_methods)] if std::env::var_os("CFG_OMIT_GIT_HASH").is_some() { return; } let Some(git) = commit_info_from_git().or_else(commit_info_from_rustc_source_tarball) else { return; }; println!("cargo:rustc-env=CARGO_COMMIT_HASH={}", git.hash); println!("cargo:rustc-env=CARGO_COMMIT_SHORT_HASH={}", git.short_hash); println!("cargo:rustc-env=CARGO_COMMIT_DATE={}", git.date); } #[allow(clippy::disallowed_methods)] fn windows_manifest() { use std::env; let target_os = env::var("CARGO_CFG_TARGET_OS"); let target_env = env::var("CARGO_CFG_TARGET_ENV"); if Ok("windows") == target_os.as_deref() && Ok("msvc") == target_env.as_deref() { static WINDOWS_MANIFEST_FILE: &str = "windows.manifest.xml"; let mut manifest = env::current_dir().unwrap(); manifest.push(WINDOWS_MANIFEST_FILE); println!("cargo:rerun-if-changed={WINDOWS_MANIFEST_FILE}"); // Embed the Windows application manifest file. println!("cargo:rustc-link-arg-bin=cargo=/MANIFEST:EMBED"); println!( "cargo:rustc-link-arg-bin=cargo=/MANIFESTINPUT:{}", manifest.to_str().unwrap() ); // Turn linker warnings into errors. println!("cargo:rustc-link-arg-bin=cargo=/WX"); } } cargo-0.91.0/ci/clean-test-output.sh000075500000000000000000000002061046102023000153330ustar 00000000000000#!/bin/bash # This script remove test and benchmark output and displays disk usage. set -euo pipefail df -h rm -rf target/tmp df -h cargo-0.91.0/ci/dump-environment.sh000075500000000000000000000006071046102023000152520ustar 00000000000000#!/bin/bash # This script dumps information about the build environment to stdout. set -euo pipefail IFS=$'\n\t' echo "environment variables:" printenv | sort echo echo "disk usage:" df -h echo echo "CPU info:" if [[ "${OSTYPE}" = "darwin"* ]]; then system_profiler SPHardwareDataType || true sysctl hw || true else cat /proc/cpuinfo || true cat /proc/meminfo || true fi cargo-0.91.0/ci/fetch-smoke-test.sh000075500000000000000000000011251046102023000151210ustar 00000000000000#!/bin/bash # This script builds with static curl, and verifies that fetching works. set -ex if [[ -z "$RUNNER_TEMP" ]] then echo "RUNNER_TEMP must be set" exit 1 fi if [ ! -f Cargo.toml ]; then echo "Must be run from root of project." exit 1 fi # Building openssl on Windows is a pain. if [[ $(rustc -Vv | grep host:) != *windows* ]]; then FEATURES='vendored-openssl,curl-sys/static-curl,curl-sys/force-system-lib-on-osx' export LIBZ_SYS_STATIC=1 fi cargo build --features "$FEATURES" export CARGO_HOME=$RUNNER_TEMP/chome target/debug/cargo fetch rm -rf $CARGO_HOME cargo-0.91.0/ci/generate.py000064400000000000000000000032521046102023000135470ustar 00000000000000#!/usr/bin/env python3 MAPPING = { "build-script.html": "https://doc.rust-lang.org/cargo/reference/build-scripts.html", "config.html": None, "crates-io.html": "https://doc.rust-lang.org/cargo/reference/publishing.html", "environment-variables.html": None, "external-tools.html": None, "faq.html": "https://doc.rust-lang.org/cargo/faq.html", "guide.html": "https://doc.rust-lang.org/cargo/guide/", "index.html": "https://doc.rust-lang.org/cargo/", "manifest.html": None, "pkgid-spec.html": None, "policies.html": "https://crates.io/policies", "source-replacement.html": None, "specifying-dependencies.html": None, } TEMPLATE = """\ Page Moved This page has moved. Click here to go to the new page. """ def main(): for name in sorted(MAPPING): with open(name, 'w') as f: mapped = MAPPING[name] if mapped is None: mapped = "https://doc.rust-lang.org/cargo/reference/{}".format(name) f.write(TEMPLATE.format(name=name, mapped=mapped)) # WARN: The CNAME file is for GitHub to redirect requests to the custom domain. # Missing this may entail security hazard and domain takeover. # See with open('CNAME', 'w') as f: f.write('doc.crates.io') if __name__ == '__main__': main() cargo-0.91.0/ci/validate-man.sh000075500000000000000000000011251046102023000143010ustar 00000000000000#!/bin/bash # This script validates that there aren't any changes to the man pages. set -e cargo_man="src/doc" mdman_man="crates/mdman/doc" changes=$(git status --porcelain -- $cargo_man $mdman_man) if [ -n "$changes" ] then echo "git directory must be clean before running this script." exit 1 fi cargo build-man changes=$(git status --porcelain -- $cargo_man $mdman_man) if [ -n "$changes" ] then echo "Detected changes of man pages:" echo "$changes" echo echo 'Please run `cargo build-man` to rebuild the man pages' echo "and commit the changes." exit 1 fi cargo-0.91.0/ci/validate-version-bump.sh000075500000000000000000000015731046102023000161630ustar 00000000000000#!/bin/bash # This script checks if a crate needs a version bump. # # At the time of writing, it doesn't check what kind of bump is required. # In the future, we could take SemVer compatibliity into account, like # integrating `cargo-semver-checks` of else # # Inputs: # BASE_SHA The commit SHA of the branch where the PR wants to merge into. # HEAD_SHA The commit SHA that triggered the workflow. set -euo pipefail # When `BASE_SHA` is missing, we assume it is from GitHub merge queue merge commit, # so hope `HEAD~` to find the previous commit on master branch. base_sha=$(git rev-parse "${BASE_SHA:-HEAD~1}") head_sha=$(git rev-parse "${HEAD_SHA:-HEAD}") echo "Base revision is $base_sha" echo "Head revision is $head_sha" echo "::group::Building xtask" cargo bump-check --help echo "::endgroup::" cargo bump-check --github --base-rev "$base_sha" --head-rev "$head_sha" cargo-0.91.0/clippy.toml000064400000000000000000000011541046102023000132040ustar 00000000000000allow-print-in-tests = true allow-dbg-in-tests = true disallowed-methods = [ { path = "std::env::var", reason = "use `Config::get_env` instead. See rust-lang/cargo#11588" }, { path = "std::env::var_os", reason = "use `Config::get_env_os` instead. See rust-lang/cargo#11588" }, { path = "std::env::vars", reason = "not recommended to use in Cargo. See rust-lang/cargo#11588" }, { path = "std::env::vars_os", reason = "not recommended to use in Cargo. See rust-lang/cargo#11588" }, ] disallowed-types = [ { path = "std::sync::atomic::AtomicU64", reason = "not portable. See rust-lang/cargo#12988" }, ] cargo-0.91.0/credential/README.md000064400000000000000000000004471046102023000144040ustar 00000000000000# Cargo Credential Packages This directory contains Cargo packages for handling storage of tokens in a secure manner. `cargo-credential` is a generic library to assist writing a credential process. The other directories contain implementations that integrate with specific credential systems. cargo-0.91.0/deny.toml000064400000000000000000000256161046102023000126540ustar 00000000000000# This template contains all of the possible sections and their default values # Note that all fields that take a lint level have these possible values: # * deny - An error will be produced and the check will fail # * warn - A warning will be produced, but the check will not fail # * allow - No warning or error will be produced, though in some cases a note # will be # The values provided in this template are the default values that will be used # when any section or field is not specified in your own configuration # Root options # The graph table configures how the dependency graph is constructed and thus # which crates the checks are performed against [graph] # If 1 or more target triples (and optionally, target_features) are specified, # only the specified targets will be checked when running `cargo deny check`. # This means, if a particular package is only ever used as a target specific # dependency, such as, for example, the `nix` crate only being used via the # `target_family = "unix"` configuration, that only having windows targets in # this list would mean the nix crate, as well as any of its exclusive # dependencies not shared by any other crates, would be ignored, as the target # list here is effectively saying which targets you are building for. targets = [ # The triple can be any string, but only the target triples built in to # rustc (as of 1.40) can be checked against actual config expressions #"x86_64-unknown-linux-musl", # You can also specify which target_features you promise are enabled for a # particular target. target_features are currently not validated against # the actual valid features supported by the target architecture. #{ triple = "wasm32-unknown-unknown", features = ["atomics"] }, ] # When creating the dependency graph used as the source of truth when checks are # executed, this field can be used to prune crates from the graph, removing them # from the view of cargo-deny. This is an extremely heavy hammer, as if a crate # is pruned from the graph, all of its dependencies will also be pruned unless # they are connected to another crate in the graph that hasn't been pruned, # so it should be used with care. The identifiers are [Package ID Specifications] # (https://doc.rust-lang.org/cargo/reference/pkgid-spec.html) #exclude = [] # If true, metadata will be collected with `--all-features`. Note that this can't # be toggled off if true, if you want to conditionally enable `--all-features` it # is recommended to pass `--all-features` on the cmd line instead all-features = false # If true, metadata will be collected with `--no-default-features`. The same # caveat with `all-features` applies no-default-features = false # If set, these feature will be enabled when collecting metadata. If `--features` # is specified on the cmd line they will take precedence over this option. #features = [] # The output table provides options for how/if diagnostics are outputted [output] # When outputting inclusion graphs in diagnostics that include features, this # option can be used to specify the depth at which feature edges will be added. # This option is included since the graphs can be quite large and the addition # of features from the crate(s) to all of the graph roots can be far too verbose. # This option can be overridden via `--feature-depth` on the cmd line feature-depth = 1 # This section is considered when running `cargo deny check advisories` # More documentation for the advisories section can be found here: # https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html [advisories] # The path where the advisory databases are cloned/fetched into #db-path = "$CARGO_HOME/advisory-dbs" # The url(s) of the advisory databases to use #db-urls = ["https://github.com/rustsec/advisory-db"] yanked = "warn" # A list of advisory IDs to ignore. Note that ignored advisories will still # output a note when they are encountered. ignore = [ #"RUSTSEC-0000-0000", #{ id = "RUSTSEC-0000-0000", reason = "you can specify a reason the advisory is ignored" }, #"a-crate-that-is-yanked@0.1.1", # you can also ignore yanked crate versions if you wish #{ crate = "a-crate-that-is-yanked@0.1.1", reason = "you can specify why you are ignoring the yanked crate" }, ] # If this is true, then cargo deny will use the git executable to fetch advisory database. # If this is false, then it uses a built-in git library. # Setting this to true can be helpful if you have special authentication requirements that cargo-deny does not support. # See Git Authentication for more information about setting up git authentication. #git-fetch-with-cli = true # This section is considered when running `cargo deny check licenses` # More documentation for the licenses section can be found here: # https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html [licenses] # List of explicitly allowed licenses # See https://spdx.org/licenses/ for list of possible licenses # [possible values: any SPDX 3.11 short identifier (+ optional exception)]. allow = [ "MIT", "MIT-0", "Apache-2.0", "BSD-2-Clause", "BSD-3-Clause", "MPL-2.0", "Unicode-3.0", "CC0-1.0", "ISC", "Zlib", ] # The confidence threshold for detecting a license from license text. # The higher the value, the more closely the license text must be to the # canonical license text of a valid SPDX license file. # [possible values: any between 0.0 and 1.0]. confidence-threshold = 0.8 # Allow 1 or more licenses on a per-crate basis, so that particular licenses # aren't accepted for every possible crate as with the normal allow list exceptions = [ # Each entry is the crate and version constraint, and its specific allow # list #{ allow = ["Zlib"], crate = "adler32" }, ] # Some crates don't have (easily) machine readable licensing information, # adding a clarification entry for it allows you to manually specify the # licensing information #[[licenses.clarify]] # The package spec the clarification applies to #crate = "ring" # The SPDX expression for the license requirements of the crate #expression = "MIT AND ISC AND OpenSSL" # One or more files in the crate's source used as the "source of truth" for # the license expression. If the contents match, the clarification will be used # when running the license check, otherwise the clarification will be ignored # and the crate will be checked normally, which may produce warnings or errors # depending on the rest of your configuration #license-files = [ # Each entry is a crate relative path, and the (opaque) hash of its contents #{ path = "LICENSE", hash = 0xbd0eed23 } #] [licenses.private] # If true, ignores workspace crates that aren't published, or are only # published to private registries. # To see how to mark a crate as unpublished (to the official registry), # visit https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field. ignore = false # One or more private registries that you might publish crates to, if a crate # is only published to private registries, and ignore is true, the crate will # not have its license(s) checked registries = [ #"https://sekretz.com/registry ] # This section is considered when running `cargo deny check bans`. # More documentation about the 'bans' section can be found here: # https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html [bans] # Lint level for when multiple versions of the same crate are detected multiple-versions = "warn" # Lint level for when a crate version requirement is `*` wildcards = "allow" # The graph highlighting used when creating dotgraphs for crates # with multiple versions # * lowest-version - The path to the lowest versioned duplicate is highlighted # * simplest-path - The path to the version with the fewest edges is highlighted # * all - Both lowest-version and simplest-path are used highlight = "all" # The default lint level for `default` features for crates that are members of # the workspace that is being checked. This can be overridden by allowing/denying # `default` on a crate-by-crate basis if desired. workspace-default-features = "allow" # The default lint level for `default` features for external crates that are not # members of the workspace. This can be overridden by allowing/denying `default` # on a crate-by-crate basis if desired. external-default-features = "allow" # List of crates that are allowed. Use with care! allow = [ #"ansi_term@0.11.0", #{ crate = "ansi_term@0.11.0", reason = "you can specify a reason it is allowed" }, ] # List of crates to deny deny = [ #"ansi_term@0.11.0", #{ crate = "ansi_term@0.11.0", reason = "you can specify a reason it is banned" }, # Wrapper crates can optionally be specified to allow the crate when it # is a direct dependency of the otherwise banned crate #{ crate = "ansi_term@0.11.0", wrappers = ["this-crate-directly-depends-on-ansi_term"] }, ] # List of features to allow/deny # Each entry the name of a crate and a version range. If version is # not specified, all versions will be matched. #[[bans.features]] #crate = "reqwest" # Features to not allow #deny = ["json"] # Features to allow #allow = [ # "rustls", # "__rustls", # "__tls", # "hyper-rustls", # "rustls", # "rustls-pemfile", # "rustls-tls-webpki-roots", # "tokio-rustls", # "webpki-roots", #] # If true, the allowed features must exactly match the enabled feature set. If # this is set there is no point setting `deny` #exact = true # Certain crates/versions that will be skipped when doing duplicate detection. skip = [ #"ansi_term@0.11.0", #{ crate = "ansi_term@0.11.0", reason = "you can specify a reason why it can't be updated/removed" }, ] # Similarly to `skip` allows you to skip certain crates during duplicate # detection. Unlike skip, it also includes the entire tree of transitive # dependencies starting at the specified crate, up to a certain depth, which is # by default infinite. skip-tree = [ #"ansi_term@0.11.0", # will be skipped along with _all_ of its direct and transitive dependencies #{ crate = "ansi_term@0.11.0", depth = 20 }, ] # This section is considered when running `cargo deny check sources`. # More documentation about the 'sources' section can be found here: # https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html [sources] # Lint level for what to happen when a crate from a crate registry that is not # in the allow list is encountered unknown-registry = "warn" # Lint level for what to happen when a crate from a git repository that is not # in the allow list is encountered unknown-git = "warn" # List of URLs for allowed crate registries. Defaults to the crates.io index # if not specified. If it is specified but empty, no registries are allowed. allow-registry = ["https://github.com/rust-lang/crates.io-index"] # List of URLs for allowed Git repositories allow-git = [] [sources.allow-org] # github.com organizations to allow git sources for github = [] # gitlab.com organizations to allow git sources for gitlab = [] # bitbucket.org organizations to allow git sources for bitbucket = [] cargo-0.91.0/publish.py000075500000000000000000000042531046102023000130350ustar 00000000000000#!/usr/bin/env python3 # This script is used to publish Cargo to crates.io. # # This is run automatically every 6 weeks by the Release team's automation # whose source is at https://github.com/rust-lang/simpleinfra/. # # See https://doc.crates.io/contrib/process/release.html for more about # Cargo's release process. import os import re import subprocess import urllib.request from urllib.error import HTTPError # Whenever you add a new crate to this list that does NOT start with "cargo-" # you must reach out to the infra team to add the crate to the list of crates # allowed to be published from the "cargo CI" crates.io token. TO_PUBLISH = [ 'credential/cargo-credential', 'credential/cargo-credential-libsecret', 'credential/cargo-credential-wincred', 'credential/cargo-credential-1password', 'credential/cargo-credential-macos-keychain', 'crates/rustfix', 'crates/cargo-platform', 'crates/cargo-util', 'crates/crates-io', 'crates/cargo-util-schemas', 'crates/cargo-test-macro', 'crates/cargo-test-support', 'crates/build-rs', '.', ] def already_published(name, version): url = f'https://static.crates.io/crates/{name}/{version}/download' try: urllib.request.urlopen(url) except HTTPError as e: # 403 and 404 are common responses to assume it is not published if 400 <= e.code < 500: return False print(f'error: failed to check if {name} {version} is already published') print(f' HTTP response error code {e.code} checking {url}') raise return True def maybe_publish(path): content = open(os.path.join(path, 'Cargo.toml')).read() name = re.search('^name = "([^"]+)"', content, re.M).group(1) version = re.search('^version = "([^"]+)"', content, re.M).group(1) if already_published(name, version): print('%s %s is already published, skipping' % (name, version)) return False subprocess.check_call(['cargo', 'publish', '--no-verify'], cwd=path) return True def main(): print('Starting publish...') for path in TO_PUBLISH: maybe_publish(path) print('Publish complete!') if __name__ == '__main__': main() cargo-0.91.0/rustfmt.toml000064400000000000000000000000271046102023000134060ustar 00000000000000style_edition = "2024" cargo-0.91.0/src/bin/cargo/cli.rs000064400000000000000000000706501046102023000145650ustar 00000000000000use anyhow::{Context as _, anyhow}; use cargo::core::{CliUnstable, features}; use cargo::util::context::TermConfig; use cargo::{CargoResult, drop_print, drop_println}; use clap::builder::UnknownArgumentValueParser; use itertools::Itertools; use std::collections::HashMap; use std::ffi::OsStr; use std::ffi::OsString; use std::fmt::Write; use super::commands; use super::list_commands; use super::user_defined_aliases; use crate::command_prelude::*; use crate::util::is_rustup; use cargo::core::shell::ColorChoice; use cargo::util::style; #[tracing::instrument(skip_all)] pub fn main(gctx: &mut GlobalContext) -> CliResult { // CAUTION: Be careful with using `config` until it is configured below. // In general, try to avoid loading config values unless necessary (like // the [alias] table). let args = cli(gctx).try_get_matches()?; // Update the process-level notion of cwd if let Some(new_cwd) = args.get_one::("directory") { // This is a temporary hack. // This cannot access `GlobalContext`, so this is a bit messy. // This does not properly parse `-Z` flags that appear after the subcommand. // The error message is not as helpful as the standard one. let nightly_features_allowed = matches!(&*features::channel(), "nightly" | "dev"); if !nightly_features_allowed || (nightly_features_allowed && !args .get_many("unstable-features") .map(|mut z| z.any(|value: &String| value == "unstable-options")) .unwrap_or(false)) { return Err(anyhow::format_err!( "the `-C` flag is unstable, \ pass `-Z unstable-options` on the nightly channel to enable it" ) .into()); } std::env::set_current_dir(&new_cwd).context("could not change to requested directory")?; gctx.reload_cwd()?; } let (expanded_args, global_args) = expand_aliases(gctx, args, vec![])?; let is_verbose = expanded_args.verbose() > 0; if expanded_args .get_one::("unstable-features") .map(String::as_str) == Some("help") { // Don't let config errors get in the way of parsing arguments let _ = configure_gctx(gctx, &expanded_args, None, global_args, None); print_zhelp(gctx); } else if expanded_args.flag("version") { // Don't let config errors get in the way of parsing arguments let _ = configure_gctx(gctx, &expanded_args, None, global_args, None); let version = get_version_string(is_verbose); drop_print!(gctx, "{}", version); } else if let Some(code) = expanded_args.get_one::("explain") { // Don't let config errors get in the way of parsing arguments let _ = configure_gctx(gctx, &expanded_args, None, global_args, None); let mut procss = gctx.load_global_rustc(None)?.process(); procss.arg("--explain").arg(code).exec()?; } else if expanded_args.flag("list") { // Don't let config errors get in the way of parsing arguments let _ = configure_gctx(gctx, &expanded_args, None, global_args, None); print_list(gctx, is_verbose); } else { let (cmd, subcommand_args) = match expanded_args.subcommand() { Some((cmd, args)) => (cmd, args), _ => { // No subcommand provided. cli(gctx).print_help()?; return Ok(()); } }; let exec = Exec::infer(cmd)?; configure_gctx( gctx, &expanded_args, Some(subcommand_args), global_args, Some(&exec), )?; super::init_git(gctx); exec.exec(gctx, subcommand_args)?; } Ok(()) } fn print_zhelp(gctx: &GlobalContext) { let header = style::HEADER; let literal = style::LITERAL; let placeholder = style::PLACEHOLDER; let options = CliUnstable::help(); let max_length = options .iter() .filter(|(_, help)| help.is_some()) .map(|(option_name, _)| option_name.len()) .max() .unwrap_or(0); let z_flags = options .iter() .filter(|(_, help)| help.is_some()) .map(|(opt, help)| { let opt = opt.replace("_", "-"); let help = help.unwrap(); format!(" {literal}-Z {opt:Installed Commands:") ); for (name, command) in list_commands(gctx) { let known_external_desc = known_external_command_descriptions.get(name.as_str()); let literal = style::LITERAL; match command { CommandInfo::BuiltIn { about } => { assert!( known_external_desc.is_none(), "known_external_commands shouldn't contain builtin `{name}`", ); let summary = about.unwrap_or_default(); let summary = summary.lines().next().unwrap_or(&summary); // display only the first line drop_println!(gctx, " {literal}{name:<20}{literal:#} {summary}"); } CommandInfo::External { path } => { if let Some(desc) = known_external_desc { drop_println!(gctx, " {literal}{name:<20}{literal:#} {desc}"); } else if is_verbose { drop_println!( gctx, " {literal}{name:<20}{literal:#} {}", path.display() ); } else { drop_println!(gctx, " {literal}{name}{literal:#}"); } } CommandInfo::Alias { target } => { drop_println!( gctx, " {literal}{name:<20}{literal:#} alias: {}", target.iter().join(" ") ); } } } } pub fn get_version_string(is_verbose: bool) -> String { let version = cargo::version(); let mut version_string = format!("cargo {}\n", version); if is_verbose { version_string.push_str(&format!("release: {}\n", version.version)); if let Some(ref ci) = version.commit_info { version_string.push_str(&format!("commit-hash: {}\n", ci.commit_hash)); version_string.push_str(&format!("commit-date: {}\n", ci.commit_date)); } writeln!(version_string, "host: {}", env!("RUST_HOST_TARGET")).unwrap(); add_libgit2(&mut version_string); add_curl(&mut version_string); add_ssl(&mut version_string); writeln!(version_string, "os: {}", os_info::get()).unwrap(); } version_string } fn add_libgit2(version_string: &mut String) { let git2_v = git2::Version::get(); let lib_v = git2_v.libgit2_version(); let vendored = if git2_v.vendored() { format!("vendored") } else { format!("system") }; writeln!( version_string, "libgit2: {}.{}.{} (sys:{} {})", lib_v.0, lib_v.1, lib_v.2, git2_v.crate_version(), vendored ) .unwrap(); } fn add_curl(version_string: &mut String) { let curl_v = curl::Version::get(); let vendored = if curl_v.vendored() { format!("vendored") } else { format!("system") }; writeln!( version_string, "libcurl: {} (sys:{} {} ssl:{})", curl_v.version(), curl_sys::rust_crate_version(), vendored, curl_v.ssl_version().unwrap_or("none") ) .unwrap(); } fn add_ssl(version_string: &mut String) { #[cfg(feature = "openssl")] { writeln!(version_string, "ssl: {}", openssl::version::version()).unwrap(); } #[cfg(not(feature = "openssl"))] { let _ = version_string; // Silence unused warning. } } /// Expands aliases recursively to collect all the command line arguments. /// /// [`GlobalArgs`] need to be extracted before expanding aliases because the /// clap code for extracting a subcommand discards global options /// (appearing before the subcommand). #[tracing::instrument(skip_all)] fn expand_aliases( gctx: &mut GlobalContext, args: ArgMatches, mut already_expanded: Vec, ) -> Result<(ArgMatches, GlobalArgs), CliError> { if let Some((cmd, sub_args)) = args.subcommand() { let exec = commands::builtin_exec(cmd); let aliased_cmd = super::aliased_command(gctx, cmd); match (exec, aliased_cmd) { (Some(_), Ok(Some(_))) => { // User alias conflicts with a built-in subcommand gctx.shell().warn(format!( "user-defined alias `{}` is ignored, because it is shadowed by a built-in command", cmd, ))?; } (Some(_), Ok(None) | Err(_)) => { // Here we ignore errors from aliasing as we already favor built-in command, // and alias doesn't involve in this context. if let Some(values) = sub_args.get_many::("") { // Command is built-in and is not conflicting with alias, but contains ignored values. return Err(anyhow::format_err!( "\ trailing arguments after built-in command `{}` are unsupported: `{}` To pass the arguments to the subcommand, remove `--`", cmd, values.map(|s| s.to_string_lossy()).join(" "), ) .into()); } } (None, Ok(None)) => {} (None, Ok(Some(alias))) => { // Check if a user-defined alias is shadowing an external subcommand // (binary of the form `cargo-`) // Currently this is only a warning, but after a transition period this will become // a hard error. if super::builtin_aliases_execs(cmd).is_none() { if let Some(path) = super::find_external_subcommand(gctx, cmd) { gctx.shell().warn(format!( "\ user-defined alias `{}` is shadowing an external subcommand found at: `{}` This was previously accepted but is being phased out; it will become a hard error in a future release. For more information, see issue #10049 .", cmd, path.display(), ))?; } } if commands::run::is_manifest_command(cmd) { if gctx.cli_unstable().script { return Ok((args, GlobalArgs::default())); } else { gctx.shell().warn(format_args!( "\ user-defined alias `{cmd}` has the appearance of a manifest-command This was previously accepted but will be phased out when `-Zscript` is stabilized. For more information, see issue #12207 ." ))?; } } let mut alias = alias .into_iter() .map(|s| OsString::from(s)) .collect::>(); alias.extend( sub_args .get_many::("") .unwrap_or_default() .cloned(), ); // new_args strips out everything before the subcommand, so // capture those global options now. // Note that an alias to an external command will not receive // these arguments. That may be confusing, but such is life. let global_args = GlobalArgs::new(sub_args); let new_args = cli(gctx).no_binary_name(true).try_get_matches_from(alias)?; let Some(new_cmd) = new_args.subcommand_name() else { return Err(anyhow!( "subcommand is required, add a subcommand to the command alias `alias.{cmd}`" ) .into()); }; already_expanded.push(cmd.to_string()); if already_expanded.contains(&new_cmd.to_string()) { // Crash if the aliases are corecursive / unresolvable return Err(anyhow!( "alias {} has unresolvable recursive definition: {} -> {}", already_expanded[0], already_expanded.join(" -> "), new_cmd, ) .into()); } let (expanded_args, _) = expand_aliases(gctx, new_args, already_expanded)?; return Ok((expanded_args, global_args)); } (None, Err(e)) => return Err(e.into()), } }; Ok((args, GlobalArgs::default())) } #[tracing::instrument(skip_all)] fn configure_gctx( gctx: &mut GlobalContext, args: &ArgMatches, subcommand_args: Option<&ArgMatches>, global_args: GlobalArgs, exec: Option<&Exec>, ) -> CliResult { let arg_target_dir = &subcommand_args.and_then(|a| a.value_of_path("target-dir", gctx)); let mut verbose = global_args.verbose + args.verbose(); // quiet is unusual because it is redefined in some subcommands in order // to provide custom help text. let mut quiet = args.flag("quiet") || subcommand_args.map(|a| a.flag("quiet")).unwrap_or_default() || global_args.quiet; if matches!(exec, Some(Exec::Manifest(_))) && !quiet { // Verbosity is shifted quieter for `Exec::Manifest` as it is can be used as if you ran // `cargo install` and we especially shouldn't pollute programmatic output. // // For now, interactive output has the same default output as `cargo run` but that is // subject to change. if let Some(lower) = verbose.checked_sub(1) { verbose = lower; } else if !gctx.shell().is_err_tty() { // Don't pollute potentially-scripted output quiet = true; } } let global_color = global_args.color; // Extract so it can take reference. let color = args .get_one::("color") .map(String::as_str) .or_else(|| global_color.as_deref()); let frozen = args.flag("frozen") || global_args.frozen; let locked = args.flag("locked") || global_args.locked; let offline = args.flag("offline") || global_args.offline; let mut unstable_flags = global_args.unstable_flags; if let Some(values) = args.get_many::("unstable-features") { unstable_flags.extend(values.cloned()); } let mut config_args = global_args.config_args; if let Some(values) = args.get_many::("config") { config_args.extend(values.cloned()); } gctx.configure( verbose, quiet, color, frozen, locked, offline, arg_target_dir, &unstable_flags, &config_args, )?; Ok(()) } enum Exec { Builtin(commands::Exec), Manifest(String), External(String), } impl Exec { /// Precedence isn't the most obvious from this function because /// - Some is determined by `expand_aliases` /// - Some is enforced by `avoid_ambiguity_between_builtins_and_manifest_commands` /// /// In actuality, it is: /// 1. built-ins xor manifest-command /// 2. aliases /// 3. external subcommands fn infer(cmd: &str) -> CargoResult { if let Some(exec) = commands::builtin_exec(cmd) { Ok(Self::Builtin(exec)) } else if commands::run::is_manifest_command(cmd) { Ok(Self::Manifest(cmd.to_owned())) } else { Ok(Self::External(cmd.to_owned())) } } #[tracing::instrument(skip_all)] fn exec(self, gctx: &mut GlobalContext, subcommand_args: &ArgMatches) -> CliResult { match self { Self::Builtin(exec) => exec(gctx, subcommand_args), Self::Manifest(cmd) => { let ext_path = super::find_external_subcommand(gctx, &cmd); if !gctx.cli_unstable().script && ext_path.is_some() { gctx.shell().warn(format_args!( "\ external subcommand `{cmd}` has the appearance of a manifest-command This was previously accepted but will be phased out when `-Zscript` is stabilized. For more information, see issue #12207 .", ))?; Self::External(cmd).exec(gctx, subcommand_args) } else { let ext_args: Vec = subcommand_args .get_many::("") .unwrap_or_default() .cloned() .collect(); commands::run::exec_manifest_command(gctx, &cmd, &ext_args) } } Self::External(cmd) => { let mut ext_args = vec![OsStr::new(&cmd)]; ext_args.extend( subcommand_args .get_many::("") .unwrap_or_default() .map(OsString::as_os_str), ); super::execute_external_subcommand(gctx, &cmd, &ext_args) } } } } #[derive(Default)] struct GlobalArgs { verbose: u32, quiet: bool, color: Option, frozen: bool, locked: bool, offline: bool, unstable_flags: Vec, config_args: Vec, } impl GlobalArgs { fn new(args: &ArgMatches) -> GlobalArgs { GlobalArgs { verbose: args.verbose(), quiet: args.flag("quiet"), color: args.get_one::("color").cloned(), frozen: args.flag("frozen"), locked: args.flag("locked"), offline: args.flag("offline"), unstable_flags: args .get_many::("unstable-features") .unwrap_or_default() .cloned() .collect(), config_args: args .get_many::("config") .unwrap_or_default() .cloned() .collect(), } } } #[tracing::instrument(skip_all)] pub fn cli(gctx: &GlobalContext) -> Command { // Don't let config errors get in the way of parsing arguments let term = gctx.get::("term").unwrap_or_default(); let color = term .color .and_then(|c| c.parse().ok()) .unwrap_or(ColorChoice::CargoAuto); let color = match color { ColorChoice::Always => clap::ColorChoice::Always, ColorChoice::Never => clap::ColorChoice::Never, ColorChoice::CargoAuto => clap::ColorChoice::Auto, }; let usage = if is_rustup() { color_print::cstr!( "cargo [+toolchain] [OPTIONS] [COMMAND]\n cargo [+toolchain] [OPTIONS] -Zscript <> [ARGS]..." ) } else { color_print::cstr!( "cargo [OPTIONS] [COMMAND]\n cargo [OPTIONS] -Zscript <> [ARGS]..." ) }; let styles = { clap::builder::styling::Styles::styled() .header(style::HEADER) .usage(style::USAGE) .literal(style::LITERAL) .placeholder(style::PLACEHOLDER) .error(style::ERROR) .valid(style::VALID) .invalid(style::INVALID) }; Command::new("cargo") // Subcommands all count their args' display order independently (from 0), // which makes their args interspersed with global args. This puts global args last. // // We also want these to come before auto-generated `--help` .next_display_order(800) .allow_external_subcommands(true) .color(color) .styles(styles) // Provide a custom help subcommand for calling into man pages .disable_help_subcommand(true) .override_usage(usage) .help_template(color_print::cstr!( "\ Rust's package manager Usage: {usage} Options: {options} Commands: build, b Compile the current package check, c Analyze the current package and report errors, but don't build object files clean Remove the target directory doc, d Build this package's and its dependencies' documentation new Create a new cargo package init Create a new cargo package in an existing directory add Add dependencies to a manifest file remove Remove dependencies from a manifest file run, r Run a binary or example of the local package test, t Run the tests bench Run the benchmarks update Update dependencies listed in Cargo.lock search Search registry for crates publish Package and upload this package to the registry install Install a Rust binary uninstall Uninstall a Rust binary ... See all commands with --list See 'cargo help <>' for more information on a specific command.\n", )) .arg(flag("version", "Print version info and exit").short('V')) .arg(flag("list", "List installed commands")) .arg( opt( "explain", "Provide a detailed explanation of a rustc error message", ) .value_name("CODE"), ) .arg( opt( "verbose", "Use verbose output (-vv very verbose/build.rs output)", ) .short('v') .action(ArgAction::Count) .global(true), ) .arg(flag("quiet", "Do not print cargo log messages").short('q').global(true)) .arg( opt("color", "Coloring") .value_name("WHEN") .global(true) .value_parser(["auto", "always", "never"]) .ignore_case(true), ) .arg( Arg::new("directory") .help("Change to DIRECTORY before doing anything (nightly-only)") .short('C') .value_name("DIRECTORY") .value_hint(clap::ValueHint::DirPath) .value_parser(clap::builder::ValueParser::path_buf()), ) .arg( flag("locked", "Assert that `Cargo.lock` will remain unchanged") .help_heading(heading::MANIFEST_OPTIONS) .global(true), ) .arg( flag("offline", "Run without accessing the network") .help_heading(heading::MANIFEST_OPTIONS) .global(true), ) .arg( flag("frozen", "Equivalent to specifying both --locked and --offline") .help_heading(heading::MANIFEST_OPTIONS) .global(true), ) // Better suggestion for the unsupported short config flag. .arg( Arg::new("unsupported-short-config-flag") .help("") .short('c') .value_parser(UnknownArgumentValueParser::suggest_arg("--config")) .action(ArgAction::SetTrue) .global(true) .hide(true)) .arg(multi_opt("config", "KEY=VALUE|PATH", "Override a configuration value").global(true)) // Better suggestion for the unsupported lowercase unstable feature flag. .arg( Arg::new("unsupported-lowercase-unstable-feature-flag") .help("") .short('z') .value_parser(UnknownArgumentValueParser::suggest_arg("-Z")) .action(ArgAction::SetTrue) .global(true) .hide(true)) .arg(Arg::new("unstable-features") .help("Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details") .short('Z') .value_name("FLAG") .action(ArgAction::Append) .global(true) .add(clap_complete::ArgValueCandidates::new(|| { let flags = CliUnstable::help(); flags.into_iter().map(|flag| { clap_complete::CompletionCandidate::new(flag.0.replace("_", "-")).help(flag.1.map(|help| { help.into() })) }).collect() }))) .add(clap_complete::engine::SubcommandCandidates::new(move || { let mut candidates = get_toolchains_from_rustup() .into_iter() .map(|t| clap_complete::CompletionCandidate::new(t)) .collect::>(); candidates.extend(get_alias_candidates()); candidates })) .subcommands(commands::builtin()) } fn get_toolchains_from_rustup() -> Vec { let output = std::process::Command::new("rustup") .arg("toolchain") .arg("list") .arg("-q") .output() .unwrap(); if !output.status.success() { return vec![]; } let stdout = String::from_utf8(output.stdout).unwrap(); stdout.lines().map(|line| format!("+{}", line)).collect() } fn get_alias_candidates() -> Vec { if let Ok(gctx) = new_gctx_for_completions() { let alias_map = user_defined_aliases(&gctx); return alias_map .iter() .map(|(alias, cmd_info)| { let help_text = match cmd_info { CommandInfo::Alias { target } => { let cmd_str = target .iter() .map(String::as_str) .collect::>() .join(" "); format!("alias for {}", cmd_str) } CommandInfo::BuiltIn { .. } => { unreachable!("BuiltIn command shouldn't appear in alias map") } CommandInfo::External { .. } => { unreachable!("External command shouldn't appear in alias map") } }; clap_complete::CompletionCandidate::new(alias.clone()).help(Some(help_text.into())) }) .collect(); } Vec::new() } #[test] fn verify_cli() { let gctx = GlobalContext::default().unwrap(); cli(&gctx).debug_assert(); } #[test] fn avoid_ambiguity_between_builtins_and_manifest_commands() { for cmd in commands::builtin() { let name = cmd.get_name(); assert!( !commands::run::is_manifest_command(&name), "built-in command {name} is ambiguous with manifest-commands" ) } } cargo-0.91.0/src/bin/cargo/commands/add.rs000064400000000000000000000355201046102023000163440ustar 00000000000000use cargo::sources::CRATES_IO_REGISTRY; use cargo::util::print_available_packages; use indexmap::IndexMap; use indexmap::IndexSet; use cargo::CargoResult; use cargo::core::FeatureValue; use cargo::core::dependency::DepKind; use cargo::ops::cargo_add::AddOptions; use cargo::ops::cargo_add::DepOp; use cargo::ops::cargo_add::add; use cargo::ops::resolve_ws; use cargo::util::command_prelude::*; use cargo::util::toml_mut::manifest::DepTable; pub fn cli() -> Command { clap::Command::new("add") .about("Add dependencies to a Cargo.toml manifest file") .override_usage( color_print::cstr!("\ cargo add [OPTIONS] <>[@<>] ... cargo add [OPTIONS] --path <> ... cargo add [OPTIONS] --git <> ..." )) .after_help(color_print::cstr!("Run `cargo help add` for more detailed information.\n")) .group(clap::ArgGroup::new("selected").multiple(true).required(true)) .args([ clap::Arg::new("crates") .value_name("DEP_ID") .num_args(0..) .help("Reference to a package to add as a dependency") .long_help( "Reference to a package to add as a dependency You can reference a package by: - ``, like `cargo add serde` (latest version will be used) - `@`, like `cargo add serde@1` or `cargo add serde@=1.0.38`" ) .group("selected"), flag("no-default-features", "Disable the default features"), flag("default-features", "Re-enable the default features") .overrides_with("no-default-features"), clap::Arg::new("features") .short('F') .long("features") .value_name("FEATURES") .action(ArgAction::Append) .help("Space or comma separated list of features to activate"), flag("optional", "Mark the dependency as optional") .long_help("Mark the dependency as optional The package name will be exposed as feature of your crate.") .conflicts_with("dev"), flag("no-optional", "Mark the dependency as required") .long_help("Mark the dependency as required The package will be removed from your features.") .conflicts_with("dev") .overrides_with("optional"), flag("public", "Mark the dependency as public (unstable)") .conflicts_with("dev") .conflicts_with("build") .long_help("Mark the dependency as public (unstable) The dependency can be referenced in your library's public API."), flag("no-public", "Mark the dependency as private (unstable)") .conflicts_with("dev") .conflicts_with("build") .overrides_with("public") .long_help("Mark the dependency as private (unstable) While you can use the crate in your implementation, it cannot be referenced in your public API."), clap::Arg::new("rename") .long("rename") .action(ArgAction::Set) .value_name("NAME") .help("Rename the dependency") .long_help("Rename the dependency Example uses: - Depending on multiple versions of a crate - Depend on crates with the same name from different registries"), ]) .arg_manifest_path_without_unsupported_path_tip() .arg_lockfile_path() .arg_package("Package to modify") .arg_ignore_rust_version() .arg_dry_run("Don't actually write the manifest") .arg_silent_suggestion() .next_help_heading("Source") .args([ clap::Arg::new("path") .long("path") .action(ArgAction::Set) .value_name("PATH") .help("Filesystem path to local crate to add") .group("selected") .conflicts_with("git") .add(clap_complete::engine::ArgValueCompleter::new( clap_complete::engine::PathCompleter::any() .filter(|path| path.join("Cargo.toml").exists()), )), clap::Arg::new("base") .long("base") .action(ArgAction::Set) .value_name("BASE") .help("The path base to use when adding from a local crate (unstable).") .requires("path"), clap::Arg::new("git") .long("git") .action(ArgAction::Set) .value_name("URI") .help("Git repository location") .long_help("Git repository location Without any other information, cargo will use latest commit on the main branch.") .group("selected"), clap::Arg::new("branch") .long("branch") .action(ArgAction::Set) .value_name("BRANCH") .help("Git branch to download the crate from") .requires("git") .group("git-ref"), clap::Arg::new("tag") .long("tag") .action(ArgAction::Set) .value_name("TAG") .help("Git tag to download the crate from") .requires("git") .group("git-ref"), clap::Arg::new("rev") .long("rev") .action(ArgAction::Set) .value_name("REV") .help("Git reference to download the crate from") .long_help("Git reference to download the crate from This is the catch all, handling hashes to named references in remote repositories.") .requires("git") .group("git-ref"), clap::Arg::new("registry") .long("registry") .action(ArgAction::Set) .value_name("NAME") .help("Package registry for this dependency") .add(clap_complete::ArgValueCandidates::new(|| { let candidates = get_registry_candidates(); candidates.unwrap_or_default() })), ]) .next_help_heading("Section") .args([ flag("dev", "Add as development dependency") .long_help("Add as development dependency Dev-dependencies are not used when compiling a package for building, but are used for compiling tests, examples, and benchmarks. These dependencies are not propagated to other packages which depend on this package.") .group("section"), flag("build", "Add as build dependency") .long_help("Add as build dependency Build-dependencies are the only dependencies available for use by build scripts (`build.rs` files).") .group("section"), clap::Arg::new("target") .long("target") .action(ArgAction::Set) .value_name("TARGET") .value_parser(clap::builder::NonEmptyStringValueParser::new()) .help("Add as dependency to the given target platform") ]) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let dry_run = args.dry_run(); let section = parse_section(args); let ws = args.workspace(gctx)?; if args.is_present_with_zero_values("package") { print_available_packages(&ws)?; } let packages = args.packages_from_flags()?; let packages = packages.get_packages(&ws)?; let spec = match packages.len() { 0 => { return Err(CliError::new( anyhow::format_err!( "no packages selected to modify. Please specify one with `-p `" ), 101, )); } 1 => packages[0], _ => { let names = packages.iter().map(|p| p.name()).collect::>(); return Err(CliError::new( anyhow::format_err!( "`cargo add` could not determine which package to modify. \ Use the `--package` option to specify a package. \n\ available packages: {}", names.join(", ") ), 101, )); } }; let dependencies = parse_dependencies(gctx, args)?; let honor_rust_version = args.honor_rust_version(); let options = AddOptions { gctx, spec, dependencies, section, dry_run, honor_rust_version, }; add(&ws, &options)?; // Reload the workspace since we've changed dependencies let ws = args.workspace(gctx)?; resolve_ws(&ws, dry_run)?; Ok(()) } fn parse_dependencies(gctx: &GlobalContext, matches: &ArgMatches) -> CargoResult> { let path = matches.get_one::("path"); let base = matches.get_one::("base"); let git = matches.get_one::("git"); let branch = matches.get_one::("branch"); let rev = matches.get_one::("rev"); let tag = matches.get_one::("tag"); let rename = matches.get_one::("rename"); let registry = match matches.registry(gctx)? { Some(reg) if reg == CRATES_IO_REGISTRY => None, reg => reg, }; let default_features = default_features(matches); let optional = optional(matches); let public = public(matches); let mut crates = matches .get_many::("crates") .into_iter() .flatten() .map(|c| (Some(c.clone()), None)) .collect::>(); let mut infer_crate_name = false; for (crate_name, _) in crates.iter() { let crate_name = crate_name.as_ref().unwrap(); if let Some(toolchain) = crate_name.strip_prefix("+") { anyhow::bail!( "invalid character `+` in dependency name: `+{toolchain}` Use `cargo +{toolchain} add` if you meant to use the `{toolchain}` toolchain." ); } } if crates.is_empty() { if path.is_some() || git.is_some() { crates.insert(None, None); infer_crate_name = true; } else { unreachable!("clap should ensure we have some source selected"); } } for feature in matches .get_many::("features") .into_iter() .flatten() .map(String::as_str) .flat_map(parse_feature) { let parsed_value = FeatureValue::new(feature.into()); match parsed_value { FeatureValue::Feature(_) => { if 1 < crates.len() { let candidates = crates .keys() .map(|c| { format!( "`{}/{}`", c.as_deref().expect("only none when there is 1"), feature ) }) .collect::>(); anyhow::bail!( "feature `{feature}` must be qualified by the dependency it's being activated for, like {}", candidates.join(", ") ); } crates .first_mut() .expect("always at least one crate") .1 .get_or_insert_with(IndexSet::new) .insert(feature.to_owned()); } FeatureValue::Dep { .. } => { anyhow::bail!("feature `{feature}` is not allowed to use explicit `dep:` syntax",) } FeatureValue::DepFeature { dep_name, dep_feature, .. } => { if infer_crate_name { anyhow::bail!( "`{feature}` is unsupported when inferring the crate name, use `{dep_feature}`" ); } if dep_feature.contains('/') { anyhow::bail!("multiple slashes in feature `{feature}` is not allowed"); } crates.get_mut(&Some(dep_name.as_str().to_owned())).ok_or_else(|| { anyhow::format_err!("feature `{dep_feature}` activated for crate `{dep_name}` but the crate wasn't specified") })? .get_or_insert_with(IndexSet::new) .insert(dep_feature.as_str().to_owned()); } } } let mut deps: Vec = Vec::new(); for (crate_spec, features) in crates { let dep = DepOp { crate_spec, rename: rename.map(String::from), features, default_features, optional, public, registry: registry.clone(), path: path.map(String::from), base: base.map(String::from), git: git.map(String::from), branch: branch.map(String::from), rev: rev.map(String::from), tag: tag.map(String::from), }; deps.push(dep); } if deps.len() > 1 && rename.is_some() { anyhow::bail!("cannot specify multiple crates with `--rename`"); } Ok(deps) } fn default_features(matches: &ArgMatches) -> Option { resolve_bool_arg( matches.flag("default-features"), matches.flag("no-default-features"), ) } fn optional(matches: &ArgMatches) -> Option { resolve_bool_arg(matches.flag("optional"), matches.flag("no-optional")) } fn public(matches: &ArgMatches) -> Option { resolve_bool_arg(matches.flag("public"), matches.flag("no-public")) } fn resolve_bool_arg(yes: bool, no: bool) -> Option { match (yes, no) { (true, false) => Some(true), (false, true) => Some(false), (false, false) => None, (_, _) => unreachable!("clap should make this impossible"), } } fn parse_section(matches: &ArgMatches) -> DepTable { let kind = if matches.flag("dev") { DepKind::Development } else if matches.flag("build") { DepKind::Build } else { DepKind::Normal }; let mut table = DepTable::new().set_kind(kind); if let Some(target) = matches.get_one::("target") { assert!(!target.is_empty(), "Target specification may not be empty"); table = table.set_target(target); } table } /// Split feature flag list fn parse_feature(feature: &str) -> impl Iterator { // Not re-using `CliFeatures` because it uses a BTreeSet and loses user's ordering feature .split_whitespace() .flat_map(|s| s.split(',')) .filter(|s| !s.is_empty()) } cargo-0.91.0/src/bin/cargo/commands/bench.rs000064400000000000000000000054721046102023000166760ustar 00000000000000use crate::command_prelude::*; use cargo::ops::{self, TestOptions}; pub fn cli() -> Command { subcommand("bench") .about("Execute all benchmarks of a local package") .next_display_order(0) .arg( Arg::new("BENCHNAME") .action(ArgAction::Set) .help("If specified, only run benches containing this string in their names"), ) .arg( Arg::new("args") .value_name("ARGS") .help("Arguments for the bench binary") .num_args(0..) .last(true), ) .arg(flag("no-run", "Compile, but don't run benchmarks")) .arg(flag( "no-fail-fast", "Run all benchmarks regardless of failure", )) .arg_message_format() .arg_silent_suggestion() .arg_package_spec( "Package to run benchmarks for", "Benchmark all packages in the workspace", "Exclude packages from the benchmark", ) .arg_targets_all( "Benchmark only this package's library", "Benchmark only the specified binary", "Benchmark all binaries", "Benchmark only the specified example", "Benchmark all examples", "Benchmark only the specified test target", "Benchmark all targets that have `test = true` set", "Benchmark only the specified bench target", "Benchmark all targets that have `bench = true` set", "Benchmark all targets", ) .arg_features() .arg_jobs() .arg_unsupported_keep_going() .arg_profile("Build artifacts with the specified profile") .arg_target_triple("Build for the target triple") .arg_target_dir() .arg_unit_graph() .arg_timings() .arg_manifest_path() .arg_lockfile_path() .arg_ignore_rust_version() .after_help(color_print::cstr!( "Run `cargo help bench` for more detailed information.\n" )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let ws = args.workspace(gctx)?; let mut compile_opts = args.compile_options(gctx, UserIntent::Bench, Some(&ws), ProfileChecking::Custom)?; compile_opts.build_config.requested_profile = args.get_profile_name("bench", ProfileChecking::Custom)?; let ops = TestOptions { no_run: args.flag("no-run"), no_fail_fast: args.flag("no-fail-fast"), compile_opts, }; let bench_args = args.get_one::("BENCHNAME").into_iter(); let bench_args = bench_args.chain(args.get_many::("args").unwrap_or_default()); let bench_args = bench_args.map(String::as_str).collect::>(); ops::run_benches(&ws, &ops, &bench_args) } cargo-0.91.0/src/bin/cargo/commands/build.rs000064400000000000000000000065601046102023000167150ustar 00000000000000use crate::command_prelude::*; use cargo::ops; pub fn cli() -> Command { subcommand("build") // subcommand aliases are handled in aliased_command() // .alias("b") .about("Compile a local package and all of its dependencies") .arg_future_incompat_report() .arg_message_format() .arg_silent_suggestion() .arg_package_spec( "Package to build (see `cargo help pkgid`)", "Build all packages in the workspace", "Exclude packages from the build", ) .arg_targets_all( "Build only this package's library", "Build only the specified binary", "Build all binaries", "Build only the specified example", "Build all examples", "Build only the specified test target", "Build all targets that have `test = true` set", "Build only the specified bench target", "Build all targets that have `bench = true` set", "Build all targets", ) .arg_features() .arg_release("Build artifacts in release mode, with optimizations") .arg_redundant_default_mode("debug", "build", "release") .arg_profile("Build artifacts with the specified profile") .arg_parallel() .arg_target_triple("Build for the target triple") .arg_target_dir() .arg_artifact_dir() .arg_build_plan() .arg_unit_graph() .arg_timings() .arg_compile_time_deps() .arg_manifest_path() .arg_lockfile_path() .arg_ignore_rust_version() .after_help(color_print::cstr!( "Run `cargo help build` for more detailed information.\n" )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let ws = args.workspace(gctx)?; let mut compile_opts = args.compile_options(gctx, UserIntent::Build, Some(&ws), ProfileChecking::Custom)?; if let Some(artifact_dir) = args.value_of_path("artifact-dir", gctx) { // If the user specifies `--artifact-dir`, use that compile_opts.build_config.export_dir = Some(artifact_dir); } else if let Some(artifact_dir) = args.value_of_path("out-dir", gctx) { // `--out-dir` is deprecated, but still supported for now gctx.shell() .warn("the --out-dir flag has been changed to --artifact-dir")?; compile_opts.build_config.export_dir = Some(artifact_dir); } else if let Some(artifact_dir) = gctx.build_config()?.artifact_dir.as_ref() { // If a CLI option is not specified for choosing the artifact dir, use the `artifact-dir` from the build config, if // present let artifact_dir = artifact_dir.resolve_path(gctx); compile_opts.build_config.export_dir = Some(artifact_dir); } else if let Some(artifact_dir) = gctx.build_config()?.out_dir.as_ref() { // As a last priority, check `out-dir` in the build config gctx.shell() .warn("the out-dir config option has been changed to artifact-dir")?; let artifact_dir = artifact_dir.resolve_path(gctx); compile_opts.build_config.export_dir = Some(artifact_dir); } if compile_opts.build_config.export_dir.is_some() { gctx.cli_unstable() .fail_if_stable_opt("--artifact-dir", 6790)?; } ops::compile(&ws, &compile_opts)?; Ok(()) } cargo-0.91.0/src/bin/cargo/commands/check.rs000064400000000000000000000041371046102023000166710ustar 00000000000000use crate::command_prelude::*; use cargo::ops; pub fn cli() -> Command { subcommand("check") // subcommand aliases are handled in aliased_command() // .alias("c") .about("Check a local package and all of its dependencies for errors") .arg_future_incompat_report() .arg_message_format() .arg_silent_suggestion() .arg_package_spec( "Package(s) to check", "Check all packages in the workspace", "Exclude packages from the check", ) .arg_targets_all( "Check only this package's library", "Check only the specified binary", "Check all binaries", "Check only the specified example", "Check all examples", "Check only the specified test target", "Check all targets that have `test = true` set", "Check only the specified bench target", "Check all targets that have `bench = true` set", "Check all targets", ) .arg_features() .arg_parallel() .arg_release("Check artifacts in release mode, with optimizations") .arg_profile("Check artifacts with the specified profile") .arg_target_triple("Check for the target triple") .arg_target_dir() .arg_unit_graph() .arg_timings() .arg_compile_time_deps() .arg_manifest_path() .arg_lockfile_path() .arg_ignore_rust_version() .after_help(color_print::cstr!( "Run `cargo help check` for more detailed information.\n" )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let ws = args.workspace(gctx)?; // This is a legacy behavior that causes `cargo check` to pass `--test`. let test = matches!( args.get_one::("profile").map(String::as_str), Some("test") ); let intent = UserIntent::Check { test }; let compile_opts = args.compile_options(gctx, intent, Some(&ws), ProfileChecking::LegacyTestOnly)?; ops::compile(&ws, &compile_opts)?; Ok(()) } cargo-0.91.0/src/bin/cargo/commands/clean.rs000064400000000000000000000167541046102023000167060ustar 00000000000000use crate::command_prelude::*; use crate::util::cache_lock::CacheLockMode; use cargo::core::gc::Gc; use cargo::core::gc::{GcOpts, parse_human_size, parse_time_span}; use cargo::core::global_cache_tracker::GlobalCacheTracker; use cargo::ops::CleanContext; use cargo::ops::{self, CleanOptions}; use cargo::util::print_available_packages; use std::time::Duration; pub fn cli() -> Command { subcommand("clean") .about("Remove artifacts that cargo has generated in the past") .arg_doc("Whether or not to clean just the documentation directory") .arg_silent_suggestion() .arg_package_spec_simple("Package to clean artifacts for") .arg_release("Whether or not to clean release artifacts") .arg_profile("Clean artifacts of the specified profile") .arg_target_triple("Target triple to clean output for") .arg_target_dir() .arg_manifest_path() .arg_lockfile_path() .arg_dry_run("Display what would be deleted without deleting anything") .args_conflicts_with_subcommands(true) .subcommand( subcommand("gc") .about("Clean global caches") .hide(true) .arg_silent_suggestion() .arg_dry_run("Display what would be deleted without deleting anything") // NOTE: Not all of these options may get stabilized. Some of them are // very low-level details, and may not be something typical users need. .arg( opt( "max-src-age", "Deletes source cache files that have not been used \ since the given age (unstable)", ) .value_name("DURATION") .value_parser(parse_time_span), ) .arg( opt( "max-crate-age", "Deletes crate cache files that have not been used \ since the given age (unstable)", ) .value_name("DURATION") .value_parser(parse_time_span), ) .arg( opt( "max-index-age", "Deletes registry indexes that have not been used \ since the given age (unstable)", ) .value_name("DURATION") .value_parser(parse_time_span), ) .arg( opt( "max-git-co-age", "Deletes git dependency checkouts that have not been used \ since the given age (unstable)", ) .value_name("DURATION") .value_parser(parse_time_span), ) .arg( opt( "max-git-db-age", "Deletes git dependency clones that have not been used \ since the given age (unstable)", ) .value_name("DURATION") .value_parser(parse_time_span), ) .arg( opt( "max-download-age", "Deletes any downloaded cache data that has not been used \ since the given age (unstable)", ) .value_name("DURATION") .value_parser(parse_time_span), ) .arg( opt( "max-src-size", "Deletes source cache files until the cache is under the \ given size (unstable)", ) .value_name("SIZE") .value_parser(parse_human_size), ) .arg( opt( "max-crate-size", "Deletes crate cache files until the cache is under the \ given size (unstable)", ) .value_name("SIZE") .value_parser(parse_human_size), ) .arg( opt( "max-git-size", "Deletes git dependency caches until the cache is under \ the given size (unstable)", ) .value_name("SIZE") .value_parser(parse_human_size), ) .arg( opt( "max-download-size", "Deletes downloaded cache data until the cache is under \ the given size (unstable)", ) .value_name("SIZE") .value_parser(parse_human_size), ), ) .after_help(color_print::cstr!( "Run `cargo help clean` for more detailed information.\n" )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { match args.subcommand() { Some(("gc", args)) => { return gc(gctx, args); } Some((cmd, _)) => { unreachable!("unexpected command {}", cmd) } None => {} } let ws = args.workspace(gctx)?; if args.is_present_with_zero_values("package") { print_available_packages(&ws)?; } let opts = CleanOptions { gctx, spec: values(args, "package"), targets: args.targets()?, requested_profile: args.get_profile_name("dev", ProfileChecking::Custom)?, profile_specified: args.contains_id("profile") || args.flag("release"), doc: args.flag("doc"), dry_run: args.dry_run(), }; ops::clean(&ws, &opts)?; Ok(()) } fn gc(gctx: &GlobalContext, args: &ArgMatches) -> CliResult { gctx.cli_unstable().fail_if_stable_command( gctx, "clean gc", 12633, "gc", gctx.cli_unstable().gc, )?; let size_opt = |opt| -> Option { args.get_one::(opt).copied() }; let duration_opt = |opt| -> Option { args.get_one::(opt).copied() }; let mut gc_opts = GcOpts { max_src_age: duration_opt("max-src-age"), max_crate_age: duration_opt("max-crate-age"), max_index_age: duration_opt("max-index-age"), max_git_co_age: duration_opt("max-git-co-age"), max_git_db_age: duration_opt("max-git-db-age"), max_src_size: size_opt("max-src-size"), max_crate_size: size_opt("max-crate-size"), max_git_size: size_opt("max-git-size"), max_download_size: size_opt("max-download-size"), }; if let Some(age) = duration_opt("max-download-age") { gc_opts.set_max_download_age(age); } // If the user sets any options, then only perform the options requested. // If no options are set, do the default behavior. if !gc_opts.is_download_cache_opt_set() { gc_opts.update_for_auto_gc(gctx)?; } let _lock = gctx.acquire_package_cache_lock(CacheLockMode::MutateExclusive)?; let mut cache_track = GlobalCacheTracker::new(&gctx)?; let mut gc = Gc::new(gctx, &mut cache_track)?; let mut clean_ctx = CleanContext::new(gctx); clean_ctx.dry_run = args.dry_run(); gc.gc(&mut clean_ctx, &gc_opts)?; clean_ctx.display_summary()?; Ok(()) } cargo-0.91.0/src/bin/cargo/commands/config.rs000064400000000000000000000036741046102023000170660ustar 00000000000000use crate::command_prelude::*; use cargo::ops::cargo_config; pub fn cli() -> Command { subcommand("config") .about("Inspect configuration values") .subcommand_required(true) .arg_required_else_help(true) .subcommand( subcommand("get") .arg( Arg::new("key") .action(ArgAction::Set) .help("The config key to display"), ) .arg( opt("format", "Display format") .value_parser(cargo_config::ConfigFormat::POSSIBLE_VALUES) .default_value("toml"), ) .arg(flag( "show-origin", "Display where the config value is defined", )) .arg( opt("merged", "Whether or not to merge config values") .value_parser(["yes", "no"]) .default_value("yes"), ), ) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { gctx.cli_unstable().fail_if_stable_command( gctx, "config", 9301, "unstable-options", gctx.cli_unstable().unstable_options, )?; match args.subcommand() { Some(("get", args)) => { let opts = cargo_config::GetOptions { key: args.get_one::("key").map(String::as_str), format: args.get_one::("format").unwrap().parse()?, show_origin: args.flag("show-origin"), merged: args.get_one::("merged").map(String::as_str) == Some("yes"), }; cargo_config::get(gctx, &opts)?; } Some((cmd, _)) => { unreachable!("unexpected command {}", cmd) } None => { unreachable!("unexpected command") } } Ok(()) } cargo-0.91.0/src/bin/cargo/commands/doc.rs000064400000000000000000000042411046102023000163550ustar 00000000000000use crate::command_prelude::*; use cargo::ops::{self, DocOptions}; pub fn cli() -> Command { subcommand("doc") // subcommand aliases are handled in aliased_command() // .alias("d") .about("Build a package's documentation") .arg(flag( "open", "Opens the docs in a browser after the operation", )) .arg(flag( "no-deps", "Don't build documentation for dependencies", )) .arg(flag("document-private-items", "Document private items")) .arg_message_format() .arg_silent_suggestion() .arg_package_spec( "Package to document", "Document all packages in the workspace", "Exclude packages from the build", ) .arg_features() .arg_targets_lib_bin_example( "Document only this package's library", "Document only the specified binary", "Document all binaries", "Document only the specified example", "Document all examples", ) .arg_parallel() .arg_release("Build artifacts in release mode, with optimizations") .arg_profile("Build artifacts with the specified profile") .arg_target_triple("Build for the target triple") .arg_target_dir() .arg_unit_graph() .arg_timings() .arg_manifest_path() .arg_lockfile_path() .arg_ignore_rust_version() .after_help(color_print::cstr!( "Run `cargo help doc` for more detailed information.\n" )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let ws = args.workspace(gctx)?; let intent = UserIntent::Doc { deps: !args.flag("no-deps"), json: false, }; let mut compile_opts = args.compile_options(gctx, intent, Some(&ws), ProfileChecking::Custom)?; compile_opts.rustdoc_document_private_items = args.flag("document-private-items"); let doc_opts = DocOptions { open_result: args.flag("open"), output_format: ops::OutputFormat::Html, compile_opts, }; ops::doc(&ws, &doc_opts)?; Ok(()) } cargo-0.91.0/src/bin/cargo/commands/fetch.rs000064400000000000000000000013441046102023000167020ustar 00000000000000use crate::command_prelude::*; use cargo::ops; use cargo::ops::FetchOptions; pub fn cli() -> Command { subcommand("fetch") .about("Fetch dependencies of a package from the network") .arg_silent_suggestion() .arg_target_triple("Fetch dependencies for the target triple") .arg_manifest_path() .arg_lockfile_path() .after_help(color_print::cstr!( "Run `cargo help fetch` for more detailed information.\n" )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let ws = args.workspace(gctx)?; let opts = FetchOptions { gctx, targets: args.targets()?, }; let _ = ops::fetch(&ws, &opts)?; Ok(()) } cargo-0.91.0/src/bin/cargo/commands/fix.rs000064400000000000000000000100371046102023000163760ustar 00000000000000use crate::command_prelude::*; use cargo::core::Workspace; use cargo::ops; pub fn cli() -> Command { subcommand("fix") .about("Automatically fix lint warnings reported by rustc") .arg(flag("edition", "Fix in preparation for the next edition")) .arg(flag( "edition-idioms", "Fix warnings to migrate to the idioms of an edition", )) .arg(flag( "broken-code", "Fix code even if it already has compiler errors", )) .arg(flag( "allow-no-vcs", "Fix code even if a VCS was not detected", )) .arg(flag( "allow-dirty", "Fix code even if the working directory is dirty or has staged changes", )) .arg(flag( "allow-staged", "Fix code even if the working directory has staged changes", )) .arg_message_format() .arg_silent_suggestion() .arg_package_spec( "Package(s) to fix", "Fix all packages in the workspace", "Exclude packages from the fixes", ) .arg_targets_all( "Fix only this package's library", "Fix only the specified binary", "Fix all binaries", "Fix only the specified example", "Fix all examples", "Fix only the specified test target", "Fix all targets that have `test = true` set", "Fix only the specified bench target", "Fix all targets that have `bench = true` set", "Fix all targets (default)", ) .arg_features() .arg_parallel() .arg_release("Fix artifacts in release mode, with optimizations") .arg_profile("Build artifacts with the specified profile") .arg_target_triple("Fix for the target triple") .arg_target_dir() .arg_timings() .arg_manifest_path() .arg_lockfile_path() .arg_ignore_rust_version() .after_help(color_print::cstr!( "Run `cargo help fix` for more detailed information.\n" )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { // This is a legacy behavior that causes `cargo fix` to pass `--test`. let test = matches!( args.get_one::("profile").map(String::as_str), Some("test") ); let intent = UserIntent::Check { test }; // Unlike other commands default `cargo fix` to all targets to fix as much // code as we can. let root_manifest = args.root_manifest(gctx)?; // Can't use workspace() to avoid using -Zavoid-dev-deps (if passed) let mut ws = Workspace::new(&root_manifest, gctx)?; ws.set_resolve_honors_rust_version(args.honor_rust_version()); let lockfile_path = args.lockfile_path(gctx)?; ws.set_requested_lockfile_path(lockfile_path.clone()); let mut opts = args.compile_options(gctx, intent, Some(&ws), ProfileChecking::LegacyTestOnly)?; let edition = args.flag("edition") || args.flag("edition-idioms"); if !opts.filter.is_specific() && edition { // When `cargo fix` is run without specifying targets but with `--edition` or `--edition-idioms`, // it should default to fixing all targets. // See: https://github.com/rust-lang/cargo/issues/13527 opts.filter = ops::CompileFilter::new_all_targets(); } let allow_dirty = args.flag("allow-dirty"); let mut opts = ops::FixOptions { edition: args .flag("edition") .then_some(ops::EditionFixMode::NextRelative), idioms: args.flag("edition-idioms"), compile_opts: opts, allow_dirty, allow_staged: allow_dirty || args.flag("allow-staged"), allow_no_vcs: args.flag("allow-no-vcs"), broken_code: args.flag("broken-code"), requested_lockfile_path: lockfile_path, }; if let Some(fe) = &gctx.cli_unstable().fix_edition { ops::fix_edition(gctx, &ws, &mut opts, fe)?; } else { ops::fix(gctx, &ws, &mut opts)?; } Ok(()) } cargo-0.91.0/src/bin/cargo/commands/generate_lockfile.rs000064400000000000000000000012171046102023000212520ustar 00000000000000use crate::command_prelude::*; use cargo::ops; pub fn cli() -> Command { subcommand("generate-lockfile") .about("Generate the lockfile for a package") .arg_silent_suggestion() .arg_manifest_path() .arg_lockfile_path() .arg_ignore_rust_version_with_help("Ignore `rust-version` specification in packages") .after_help(color_print::cstr!( "Run `cargo help generate-lockfile` for more detailed information.\n" )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let ws = args.workspace(gctx)?; ops::generate_lockfile(&ws)?; Ok(()) } cargo-0.91.0/src/bin/cargo/commands/git_checkout.rs000064400000000000000000000006271046102023000202640ustar 00000000000000//! Removed. use crate::command_prelude::*; const REMOVED: &str = "The `git-checkout` command has been removed."; pub fn cli() -> Command { subcommand("git-checkout") .about("REMOVED: This command has been removed") .hide(true) .override_help(REMOVED) } pub fn exec(_gctx: &mut GlobalContext, _args: &ArgMatches) -> CliResult { Err(anyhow::format_err!(REMOVED).into()) } cargo-0.91.0/src/bin/cargo/commands/help.rs000064400000000000000000000130071046102023000165400ustar 00000000000000use crate::aliased_command; use crate::command_prelude::*; use cargo::drop_println; use cargo::util::errors::CargoResult; use cargo_util::paths::resolve_executable; use flate2::read::GzDecoder; use std::ffi::OsStr; use std::ffi::OsString; use std::io::Read; use std::io::Write; use std::path::Path; const COMPRESSED_MAN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/man.tgz")); pub fn cli() -> Command { subcommand("help") .about("Displays help for a cargo subcommand") .arg(Arg::new("COMMAND").action(ArgAction::Set).add( clap_complete::ArgValueCandidates::new(|| { super::builtin() .iter() .map(|cmd| { let name = cmd.get_name(); clap_complete::CompletionCandidate::new(name) .help(cmd.get_about().cloned()) .hide(cmd.is_hide_set()) }) .collect() }), )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let subcommand = args.get_one::("COMMAND"); if let Some(subcommand) = subcommand { if !try_help(gctx, subcommand)? { match check_builtin(&subcommand) { Some(s) => { crate::execute_internal_subcommand( gctx, &[OsStr::new(s), OsStr::new("--help")], )?; } None => { crate::execute_external_subcommand( gctx, subcommand, &[OsStr::new(subcommand), OsStr::new("--help")], )?; } } } } else { let mut cmd = crate::cli::cli(gctx); let _ = cmd.print_help(); } Ok(()) } fn try_help(gctx: &GlobalContext, subcommand: &str) -> CargoResult { let subcommand = match check_alias(gctx, subcommand) { // If this alias is more than a simple subcommand pass-through, show the alias. Some(argv) if argv.len() > 1 => { let alias = argv.join(" "); drop_println!(gctx, "`{}` is aliased to `{}`", subcommand, alias); return Ok(true); } // Otherwise, resolve the alias into its subcommand. Some(argv) => { // An alias with an empty argv can be created via `"empty-alias" = ""`. let first = argv.get(0).map(String::as_str).unwrap_or(subcommand); first.to_string() } None => subcommand.to_string(), }; let subcommand = match check_builtin(&subcommand) { Some(s) => s, None => return Ok(false), }; if resolve_executable(Path::new("man")).is_ok() { let man = match extract_man(subcommand, "1") { Some(man) => man, None => return Ok(false), }; write_and_spawn(subcommand, &man, "man")?; } else { let txt = match extract_man(subcommand, "txt") { Some(txt) => txt, None => return Ok(false), }; if resolve_executable(Path::new("less")).is_ok() { write_and_spawn(subcommand, &txt, "less")?; } else if resolve_executable(Path::new("more")).is_ok() { write_and_spawn(subcommand, &txt, "more")?; } else { drop(std::io::stdout().write_all(&txt)); } } Ok(true) } /// Checks if the given subcommand is an alias. /// /// Returns None if it is not an alias. fn check_alias(gctx: &GlobalContext, subcommand: &str) -> Option> { aliased_command(gctx, subcommand).ok().flatten() } /// Checks if the given subcommand is a built-in command (not via an alias). /// /// Returns None if it is not a built-in command. fn check_builtin(subcommand: &str) -> Option<&str> { super::builtin_exec(subcommand).map(|_| subcommand) } /// Extracts the given man page from the compressed archive. /// /// Returns None if the command wasn't found. fn extract_man(subcommand: &str, extension: &str) -> Option> { let extract_name = OsString::from(format!("cargo-{}.{}", subcommand, extension)); let gz = GzDecoder::new(COMPRESSED_MAN); let mut ar = tar::Archive::new(gz); // Unwraps should be safe here, since this is a static archive generated // by our build script. It should never be an invalid format! for entry in ar.entries().unwrap() { let mut entry = entry.unwrap(); let path = entry.path().unwrap(); if path.file_name().unwrap() != extract_name { continue; } let mut result = Vec::new(); entry.read_to_end(&mut result).unwrap(); return Some(result); } None } /// Write the contents of a man page to disk and spawn the given command to /// display it. fn write_and_spawn(name: &str, contents: &[u8], command: &str) -> CargoResult<()> { let prefix = format!("cargo-{}.", name); let mut tmp = tempfile::Builder::new().prefix(&prefix).tempfile()?; let f = tmp.as_file_mut(); f.write_all(contents)?; f.flush()?; let path = tmp.path(); // Use a path relative to the temp directory so that it can work on // cygwin/msys systems which don't handle windows-style paths. let mut relative_name = std::ffi::OsString::from("./"); relative_name.push(path.file_name().unwrap()); let mut cmd = std::process::Command::new(command) .arg(relative_name) .current_dir(path.parent().unwrap()) .spawn()?; drop(cmd.wait()); Ok(()) } cargo-0.91.0/src/bin/cargo/commands/info.rs000064400000000000000000000022141046102023000165410ustar 00000000000000use anyhow::Context; use cargo::ops::info; use cargo::util::command_prelude::*; use cargo_util_schemas::core::PackageIdSpec; pub fn cli() -> Command { Command::new("info") .about("Display information about a package") .arg( Arg::new("package") .required(true) .value_name("SPEC") .help_heading(heading::PACKAGE_SELECTION) .help("Package to inspect"), ) .arg_index("Registry index URL to search packages in") .arg_registry("Registry to search packages in") .arg_silent_suggestion() .after_help(color_print::cstr!( "Run `cargo help info` for more detailed information.\n" )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let package = args .get_one::("package") .map(String::as_str) .unwrap(); let spec = PackageIdSpec::parse(package) .with_context(|| format!("invalid package ID specification: `{package}`"))?; let reg_or_index = args.registry_or_index(gctx)?; info(&spec, gctx, reg_or_index)?; Ok(()) } cargo-0.91.0/src/bin/cargo/commands/init.rs000064400000000000000000000013231046102023000165510ustar 00000000000000use crate::command_prelude::*; use cargo::ops; pub fn cli() -> Command { subcommand("init") .about("Create a new cargo package in an existing directory") .arg( Arg::new("path") .value_name("PATH") .action(ArgAction::Set) .default_value("."), ) .arg_new_opts() .arg_registry("Registry to use") .arg_silent_suggestion() .after_help(color_print::cstr!( "Run `cargo help init` for more detailed information.\n" )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let opts = args.new_options(gctx)?; ops::init(&opts, gctx)?; Ok(()) } cargo-0.91.0/src/bin/cargo/commands/install.rs000064400000000000000000000277361046102023000172740ustar 00000000000000use crate::command_prelude::*; use anyhow::anyhow; use anyhow::bail; use anyhow::format_err; use cargo::CargoResult; use cargo::core::{GitReference, SourceId, Workspace}; use cargo::ops; use cargo::util::IntoUrl; use cargo::util::VersionExt; use cargo_util_schemas::manifest::PackageName; use itertools::Itertools; use semver::VersionReq; use cargo_util::paths; pub fn cli() -> Command { subcommand("install") .about("Install a Rust binary") .arg( Arg::new("crate") .value_name("CRATE[@]") .help("Select the package from the given source") .value_parser(parse_crate) .num_args(0..), ) .arg( opt("version", "Specify a version to install") .alias("vers") .value_name("VERSION") .value_parser(parse_semver_flag) .requires("crate"), ) .arg( opt("index", "Registry index to install from") .value_name("INDEX") .requires("crate") .conflicts_with_all(&["git", "path", "registry"]), ) .arg( opt("registry", "Registry to use") .value_name("REGISTRY") .requires("crate") .conflicts_with_all(&["git", "path", "index"]), ) .arg( opt("git", "Git URL to install the specified crate from") .value_name("URL") .conflicts_with_all(&["path", "index", "registry"]), ) .arg( opt("branch", "Branch to use when installing from git") .value_name("BRANCH") .requires("git"), ) .arg( opt("tag", "Tag to use when installing from git") .value_name("TAG") .requires("git"), ) .arg( opt("rev", "Specific commit to use when installing from git") .value_name("SHA") .requires("git"), ) .arg( opt("path", "Filesystem path to local crate to install from") .value_name("PATH") .conflicts_with_all(&["git", "index", "registry"]) .add(clap_complete::engine::ArgValueCompleter::new( clap_complete::engine::PathCompleter::any() .filter(|path| path.join("Cargo.toml").exists()), )), ) .arg(opt("root", "Directory to install packages into").value_name("DIR")) .arg(flag("force", "Force overwriting existing crates or binaries").short('f')) .arg_dry_run("Perform all checks without installing (unstable)") .arg(flag("no-track", "Do not save tracking information")) .arg(flag( "list", "List all installed packages and their versions", )) .arg_ignore_rust_version() .arg_message_format() .arg_silent_suggestion() .arg_targets_bins_examples( "Install only the specified binary", "Install all binaries", "Install only the specified example", "Install all examples", ) .arg_features() .arg_parallel() .arg( flag( "debug", "Build in debug mode (with the 'dev' profile) instead of release mode", ) .conflicts_with("profile"), ) .arg_redundant_default_mode("release", "install", "debug") .arg_profile("Install artifacts with the specified profile") .arg_target_triple("Build for the target triple") .arg_target_dir() .arg_timings() .arg_lockfile_path() .after_help(color_print::cstr!( "Run `cargo help install` for more detailed information.\n" )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let path = args.value_of_path("path", gctx); if let Some(path) = &path { gctx.reload_rooted_at(path)?; } else { // TODO: Consider calling set_search_stop_path(home). gctx.reload_rooted_at(gctx.home().clone().into_path_unlocked())?; } // In general, we try to avoid normalizing paths in Cargo, // but in these particular cases we need it to fix rust-lang/cargo#10283. // (Handle `SourceId::for_path` and `Workspace::new`, // but not `GlobalContext::reload_rooted_at` which is always cwd) let path = path.map(|p| paths::normalize_path(&p)); let version = args.get_one::("version"); let krates = args .get_many::("crate") .unwrap_or_default() .cloned() .dedup_by(|x, y| x == y) .map(|(krate, local_version)| resolve_crate(krate, local_version, version)) .collect::>>()?; for (crate_name, _) in krates.iter() { let package_name = PackageName::new(crate_name); if !crate_name.contains("@") && package_name.is_err() { for (idx, ch) in crate_name.char_indices() { if !(unicode_xid::UnicodeXID::is_xid_continue(ch) || ch == '-') { let mut suggested_crate_name = crate_name.to_string(); suggested_crate_name.insert_str(idx, "@"); if let Ok((_, Some(_))) = parse_crate(&suggested_crate_name.as_str()) { let err = package_name.unwrap_err(); return Err( anyhow::format_err!("{err}\n\n\ help: if this is meant to be a package name followed by a version, insert an `@` like `{suggested_crate_name}`").into()); } } } } if let Some(toolchain) = crate_name.strip_prefix("+") { return Err(anyhow!( "invalid character `+` in package name: `+{toolchain}` Use `cargo +{toolchain} install` if you meant to use the `{toolchain}` toolchain." ) .into()); } if let Ok(url) = crate_name.into_url() { if matches!(url.scheme(), "http" | "https") { return Err(anyhow!( "invalid package name: `{url}` Use `cargo install --git {url}` if you meant to install from a git repository." ) .into()); } } } let mut from_cwd = false; let source = if let Some(url) = args.get_one::("git") { let url = url.into_url()?; let gitref = if let Some(branch) = args.get_one::("branch") { GitReference::Branch(branch.clone()) } else if let Some(tag) = args.get_one::("tag") { GitReference::Tag(tag.clone()) } else if let Some(rev) = args.get_one::("rev") { GitReference::Rev(rev.clone()) } else { GitReference::DefaultBranch }; SourceId::for_git(&url, gitref)? } else if let Some(path) = &path { SourceId::for_path(path)? } else if krates.is_empty() { from_cwd = true; SourceId::for_path(gctx.cwd())? } else if let Some(reg_or_index) = args.registry_or_index(gctx)? { match reg_or_index { ops::RegistryOrIndex::Registry(r) => SourceId::alt_registry(gctx, &r)?, ops::RegistryOrIndex::Index(url) => SourceId::for_registry(&url)?, } } else { SourceId::crates_io(gctx)? }; let root = args.get_one::("root").map(String::as_str); // We only provide workspace information for local crate installation from // one of the following sources: // - From current working directory (only work for edition 2015). // - From a specific local file path (from `--path` arg). // // This workspace information is for emitting helpful messages from // `ArgMatchesExt::compile_options` and won't affect the actual compilation. let workspace = if from_cwd { args.workspace(gctx).ok() } else if let Some(path) = &path { Workspace::new(&path.join("Cargo.toml"), gctx).ok() } else { None }; let mut compile_opts = args.compile_options( gctx, UserIntent::Build, workspace.as_ref(), ProfileChecking::Custom, )?; compile_opts.build_config.requested_profile = args.get_profile_name("release", ProfileChecking::Custom)?; if args.dry_run() { gctx.cli_unstable().fail_if_stable_opt("--dry-run", 11123)?; } let requested_lockfile_path = args.lockfile_path(gctx)?; // 14421: lockfile path should imply --locked on running `install` if requested_lockfile_path.is_some() { gctx.set_locked(true); } if args.flag("list") { ops::install_list(root, gctx)?; } else { ops::install( gctx, root, krates, source, from_cwd, &compile_opts, args.flag("force"), args.flag("no-track"), args.dry_run(), requested_lockfile_path.as_deref(), )?; } Ok(()) } type CrateVersion = (String, Option); fn parse_crate(krate: &str) -> crate::CargoResult { let (krate, version) = if let Some((k, v)) = krate.split_once('@') { if k.is_empty() { // by convention, arguments starting with `@` are response files anyhow::bail!("missing crate name before '@'"); } let krate = k.to_owned(); let version = Some(parse_semver_flag(v)?); (krate, version) } else { let krate = krate.to_owned(); let version = None; (krate, version) }; if krate.is_empty() { anyhow::bail!("crate name is empty"); } Ok((krate, version)) } /// Parses x.y.z as if it were =x.y.z, and gives CLI-specific error messages in the case of invalid /// values. fn parse_semver_flag(v: &str) -> CargoResult { // If the version begins with character <, >, =, ^, ~ parse it as a // version range, otherwise parse it as a specific version let first = v .chars() .next() .ok_or_else(|| format_err!("no version provided for the `--version` flag"))?; if let Some(stripped) = v.strip_prefix("v") { bail!( "the version provided, `{v}` is not a valid SemVer requirement\n\n\ help: try changing the version to `{stripped}`", ) } let is_req = "<>=^~".contains(first) || v.contains('*'); if is_req { match v.parse::() { Ok(v) => Ok(v), Err(_) => bail!( "the `--version` provided, `{}`, is \ not a valid semver version requirement\n\n\ Please have a look at \ https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html \ for the correct format", v ), } } else { match v.trim().parse::() { Ok(v) => Ok(v.to_exact_req()), Err(e) => { let mut msg = e.to_string(); // If it is not a valid version but it is a valid version // requirement, add a note to the warning if v.parse::().is_ok() { msg.push_str(&format!( "\n\n tip: if you want to specify SemVer range, \ add an explicit qualifier, like '^{}'", v )); } bail!(msg); } } } } fn resolve_crate( krate: String, local_version: Option, version: Option<&VersionReq>, ) -> crate::CargoResult { let version = match (local_version, version) { (Some(_), Some(_)) => { anyhow::bail!("cannot specify both `@` and `--version `"); } (Some(l), None) => Some(l), (None, Some(g)) => Some(g.to_owned()), (None, None) => None, }; Ok((krate, version)) } cargo-0.91.0/src/bin/cargo/commands/locate_project.rs000064400000000000000000000047541046102023000206160ustar 00000000000000use crate::command_prelude::*; use anyhow::bail; use cargo::{CargoResult, drop_println}; use serde::Serialize; pub fn cli() -> Command { subcommand("locate-project") .about("Print a JSON representation of a Cargo.toml file's location") .arg(flag("workspace", "Locate Cargo.toml of the workspace root")) .arg( opt("message-format", "Output representation") .value_name("FMT") .value_parser(["json", "plain"]) .ignore_case(true), ) .arg_silent_suggestion() .arg_manifest_path() .after_help(color_print::cstr!( "Run `cargo help locate-project` for more detailed information.\n" )) } #[derive(Serialize)] pub struct ProjectLocation<'a> { root: &'a str, } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let root_manifest; let workspace; let root = match WhatToFind::parse(args) { WhatToFind::CurrentManifest => { root_manifest = args.root_manifest(gctx)?; &root_manifest } WhatToFind::Workspace => { workspace = args.workspace(gctx)?; workspace.root_manifest() } }; let root = root .to_str() .ok_or_else(|| { anyhow::format_err!( "your package path contains characters \ not representable in Unicode" ) }) .map_err(|e| CliError::new(e, 1))?; let location = ProjectLocation { root }; match MessageFormat::parse(args)? { MessageFormat::Json => gctx.shell().print_json(&location)?, MessageFormat::Plain => drop_println!(gctx, "{}", location.root), } Ok(()) } enum WhatToFind { CurrentManifest, Workspace, } impl WhatToFind { fn parse(args: &ArgMatches) -> Self { if args.flag("workspace") { WhatToFind::Workspace } else { WhatToFind::CurrentManifest } } } enum MessageFormat { Json, Plain, } impl MessageFormat { fn parse(args: &ArgMatches) -> CargoResult { let fmt = match args.get_one::("message-format") { Some(fmt) => fmt, None => return Ok(MessageFormat::Json), }; match fmt.to_ascii_lowercase().as_str() { "json" => Ok(MessageFormat::Json), "plain" => Ok(MessageFormat::Plain), s => bail!("invalid message format specifier: `{}`", s), } } } cargo-0.91.0/src/bin/cargo/commands/login.rs000064400000000000000000000026351046102023000167250ustar 00000000000000use cargo::ops; use cargo::ops::RegistryOrIndex; use crate::command_prelude::*; pub fn cli() -> Command { subcommand("login") .about("Log in to a registry.") .arg( Arg::new("token") .value_name("TOKEN") .action(ArgAction::Set) .hide(true), ) .arg_registry("Registry to use") .arg( Arg::new("args") .help("Additional arguments for the credential provider") .num_args(0..) .last(true), ) .arg_silent_suggestion() .after_help(color_print::cstr!( "Run `cargo help login` for more detailed information.\n" )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let reg = args.registry_or_index(gctx)?; assert!( !matches!(reg, Some(RegistryOrIndex::Index(..))), "must not be index URL" ); let token = args.get_one::("token").map(|s| s.as_str().into()); if token.is_some() { let _ = gctx .shell() .warn("`cargo login ` is deprecated in favor of reading `` from stdin"); } let extra_args = args .get_many::("args") .unwrap_or_default() .map(String::as_str) .collect::>(); ops::registry_login(gctx, token, reg.as_ref(), &extra_args)?; Ok(()) } cargo-0.91.0/src/bin/cargo/commands/logout.rs000064400000000000000000000012611046102023000171200ustar 00000000000000use cargo::ops; use cargo::ops::RegistryOrIndex; use crate::command_prelude::*; pub fn cli() -> Command { subcommand("logout") .about("Remove an API token from the registry locally") .arg_registry("Registry to use") .arg_silent_suggestion() .after_help(color_print::cstr!( "Run `cargo help logout` for more detailed information.\n" )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let reg = args.registry_or_index(gctx)?; assert!( !matches!(reg, Some(RegistryOrIndex::Index(..))), "must not be index URL" ); ops::registry_logout(gctx, reg)?; Ok(()) } cargo-0.91.0/src/bin/cargo/commands/metadata.rs000064400000000000000000000034501046102023000173710ustar 00000000000000use cargo::ops::{self, OutputMetadataOptions}; use crate::command_prelude::*; pub fn cli() -> Command { subcommand("metadata") .about( "Output the resolved dependencies of a package, \ the concrete used versions including overrides, \ in machine-readable format", ) .arg(multi_opt( "filter-platform", "TRIPLE", "Only include resolve dependencies matching the given target-triple", )) .arg(flag( "no-deps", "Output information only about the workspace members \ and don't fetch dependencies", )) .arg( opt("format-version", "Format version") .value_name("VERSION") .value_parser(["1"]), ) .arg_silent_suggestion() .arg_features() .arg_manifest_path() .arg_lockfile_path() .after_help(color_print::cstr!( "Run `cargo help metadata` for more detailed information.\n" )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let ws = args.workspace(gctx)?; let version = match args.get_one::("format-version") { None => { gctx.shell().warn( "please specify `--format-version` flag explicitly \ to avoid compatibility problems", )?; 1 } Some(version) => version.parse().unwrap(), }; let options = OutputMetadataOptions { cli_features: args.cli_features()?, no_deps: args.flag("no-deps"), filter_platforms: args._values_of("filter-platform"), version, }; let result = ops::output_metadata(&ws, &options)?; gctx.shell().print_json(&result)?; Ok(()) } cargo-0.91.0/src/bin/cargo/commands/mod.rs000064400000000000000000000061511046102023000163710ustar 00000000000000use crate::command_prelude::*; pub fn builtin() -> Vec { vec![ add::cli(), bench::cli(), build::cli(), check::cli(), clean::cli(), config::cli(), doc::cli(), fetch::cli(), fix::cli(), generate_lockfile::cli(), git_checkout::cli(), help::cli(), info::cli(), init::cli(), install::cli(), locate_project::cli(), login::cli(), logout::cli(), metadata::cli(), new::cli(), owner::cli(), package::cli(), pkgid::cli(), publish::cli(), read_manifest::cli(), remove::cli(), report::cli(), run::cli(), rustc::cli(), rustdoc::cli(), search::cli(), test::cli(), tree::cli(), uninstall::cli(), update::cli(), vendor::cli(), verify_project::cli(), version::cli(), yank::cli(), ] } pub type Exec = fn(&mut GlobalContext, &ArgMatches) -> CliResult; pub fn builtin_exec(cmd: &str) -> Option { let f = match cmd { "add" => add::exec, "bench" => bench::exec, "build" => build::exec, "check" => check::exec, "clean" => clean::exec, "config" => config::exec, "doc" => doc::exec, "fetch" => fetch::exec, "fix" => fix::exec, "generate-lockfile" => generate_lockfile::exec, "git-checkout" => git_checkout::exec, "help" => help::exec, "info" => info::exec, "init" => init::exec, "install" => install::exec, "locate-project" => locate_project::exec, "login" => login::exec, "logout" => logout::exec, "metadata" => metadata::exec, "new" => new::exec, "owner" => owner::exec, "package" => package::exec, "pkgid" => pkgid::exec, "publish" => publish::exec, "read-manifest" => read_manifest::exec, "remove" => remove::exec, "report" => report::exec, "run" => run::exec, "rustc" => rustc::exec, "rustdoc" => rustdoc::exec, "search" => search::exec, "test" => test::exec, "tree" => tree::exec, "uninstall" => uninstall::exec, "update" => update::exec, "vendor" => vendor::exec, "verify-project" => verify_project::exec, "version" => version::exec, "yank" => yank::exec, _ => return None, }; Some(f) } pub mod add; pub mod bench; pub mod build; pub mod check; pub mod clean; pub mod config; pub mod doc; pub mod fetch; pub mod fix; pub mod generate_lockfile; pub mod git_checkout; pub mod help; pub mod info; pub mod init; pub mod install; pub mod locate_project; pub mod login; pub mod logout; pub mod metadata; pub mod new; pub mod owner; pub mod package; pub mod pkgid; pub mod publish; pub mod read_manifest; pub mod remove; pub mod report; pub mod run; pub mod rustc; pub mod rustdoc; pub mod search; pub mod test; pub mod tree; pub mod uninstall; pub mod update; pub mod vendor; pub mod verify_project; pub mod version; pub mod yank; cargo-0.91.0/src/bin/cargo/commands/new.rs000064400000000000000000000012761046102023000164060ustar 00000000000000use crate::command_prelude::*; use cargo::ops; pub fn cli() -> Command { subcommand("new") .about("Create a new cargo package at ") .arg( Arg::new("path") .value_name("PATH") .action(ArgAction::Set) .required(true), ) .arg_new_opts() .arg_registry("Registry to use") .arg_silent_suggestion() .after_help(color_print::cstr!( "Run `cargo help new` for more detailed information.\n" )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let opts = args.new_options(gctx)?; ops::new(&opts, gctx)?; Ok(()) } cargo-0.91.0/src/bin/cargo/commands/owner.rs000064400000000000000000000032751046102023000167500ustar 00000000000000use crate::command_prelude::*; use cargo::ops::{self, OwnersOptions}; use cargo_credential::Secret; pub fn cli() -> Command { subcommand("owner") .about("Manage the owners of a crate on the registry") .arg(Arg::new("crate").value_name("CRATE").action(ArgAction::Set)) .arg( multi_opt( "add", "LOGIN", "Name of a user or team to invite as an owner", ) .short('a'), ) .arg( multi_opt( "remove", "LOGIN", "Name of a user or team to remove as an owner", ) .short('r'), ) .arg(flag("list", "List owners of a crate").short('l')) .arg_index("Registry index URL to modify owners for") .arg_registry("Registry to modify owners for") .arg(opt("token", "API token to use when authenticating").value_name("TOKEN")) .arg_silent_suggestion() .after_help(color_print::cstr!( "Run `cargo help owner` for more detailed information.\n" )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let opts = OwnersOptions { krate: args.get_one::("crate").cloned(), token: args.get_one::("token").cloned().map(Secret::from), reg_or_index: args.registry_or_index(gctx)?, to_add: args .get_many::("add") .map(|xs| xs.cloned().collect()), to_remove: args .get_many::("remove") .map(|xs| xs.cloned().collect()), list: args.flag("list"), }; ops::modify_owners(gctx, &opts)?; Ok(()) } cargo-0.91.0/src/bin/cargo/commands/package.rs000064400000000000000000000061131046102023000172030ustar 00000000000000use crate::command_prelude::*; use cargo::ops; use cargo::ops::PackageMessageFormat; use cargo::ops::PackageOpts; pub fn cli() -> Command { subcommand("package") .about("Assemble the local package into a distributable tarball") .arg_index("Registry index URL to prepare the package for") .arg_registry("Registry to prepare the package for") .arg( flag( "list", "Print files included in a package without making one", ) .short('l'), ) .arg(flag( "no-verify", "Don't verify the contents by building them", )) .arg(flag( "no-metadata", "Ignore warnings about a lack of human-usable metadata", )) .arg(flag( "allow-dirty", "Allow dirty working directories to be packaged", )) .arg(flag( "exclude-lockfile", "Don't include the lock file when packaging", )) .arg( opt("message-format", "Output representation (unstable)") .value_name("FMT") // This currently requires and only works with `--list`. .requires("list") .value_parser(PackageMessageFormat::POSSIBLE_VALUES), ) .arg_silent_suggestion() .arg_package_spec_no_all( "Package(s) to assemble", "Assemble all packages in the workspace", "Don't assemble specified packages", ) .arg_features() .arg_target_triple("Build for the target triple") .arg_target_dir() .arg_parallel() .arg_manifest_path() .arg_lockfile_path() .after_help(color_print::cstr!( "Run `cargo help package` for more detailed information.\n" )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let reg_or_index = args.registry_or_index(gctx)?; let ws = args.workspace(gctx)?; if ws.root_maybe().is_embedded() { return Err(anyhow::format_err!( "{} is unsupported by `cargo package`", ws.root_manifest().display() ) .into()); } let specs = args.packages_from_flags()?; let fmt = if let Some(fmt) = args._value_of("message-format") { gctx.cli_unstable() .fail_if_stable_opt("--message-format", 15353)?; fmt.parse()? } else { PackageMessageFormat::Human }; ops::package( &ws, &PackageOpts { gctx, verify: !args.flag("no-verify"), list: args.flag("list"), fmt, check_metadata: !args.flag("no-metadata"), allow_dirty: args.flag("allow-dirty"), include_lockfile: !args.flag("exclude-lockfile"), to_package: specs, targets: args.targets()?, jobs: args.jobs()?, keep_going: args.keep_going(), cli_features: args.cli_features()?, reg_or_index, dry_run: false, }, )?; Ok(()) } cargo-0.91.0/src/bin/cargo/commands/pkgid.rs000064400000000000000000000017731046102023000167150ustar 00000000000000use crate::command_prelude::*; use cargo::ops; use cargo::util::print_available_packages; pub fn cli() -> Command { subcommand("pkgid") .about("Print a fully qualified package specification") .arg(Arg::new("spec").value_name("SPEC").action(ArgAction::Set)) .arg_silent_suggestion() .arg_package("Argument to get the package ID specifier for") .arg_manifest_path() .arg_lockfile_path() .after_help(color_print::cstr!( "Run `cargo help pkgid` for more detailed information.\n" )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let ws = args.workspace(gctx)?; if args.is_present_with_zero_values("package") { print_available_packages(&ws)? } let spec = args .get_one::("spec") .or_else(|| args.get_one::("package")) .map(String::as_str); let spec = ops::pkgid(&ws, spec)?; cargo::drop_println!(gctx, "{}", spec); Ok(()) } cargo-0.91.0/src/bin/cargo/commands/publish.rs000064400000000000000000000042001046102023000172510ustar 00000000000000use crate::command_prelude::*; use cargo::ops::{self, PublishOpts}; pub fn cli() -> Command { subcommand("publish") .about("Upload a package to the registry") .arg_dry_run("Perform all checks without uploading") .arg_index("Registry index URL to upload the package to") .arg_registry("Registry to upload the package to") .arg(opt("token", "Token to use when uploading").value_name("TOKEN")) .arg(flag( "no-verify", "Don't verify the contents by building them", )) .arg(flag( "allow-dirty", "Allow dirty working directories to be packaged", )) .arg_silent_suggestion() .arg_package_spec_no_all( "Package(s) to publish", "Publish all packages in the workspace", "Don't publish specified packages", ) .arg_features() .arg_parallel() .arg_target_triple("Build for the target triple") .arg_target_dir() .arg_manifest_path() .arg_lockfile_path() .after_help(color_print::cstr!( "Run `cargo help publish` for more detailed information.\n" )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let reg_or_index = args.registry_or_index(gctx)?; let ws = args.workspace(gctx)?; if ws.root_maybe().is_embedded() { return Err(anyhow::format_err!( "{} is unsupported by `cargo publish`", ws.root_manifest().display() ) .into()); } ops::publish( &ws, &PublishOpts { gctx, token: args .get_one::("token") .map(|s| s.to_string().into()), reg_or_index, verify: !args.flag("no-verify"), allow_dirty: args.flag("allow-dirty"), to_publish: args.packages_from_flags()?, targets: args.targets()?, jobs: args.jobs()?, keep_going: args.keep_going(), dry_run: args.dry_run(), cli_features: args.cli_features()?, }, )?; Ok(()) } cargo-0.91.0/src/bin/cargo/commands/read_manifest.rs000064400000000000000000000011641046102023000204120ustar 00000000000000//! Deprecated. use crate::command_prelude::*; pub fn cli() -> Command { subcommand("read-manifest") .hide(true) .about(color_print::cstr!( "\ DEPRECATED: Print a JSON representation of a Cargo.toml manifest. Use `cargo metadata --no-deps` instead.\ " )) .arg_silent_suggestion() .arg_manifest_path() } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let ws = args.workspace(gctx)?; gctx.shell().print_json( &ws.current()? .serialized(gctx.cli_unstable(), ws.unstable_features()), )?; Ok(()) } cargo-0.91.0/src/bin/cargo/commands/remove.rs000064400000000000000000000317361046102023000171160ustar 00000000000000use cargo::CargoResult; use cargo::core::PackageIdSpec; use cargo::core::PackageIdSpecQuery; use cargo::core::Resolve; use cargo::core::Workspace; use cargo::core::dependency::DepKind; use cargo::ops::cargo_remove::RemoveOptions; use cargo::ops::cargo_remove::remove; use cargo::ops::resolve_ws; use cargo::util::command_prelude::*; use cargo::util::print_available_packages; use cargo::util::toml_mut::dependency::Dependency; use cargo::util::toml_mut::dependency::MaybeWorkspace; use cargo::util::toml_mut::dependency::Source; use cargo::util::toml_mut::manifest::DepTable; use cargo::util::toml_mut::manifest::LocalManifest; pub fn cli() -> clap::Command { clap::Command::new("remove") // Subcommand aliases are handled in `aliased_command()`. // .alias("rm") .about("Remove dependencies from a Cargo.toml manifest file") .args([clap::Arg::new("dependencies") .action(clap::ArgAction::Append) .required(true) .num_args(1..) .value_name("DEP_ID") .help("Dependencies to be removed") .add(clap_complete::ArgValueCandidates::new( get_direct_dependencies_pkg_name_candidates, ))]) .arg_dry_run("Don't actually write the manifest") .arg_silent_suggestion() .next_help_heading("Section") .args([ clap::Arg::new("dev") .long("dev") .conflicts_with("build") .action(clap::ArgAction::SetTrue) .group("section") .help("Remove from dev-dependencies"), clap::Arg::new("build") .long("build") .conflicts_with("dev") .action(clap::ArgAction::SetTrue) .group("section") .help("Remove from build-dependencies"), clap::Arg::new("target") .long("target") .num_args(1) .value_name("TARGET") .value_parser(clap::builder::NonEmptyStringValueParser::new()) .help("Remove from target-dependencies"), ]) .arg_package("Package to remove from") .arg_manifest_path() .arg_lockfile_path() .after_help(color_print::cstr!( "Run `cargo help remove` for more detailed information.\n" )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let dry_run = args.dry_run(); let workspace = args.workspace(gctx)?; if args.is_present_with_zero_values("package") { print_available_packages(&workspace)?; } let packages = args.packages_from_flags()?; let packages = packages.get_packages(&workspace)?; let spec = match packages.len() { 0 => { return Err(CliError::new( anyhow::format_err!( "no packages selected to modify. Please specify one with `-p `" ), 101, )); } 1 => packages[0], _ => { let names = packages.iter().map(|p| p.name()).collect::>(); return Err(CliError::new( anyhow::format_err!( "`cargo remove` could not determine which package to modify. \ Use the `--package` option to specify a package. \n\ available packages: {}", names.join(", ") ), 101, )); } }; let dependencies = args .get_many::("dependencies") .expect("required(true)") .cloned() .collect::>(); let section = parse_section(args); let options = RemoveOptions { gctx, spec, dependencies, section, dry_run, }; remove(&options)?; if !dry_run { // Clean up the workspace gc_workspace(&workspace)?; // Reload the workspace since we've changed dependencies let ws = args.workspace(gctx)?; let resolve = { // HACK: Avoid unused patch warnings by temporarily changing the verbosity. // In rare cases, this might cause index update messages to not show up let verbosity = ws.gctx().shell().verbosity(); ws.gctx() .shell() .set_verbosity(cargo::core::Verbosity::Quiet); let resolve = resolve_ws(&ws, dry_run); ws.gctx().shell().set_verbosity(verbosity); resolve?.1 }; // Attempt to gc unused patches and re-resolve if anything is removed if gc_unused_patches(&workspace, &resolve)? { let ws = args.workspace(gctx)?; resolve_ws(&ws, dry_run)?; } } Ok(()) } fn parse_section(args: &ArgMatches) -> DepTable { let dev = args.flag("dev"); let build = args.flag("build"); let kind = if dev { DepKind::Development } else if build { DepKind::Build } else { DepKind::Normal }; let mut table = DepTable::new().set_kind(kind); if let Some(target) = args.get_one::("target") { assert!(!target.is_empty(), "Target specification may not be empty"); table = table.set_target(target); } table } /// Clean up the workspace.dependencies, profile, patch, and replace sections of the root manifest /// by removing dependencies which no longer have a reference to them. fn gc_workspace(workspace: &Workspace<'_>) -> CargoResult<()> { let mut workspace_manifest = LocalManifest::try_new(workspace.root_manifest())?; let mut is_modified = true; let members = workspace .members() .map(|p| { Ok(( LocalManifest::try_new(p.manifest_path())?, p.manifest().unstable_features(), )) }) .collect::>>()?; let mut dependencies = members .into_iter() .flat_map(|(member_manifest, unstable_features)| { member_manifest .get_sections() .into_iter() .flat_map(move |(_, table)| { table .as_table_like() .unwrap() .iter() .map(|(key, item)| { Dependency::from_toml( workspace.gctx(), workspace.root(), &member_manifest.path, &unstable_features, key, item, ) }) .collect::>() }) }) .collect::>>()?; // Clean up the workspace.dependencies section and replace instances of // workspace dependencies with their definitions if let Some(toml_edit::Item::Table(deps_table)) = workspace_manifest .data .get_mut("workspace") .and_then(|t| t.get_mut("dependencies")) { deps_table.set_implicit(true); for (key, item) in deps_table.iter_mut() { let ws_dep = Dependency::from_toml( workspace.gctx(), workspace.root(), &workspace.root(), workspace.unstable_features(), key.get(), item, )?; // search for uses of this workspace dependency let mut is_used = false; for dep in dependencies.iter_mut().filter(|d| { d.toml_key() == key.get() && matches!(d.source(), Some(Source::Workspace(_))) }) { // HACK: Replace workspace references in `dependencies` to simplify later GC steps: // 1. Avoid having to look it up again to determine the dependency source / spec // 2. The entry might get deleted, preventing us from looking it up again // // This does lose extra information, like features enabled, but that shouldn't be a // problem for GC *dep = ws_dep.clone(); is_used = true; } if !is_used { *item = toml_edit::Item::None; is_modified = true; } } } // Clean up the profile section // // Example tables: // - profile.dev.package.foo // - profile.release.package."foo:2.1.0" if let Some(toml_edit::Item::Table(profile_section_table)) = workspace_manifest.data.get_mut("profile") { profile_section_table.set_implicit(true); for (_, item) in profile_section_table.iter_mut() { if let toml_edit::Item::Table(profile_table) = item { profile_table.set_implicit(true); if let Some(toml_edit::Item::Table(package_table)) = profile_table.get_mut("package") { package_table.set_implicit(true); for (key, item) in package_table.iter_mut() { let key = key.get(); // Skip globs. Can't do anything with them. // For example, profile.release.package."*". if crate::util::restricted_names::is_glob_pattern(key) { continue; } if !spec_has_match( &PackageIdSpec::parse(key)?, &dependencies, workspace.gctx(), )? { *item = toml_edit::Item::None; is_modified = true; } } } } } } // Clean up the replace section if let Some(toml_edit::Item::Table(table)) = workspace_manifest.data.get_mut("replace") { table.set_implicit(true); for (key, item) in table.iter_mut() { if !spec_has_match( &PackageIdSpec::parse(key.get())?, &dependencies, workspace.gctx(), )? { *item = toml_edit::Item::None; is_modified = true; } } } if is_modified { workspace_manifest.write()?; } Ok(()) } /// Check whether or not a package ID spec matches any non-workspace dependencies. fn spec_has_match( spec: &PackageIdSpec, dependencies: &[Dependency], gctx: &GlobalContext, ) -> CargoResult { for dep in dependencies { if spec.name() != &dep.name { continue; } let version_matches = match (spec.version(), dep.version()) { (Some(v), Some(vq)) => semver::VersionReq::parse(vq)?.matches(&v), (Some(_), None) => false, (None, None | Some(_)) => true, }; if !version_matches { continue; } match dep.source_id(gctx)? { MaybeWorkspace::Other(source_id) => { if spec.url().map(|u| u == source_id.url()).unwrap_or(true) { return Ok(true); } } MaybeWorkspace::Workspace(_) => {} } } Ok(false) } /// Removes unused patches from the manifest fn gc_unused_patches(workspace: &Workspace<'_>, resolve: &Resolve) -> CargoResult { let mut workspace_manifest = LocalManifest::try_new(workspace.root_manifest())?; let mut modified = false; // Clean up the patch section if let Some(toml_edit::Item::Table(patch_section_table)) = workspace_manifest.data.get_mut("patch") { patch_section_table.set_implicit(true); for (_, item) in patch_section_table.iter_mut() { if let toml_edit::Item::Table(patch_table) = item { patch_table.set_implicit(true); for (key, item) in patch_table.iter_mut() { let dep = Dependency::from_toml( workspace.gctx(), workspace.root(), &workspace.root_manifest(), workspace.unstable_features(), key.get(), item, )?; // Generate a PackageIdSpec url for querying let url = if let MaybeWorkspace::Other(source_id) = dep.source_id(workspace.gctx())? { format!("{}#{}", source_id.url(), dep.name) } else { continue; }; if PackageIdSpec::query_str(&url, resolve.unused_patches().iter().cloned()) .is_ok() { *item = toml_edit::Item::None; modified = true; } } } } } if modified { workspace_manifest.write()?; } Ok(modified) } cargo-0.91.0/src/bin/cargo/commands/report.rs000064400000000000000000000035111046102023000171220ustar 00000000000000use crate::command_prelude::*; use cargo::core::compiler::future_incompat::{OnDiskReports, REPORT_PREAMBLE}; use cargo::drop_println; pub fn cli() -> Command { subcommand("report") .about("Generate and display various kinds of reports") .after_help(color_print::cstr!( "Run `cargo help report` for more detailed information.\n" )) .subcommand_required(true) .arg_required_else_help(true) .subcommand( subcommand("future-incompatibilities") .alias("future-incompat") .about("Reports any crates which will eventually stop compiling") .arg( opt( "id", "identifier of the report generated by a Cargo command invocation", ) .value_name("id"), ) .arg_package("Package to display a report for"), ) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { match args.subcommand() { Some(("future-incompatibilities", args)) => report_future_incompatibilities(gctx, args), Some((cmd, _)) => { unreachable!("unexpected command {}", cmd) } None => { unreachable!("unexpected command") } } } fn report_future_incompatibilities(gctx: &GlobalContext, args: &ArgMatches) -> CliResult { let ws = args.workspace(gctx)?; let reports = OnDiskReports::load(&ws)?; let id = args .value_of_u32("id")? .unwrap_or_else(|| reports.last_id()); let krate = args.get_one::("package").map(String::as_str); let report = reports.get_report(id, krate)?; drop_println!(gctx, "{}", REPORT_PREAMBLE); drop(gctx.shell().print_ansi_stdout(report.as_bytes())); Ok(()) } cargo-0.91.0/src/bin/cargo/commands/run.rs000064400000000000000000000220351046102023000164150ustar 00000000000000use std::ffi::OsStr; use std::ffi::OsString; use std::path::Path; use crate::command_prelude::*; use crate::util::restricted_names::is_glob_pattern; use cargo::core::Verbosity; use cargo::core::Workspace; use cargo::ops::{self, CompileFilter, Packages}; use cargo::util::closest; use cargo_util::ProcessError; use itertools::Itertools as _; pub fn cli() -> Command { subcommand("run") // subcommand aliases are handled in aliased_command() // .alias("r") .about("Run a binary or example of the local package") .arg( Arg::new("args") .value_name("ARGS") .help("Arguments for the binary or example to run") .value_parser(value_parser!(OsString)) .num_args(0..) .trailing_var_arg(true), ) .arg_message_format() .arg_silent_suggestion() .arg_package("Package with the target to run") .arg_targets_bin_example( "Name of the bin target to run", "Name of the example target to run", ) .arg_features() .arg_parallel() .arg_release("Build artifacts in release mode, with optimizations") .arg_profile("Build artifacts with the specified profile") .arg_target_triple("Build for the target triple") .arg_target_dir() .arg_manifest_path() .arg_lockfile_path() .arg_ignore_rust_version() .arg_unit_graph() .arg_timings() .after_help(color_print::cstr!( "Run `cargo help run` for more detailed information.\n" )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let ws = args.workspace(gctx)?; let mut compile_opts = args.compile_options(gctx, UserIntent::Build, Some(&ws), ProfileChecking::Custom)?; // Disallow `spec` to be an glob pattern if let Packages::Packages(opt_in) = &compile_opts.spec { if let Some(pattern) = opt_in.iter().find(|s| is_glob_pattern(s)) { return Err(anyhow::anyhow!( "`cargo run` does not support glob pattern `{}` on package selection", pattern, ) .into()); } } if !args.contains_id("example") && !args.contains_id("bin") { let default_runs: Vec<_> = compile_opts .spec .get_packages(&ws)? .iter() .filter_map(|pkg| pkg.manifest().default_run()) .collect(); if let [bin] = &default_runs[..] { compile_opts.filter = CompileFilter::single_bin(bin.to_string()); } else { // ops::run will take care of errors if len pkgs != 1. compile_opts.filter = CompileFilter::Default { // Force this to false because the code in ops::run is not // able to pre-check features before compilation starts to // enforce that only 1 binary is built. required_features_filterable: false, }; } }; ops::run(&ws, &compile_opts, &values_os(args, "args")).map_err(|err| to_run_error(gctx, err)) } /// See also `util/toml/mod.rs`s `is_embedded` pub fn is_manifest_command(arg: &str) -> bool { let path = Path::new(arg); 1 < path.components().count() || path.extension() == Some(OsStr::new("rs")) } pub fn exec_manifest_command(gctx: &mut GlobalContext, cmd: &str, args: &[OsString]) -> CliResult { let manifest_path = Path::new(cmd); match (manifest_path.is_file(), gctx.cli_unstable().script) { (true, true) => {} (true, false) => { return Err(anyhow::anyhow!("running the file `{cmd}` requires `-Zscript`").into()); } (false, true) => { let possible_commands = crate::list_commands(gctx); let is_dir = if manifest_path.is_dir() { format!(": `{cmd}` is a directory") } else { "".to_owned() }; let suggested_command = if let Some(suggested_command) = possible_commands .keys() .filter(|c| cmd.starts_with(c.as_str())) .max_by_key(|c| c.len()) { let actual_args = cmd.strip_prefix(suggested_command).unwrap(); let args = if args.is_empty() { "".to_owned() } else { format!( " {}", args.into_iter().map(|os| os.to_string_lossy()).join(" ") ) }; format!( "\nhelp: there is a command with a similar name: `{suggested_command} {actual_args}{args}`" ) } else { "".to_owned() }; let suggested_script = if let Some(suggested_script) = suggested_script(cmd) { format!("\nhelp: there is a script with a similar name: `{suggested_script}`") } else { "".to_owned() }; return Err(anyhow::anyhow!( "no such file or subcommand `{cmd}`{is_dir}{suggested_command}{suggested_script}" ) .into()); } (false, false) => { // HACK: duplicating the above for minor tweaks but this will all go away on // stabilization let possible_commands = crate::list_commands(gctx); let suggested_command = if let Some(suggested_command) = possible_commands .keys() .filter(|c| cmd.starts_with(c.as_str())) .max_by_key(|c| c.len()) { let actual_args = cmd.strip_prefix(suggested_command).unwrap(); let args = if args.is_empty() { "".to_owned() } else { format!( " {}", args.into_iter().map(|os| os.to_string_lossy()).join(" ") ) }; format!( "\nhelp: there is a command with a similar name: `{suggested_command} {actual_args}{args}`" ) } else { "".to_owned() }; let suggested_script = if let Some(suggested_script) = suggested_script(cmd) { format!( "\nhelp: there is a script with a similar name: `{suggested_script}` (requires `-Zscript`)" ) } else { "".to_owned() }; return Err(anyhow::anyhow!( "no such subcommand `{cmd}`{suggested_command}{suggested_script}" ) .into()); } } let manifest_path = root_manifest(Some(manifest_path), gctx)?; // Reload to cargo home. gctx.reload_rooted_at(gctx.home().clone().into_path_unlocked())?; let mut ws = Workspace::new(&manifest_path, gctx)?; if gctx.cli_unstable().avoid_dev_deps { ws.set_require_optional_deps(false); } let mut compile_opts = cargo::ops::CompileOptions::new(gctx, cargo::core::compiler::UserIntent::Build)?; compile_opts.spec = cargo::ops::Packages::Default; cargo::ops::run(&ws, &compile_opts, args).map_err(|err| to_run_error(gctx, err)) } fn suggested_script(cmd: &str) -> Option { let cmd_path = Path::new(cmd); let mut suggestion = Path::new(".").to_owned(); for cmd_part in cmd_path.components() { let exact_match = suggestion.join(cmd_part); suggestion = if exact_match.exists() { exact_match } else { let possible: Vec<_> = std::fs::read_dir(suggestion) .into_iter() .flatten() .filter_map(|e| e.ok()) .map(|e| e.path()) .filter(|p| p.to_str().is_some()) .collect(); if let Some(possible) = closest( cmd_part.as_os_str().to_str().unwrap(), possible.iter(), |p| p.file_name().unwrap().to_str().unwrap(), ) { possible.to_owned() } else { return None; } }; } if suggestion.is_dir() { None } else { suggestion.into_os_string().into_string().ok() } } fn to_run_error(gctx: &GlobalContext, err: anyhow::Error) -> CliError { let proc_err = match err.downcast_ref::() { Some(e) => e, None => return CliError::new(err, 101), }; // If we never actually spawned the process then that sounds pretty // bad and we always want to forward that up. let exit_code = match proc_err.code { Some(exit) => exit, None => return CliError::new(err, 101), }; // If `-q` was passed then we suppress extra error information about // a failed process, we assume the process itself printed out enough // information about why it failed so we don't do so as well let is_quiet = gctx.shell().verbosity() == Verbosity::Quiet; if is_quiet { CliError::code(exit_code) } else { CliError::new(err, exit_code) } } cargo-0.91.0/src/bin/cargo/commands/rustc.rs000064400000000000000000000074441046102023000167600ustar 00000000000000use crate::command_prelude::*; use cargo::ops; const PRINT_ARG_NAME: &str = "print"; const CRATE_TYPE_ARG_NAME: &str = "crate-type"; pub fn cli() -> Command { subcommand("rustc") .about("Compile a package, and pass extra options to the compiler") .arg( Arg::new("args") .value_name("ARGS") .num_args(0..) .help("Extra rustc flags") .trailing_var_arg(true), ) .arg( opt( PRINT_ARG_NAME, "Output compiler information without compiling", ) .value_name("INFO"), ) .arg(multi_opt( CRATE_TYPE_ARG_NAME, "CRATE-TYPE", "Comma separated list of types of crates for the compiler to emit", )) .arg_future_incompat_report() .arg_message_format() .arg_silent_suggestion() .arg_package("Package to build") .arg_targets_all( "Build only this package's library", "Build only the specified binary", "Build all binaries", "Build only the specified example", "Build all examples", "Build only the specified test target", "Build all targets that have `test = true` set", "Build only the specified bench target", "Build all targets that have `bench = true` set", "Build all targets", ) .arg_features() .arg_parallel() .arg_release("Build artifacts in release mode, with optimizations") .arg_profile("Build artifacts with the specified profile") .arg_target_triple("Target triple which compiles will be for") .arg_target_dir() .arg_unit_graph() .arg_timings() .arg_manifest_path() .arg_lockfile_path() .arg_ignore_rust_version() .after_help(color_print::cstr!( "Run `cargo help rustc` for more detailed information.\n" )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let ws = args.workspace(gctx)?; // This is a legacy behavior that changes the behavior based on the profile. // If we want to support this more formally, I think adding a --mode flag // would be warranted. let intent = match args.get_one::("profile").map(String::as_str) { Some("test") => UserIntent::Test, Some("bench") => UserIntent::Bench, Some("check") => UserIntent::Check { test: false }, _ => UserIntent::Build, }; let mut compile_opts = args.compile_options_for_single_package( gctx, intent, Some(&ws), ProfileChecking::LegacyRustc, )?; if compile_opts.build_config.requested_profile == "check" { compile_opts.build_config.requested_profile = "dev".into(); } let target_args = values(args, "args"); compile_opts.target_rustc_args = if target_args.is_empty() { None } else { Some(target_args) }; if let Some(opt_value) = args.get_one::(PRINT_ARG_NAME) { gctx.cli_unstable() .fail_if_stable_opt(PRINT_ARG_NAME, 9357)?; ops::print(&ws, &compile_opts, opt_value)?; return Ok(()); } let crate_types = { let mut seen = std::collections::HashSet::new(); args.get_many::(CRATE_TYPE_ARG_NAME) .into_iter() .flatten() .flat_map(|s| s.split(',')) .filter(|s| !s.is_empty()) .map(String::from) .filter(|s| seen.insert(s.clone())) .collect::>() }; compile_opts.target_rustc_crate_types = if crate_types.is_empty() { None } else { Some(crate_types) }; ops::compile(&ws, &compile_opts)?; Ok(()) } cargo-0.91.0/src/bin/cargo/commands/rustdoc.rs000064400000000000000000000054661046102023000173050ustar 00000000000000use cargo::ops::{self, DocOptions, OutputFormat}; use crate::command_prelude::*; pub fn cli() -> Command { subcommand("rustdoc") .about("Build a package's documentation, using specified custom flags.") .arg( Arg::new("args") .value_name("ARGS") .help("Extra rustdoc flags") .num_args(0..) .trailing_var_arg(true), ) .arg(flag( "open", "Opens the docs in a browser after the operation", )) .arg_message_format() .arg_silent_suggestion() .arg_package("Package to document") .arg_targets_all( "Build only this package's library", "Build only the specified binary", "Build all binaries", "Build only the specified example", "Build all examples", "Build only the specified test target", "Build all targets that have `test = true` set", "Build only the specified bench target", "Build all targets that have `bench = true` set", "Build all targets", ) .arg_features() .arg_parallel() .arg_release("Build artifacts in release mode, with optimizations") .arg_profile("Build artifacts with the specified profile") .arg_target_triple("Build for the target triple") .arg_target_dir() .arg( opt("output-format", "The output type to write (unstable)") .value_name("FMT") .value_parser(OutputFormat::POSSIBLE_VALUES), ) .arg_unit_graph() .arg_timings() .arg_manifest_path() .arg_lockfile_path() .arg_ignore_rust_version() .after_help(color_print::cstr!( "Run `cargo help rustdoc` for more detailed information.\n" )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let ws = args.workspace(gctx)?; let output_format = if let Some(output_format) = args._value_of("output-format") { gctx.cli_unstable() .fail_if_stable_opt("--output-format", 12103)?; output_format.parse()? } else { OutputFormat::Html }; let mut compile_opts = args.compile_options_for_single_package( gctx, UserIntent::Doc { deps: false, json: matches!(output_format, OutputFormat::Json), }, Some(&ws), ProfileChecking::Custom, )?; let target_args = values(args, "args"); compile_opts.target_rustdoc_args = if target_args.is_empty() { None } else { Some(target_args) }; let doc_opts = DocOptions { open_result: args.flag("open"), output_format, compile_opts, }; ops::doc(&ws, &doc_opts)?; Ok(()) } cargo-0.91.0/src/bin/cargo/commands/search.rs000064400000000000000000000022771046102023000170640ustar 00000000000000use crate::command_prelude::*; use std::cmp::min; use cargo::ops; pub fn cli() -> Command { subcommand("search") .about("Search packages in the registry. Default registry is crates.io") .arg(Arg::new("query").value_name("QUERY").num_args(0..)) .arg( opt( "limit", "Limit the number of results (default: 10, max: 100)", ) .value_name("LIMIT"), ) .arg_index("Registry index URL to search packages in") .arg_registry("Registry to search packages in") .arg_silent_suggestion() .after_help(color_print::cstr!( "Run `cargo help search` for more detailed information.\n" )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let reg_or_index = args.registry_or_index(gctx)?; let limit = args.value_of_u32("limit")?; let limit = min(100, limit.unwrap_or(10)); let query: Vec<&str> = args .get_many::("query") .unwrap_or_default() .map(String::as_str) .collect(); let query: String = query.join("+"); ops::search(&query, gctx, reg_or_index, limit)?; Ok(()) } cargo-0.91.0/src/bin/cargo/commands/test.rs000064400000000000000000000105701046102023000165710ustar 00000000000000use crate::command_prelude::*; use cargo::ops; pub fn cli() -> Command { subcommand("test") // Subcommand aliases are handled in `aliased_command()`. // .alias("t") .about("Execute all unit and integration tests and build examples of a local package") .arg( Arg::new("TESTNAME") .action(ArgAction::Set) .help("If specified, only run tests containing this string in their names"), ) .arg( Arg::new("args") .value_name("ARGS") .help("Arguments for the test binary") .num_args(0..) .last(true), ) .arg(flag("no-run", "Compile, but don't run tests")) .arg(flag("no-fail-fast", "Run all tests regardless of failure")) .arg_future_incompat_report() .arg_message_format() .arg( flag( "quiet", "Display one character per test instead of one line", ) .short('q'), ) .arg_package_spec( "Package to run tests for", "Test all packages in the workspace", "Exclude packages from the test", ) .arg_targets_all( "Test only this package's library", "Test only the specified binary", "Test all binaries", "Test only the specified example", "Test all examples", "Test only the specified test target", "Test all targets that have `test = true` set", "Test only the specified bench target", "Test all targets that have `bench = true` set", "Test all targets (does not include doctests)", ) .arg( flag("doc", "Test only this library's documentation") .help_heading(heading::TARGET_SELECTION), ) .arg_features() .arg_jobs() .arg_unsupported_keep_going() .arg_release("Build artifacts in release mode, with optimizations") .arg_profile("Build artifacts with the specified profile") .arg_target_triple("Build for the target triple") .arg_target_dir() .arg_unit_graph() .arg_timings() .arg_manifest_path() .arg_lockfile_path() .arg_ignore_rust_version() .after_help(color_print::cstr!( "Run `cargo help test` for more detailed information.\n\ Run `cargo test -- --help` for test binary options.\n", )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let ws = args.workspace(gctx)?; let mut compile_opts = args.compile_options(gctx, UserIntent::Test, Some(&ws), ProfileChecking::Custom)?; compile_opts.build_config.requested_profile = args.get_profile_name("test", ProfileChecking::Custom)?; // `TESTNAME` is actually an argument of the test binary, but it's // important, so we explicitly mention it and reconfigure. let test_name = args.get_one::("TESTNAME"); let test_args = args.get_one::("TESTNAME").into_iter(); let test_args = test_args.chain(args.get_many::("args").unwrap_or_default()); let test_args = test_args.map(String::as_str).collect::>(); let no_run = args.flag("no-run"); let doc = args.flag("doc"); if doc { if compile_opts.filter.is_specific() { return Err( anyhow::format_err!("Can't mix --doc with other target selecting options").into(), ); } if no_run { return Err(anyhow::format_err!("Can't skip running doc tests with --no-run").into()); } compile_opts.build_config.intent = UserIntent::Doctest; compile_opts.filter = ops::CompileFilter::lib_only(); } else if test_name.is_some() && !compile_opts.filter.is_specific() { // If arg `TESTNAME` is provided, assumed that the user knows what // exactly they wants to test, so we use `all_test_targets` to // avoid compiling unnecessary targets such as examples, which are // included by the logic of default target filter. compile_opts.filter = ops::CompileFilter::all_test_targets(); } let ops = ops::TestOptions { no_run, no_fail_fast: args.flag("no-fail-fast"), compile_opts, }; ops::run_tests(&ws, &ops, &test_args) } cargo-0.91.0/src/bin/cargo/commands/tree.rs000064400000000000000000000262551046102023000165600ustar 00000000000000use crate::cli; use crate::command_prelude::*; use anyhow::{bail, format_err}; use cargo::core::dependency::DepKind; use cargo::ops::Packages; use cargo::ops::tree::{self, DisplayDepth, EdgeKind}; use cargo::util::CargoResult; use cargo::util::print_available_packages; use std::collections::HashSet; use std::str::FromStr; pub fn cli() -> Command { subcommand("tree") .about("Display a tree visualization of a dependency graph") .arg( flag("all", "Deprecated, use --no-dedupe instead") .short('a') .hide(true), ) .arg_silent_suggestion() .arg(flag("no-dev-dependencies", "Deprecated, use -e=no-dev instead").hide(true)) .arg( multi_opt( "edges", "KINDS", "The kinds of dependencies to display \ (features, normal, build, dev, all, \ no-normal, no-build, no-dev, no-proc-macro)", ) .short('e'), ) .arg( optional_multi_opt( "invert", "SPEC", "Invert the tree direction and focus on the given package", ) .short('i'), ) .arg(multi_opt( "prune", "SPEC", "Prune the given package from the display of the dependency tree", )) .arg(opt("depth", "Maximum display depth of the dependency tree").value_name("DEPTH")) .arg(flag("no-indent", "Deprecated, use --prefix=none instead").hide(true)) .arg(flag("prefix-depth", "Deprecated, use --prefix=depth instead").hide(true)) .arg( opt( "prefix", "Change the prefix (indentation) of how each entry is displayed", ) .value_name("PREFIX") .value_parser(["depth", "indent", "none"]) .default_value("indent"), ) .arg(flag( "no-dedupe", "Do not de-duplicate (repeats all shared dependencies)", )) .arg( flag( "duplicates", "Show only dependencies which come in multiple versions (implies -i)", ) .short('d') .alias("duplicate"), ) .arg( opt("charset", "Character set to use in output") .value_name("CHARSET") .value_parser(["utf8", "ascii"]), ) .arg( opt("format", "Format string used for printing dependencies") .value_name("FORMAT") .short('f') .default_value("{p}"), ) .arg( // Backwards compatibility with old cargo-tree. flag("version", "Print version info and exit") .short('V') .hide(true), ) .arg_package_spec_no_all( "Package to be used as the root of the tree", "Display the tree for all packages in the workspace", "Exclude specific workspace members", ) .arg_features() .arg(flag("all-targets", "Deprecated, use --target=all instead").hide(true)) .arg_target_triple( "Filter dependencies matching the given target-triple (default host platform). \ Pass `all` to include all targets.", ) .arg_manifest_path() .arg_lockfile_path() .after_help(color_print::cstr!( "Run `cargo help tree` for more detailed information.\n" )) } #[derive(Copy, Clone)] pub enum Charset { Utf8, Ascii, } impl FromStr for Charset { type Err = &'static str; fn from_str(s: &str) -> Result { match s { "utf8" => Ok(Charset::Utf8), "ascii" => Ok(Charset::Ascii), _ => Err("invalid charset"), } } } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { if args.flag("version") { let verbose = args.verbose() > 0; let version = cli::get_version_string(verbose); cargo::drop_print!(gctx, "{}", version); return Ok(()); } let prefix = if args.flag("no-indent") { gctx.shell() .warn("the --no-indent flag has been changed to --prefix=none")?; "none" } else if args.flag("prefix-depth") { gctx.shell() .warn("the --prefix-depth flag has been changed to --prefix=depth")?; "depth" } else { args.get_one::("prefix").unwrap().as_str() }; let prefix = tree::Prefix::from_str(prefix).map_err(|e| anyhow::anyhow!("{}", e))?; let no_dedupe = args.flag("no-dedupe") || args.flag("all"); if args.flag("all") { gctx.shell().warn( "The `cargo tree` --all flag has been changed to --no-dedupe, \ and may be removed in a future version.\n\ If you are looking to display all workspace members, use the --workspace flag.", )?; } let targets = if args.flag("all-targets") { gctx.shell() .warn("the --all-targets flag has been changed to --target=all")?; vec!["all".to_string()] } else { args.targets()? }; let target = tree::Target::from_cli(targets); let (edge_kinds, no_proc_macro) = parse_edge_kinds(gctx, args)?; let graph_features = edge_kinds.contains(&EdgeKind::Feature); let pkgs_to_prune = args._values_of("prune"); let display_depth = args ._value_of("depth") .map(|s| s.parse::()) .transpose()? .unwrap_or(DisplayDepth::MaxDisplayDepth(u32::MAX)); let packages = args.packages_from_flags()?; let mut invert = args .get_many::("invert") .map_or_else(|| Vec::new(), |is| is.map(|s| s.to_string()).collect()); if args.is_present_with_zero_values("invert") { match &packages { Packages::Packages(ps) => { // Backwards compatibility with old syntax of `cargo tree -i -p foo`. invert.extend(ps.clone()); } _ => { return Err(format_err!( "The `-i` flag requires a package name.\n\ \n\ The `-i` flag is used to inspect the reverse dependencies of a specific\n\ package. It will invert the tree and display the packages that depend on the\n\ given package.\n\ \n\ Note that in a workspace, by default it will only display the package's\n\ reverse dependencies inside the tree of the workspace member in the current\n\ directory. The --workspace flag can be used to extend it so that it will show\n\ the package's reverse dependencies across the entire workspace. The -p flag\n\ can be used to display the package's reverse dependencies only with the\n\ subtree of the package given to -p.\n\ " ) .into()); } } } let ws = args.workspace(gctx)?; if args.is_present_with_zero_values("package") { print_available_packages(&ws)?; } let charset = args.get_one::("charset"); if let Some(charset) = charset .map(|c| Charset::from_str(c)) .transpose() .map_err(|e| anyhow::anyhow!("{}", e))? { match charset { Charset::Utf8 => gctx.shell().set_unicode(true)?, Charset::Ascii => gctx.shell().set_unicode(false)?, } } let opts = tree::TreeOptions { cli_features: args.cli_features()?, packages, target, edge_kinds, invert, pkgs_to_prune, prefix, no_dedupe, duplicates: args.flag("duplicates"), format: args.get_one::("format").cloned().unwrap(), graph_features, display_depth, no_proc_macro, }; if opts.graph_features && opts.duplicates { return Err(format_err!("the `-e features` flag does not support `--duplicates`").into()); } tree::build_and_print(&ws, &opts)?; Ok(()) } /// Parses `--edges` option. /// /// Returns a tuple of `EdgeKind` map and `no_proc_marco` flag. fn parse_edge_kinds( gctx: &GlobalContext, args: &ArgMatches, ) -> CargoResult<(HashSet, bool)> { let (kinds, no_proc_macro) = { let mut no_proc_macro = false; let mut kinds = args.get_many::("edges").map_or_else( || Vec::new(), |es| { es.flat_map(|e| e.split(',')) .filter(|e| { if *e == "no-proc-macro" { no_proc_macro = true; false } else { true } }) .collect() }, ); if args.flag("no-dev-dependencies") { gctx.shell() .warn("the --no-dev-dependencies flag has changed to -e=no-dev")?; kinds.push("no-dev"); } if kinds.is_empty() { kinds.extend(&["normal", "build", "dev"]); } (kinds, no_proc_macro) }; let mut result = HashSet::new(); let insert_defaults = |result: &mut HashSet| { result.insert(EdgeKind::Dep(DepKind::Normal)); result.insert(EdgeKind::Dep(DepKind::Build)); result.insert(EdgeKind::Dep(DepKind::Development)); }; let unknown = |k| { bail!( "unknown edge kind `{}`, valid values are \ \"normal\", \"build\", \"dev\", \ \"no-normal\", \"no-build\", \"no-dev\", \"no-proc-macro\", \ \"features\", or \"all\"", k ) }; if kinds.iter().any(|k| k.starts_with("no-")) { insert_defaults(&mut result); for kind in &kinds { match *kind { "no-normal" => result.remove(&EdgeKind::Dep(DepKind::Normal)), "no-build" => result.remove(&EdgeKind::Dep(DepKind::Build)), "no-dev" => result.remove(&EdgeKind::Dep(DepKind::Development)), "features" => result.insert(EdgeKind::Feature), "normal" | "build" | "dev" | "all" => { bail!( "`{}` dependency kind cannot be mixed with \ \"no-normal\", \"no-build\", or \"no-dev\" \ dependency kinds", kind ) } k => return unknown(k), }; } return Ok((result, no_proc_macro)); } for kind in &kinds { match *kind { "all" => { insert_defaults(&mut result); result.insert(EdgeKind::Feature); } "features" => { result.insert(EdgeKind::Feature); } "normal" => { result.insert(EdgeKind::Dep(DepKind::Normal)); } "build" => { result.insert(EdgeKind::Dep(DepKind::Build)); } "dev" => { result.insert(EdgeKind::Dep(DepKind::Development)); } k => return unknown(k), } } if kinds.len() == 1 && kinds[0] == "features" { insert_defaults(&mut result); } Ok((result, no_proc_macro)) } cargo-0.91.0/src/bin/cargo/commands/uninstall.rs000064400000000000000000000041631046102023000176240ustar 00000000000000use crate::command_prelude::*; use cargo::ops; pub fn cli() -> Command { subcommand("uninstall") .about("Remove a Rust binary") .arg( Arg::new("spec") .value_name("SPEC") .num_args(0..) .add::(clap_complete::ArgValueCandidates::new( || get_installed_crates(), )), ) .arg(opt("root", "Directory to uninstall packages from").value_name("DIR")) .arg_silent_suggestion() .arg_package_spec_simple("Package to uninstall") .arg( multi_opt("bin", "NAME", "Only uninstall the binary NAME") .help_heading(heading::TARGET_SELECTION), ) .after_help(color_print::cstr!( "Run `cargo help uninstall` for more detailed information.\n" )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let root = args.get_one::("root").map(String::as_str); if args.is_present_with_zero_values("package") { return Err(anyhow::anyhow!( "\"--package \" requires a SPEC format value.\n\ Run `cargo help pkgid` for more information about SPEC format." ) .into()); } let specs = args .get_many::("spec") .unwrap_or_else(|| args.get_many::("package").unwrap_or_default()) .map(String::as_str) .collect(); ops::uninstall(root, specs, &values(args, "bin"), gctx)?; Ok(()) } fn get_installed_crates() -> Vec { get_installed_crates_().unwrap_or_default() } fn get_installed_crates_() -> Option> { let mut candidates = Vec::new(); let gctx = GlobalContext::default().ok()?; let root = ops::resolve_root(None, &gctx).ok()?; let tracker = ops::InstallTracker::load(&gctx, &root).ok()?; for (_, v) in tracker.all_installed_bins() { for bin in v { candidates.push(clap_complete::CompletionCandidate::new(bin)); } } Some(candidates) } cargo-0.91.0/src/bin/cargo/commands/update.rs000064400000000000000000000070371046102023000171000ustar 00000000000000use crate::command_prelude::*; use anyhow::anyhow; use cargo::ops::{self, UpdateOptions}; use cargo::util::print_available_packages; pub fn cli() -> Command { subcommand("update") .about("Update dependencies as recorded in the local lock file") .args([clap::Arg::new("package2") .action(clap::ArgAction::Append) .num_args(1..) .value_name("SPEC") .help_heading(heading::PACKAGE_SELECTION) .group("package-group") .help("Package to update") .add(clap_complete::ArgValueCandidates::new( get_pkg_id_spec_candidates, ))]) .arg( optional_multi_opt("package", "SPEC", "Package to update") .short('p') .hide(true) .help_heading(heading::PACKAGE_SELECTION) .group("package-group"), ) .arg_dry_run("Don't actually write the lockfile") .arg( flag( "recursive", "Force updating all dependencies of [SPEC]... as well", ) .alias("aggressive") .conflicts_with("precise"), ) .arg( opt("precise", "Update [SPEC] to exactly PRECISE") .value_name("PRECISE") .requires("package-group"), ) .arg( flag( "breaking", "Update [SPEC] to latest SemVer-breaking version (unstable)", ) .short('b'), ) .arg_silent_suggestion() .arg( flag("workspace", "Only update the workspace packages") .short('w') .help_heading(heading::PACKAGE_SELECTION), ) .arg_manifest_path() .arg_lockfile_path() .arg_ignore_rust_version_with_help("Ignore `rust-version` specification in packages") .after_help(color_print::cstr!( "Run `cargo help update` for more detailed information.\n" )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let mut ws = args.workspace(gctx)?; if args.is_present_with_zero_values("package") { print_available_packages(&ws)?; } let to_update = if args.contains_id("package") { "package" } else { "package2" }; let to_update = values(args, to_update); for crate_name in to_update.iter() { if let Some(toolchain) = crate_name.strip_prefix("+") { return Err(anyhow!( "invalid character `+` in package name: `+{toolchain}` Use `cargo +{toolchain} update` if you meant to use the `{toolchain}` toolchain." ) .into()); } } let update_opts = UpdateOptions { recursive: args.flag("recursive"), precise: args.get_one::("precise").map(String::as_str), to_update, dry_run: args.dry_run(), workspace: args.flag("workspace"), gctx, }; if args.flag("breaking") { gctx.cli_unstable() .fail_if_stable_opt("--breaking", 12425)?; let upgrades = ops::upgrade_manifests(&mut ws, &update_opts.to_update)?; ops::resolve_ws(&ws, update_opts.dry_run)?; ops::write_manifest_upgrades(&ws, &upgrades, update_opts.dry_run)?; if update_opts.dry_run { update_opts .gctx .shell() .warn("aborting update due to dry run")?; } } else { ops::update_lockfile(&ws, &update_opts)?; } Ok(()) } cargo-0.91.0/src/bin/cargo/commands/vendor.rs000064400000000000000000000064111046102023000171060ustar 00000000000000use crate::command_prelude::*; use cargo::ops; use std::path::PathBuf; pub fn cli() -> Command { subcommand("vendor") .about("Vendor all dependencies for a project locally") .arg( Arg::new("path") .action(ArgAction::Set) .value_parser(clap::value_parser!(PathBuf)) .help("Where to vendor crates (`vendor` by default)"), ) .arg(flag( "no-delete", "Don't delete older crates in the vendor directory", )) .arg( Arg::new("tomls") .short('s') .long("sync") .help("Additional `Cargo.toml` to sync and vendor") .value_name("TOML") .value_parser(clap::value_parser!(PathBuf)) .action(clap::ArgAction::Append), ) .arg(flag( "respect-source-config", "Respect `[source]` config in `.cargo/config`", )) .arg(flag( "versioned-dirs", "Always include version in subdir name", )) .arg(unsupported("no-merge-sources")) .arg(unsupported("relative-path")) .arg(unsupported("only-git-deps")) .arg(unsupported("disallow-duplicates")) .arg_manifest_path() .arg_lockfile_path() .after_help(color_print::cstr!( "Run `cargo help vendor` for more detailed information.\n" )) } fn unsupported(name: &'static str) -> Arg { // When we moved `cargo vendor` into Cargo itself we didn't stabilize a few // flags, so try to provide a helpful error message in that case to ensure // that users currently using the flag aren't tripped up. let value_parser = clap::builder::UnknownArgumentValueParser::suggest("the crates.io `cargo vendor` command has been merged into Cargo") .and_suggest(format!("and the flag `--{name}` isn't supported currently")) .and_suggest("to continue using the flag, execute `cargo-vendor vendor ...`") .and_suggest("to suggest this flag supported in Cargo, file an issue at "); flag(name, "").value_parser(value_parser).hide(true) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { // We're doing the vendoring operation ourselves, so we don't actually want // to respect any of the `source` configuration in Cargo itself. That's // intended for other consumers of Cargo, but we want to go straight to the // source, e.g. crates.io, to fetch crates. let respect_source_config = args.flag("respect-source-config"); if !respect_source_config { gctx.values_mut()?.remove("source"); } let ws = args.workspace(gctx)?; let path = args .get_one::("path") .cloned() .unwrap_or_else(|| PathBuf::from("vendor")); ops::vendor( &ws, &ops::VendorOptions { no_delete: args.flag("no-delete"), destination: &path, versioned_dirs: args.flag("versioned-dirs"), extra: args .get_many::("tomls") .unwrap_or_default() .cloned() .collect(), respect_source_config, }, )?; Ok(()) } cargo-0.91.0/src/bin/cargo/commands/verify_project.rs000064400000000000000000000013201046102023000206350ustar 00000000000000//! Deprecated. use crate::command_prelude::*; use std::collections::HashMap; use std::process; pub fn cli() -> Command { subcommand("verify-project") .hide(true) .about( "\ DEPRECATED: Check correctness of crate manifest. See https://github.com/rust-lang/cargo/issues/14679.", ) .arg_silent_suggestion() .arg_manifest_path() } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { if let Err(e) = args.workspace(gctx) { gctx.shell() .print_json(&HashMap::from([("invalid", e.to_string())]))?; process::exit(1) } gctx.shell() .print_json(&HashMap::from([("success", "true")]))?; Ok(()) } cargo-0.91.0/src/bin/cargo/commands/version.rs000064400000000000000000000010301046102023000172660ustar 00000000000000use crate::cli; use crate::command_prelude::*; pub fn cli() -> Command { subcommand("version") .about("Show version information") .arg_silent_suggestion() .after_help(color_print::cstr!( "Run `cargo help version` for more detailed information.\n" )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let verbose = args.verbose() > 0; let version = cli::get_version_string(verbose); cargo::drop_print!(gctx, "{}", version); Ok(()) } cargo-0.91.0/src/bin/cargo/commands/yank.rs000064400000000000000000000050401046102023000165500ustar 00000000000000use crate::command_prelude::*; use anyhow::Context; use cargo::ops; use cargo_credential::Secret; pub fn cli() -> Command { subcommand("yank") .about("Remove a pushed crate from the index") .arg(Arg::new("crate").value_name("CRATE").action(ArgAction::Set)) .arg( opt("version", "The version to yank or un-yank") .alias("vers") .value_name("VERSION"), ) .arg(flag( "undo", "Undo a yank, putting a version back into the index", )) .arg_index("Registry index URL to yank from") .arg_registry("Registry to yank from") .arg(opt("token", "API token to use when authenticating").value_name("TOKEN")) .arg_silent_suggestion() .after_help(color_print::cstr!( "Run `cargo help yank` for more detailed information.\n" )) } pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let (krate, version) = resolve_crate( args.get_one::("crate").map(String::as_str), args.get_one::("version").map(String::as_str), )?; if version.is_none() { return Err(anyhow::format_err!("`--version` is required").into()); } ops::yank( gctx, krate.map(|s| s.to_string()), version.map(|s| s.to_string()), args.get_one::("token").cloned().map(Secret::from), args.registry_or_index(gctx)?, args.flag("undo"), )?; Ok(()) } fn resolve_crate<'k>( mut krate: Option<&'k str>, mut version: Option<&'k str>, ) -> crate::CargoResult<(Option<&'k str>, Option<&'k str>)> { if let Some((k, v)) = krate.and_then(|k| k.split_once('@')) { if version.is_some() { anyhow::bail!("cannot specify both `@{v}` and `--version`"); } if k.is_empty() { // by convention, arguments starting with `@` are response files anyhow::bail!("missing crate name for `@{v}`"); } krate = Some(k); version = Some(v); } if let Some(version) = version { semver::Version::parse(version).with_context(|| { if let Some(stripped) = version.strip_prefix("v") { return format!( "the version provided, `{version}` is not a \ valid SemVer version\n\n\ help: try changing the version to `{stripped}`", ); } format!("invalid version `{version}`") })?; } Ok((krate, version)) } cargo-0.91.0/src/bin/cargo/main.rs000064400000000000000000000347321046102023000147430ustar 00000000000000#![allow(clippy::self_named_module_files)] // false positive in `commands/build.rs` use cargo::core::features; use cargo::core::shell::Shell; use cargo::util::network::http::http_handle; use cargo::util::network::http::needs_custom_http_transport; use cargo::util::{self, CargoResult, closest_msg, command_prelude}; use cargo_util::{ProcessBuilder, ProcessError}; use cargo_util_schemas::manifest::StringOrVec; use std::collections::BTreeMap; use std::env; use std::ffi::OsStr; use std::fs; use std::path::{Path, PathBuf}; mod cli; mod commands; use crate::command_prelude::*; fn main() { let _guard = setup_logger(); let mut gctx = match GlobalContext::default() { Ok(gctx) => gctx, Err(e) => { let mut shell = Shell::new(); cargo::exit_with_error(e.into(), &mut shell) } }; let nightly_features_allowed = matches!(&*features::channel(), "nightly" | "dev"); if nightly_features_allowed { let _span = tracing::span!(tracing::Level::TRACE, "completions").entered(); let args = std::env::args_os(); let current_dir = std::env::current_dir().ok(); let completer = clap_complete::CompleteEnv::with_factory(|| { let mut gctx = GlobalContext::default().expect("already loaded without errors"); cli::cli(&mut gctx) }) .var("CARGO_COMPLETE"); if completer .try_complete(args, current_dir.as_deref()) .unwrap_or_else(|e| { let mut shell = Shell::new(); cargo::exit_with_error(e.into(), &mut shell) }) { return; } } let result = if let Some(lock_addr) = cargo::ops::fix_get_proxy_lock_addr() { cargo::ops::fix_exec_rustc(&gctx, &lock_addr).map_err(|e| CliError::from(e)) } else { let _token = cargo::util::job::setup(); cli::main(&mut gctx) }; match result { Err(e) => cargo::exit_with_error(e, &mut *gctx.shell()), Ok(()) => {} } } fn setup_logger() -> Option { use tracing_subscriber::prelude::*; let env = tracing_subscriber::EnvFilter::from_env("CARGO_LOG"); let fmt_layer = tracing_subscriber::fmt::layer() .with_timer(tracing_subscriber::fmt::time::Uptime::default()) .with_ansi(std::io::IsTerminal::is_terminal(&std::io::stderr())) .with_writer(std::io::stderr) .with_filter(env); let (profile_layer, profile_guard) = chrome_layer(); let registry = tracing_subscriber::registry() .with(fmt_layer) .with(profile_layer); registry.init(); tracing::trace!(start = jiff::Timestamp::now().to_string()); profile_guard } #[cfg(target_has_atomic = "64")] type ChromeFlushGuard = tracing_chrome::FlushGuard; #[cfg(target_has_atomic = "64")] fn chrome_layer() -> ( Option>, Option, ) where S: tracing::Subscriber + for<'span> tracing_subscriber::registry::LookupSpan<'span> + Send + Sync, { #![allow(clippy::disallowed_methods)] if env_to_bool(std::env::var_os("CARGO_LOG_PROFILE").as_deref()) { let capture_args = env_to_bool(std::env::var_os("CARGO_LOG_PROFILE_CAPTURE_ARGS").as_deref()); let (layer, guard) = tracing_chrome::ChromeLayerBuilder::new() .include_args(capture_args) .build(); (Some(layer), Some(guard)) } else { (None, None) } } #[cfg(not(target_has_atomic = "64"))] type ChromeFlushGuard = (); #[cfg(not(target_has_atomic = "64"))] fn chrome_layer() -> ( Option, Option, ) { (None, None) } #[cfg(target_has_atomic = "64")] fn env_to_bool(os: Option<&OsStr>) -> bool { match os.and_then(|os| os.to_str()) { Some("1") | Some("true") => true, _ => false, } } /// Table for defining the aliases which come builtin in `Cargo`. /// The contents are structured as: `(alias, aliased_command, description)`. const BUILTIN_ALIASES: [(&str, &str, &str); 6] = [ ("b", "build", "alias: build"), ("c", "check", "alias: check"), ("d", "doc", "alias: doc"), ("r", "run", "alias: run"), ("t", "test", "alias: test"), ("rm", "remove", "alias: remove"), ]; /// Function which contains the list of all of the builtin aliases and it's /// corresponding execs represented as &str. fn builtin_aliases_execs(cmd: &str) -> Option<&(&str, &str, &str)> { BUILTIN_ALIASES.iter().find(|alias| alias.0 == cmd) } /// Resolve the aliased command from the [`GlobalContext`] with a given command string. /// /// The search fallback chain is: /// /// 1. Get the aliased command as a string. /// 2. If an `Err` occurs (missing key, type mismatch, or any possible error), /// try to get it as an array again. /// 3. If still cannot find any, finds one insides [`BUILTIN_ALIASES`]. fn aliased_command(gctx: &GlobalContext, command: &str) -> CargoResult>> { let alias_name = format!("alias.{}", command); let user_alias = match gctx.get_string(&alias_name) { Ok(Some(record)) => Some( record .val .split_whitespace() .map(|s| s.to_string()) .collect(), ), Ok(None) => None, Err(_) => gctx.get::>>(&alias_name)?, }; let result = user_alias.or_else(|| { builtin_aliases_execs(command).map(|command_str| vec![command_str.1.to_string()]) }); if result .as_ref() .map(|alias| alias.is_empty()) .unwrap_or_default() { anyhow::bail!("subcommand is required, but `{alias_name}` is empty"); } Ok(result) } /// List all runnable commands fn list_commands(gctx: &GlobalContext) -> BTreeMap { let mut commands = third_party_subcommands(gctx); for cmd in commands::builtin() { commands.insert( cmd.get_name().to_string(), CommandInfo::BuiltIn { about: cmd.get_about().map(|s| s.to_string()), }, ); } // Add the builtin_aliases and them descriptions to the // `commands` `BTreeMap`. for command in &BUILTIN_ALIASES { commands.insert( command.0.to_string(), CommandInfo::BuiltIn { about: Some(command.2.to_string()), }, ); } // Add the user-defined aliases let alias_commands = user_defined_aliases(gctx); commands.extend(alias_commands); // `help` is special, so it needs to be inserted separately. commands.insert( "help".to_string(), CommandInfo::BuiltIn { about: Some("Displays help for a cargo subcommand".to_string()), }, ); commands } fn third_party_subcommands(gctx: &GlobalContext) -> BTreeMap { let prefix = "cargo-"; let suffix = env::consts::EXE_SUFFIX; let mut commands = BTreeMap::new(); for dir in search_directories(gctx) { let entries = match fs::read_dir(dir) { Ok(entries) => entries, _ => continue, }; for entry in entries.filter_map(|e| e.ok()) { let path = entry.path(); let Some(filename) = path.file_name().and_then(|s| s.to_str()) else { continue; }; let Some(name) = filename .strip_prefix(prefix) .and_then(|s| s.strip_suffix(suffix)) else { continue; }; if is_executable(entry.path()) { commands.insert( name.to_string(), CommandInfo::External { path: path.clone() }, ); } } } commands } fn user_defined_aliases(gctx: &GlobalContext) -> BTreeMap { let mut commands = BTreeMap::new(); if let Ok(aliases) = gctx.get::>("alias") { for (name, target) in aliases.iter() { commands.insert( name.to_string(), CommandInfo::Alias { target: target.clone(), }, ); } } commands } fn find_external_subcommand(gctx: &GlobalContext, cmd: &str) -> Option { let command_exe = format!("cargo-{}{}", cmd, env::consts::EXE_SUFFIX); search_directories(gctx) .iter() .map(|dir| dir.join(&command_exe)) .find(|file| is_executable(file)) } fn execute_external_subcommand(gctx: &GlobalContext, cmd: &str, args: &[&OsStr]) -> CliResult { let path = find_external_subcommand(gctx, cmd); let command = match path { Some(command) => command, None => { let script_suggestion = if gctx.cli_unstable().script && std::path::Path::new(cmd).is_file() { let sep = std::path::MAIN_SEPARATOR; format!( "\nhelp: To run the file `{cmd}`, provide a relative path like `.{sep}{cmd}`" ) } else { "".to_owned() }; let err = if cmd.starts_with('+') { anyhow::format_err!( "no such command: `{cmd}`\n\n\ help: invoke `cargo` through `rustup` to handle `+toolchain` directives{script_suggestion}", ) } else { let suggestions = list_commands(gctx); let did_you_mean = closest_msg(cmd, suggestions.keys(), |c| c, "command"); anyhow::format_err!( "no such command: `{cmd}`{did_you_mean}\n\n\ help: view all installed commands with `cargo --list`\n\ help: find a package to install `{cmd}` with `cargo search cargo-{cmd}`{script_suggestion}", ) }; return Err(CliError::new(err, 101)); } }; execute_subcommand(gctx, Some(&command), args) } fn execute_internal_subcommand(gctx: &GlobalContext, args: &[&OsStr]) -> CliResult { execute_subcommand(gctx, None, args) } // This function is used to execute a subcommand. It is used to execute both // internal and external subcommands. // If `cmd_path` is `None`, then the subcommand is an internal subcommand. fn execute_subcommand( gctx: &GlobalContext, cmd_path: Option<&PathBuf>, args: &[&OsStr], ) -> CliResult { let cargo_exe = gctx.cargo_exe()?; let mut cmd = match cmd_path { Some(cmd_path) => ProcessBuilder::new(cmd_path), None => ProcessBuilder::new(&cargo_exe), }; cmd.env(cargo::CARGO_ENV, cargo_exe).args(args); if let Some(client) = gctx.jobserver_from_env() { cmd.inherit_jobserver(client); } let err = match cmd.exec_replace() { Ok(()) => return Ok(()), Err(e) => e, }; if let Some(perr) = err.downcast_ref::() { if let Some(code) = perr.code { return Err(CliError::code(code)); } } Err(CliError::new(err, 101)) } #[cfg(unix)] fn is_executable>(path: P) -> bool { use std::os::unix::prelude::*; fs::metadata(path) .map(|metadata| metadata.is_file() && metadata.permissions().mode() & 0o111 != 0) .unwrap_or(false) } #[cfg(windows)] fn is_executable>(path: P) -> bool { path.as_ref().is_file() } fn search_directories(gctx: &GlobalContext) -> Vec { let mut path_dirs = if let Some(val) = gctx.get_env_os("PATH") { env::split_paths(&val).collect() } else { vec![] }; let home_bin = gctx.home().clone().into_path_unlocked().join("bin"); // If any of that PATH elements contains `home_bin`, do not // add it again. This is so that the users can control priority // of it using PATH, while preserving the historical // behavior of preferring it over system global directories even // when not in PATH at all. // See https://github.com/rust-lang/cargo/issues/11020 for details. // // Note: `p == home_bin` will ignore trailing slash, but we don't // `canonicalize` the paths. if !path_dirs.iter().any(|p| p == &home_bin) { path_dirs.insert(0, home_bin); }; path_dirs } /// Initialize libgit2. #[tracing::instrument(skip_all)] fn init_git(gctx: &GlobalContext) { // Disabling the owner validation in git can, in theory, lead to code execution // vulnerabilities. However, libgit2 does not launch executables, which is the foundation of // the original security issue. Meanwhile, issues with refusing to load git repos in // `CARGO_HOME` for example will likely be very frustrating for users. So, we disable the // validation. // // For further discussion of Cargo's current interactions with git, see // // https://github.com/rust-lang/rfcs/pull/3279 // // and in particular the subsection on "Git support". // // Note that we only disable this when Cargo is run as a binary. If Cargo is used as a library, // this code won't be invoked. Instead, developers will need to explicitly disable the // validation in their code. This is inconvenient, but won't accidentally open consuming // applications up to security issues if they use git2 to open repositories elsewhere in their // code. unsafe { git2::opts::set_verify_owner_validation(false) .expect("set_verify_owner_validation should never fail"); } init_git_transports(gctx); } /// Configure libgit2 to use libcurl if necessary. /// /// If the user has a non-default network configuration, then libgit2 will be /// configured to use libcurl instead of the built-in networking support so /// that those configuration settings can be used. #[tracing::instrument(skip_all)] fn init_git_transports(gctx: &GlobalContext) { match needs_custom_http_transport(gctx) { Ok(true) => {} _ => return, } let handle = match http_handle(gctx) { Ok(handle) => handle, Err(..) => return, }; // The unsafety of the registration function derives from two aspects: // // 1. This call must be synchronized with all other registration calls as // well as construction of new transports. // 2. The argument is leaked. // // We're clear on point (1) because this is only called at the start of this // binary (we know what the state of the world looks like) and we're mostly // clear on point (2) because we'd only free it after everything is done // anyway unsafe { git2_curl::register(handle); } } cargo-0.91.0/src/cargo/core/compiler/artifact.rs000064400000000000000000000124151046102023000176000ustar 00000000000000//! Generate artifact information from unit dependencies for configuring the compiler environment. use crate::CargoResult; use crate::core::compiler::unit_graph::UnitDep; use crate::core::compiler::{BuildRunner, CrateType, FileFlavor, Unit}; use crate::core::dependency::ArtifactKind; use crate::core::{Dependency, Target, TargetKind}; use std::collections::{HashMap, HashSet}; use std::ffi::OsString; /// Return all environment variables for the given unit-dependencies /// if artifacts are present. pub fn get_env( build_runner: &BuildRunner<'_, '_>, dependencies: &[UnitDep], ) -> CargoResult> { let mut env = HashMap::new(); for unit_dep in dependencies.iter().filter(|d| d.unit.artifact.is_true()) { for artifact_path in build_runner .outputs(&unit_dep.unit)? .iter() .filter_map(|f| (f.flavor == FileFlavor::Normal).then(|| &f.path)) { let artifact_type_upper = unit_artifact_type_name_upper(&unit_dep.unit); let dep_name = unit_dep.dep_name.unwrap_or(unit_dep.unit.pkg.name()); let dep_name_upper = dep_name.to_uppercase().replace("-", "_"); let var = format!("CARGO_{}_DIR_{}", artifact_type_upper, dep_name_upper); let path = artifact_path.parent().expect("parent dir for artifacts"); env.insert(var, path.to_owned().into()); let var_file = format!( "CARGO_{}_FILE_{}_{}", artifact_type_upper, dep_name_upper, unit_dep.unit.target.name() ); // In older releases, lib-targets defaulted to the name of the package. Newer releases // use the same name as default, but with dashes replaced. Hence, if the name of the // target was inferred by Cargo, we also set the env-var with the unconverted name for // backwards compatibility. let need_compat = unit_dep.unit.target.is_lib() && unit_dep.unit.target.name_inferred(); if need_compat { let var_compat = format!( "CARGO_{}_FILE_{}_{}", artifact_type_upper, dep_name_upper, unit_dep.unit.pkg.name(), ); if var_compat != var_file { env.insert(var_compat, artifact_path.to_owned().into()); } } env.insert(var_file, artifact_path.to_owned().into()); // If the name of the target matches the name of the dependency, we strip the // repetition and provide the simpler env-var as well. // For backwards-compatibility of inferred names, we compare against the name of the // package as well, since that used to be the default for library targets. if unit_dep.unit.target.name() == dep_name.as_str() || (need_compat && unit_dep.unit.pkg.name() == dep_name.as_str()) { let var = format!("CARGO_{}_FILE_{}", artifact_type_upper, dep_name_upper,); env.insert(var, artifact_path.to_owned().into()); } } } Ok(env) } fn unit_artifact_type_name_upper(unit: &Unit) -> &'static str { match unit.target.kind() { TargetKind::Lib(kinds) => match kinds.as_slice() { &[CrateType::Cdylib] => "CDYLIB", &[CrateType::Staticlib] => "STATICLIB", invalid => unreachable!("BUG: artifacts cannot be of type {:?}", invalid), }, TargetKind::Bin => "BIN", invalid => unreachable!("BUG: artifacts cannot be of type {:?}", invalid), } } /// Given a dependency with an artifact `artifact_dep` and a set of available `targets` /// of its package, find a target for each kind of artifacts that are to be built. /// /// Failure to match any target results in an error mentioning the parent manifests /// `parent_package` name. pub(crate) fn match_artifacts_kind_with_targets<'t, 'd>( artifact_dep: &'d Dependency, targets: &'t [Target], parent_package: &str, ) -> CargoResult> { let mut out = HashSet::new(); let artifact_requirements = artifact_dep.artifact().expect("artifact present"); for artifact_kind in artifact_requirements.kinds() { let mut extend = |kind, filter: &dyn Fn(&&Target) -> bool| { let mut iter = targets.iter().filter(filter).peekable(); let found = iter.peek().is_some(); out.extend(std::iter::repeat(kind).zip(iter)); found }; let found = match artifact_kind { ArtifactKind::Cdylib => extend(artifact_kind, &|t| t.is_cdylib()), ArtifactKind::Staticlib => extend(artifact_kind, &|t| t.is_staticlib()), ArtifactKind::AllBinaries => extend(artifact_kind, &|t| t.is_bin()), ArtifactKind::SelectedBinary(bin_name) => extend(artifact_kind, &|t| { t.is_bin() && t.name() == bin_name.as_str() }), }; if !found { anyhow::bail!( "dependency `{}` in package `{}` requires a `{}` artifact to be present.", artifact_dep.name_in_toml(), parent_package, artifact_kind ); } } Ok(out) } cargo-0.91.0/src/cargo/core/compiler/build_config.rs000064400000000000000000000267041046102023000204350ustar 00000000000000use crate::core::compiler::CompileKind; use crate::util::context::JobsConfig; use crate::util::interning::InternedString; use crate::util::{CargoResult, GlobalContext, RustfixDiagnosticServer}; use anyhow::{Context as _, bail}; use cargo_util::ProcessBuilder; use serde::ser; use std::cell::RefCell; use std::path::PathBuf; use std::rc::Rc; use std::thread::available_parallelism; /// Configuration information for a rustc build. #[derive(Debug, Clone)] pub struct BuildConfig { /// The requested kind of compilation for this session pub requested_kinds: Vec, /// Number of rustc jobs to run in parallel. pub jobs: u32, /// Do not abort the build as soon as there is an error. pub keep_going: bool, /// Build profile pub requested_profile: InternedString, /// The intent we are compiling in. pub intent: UserIntent, /// `true` to print stdout in JSON format (for machine reading). pub message_format: MessageFormat, /// Force Cargo to do a full rebuild and treat each target as changed. pub force_rebuild: bool, /// Output a build plan to stdout instead of actually compiling. pub build_plan: bool, /// Output the unit graph to stdout instead of actually compiling. pub unit_graph: bool, /// `true` to avoid really compiling. pub dry_run: bool, /// An optional override of the rustc process for primary units pub primary_unit_rustc: Option, /// A thread used by `cargo fix` to receive messages on a socket regarding /// the success/failure of applying fixes. pub rustfix_diagnostic_server: Rc>>, /// The directory to copy final artifacts to. Note that even if /// `artifact-dir` is set, a copy of artifacts still can be found at /// `target/(debug\release)` as usual. /// Named `export_dir` to avoid confusion with /// `CompilationFiles::artifact_dir`. pub export_dir: Option, /// `true` to output a future incompatibility report at the end of the build pub future_incompat_report: bool, /// Which kinds of build timings to output (empty if none). pub timing_outputs: Vec, /// Output SBOM precursor files. pub sbom: bool, /// Build compile time dependencies only, e.g., build scripts and proc macros pub compile_time_deps_only: bool, } fn default_parallelism() -> CargoResult { Ok(available_parallelism() .context("failed to determine the amount of parallelism available")? .get() as u32) } impl BuildConfig { /// Parses all config files to learn about build configuration. Currently /// configured options are: /// /// * `build.jobs` /// * `build.target` /// * `target.$target.ar` /// * `target.$target.linker` /// * `target.$target.libfoo.metadata` pub fn new( gctx: &GlobalContext, jobs: Option, keep_going: bool, requested_targets: &[String], intent: UserIntent, ) -> CargoResult { let cfg = gctx.build_config()?; let requested_kinds = CompileKind::from_requested_targets(gctx, requested_targets)?; if jobs.is_some() && gctx.jobserver_from_env().is_some() { gctx.shell().warn( "a `-j` argument was passed to Cargo but Cargo is \ also configured with an external jobserver in \ its environment, ignoring the `-j` parameter", )?; } let jobs = match jobs.or(cfg.jobs.clone()) { None => default_parallelism()?, Some(value) => match value { JobsConfig::Integer(j) => match j { 0 => anyhow::bail!("jobs may not be 0"), j if j < 0 => (default_parallelism()? as i32 + j).max(1) as u32, j => j as u32, }, JobsConfig::String(j) => match j.as_str() { "default" => default_parallelism()?, _ => { anyhow::bail!(format!( "could not parse `{j}`. Number of parallel jobs should be `default` or a number." )) } }, }, }; // If sbom flag is set, it requires the unstable feature let sbom = match (cfg.sbom, gctx.cli_unstable().sbom) { (Some(sbom), true) => sbom, (Some(_), false) => { gctx.shell() .warn("ignoring 'sbom' config, pass `-Zsbom` to enable it")?; false } (None, _) => false, }; Ok(BuildConfig { requested_kinds, jobs, keep_going, requested_profile: "dev".into(), intent, message_format: MessageFormat::Human, force_rebuild: false, build_plan: false, unit_graph: false, dry_run: false, primary_unit_rustc: None, rustfix_diagnostic_server: Rc::new(RefCell::new(None)), export_dir: None, future_incompat_report: false, timing_outputs: Vec::new(), sbom, compile_time_deps_only: false, }) } /// Whether or not the *user* wants JSON output. Whether or not rustc /// actually uses JSON is decided in `add_error_format`. pub fn emit_json(&self) -> bool { matches!(self.message_format, MessageFormat::Json { .. }) } pub fn single_requested_kind(&self) -> CargoResult { match self.requested_kinds.len() { 1 => Ok(self.requested_kinds[0]), _ => bail!("only one `--target` argument is supported"), } } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum MessageFormat { Human, Json { /// Whether rustc diagnostics are rendered by cargo or included into the /// output stream. render_diagnostics: bool, /// Whether the `rendered` field of rustc diagnostics are using the /// "short" rendering. short: bool, /// Whether the `rendered` field of rustc diagnostics embed ansi color /// codes. ansi: bool, }, Short, } /// The specific action to be performed on each `Unit` of work. #[derive(Clone, Copy, PartialEq, Debug, Eq, Hash, PartialOrd, Ord)] pub enum CompileMode { /// Test with `rustc`. Test, /// Compile with `rustc`. Build, /// Type-check with `rustc` by emitting `rmeta` metadata only. /// /// If `test` is true, then it is also compiled with `--test` to check it like /// a test. Check { test: bool }, /// Document with `rustdoc`. Doc, /// Test with `rustdoc`. Doctest, /// Scrape for function calls by `rustdoc`. Docscrape, /// Execute the binary built from the `build.rs` script. RunCustomBuild, } impl ser::Serialize for CompileMode { fn serialize(&self, s: S) -> Result where S: ser::Serializer, { use self::CompileMode::*; match *self { Test => "test".serialize(s), Build => "build".serialize(s), Check { .. } => "check".serialize(s), Doc { .. } => "doc".serialize(s), Doctest => "doctest".serialize(s), Docscrape => "docscrape".serialize(s), RunCustomBuild => "run-custom-build".serialize(s), } } } impl CompileMode { /// Returns `true` if the unit is being checked. pub fn is_check(self) -> bool { matches!(self, CompileMode::Check { .. }) } /// Returns `true` if this is generating documentation. pub fn is_doc(self) -> bool { matches!(self, CompileMode::Doc { .. }) } /// Returns `true` if this a doc test. pub fn is_doc_test(self) -> bool { self == CompileMode::Doctest } /// Returns `true` if this is scraping examples for documentation. pub fn is_doc_scrape(self) -> bool { self == CompileMode::Docscrape } /// Returns `true` if this is any type of test (test, benchmark, doc test, or /// check test). pub fn is_any_test(self) -> bool { matches!( self, CompileMode::Test | CompileMode::Check { test: true } | CompileMode::Doctest ) } /// Returns `true` if this is something that passes `--test` to rustc. pub fn is_rustc_test(self) -> bool { matches!(self, CompileMode::Test | CompileMode::Check { test: true }) } /// Returns `true` if this is the *execution* of a `build.rs` script. pub fn is_run_custom_build(self) -> bool { self == CompileMode::RunCustomBuild } /// Returns `true` if this mode may generate an executable. /// /// Note that this also returns `true` for building libraries, so you also /// have to check the target. pub fn generates_executable(self) -> bool { matches!(self, CompileMode::Test | CompileMode::Build) } } /// Represents the high-level operation requested by the user. /// /// It determines which "Cargo targets" are selected by default and influences /// how they will be processed. This is derived from the Cargo command the user /// invoked (like `cargo build` or `cargo test`). /// /// Unlike [`CompileMode`], which describes the specific compilation steps for /// individual units, [`UserIntent`] represents the overall goal of the build /// process as specified by the user. /// /// For example, when a user runs `cargo test`, the intent is [`UserIntent::Test`], /// but this might result in multiple [`CompileMode`]s for different units. #[derive(Clone, Copy, Debug)] pub enum UserIntent { /// Build benchmark binaries, e.g., `cargo bench` Bench, /// Build binaries and libraries, e.g., `cargo run`, `cargo install`, `cargo build`. Build, /// Perform type-check, e.g., `cargo check`. Check { test: bool }, /// Document packages. /// /// If `deps` is true, then it will also document all dependencies. /// if `json` is true, the documentation output is in json format. Doc { deps: bool, json: bool }, /// Build doctest binaries, e.g., `cargo test --doc` Doctest, /// Build test binaries, e.g., `cargo test` Test, } impl UserIntent { /// Returns `true` if this is generating documentation. pub fn is_doc(self) -> bool { matches!(self, UserIntent::Doc { .. }) } /// User wants rustdoc output in JSON format. pub fn wants_doc_json_output(self) -> bool { matches!(self, UserIntent::Doc { json: true, .. }) } /// User wants to document also for dependencies. pub fn wants_deps_docs(self) -> bool { matches!(self, UserIntent::Doc { deps: true, .. }) } /// Returns `true` if this is any type of test (test, benchmark, doc test, or /// check test). pub fn is_any_test(self) -> bool { matches!( self, UserIntent::Test | UserIntent::Bench | UserIntent::Check { test: true } | UserIntent::Doctest ) } /// Returns `true` if this is something that passes `--test` to rustc. pub fn is_rustc_test(self) -> bool { matches!( self, UserIntent::Test | UserIntent::Bench | UserIntent::Check { test: true } ) } } /// Kinds of build timings we can output. #[derive(Clone, Copy, PartialEq, Debug, Eq, Hash, PartialOrd, Ord)] pub enum TimingOutput { /// Human-readable HTML report Html, /// Machine-readable JSON (unstable) Json, } cargo-0.91.0/src/cargo/core/compiler/build_context/mod.rs000064400000000000000000000112411046102023000214210ustar 00000000000000//! [`BuildContext`] is a (mostly) static information about a build task. use crate::core::PackageSet; use crate::core::Workspace; use crate::core::compiler::unit_graph::UnitGraph; use crate::core::compiler::{BuildConfig, CompileKind, Unit}; use crate::core::profiles::Profiles; use crate::util::Rustc; use crate::util::context::GlobalContext; use crate::util::errors::CargoResult; use crate::util::interning::InternedString; use std::collections::{HashMap, HashSet}; mod target_info; pub use self::target_info::{ FileFlavor, FileType, RustDocFingerprint, RustcTargetData, TargetInfo, }; /// The build context, containing complete information needed for a build task /// before it gets started. /// /// It is intended that this is mostly static information. Stuff that mutates /// during the build can be found in the parent [`BuildRunner`]. (I say mostly, /// because this has internal caching, but nothing that should be observable /// or require &mut.) /// /// As a result, almost every field on `BuildContext` is public, including /// /// * a resolved [`UnitGraph`] of your dependencies, /// * a [`Profiles`] containing compiler flags presets, /// * a [`RustcTargetData`] containing host and target platform information, /// * and a [`PackageSet`] for further package downloads, /// /// just to name a few. Learn more on each own documentation. /// /// # How to use /// /// To prepare a build task, you may not want to use [`BuildContext::new`] directly, /// since it is often too lower-level. /// Instead, [`ops::create_bcx`] is usually what you are looking for. /// /// After a `BuildContext` is built, the next stage of building is handled in [`BuildRunner`]. /// /// [`BuildRunner`]: crate::core::compiler::BuildRunner /// [`ops::create_bcx`]: crate::ops::create_bcx pub struct BuildContext<'a, 'gctx> { /// The workspace the build is for. pub ws: &'a Workspace<'gctx>, /// The cargo context. pub gctx: &'gctx GlobalContext, /// This contains a collection of compiler flags presets. pub profiles: Profiles, /// Configuration information for a rustc build. pub build_config: &'a BuildConfig, /// Extra compiler args for either `rustc` or `rustdoc`. pub extra_compiler_args: HashMap>, /// Package downloader. /// /// This holds ownership of the `Package` objects. pub packages: PackageSet<'gctx>, /// Information about rustc and the target platform. pub target_data: RustcTargetData<'gctx>, /// The root units of `unit_graph` (units requested on the command-line). pub roots: Vec, /// The dependency graph of units to compile. pub unit_graph: UnitGraph, /// Reverse-dependencies of documented units, used by the `rustdoc --scrape-examples` flag. pub scrape_units: Vec, /// The list of all kinds that are involved in this build pub all_kinds: HashSet, } impl<'a, 'gctx> BuildContext<'a, 'gctx> { pub fn new( ws: &'a Workspace<'gctx>, packages: PackageSet<'gctx>, build_config: &'a BuildConfig, profiles: Profiles, extra_compiler_args: HashMap>, target_data: RustcTargetData<'gctx>, roots: Vec, unit_graph: UnitGraph, scrape_units: Vec, ) -> CargoResult> { let all_kinds = unit_graph .keys() .map(|u| u.kind) .chain(build_config.requested_kinds.iter().copied()) .chain(std::iter::once(CompileKind::Host)) .collect(); Ok(BuildContext { ws, gctx: ws.gctx(), packages, build_config, profiles, extra_compiler_args, target_data, roots, unit_graph, scrape_units, all_kinds, }) } /// Information of the `rustc` this build task will use. pub fn rustc(&self) -> &Rustc { &self.target_data.rustc } /// Gets the host architecture triple. /// /// For example, `x86_64-unknown-linux-gnu`, would be /// - machine: `x86_64`, /// - hardware-platform: `unknown`, /// - operating system: `linux-gnu`. pub fn host_triple(&self) -> InternedString { self.target_data.rustc.host } /// Gets the number of jobs specified for this build. pub fn jobs(&self) -> u32 { self.build_config.jobs } /// Extra compiler args for either `rustc` or `rustdoc`. /// /// As of now, these flags come from the trailing args of either /// `cargo rustc` or `cargo rustdoc`. pub fn extra_args_for(&self, unit: &Unit) -> Option<&Vec> { self.extra_compiler_args.get(unit) } } cargo-0.91.0/src/cargo/core/compiler/build_context/target_info.rs000064400000000000000000001340161046102023000231510ustar 00000000000000//! This modules contains types storing information of target platforms. //! //! Normally, call [`RustcTargetData::new`] to construct all the target //! platform once, and then query info on your demand. For example, //! //! * [`RustcTargetData::dep_platform_activated`] to check if platform is activated. //! * [`RustcTargetData::info`] to get a [`TargetInfo`] for an in-depth query. //! * [`TargetInfo::rustc_outputs`] to get a list of supported file types. use crate::core::compiler::apply_env_config; use crate::core::compiler::{BuildRunner, CompileKind, CompileMode, CompileTarget, CrateType}; use crate::core::{Dependency, Package, Target, TargetKind, Workspace}; use crate::util::context::{GlobalContext, StringList, TargetConfig}; use crate::util::interning::InternedString; use crate::util::{CargoResult, Rustc}; use anyhow::Context as _; use cargo_platform::{Cfg, CfgExpr}; use cargo_util::{ProcessBuilder, paths}; use serde::{Deserialize, Serialize}; use std::cell::RefCell; use std::collections::hash_map::{Entry, HashMap}; use std::path::{Path, PathBuf}; use std::rc::Rc; use std::str::{self, FromStr}; /// Information about the platform target gleaned from querying rustc. /// /// [`RustcTargetData`] keeps several of these, one for the host and the others /// for other specified targets. If no target is specified, it uses a clone from /// the host. #[derive(Clone)] pub struct TargetInfo { /// A base process builder for discovering crate type information. In /// particular, this is used to determine the output filename prefix and /// suffix for a crate type. crate_type_process: ProcessBuilder, /// Cache of output filename prefixes and suffixes. /// /// The key is the crate type name (like `cdylib`) and the value is /// `Some((prefix, suffix))`, for example `libcargo.so` would be /// `Some(("lib", ".so"))`. The value is `None` if the crate type is not /// supported. crate_types: RefCell>>, /// `cfg` information extracted from `rustc --print=cfg`. cfg: Vec, /// `supports_std` information extracted from `rustc --print=target-spec-json` pub supports_std: Option, /// Supported values for `-Csplit-debuginfo=` flag, queried from rustc support_split_debuginfo: Vec, /// Path to the sysroot. pub sysroot: PathBuf, /// Path to the "lib" directory in the sysroot which rustc uses for linking /// target libraries. pub sysroot_target_libdir: PathBuf, /// Extra flags to pass to `rustc`, see [`extra_args`]. pub rustflags: Rc<[String]>, /// Extra flags to pass to `rustdoc`, see [`extra_args`]. pub rustdocflags: Rc<[String]>, } /// Kind of each file generated by a Unit, part of `FileType`. #[derive(Clone, PartialEq, Eq, Debug)] pub enum FileFlavor { /// Not a special file type. Normal, /// Like `Normal`, but not directly executable. /// For example, a `.wasm` file paired with the "normal" `.js` file. Auxiliary, /// Something you can link against (e.g., a library). Linkable, /// An `.rmeta` Rust metadata file. Rmeta, /// Piece of external debug information (e.g., `.dSYM`/`.pdb` file). DebugInfo, /// SBOM (Software Bill of Materials pre-cursor) file (e.g. cargo-sbon.json). Sbom, } /// Type of each file generated by a Unit. #[derive(Debug)] pub struct FileType { /// The kind of file. pub flavor: FileFlavor, /// The crate-type that generates this file. /// /// `None` for things that aren't associated with a specific crate type, /// for example `rmeta` files. pub crate_type: Option, /// The suffix for the file (for example, `.rlib`). /// This is an empty string for executables on Unix-like platforms. suffix: String, /// The prefix for the file (for example, `lib`). /// This is an empty string for things like executables. prefix: String, /// Flag to convert hyphen to underscore when uplifting. should_replace_hyphens: bool, } impl FileType { /// The filename for this `FileType` created by rustc. pub fn output_filename(&self, target: &Target, metadata: Option<&str>) -> String { match metadata { Some(metadata) => format!( "{}{}-{}{}", self.prefix, target.crate_name(), metadata, self.suffix ), None => format!("{}{}{}", self.prefix, target.crate_name(), self.suffix), } } /// The filename for this `FileType` that Cargo should use when "uplifting" /// it to the destination directory. pub fn uplift_filename(&self, target: &Target) -> String { let name = match target.binary_filename() { Some(name) => name, None => { // For binary crate type, `should_replace_hyphens` will always be false. if self.should_replace_hyphens { target.crate_name() } else { target.name().to_string() } } }; format!("{}{}{}", self.prefix, name, self.suffix) } /// Creates a new instance representing a `.rmeta` file. pub fn new_rmeta() -> FileType { // Note that even binaries use the `lib` prefix. FileType { flavor: FileFlavor::Rmeta, crate_type: None, suffix: ".rmeta".to_string(), prefix: "lib".to_string(), should_replace_hyphens: true, } } } impl TargetInfo { /// Learns the information of target platform from `rustc` invocation(s). /// /// Generally, the first time calling this function is expensive, as it may /// query `rustc` several times. To reduce the cost, output of each `rustc` /// invocation is cached by [`Rustc::cached_output`]. /// /// Search `Tricky` to learn why querying `rustc` several times is needed. #[tracing::instrument(skip_all)] pub fn new( gctx: &GlobalContext, requested_kinds: &[CompileKind], rustc: &Rustc, kind: CompileKind, ) -> CargoResult { let mut rustflags = extra_args(gctx, requested_kinds, &rustc.host, None, kind, Flags::Rust)?; let mut turn = 0; loop { let extra_fingerprint = kind.fingerprint_hash(); // Query rustc for several kinds of info from each line of output: // 0) file-names (to determine output file prefix/suffix for given crate type) // 1) sysroot // 2) split-debuginfo // 3) cfg // // Search `--print` to see what we query so far. let mut process = rustc.workspace_process(); apply_env_config(gctx, &mut process)?; process .arg("-") .arg("--crate-name") .arg("___") .arg("--print=file-names") .args(&rustflags) .env_remove("RUSTC_LOG"); // Removes `FD_CLOEXEC` set by `jobserver::Client` to pass jobserver // as environment variables specify. if let Some(client) = gctx.jobserver_from_env() { process.inherit_jobserver(client); } if let CompileKind::Target(target) = kind { process.arg("--target").arg(target.rustc_target()); } let crate_type_process = process.clone(); const KNOWN_CRATE_TYPES: &[CrateType] = &[ CrateType::Bin, CrateType::Rlib, CrateType::Dylib, CrateType::Cdylib, CrateType::Staticlib, CrateType::ProcMacro, ]; for crate_type in KNOWN_CRATE_TYPES.iter() { process.arg("--crate-type").arg(crate_type.as_str()); } process.arg("--print=sysroot"); process.arg("--print=split-debuginfo"); process.arg("--print=crate-name"); // `___` as a delimiter. process.arg("--print=cfg"); // parse_crate_type() relies on "unsupported/unknown crate type" error message, // so make warnings always emitted as warnings. process.arg("-Wwarnings"); let (output, error) = rustc .cached_output(&process, extra_fingerprint) .with_context( || "failed to run `rustc` to learn about target-specific information", )?; let mut lines = output.lines(); let mut map = HashMap::new(); for crate_type in KNOWN_CRATE_TYPES { let out = parse_crate_type(crate_type, &process, &output, &error, &mut lines)?; map.insert(crate_type.clone(), out); } let Some(line) = lines.next() else { return error_missing_print_output("sysroot", &process, &output, &error); }; let sysroot = PathBuf::from(line); let sysroot_target_libdir = { let mut libdir = sysroot.clone(); libdir.push("lib"); libdir.push("rustlib"); libdir.push(match &kind { CompileKind::Host => rustc.host.as_str(), CompileKind::Target(target) => target.short_name(), }); libdir.push("lib"); libdir }; let support_split_debuginfo = { // HACK: abuse `--print=crate-name` to use `___` as a delimiter. let mut res = Vec::new(); loop { match lines.next() { Some(line) if line == "___" => break, Some(line) => res.push(line.into()), None => { return error_missing_print_output( "split-debuginfo", &process, &output, &error, ); } } } res }; let cfg = lines .map(|line| Ok(Cfg::from_str(line)?)) .filter(TargetInfo::not_user_specific_cfg) .collect::>>() .with_context(|| { format!( "failed to parse the cfg from `rustc --print=cfg`, got:\n{}", output ) })?; // recalculate `rustflags` from above now that we have `cfg` // information let new_flags = extra_args( gctx, requested_kinds, &rustc.host, Some(&cfg), kind, Flags::Rust, )?; // Tricky: `RUSTFLAGS` defines the set of active `cfg` flags, active // `cfg` flags define which `.cargo/config` sections apply, and they // in turn can affect `RUSTFLAGS`! This is a bona fide mutual // dependency, and it can even diverge (see `cfg_paradox` test). // // So what we do here is running at most *two* iterations of // fixed-point iteration, which should be enough to cover // practically useful cases, and warn if that's not enough for // convergence. let reached_fixed_point = new_flags == rustflags; if !reached_fixed_point && turn == 0 { turn += 1; rustflags = new_flags; continue; } if !reached_fixed_point { gctx.shell().warn("non-trivial mutual dependency between target-specific configuration and RUSTFLAGS")?; } let mut supports_std: Option = None; // The '--print=target-spec-json' is an unstable option of rustc, therefore only // try to fetch this information if rustc allows nightly features. Additionally, // to avoid making two rustc queries when not required, only try to fetch the // target-spec when the '-Zbuild-std' option is passed. if gctx.cli_unstable().build_std.is_some() { let mut target_spec_process = rustc.workspace_process(); apply_env_config(gctx, &mut target_spec_process)?; target_spec_process .arg("--print=target-spec-json") .arg("-Zunstable-options") .args(&rustflags) .env_remove("RUSTC_LOG"); if let CompileKind::Target(target) = kind { target_spec_process .arg("--target") .arg(target.rustc_target()); } #[derive(Deserialize)] struct Metadata { pub std: Option, } #[derive(Deserialize)] struct TargetSpec { pub metadata: Metadata, } if let Ok(output) = target_spec_process.output() { if let Ok(spec) = serde_json::from_slice::(&output.stdout) { supports_std = spec.metadata.std; } } } return Ok(TargetInfo { crate_type_process, crate_types: RefCell::new(map), sysroot, sysroot_target_libdir, rustflags: rustflags.into(), rustdocflags: extra_args( gctx, requested_kinds, &rustc.host, Some(&cfg), kind, Flags::Rustdoc, )? .into(), cfg, supports_std, support_split_debuginfo, }); } } fn not_user_specific_cfg(cfg: &CargoResult) -> bool { if let Ok(Cfg::Name(cfg_name)) = cfg { // This should also include "debug_assertions", but it causes // regressions. Maybe some day in the distant future it can be // added (and possibly change the warning to an error). if cfg_name == "proc_macro" { return false; } } true } /// All the target [`Cfg`] settings. pub fn cfg(&self) -> &[Cfg] { &self.cfg } /// Returns the list of file types generated by the given crate type. /// /// Returns `None` if the target does not support the given crate type. fn file_types( &self, crate_type: &CrateType, flavor: FileFlavor, target_triple: &str, ) -> CargoResult>> { let crate_type = if *crate_type == CrateType::Lib { CrateType::Rlib } else { crate_type.clone() }; let mut crate_types = self.crate_types.borrow_mut(); let entry = crate_types.entry(crate_type.clone()); let crate_type_info = match entry { Entry::Occupied(o) => &*o.into_mut(), Entry::Vacant(v) => { let value = self.discover_crate_type(v.key())?; &*v.insert(value) } }; let Some((prefix, suffix)) = crate_type_info else { return Ok(None); }; let mut ret = vec![FileType { suffix: suffix.clone(), prefix: prefix.clone(), flavor, crate_type: Some(crate_type.clone()), should_replace_hyphens: crate_type != CrateType::Bin, }]; // Window shared library import/export files. if crate_type.is_dynamic() { // Note: Custom JSON specs can alter the suffix. For now, we'll // just ignore non-DLL suffixes. if target_triple.ends_with("-windows-msvc") && suffix == ".dll" { // See https://docs.microsoft.com/en-us/cpp/build/reference/working-with-import-libraries-and-export-files // for more information about DLL import/export files. ret.push(FileType { suffix: ".dll.lib".to_string(), prefix: prefix.clone(), flavor: FileFlavor::Auxiliary, crate_type: Some(crate_type.clone()), should_replace_hyphens: true, }); // NOTE: lld does not produce these ret.push(FileType { suffix: ".dll.exp".to_string(), prefix: prefix.clone(), flavor: FileFlavor::Auxiliary, crate_type: Some(crate_type.clone()), should_replace_hyphens: true, }); } else if suffix == ".dll" && (target_triple.ends_with("windows-gnu") || target_triple.ends_with("windows-gnullvm") || target_triple.ends_with("cygwin")) { // See https://cygwin.com/cygwin-ug-net/dll.html for more // information about GNU import libraries. // LD can link DLL directly, but LLD requires the import library. ret.push(FileType { suffix: ".dll.a".to_string(), prefix: "lib".to_string(), flavor: FileFlavor::Auxiliary, crate_type: Some(crate_type.clone()), should_replace_hyphens: true, }) } } if target_triple.starts_with("wasm32-") && crate_type == CrateType::Bin && suffix == ".js" { // emscripten binaries generate a .js file, which loads a .wasm // file. ret.push(FileType { suffix: ".wasm".to_string(), prefix: prefix.clone(), flavor: FileFlavor::Auxiliary, crate_type: Some(crate_type.clone()), // Name `foo-bar` will generate a `foo_bar.js` and // `foo_bar.wasm`. Cargo will translate the underscore and // copy `foo_bar.js` to `foo-bar.js`. However, the wasm // filename is embedded in the .js file with an underscore, so // it should not contain hyphens. should_replace_hyphens: true, }); // And a map file for debugging. This is only emitted with debug=2 // (-g4 for emcc). ret.push(FileType { suffix: ".wasm.map".to_string(), prefix: prefix.clone(), flavor: FileFlavor::DebugInfo, crate_type: Some(crate_type.clone()), should_replace_hyphens: true, }); } // Handle separate debug files. let is_apple = target_triple.contains("-apple-"); if matches!( crate_type, CrateType::Bin | CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro ) { if is_apple { let suffix = if crate_type == CrateType::Bin { ".dSYM".to_string() } else { ".dylib.dSYM".to_string() }; ret.push(FileType { suffix, prefix: prefix.clone(), flavor: FileFlavor::DebugInfo, crate_type: Some(crate_type), // macOS tools like lldb use all sorts of magic to locate // dSYM files. See https://lldb.llvm.org/use/symbols.html // for some details. It seems like a `.dSYM` located next // to the executable with the same name is one method. The // dSYM should have the same hyphens as the executable for // the names to match. should_replace_hyphens: false, }) } else if target_triple.ends_with("-msvc") || target_triple.ends_with("-uefi") { ret.push(FileType { suffix: ".pdb".to_string(), prefix: prefix.clone(), flavor: FileFlavor::DebugInfo, crate_type: Some(crate_type), // The absolute path to the pdb file is embedded in the // executable. If the exe/pdb pair is moved to another // machine, then debuggers will look in the same directory // of the exe with the original pdb filename. Since the // original name contains underscores, they need to be // preserved. should_replace_hyphens: true, }) } else { // Because DWARF Package (dwp) files are produced after the // fact by another tool, there is nothing in the binary that // provides a means to locate them. By convention, debuggers // take the binary filename and append ".dwp" (including to // binaries that already have an extension such as shared libs) // to find the dwp. ret.push(FileType { // It is important to preserve the existing suffix for // e.g. shared libraries, where the dwp for libfoo.so is // expected to be at libfoo.so.dwp. suffix: format!("{suffix}.dwp"), prefix: prefix.clone(), flavor: FileFlavor::DebugInfo, crate_type: Some(crate_type.clone()), // Likewise, the dwp needs to match the primary artifact's // hyphenation exactly. should_replace_hyphens: crate_type != CrateType::Bin, }) } } Ok(Some(ret)) } fn discover_crate_type(&self, crate_type: &CrateType) -> CargoResult> { let mut process = self.crate_type_process.clone(); process.arg("--crate-type").arg(crate_type.as_str()); let output = process.exec_with_output().with_context(|| { format!( "failed to run `rustc` to learn about crate-type {} information", crate_type ) })?; let error = str::from_utf8(&output.stderr).unwrap(); let output = str::from_utf8(&output.stdout).unwrap(); parse_crate_type(crate_type, &process, output, error, &mut output.lines()) } /// Returns all the file types generated by rustc for the given `mode`/`target_kind`. /// /// The first value is a Vec of file types generated, the second value is /// a list of `CrateTypes` that are not supported by the given target. pub fn rustc_outputs( &self, mode: CompileMode, target_kind: &TargetKind, target_triple: &str, gctx: &GlobalContext, ) -> CargoResult<(Vec, Vec)> { match mode { CompileMode::Build => self.calc_rustc_outputs(target_kind, target_triple, gctx), CompileMode::Test => { match self.file_types(&CrateType::Bin, FileFlavor::Normal, target_triple)? { Some(fts) => Ok((fts, Vec::new())), None => Ok((Vec::new(), vec![CrateType::Bin])), } } CompileMode::Check { .. } => Ok((vec![FileType::new_rmeta()], Vec::new())), CompileMode::Doc { .. } | CompileMode::Doctest | CompileMode::Docscrape | CompileMode::RunCustomBuild => { panic!("asked for rustc output for non-rustc mode") } } } fn calc_rustc_outputs( &self, target_kind: &TargetKind, target_triple: &str, gctx: &GlobalContext, ) -> CargoResult<(Vec, Vec)> { let mut unsupported = Vec::new(); let mut result = Vec::new(); let crate_types = target_kind.rustc_crate_types(); for crate_type in &crate_types { let flavor = if crate_type.is_linkable() { FileFlavor::Linkable } else { FileFlavor::Normal }; let file_types = self.file_types(crate_type, flavor, target_triple)?; match file_types { Some(types) => { result.extend(types); } None => { unsupported.push(crate_type.clone()); } } } if !result.is_empty() { if gctx.cli_unstable().no_embed_metadata && crate_types .iter() .any(|ct| ct.benefits_from_no_embed_metadata()) { // Add .rmeta when we apply -Zembed-metadata=no to the unit. result.push(FileType::new_rmeta()); } else if !crate_types.iter().any(|ct| ct.requires_upstream_objects()) { // Only add rmeta if pipelining result.push(FileType::new_rmeta()); } } Ok((result, unsupported)) } /// Checks if the debuginfo-split value is supported by this target pub fn supports_debuginfo_split(&self, split: InternedString) -> bool { self.support_split_debuginfo .iter() .any(|sup| sup.as_str() == split.as_str()) } /// Checks if a target maybe support std. /// /// If no explicitly stated in target spec json, we treat it as "maybe support". /// /// This is only useful for `-Zbuild-std` to determine the default set of /// crates it is going to build. pub fn maybe_support_std(&self) -> bool { matches!(self.supports_std, Some(true) | None) } } /// Takes rustc output (using specialized command line args), and calculates the file prefix and /// suffix for the given crate type, or returns `None` if the type is not supported. (e.g., for a /// Rust library like `libcargo.rlib`, we have prefix "lib" and suffix "rlib"). /// /// The caller needs to ensure that the lines object is at the correct line for the given crate /// type: this is not checked. /// /// This function can not handle more than one file per type (with wasm32-unknown-emscripten, there /// are two files for bin (`.wasm` and `.js`)). fn parse_crate_type( crate_type: &CrateType, cmd: &ProcessBuilder, output: &str, error: &str, lines: &mut str::Lines<'_>, ) -> CargoResult> { let not_supported = error.lines().any(|line| { (line.contains("unsupported crate type") || line.contains("unknown crate type")) && line.contains(&format!("crate type `{}`", crate_type)) }); if not_supported { return Ok(None); } let Some(line) = lines.next() else { anyhow::bail!( "malformed output when learning about crate-type {} information\n{}", crate_type, output_err_info(cmd, output, error) ) }; let mut parts = line.trim().split("___"); let prefix = parts.next().unwrap(); let Some(suffix) = parts.next() else { return error_missing_print_output("file-names", cmd, output, error); }; Ok(Some((prefix.to_string(), suffix.to_string()))) } /// Helper for creating an error message for missing output from a certain `--print` request. fn error_missing_print_output( request: &str, cmd: &ProcessBuilder, stdout: &str, stderr: &str, ) -> CargoResult { let err_info = output_err_info(cmd, stdout, stderr); anyhow::bail!( "output of --print={request} missing when learning about \ target-specific information from rustc\n{err_info}", ) } /// Helper for creating an error message when parsing rustc output fails. fn output_err_info(cmd: &ProcessBuilder, stdout: &str, stderr: &str) -> String { let mut result = format!("command was: {}\n", cmd); if !stdout.is_empty() { result.push_str("\n--- stdout\n"); result.push_str(stdout); } if !stderr.is_empty() { result.push_str("\n--- stderr\n"); result.push_str(stderr); } if stdout.is_empty() && stderr.is_empty() { result.push_str("(no output received)"); } result } /// Compiler flags for either rustc or rustdoc. #[derive(Debug, Copy, Clone)] enum Flags { Rust, Rustdoc, } impl Flags { fn as_key(self) -> &'static str { match self { Flags::Rust => "rustflags", Flags::Rustdoc => "rustdocflags", } } fn as_env(self) -> &'static str { match self { Flags::Rust => "RUSTFLAGS", Flags::Rustdoc => "RUSTDOCFLAGS", } } } /// Acquire extra flags to pass to the compiler from various locations. /// /// The locations are: /// /// - the `CARGO_ENCODED_RUSTFLAGS` environment variable /// - the `RUSTFLAGS` environment variable /// /// then if none of those were found /// /// - `target.*.rustflags` from the config (.cargo/config) /// - `target.cfg(..).rustflags` from the config /// - `host.*.rustflags` from the config if compiling a host artifact or without `--target` /// (requires `-Zhost-config`) /// /// then if none of those were found /// /// - `build.rustflags` from the config /// /// The behavior differs slightly when cross-compiling (or, specifically, when `--target` is /// provided) for artifacts that are always built for the host (plugins, build scripts, ...). /// For those artifacts, _only_ `host.*.rustflags` is respected, and no other configuration /// sources, _regardless of the value of `target-applies-to-host`_. This is counterintuitive, but /// necessary to retain backwards compatibility with older versions of Cargo. /// /// Rules above also applies to rustdoc. Just the key would be `rustdocflags`/`RUSTDOCFLAGS`. fn extra_args( gctx: &GlobalContext, requested_kinds: &[CompileKind], host_triple: &str, target_cfg: Option<&[Cfg]>, kind: CompileKind, flags: Flags, ) -> CargoResult> { let target_applies_to_host = gctx.target_applies_to_host()?; // Host artifacts should not generally pick up rustflags from anywhere except [host]. // // The one exception to this is if `target-applies-to-host = true`, which opts into a // particular (inconsistent) past Cargo behavior where host artifacts _do_ pick up rustflags // set elsewhere when `--target` isn't passed. if kind.is_host() { if target_applies_to_host && requested_kinds == [CompileKind::Host] { // This is the past Cargo behavior where we fall back to the same logic as for other // artifacts without --target. } else { // In all other cases, host artifacts just get flags from [host], regardless of // --target. Or, phrased differently, no `--target` behaves the same as `--target // `, and host artifacts are always "special" (they don't pick up `RUSTFLAGS` for // example). return Ok(rustflags_from_host(gctx, flags, host_triple)?.unwrap_or_else(Vec::new)); } } // All other artifacts pick up the RUSTFLAGS, [target.*], and [build], in that order. // NOTE: It is impossible to have a [host] section and reach this logic with kind.is_host(), // since [host] implies `target-applies-to-host = false`, which always early-returns above. if let Some(rustflags) = rustflags_from_env(gctx, flags) { Ok(rustflags) } else if let Some(rustflags) = rustflags_from_target(gctx, host_triple, target_cfg, kind, flags)? { Ok(rustflags) } else if let Some(rustflags) = rustflags_from_build(gctx, flags)? { Ok(rustflags) } else { Ok(Vec::new()) } } /// Gets compiler flags from environment variables. /// See [`extra_args`] for more. fn rustflags_from_env(gctx: &GlobalContext, flags: Flags) -> Option> { // First try CARGO_ENCODED_RUSTFLAGS from the environment. // Prefer this over RUSTFLAGS since it's less prone to encoding errors. if let Ok(a) = gctx.get_env(format!("CARGO_ENCODED_{}", flags.as_env())) { if a.is_empty() { return Some(Vec::new()); } return Some(a.split('\x1f').map(str::to_string).collect()); } // Then try RUSTFLAGS from the environment if let Ok(a) = gctx.get_env(flags.as_env()) { let args = a .split(' ') .map(str::trim) .filter(|s| !s.is_empty()) .map(str::to_string); return Some(args.collect()); } // No rustflags to be collected from the environment None } /// Gets compiler flags from `[target]` section in the config. /// See [`extra_args`] for more. fn rustflags_from_target( gctx: &GlobalContext, host_triple: &str, target_cfg: Option<&[Cfg]>, kind: CompileKind, flag: Flags, ) -> CargoResult>> { let mut rustflags = Vec::new(); // Then the target.*.rustflags value... let target = match &kind { CompileKind::Host => host_triple, CompileKind::Target(target) => target.short_name(), }; let key = format!("target.{}.{}", target, flag.as_key()); if let Some(args) = gctx.get::>(&key)? { rustflags.extend(args.as_slice().iter().cloned()); } // ...including target.'cfg(...)'.rustflags if let Some(target_cfg) = target_cfg { gctx.target_cfgs()? .iter() .filter_map(|(key, cfg)| { match flag { Flags::Rust => cfg .rustflags .as_ref() .map(|rustflags| (key, &rustflags.val)), // `target.cfg(…).rustdocflags` is currently not supported. Flags::Rustdoc => None, } }) .filter(|(key, _rustflags)| CfgExpr::matches_key(key, target_cfg)) .for_each(|(_key, cfg_rustflags)| { rustflags.extend(cfg_rustflags.as_slice().iter().cloned()); }); } if rustflags.is_empty() { Ok(None) } else { Ok(Some(rustflags)) } } /// Gets compiler flags from `[host]` section in the config. /// See [`extra_args`] for more. fn rustflags_from_host( gctx: &GlobalContext, flag: Flags, host_triple: &str, ) -> CargoResult>> { let target_cfg = gctx.host_cfg_triple(host_triple)?; let list = match flag { Flags::Rust => &target_cfg.rustflags, Flags::Rustdoc => { // host.rustdocflags is not a thing, since it does not make sense return Ok(None); } }; Ok(list.as_ref().map(|l| l.val.as_slice().to_vec())) } /// Gets compiler flags from `[build]` section in the config. /// See [`extra_args`] for more. fn rustflags_from_build(gctx: &GlobalContext, flag: Flags) -> CargoResult>> { // Then the `build.rustflags` value. let build = gctx.build_config()?; let list = match flag { Flags::Rust => &build.rustflags, Flags::Rustdoc => &build.rustdocflags, }; Ok(list.as_ref().map(|l| l.as_slice().to_vec())) } /// Collection of information about `rustc` and the host and target. pub struct RustcTargetData<'gctx> { /// Information about `rustc` itself. pub rustc: Rustc, /// Config pub gctx: &'gctx GlobalContext, requested_kinds: Vec, /// Build information for the "host", which is information about when /// `rustc` is invoked without a `--target` flag. This is used for /// selecting a linker, and applying link overrides. /// /// The configuration read into this depends on whether or not /// `target-applies-to-host=true`. host_config: TargetConfig, /// Information about the host platform. host_info: TargetInfo, /// Build information for targets that we're building for. target_config: HashMap, /// Information about the target platform that we're building for. target_info: HashMap, } impl<'gctx> RustcTargetData<'gctx> { #[tracing::instrument(skip_all)] pub fn new( ws: &Workspace<'gctx>, requested_kinds: &[CompileKind], ) -> CargoResult> { let gctx = ws.gctx(); let rustc = gctx.load_global_rustc(Some(ws))?; let mut target_config = HashMap::new(); let mut target_info = HashMap::new(); let target_applies_to_host = gctx.target_applies_to_host()?; let host_target = CompileTarget::new(&rustc.host)?; let host_info = TargetInfo::new(gctx, requested_kinds, &rustc, CompileKind::Host)?; // This config is used for link overrides and choosing a linker. let host_config = if target_applies_to_host { gctx.target_cfg_triple(&rustc.host)? } else { gctx.host_cfg_triple(&rustc.host)? }; // This is a hack. The unit_dependency graph builder "pretends" that // `CompileKind::Host` is `CompileKind::Target(host)` if the // `--target` flag is not specified. Since the unit_dependency code // needs access to the target config data, create a copy so that it // can be found. See `rebuild_unit_graph_shared` for why this is done. if requested_kinds.iter().any(CompileKind::is_host) { target_config.insert(host_target, gctx.target_cfg_triple(&rustc.host)?); // If target_applies_to_host is true, the host_info is the target info, // otherwise we need to build target info for the target. if target_applies_to_host { target_info.insert(host_target, host_info.clone()); } else { let host_target_info = TargetInfo::new( gctx, requested_kinds, &rustc, CompileKind::Target(host_target), )?; target_info.insert(host_target, host_target_info); } }; let mut res = RustcTargetData { rustc, gctx, requested_kinds: requested_kinds.into(), host_config, host_info, target_config, target_info, }; // Get all kinds we currently know about. // // For now, targets can only ever come from the root workspace // units and artifact dependencies, so this // correctly represents all the kinds that can happen. When we have // other ways for targets to appear at places that are not the root units, // we may have to revisit this. fn artifact_targets(package: &Package) -> impl Iterator + '_ { package .manifest() .dependencies() .iter() .filter_map(|d| d.artifact()?.target()?.to_compile_kind()) } let all_kinds = requested_kinds .iter() .copied() .chain(ws.members().flat_map(|p| { p.manifest() .default_kind() .into_iter() .chain(p.manifest().forced_kind()) .chain(artifact_targets(p)) })); for kind in all_kinds { res.merge_compile_kind(kind)?; } Ok(res) } /// Insert `kind` into our `target_info` and `target_config` members if it isn't present yet. pub fn merge_compile_kind(&mut self, kind: CompileKind) -> CargoResult<()> { if let CompileKind::Target(target) = kind { if !self.target_config.contains_key(&target) { self.target_config .insert(target, self.gctx.target_cfg_triple(target.short_name())?); } if !self.target_info.contains_key(&target) { self.target_info.insert( target, TargetInfo::new(self.gctx, &self.requested_kinds, &self.rustc, kind)?, ); } } Ok(()) } /// Returns a "short" name for the given kind, suitable for keying off /// configuration in Cargo or presenting to users. pub fn short_name<'a>(&'a self, kind: &'a CompileKind) -> &'a str { match kind { CompileKind::Host => &self.rustc.host, CompileKind::Target(target) => target.short_name(), } } /// Whether a dependency should be compiled for the host or target platform, /// specified by `CompileKind`. pub fn dep_platform_activated(&self, dep: &Dependency, kind: CompileKind) -> bool { // If this dependency is only available for certain platforms, // make sure we're only enabling it for that platform. let Some(platform) = dep.platform() else { return true; }; let name = self.short_name(&kind); platform.matches(name, self.cfg(kind)) } /// Gets the list of `cfg`s printed out from the compiler for the specified kind. pub fn cfg(&self, kind: CompileKind) -> &[Cfg] { self.info(kind).cfg() } /// Information about the given target platform, learned by querying rustc. /// /// # Panics /// /// Panics, if the target platform described by `kind` can't be found. /// See [`get_info`](Self::get_info) for a non-panicking alternative. pub fn info(&self, kind: CompileKind) -> &TargetInfo { self.get_info(kind).unwrap() } /// Information about the given target platform, learned by querying rustc. /// /// Returns `None` if the target platform described by `kind` can't be found. pub fn get_info(&self, kind: CompileKind) -> Option<&TargetInfo> { match kind { CompileKind::Host => Some(&self.host_info), CompileKind::Target(s) => self.target_info.get(&s), } } /// Gets the target configuration for a particular host or target. pub fn target_config(&self, kind: CompileKind) -> &TargetConfig { match kind { CompileKind::Host => &self.host_config, CompileKind::Target(s) => &self.target_config[&s], } } pub fn get_unsupported_std_targets(&self) -> Vec<&str> { let mut unsupported = Vec::new(); for (target, target_info) in &self.target_info { if target_info.supports_std == Some(false) { unsupported.push(target.short_name()); } } unsupported } } /// Structure used to deal with Rustdoc fingerprinting #[derive(Debug, Serialize, Deserialize)] pub struct RustDocFingerprint { pub rustc_vv: String, } impl RustDocFingerprint { /// This function checks whether the latest version of `Rustc` used to compile this /// `Workspace`'s docs was the same as the one is currently being used in this `cargo doc` /// call. /// /// In case it's not, it takes care of removing the `doc/` folder as well as overwriting /// the rustdoc fingerprint info in order to guarantee that we won't end up with mixed /// versions of the `js/html/css` files that `rustdoc` autogenerates which do not have /// any versioning. pub fn check_rustdoc_fingerprint(build_runner: &BuildRunner<'_, '_>) -> CargoResult<()> { if build_runner .bcx .gctx .cli_unstable() .skip_rustdoc_fingerprint { return Ok(()); } let actual_rustdoc_target_data = RustDocFingerprint { rustc_vv: build_runner.bcx.rustc().verbose_version.clone(), }; let fingerprint_path = build_runner .files() .host_root() .join(".rustdoc_fingerprint.json"); let write_fingerprint = || -> CargoResult<()> { paths::write( &fingerprint_path, serde_json::to_string(&actual_rustdoc_target_data)?, ) }; let Ok(rustdoc_data) = paths::read(&fingerprint_path) else { // If the fingerprint does not exist, do not clear out the doc // directories. Otherwise this ran into problems where projects // like bootstrap were creating the doc directory before running // `cargo doc` in a way that deleting it would break it. return write_fingerprint(); }; match serde_json::from_str::(&rustdoc_data) { Ok(fingerprint) => { if fingerprint.rustc_vv == actual_rustdoc_target_data.rustc_vv { return Ok(()); } else { tracing::debug!( "doc fingerprint changed:\noriginal:\n{}\nnew:\n{}", fingerprint.rustc_vv, actual_rustdoc_target_data.rustc_vv ); } } Err(e) => { tracing::debug!("could not deserialize {:?}: {}", fingerprint_path, e); } }; // Fingerprint does not match, delete the doc directories and write a new fingerprint. tracing::debug!( "fingerprint {:?} mismatch, clearing doc directories", fingerprint_path ); build_runner .bcx .all_kinds .iter() .map(|kind| build_runner.files().layout(*kind).doc()) .filter(|path| path.exists()) .try_for_each(|path| clean_doc(path))?; write_fingerprint()?; return Ok(()); fn clean_doc(path: &Path) -> CargoResult<()> { let entries = path .read_dir() .with_context(|| format!("failed to read directory `{}`", path.display()))?; for entry in entries { let entry = entry?; // Don't remove hidden files. Rustdoc does not create them, // but the user might have. if entry .file_name() .to_str() .map_or(false, |name| name.starts_with('.')) { continue; } let path = entry.path(); if entry.file_type()?.is_dir() { paths::remove_dir_all(path)?; } else { paths::remove_file(path)?; } } Ok(()) } } } cargo-0.91.0/src/cargo/core/compiler/build_plan.rs000064400000000000000000000114431046102023000201140ustar 00000000000000//! A graph-like structure used to represent the rustc commands to build the package and the //! interdependencies between them. //! //! The `BuildPlan` structure is used to store the dependency graph of a dry run so that it can be //! shared with an external build system. Each Invocation in the `BuildPlan` comprises a single //! subprocess and defines the build environment, the outputs produced by the subprocess, and the //! dependencies on other Invocations. use std::collections::BTreeMap; use std::path::{Path, PathBuf}; use serde::Serialize; use super::build_runner::OutputFile; use super::{BuildRunner, CompileKind, CompileMode, Unit}; use crate::core::TargetKind; use crate::util::{CargoResult, GlobalContext, internal}; use cargo_util::ProcessBuilder; #[derive(Debug, Serialize)] struct Invocation { package_name: String, package_version: semver::Version, target_kind: TargetKind, kind: CompileKind, compile_mode: CompileMode, deps: Vec, outputs: Vec, links: BTreeMap, program: String, args: Vec, env: BTreeMap, cwd: Option, } #[derive(Debug)] pub struct BuildPlan { invocation_map: BTreeMap, plan: SerializedBuildPlan, } #[derive(Debug, Serialize)] struct SerializedBuildPlan { invocations: Vec, inputs: Vec, } impl Invocation { pub fn new(unit: &Unit, deps: Vec) -> Invocation { let id = unit.pkg.package_id(); Invocation { package_name: id.name().to_string(), package_version: id.version().clone(), kind: unit.kind, target_kind: unit.target.kind().clone(), compile_mode: unit.mode, deps, outputs: Vec::new(), links: BTreeMap::new(), program: String::new(), args: Vec::new(), env: BTreeMap::new(), cwd: None, } } pub fn add_output(&mut self, path: &Path, link: &Option) { self.outputs.push(path.to_path_buf()); if let Some(ref link) = *link { self.links.insert(link.clone(), path.to_path_buf()); } } pub fn update_cmd(&mut self, cmd: &ProcessBuilder) -> CargoResult<()> { self.program = cmd .get_program() .to_str() .ok_or_else(|| anyhow::format_err!("unicode program string required"))? .to_string(); self.cwd = Some(cmd.get_cwd().unwrap().to_path_buf()); for arg in cmd.get_args() { self.args.push( arg.to_str() .ok_or_else(|| anyhow::format_err!("unicode argument string required"))? .to_string(), ); } for (var, value) in cmd.get_envs() { let Some(value) = value else { continue }; self.env.insert( var.clone(), value .to_str() .ok_or_else(|| anyhow::format_err!("unicode environment value required"))? .to_string(), ); } Ok(()) } } impl BuildPlan { pub fn new() -> BuildPlan { BuildPlan { invocation_map: BTreeMap::new(), plan: SerializedBuildPlan::new(), } } pub fn add(&mut self, build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<()> { let id = self.plan.invocations.len(); self.invocation_map.insert(unit.buildkey(), id); let deps = build_runner .unit_deps(unit) .iter() .map(|dep| self.invocation_map[&dep.unit.buildkey()]) .collect(); let invocation = Invocation::new(unit, deps); self.plan.invocations.push(invocation); Ok(()) } pub fn update( &mut self, invocation_name: &str, cmd: &ProcessBuilder, outputs: &[OutputFile], ) -> CargoResult<()> { let id = self.invocation_map[invocation_name]; let invocation = self.plan.invocations.get_mut(id).ok_or_else(|| { internal(format!("couldn't find invocation for {}", invocation_name)) })?; invocation.update_cmd(cmd)?; for output in outputs.iter() { invocation.add_output(&output.path, &output.hardlink); } Ok(()) } pub fn set_inputs(&mut self, inputs: Vec) { self.plan.inputs = inputs; } pub fn output_plan(self, gctx: &GlobalContext) { let encoded = serde_json::to_string(&self.plan).unwrap(); crate::drop_println!(gctx, "{}", encoded); } } impl SerializedBuildPlan { pub fn new() -> SerializedBuildPlan { SerializedBuildPlan { invocations: Vec::new(), inputs: Vec::new(), } } } cargo-0.91.0/src/cargo/core/compiler/build_runner/compilation_files.rs000064400000000000000000001054101046102023000241710ustar 00000000000000//! See [`CompilationFiles`]. use std::collections::HashMap; use std::fmt; use std::hash::{Hash, Hasher}; use std::path::{Path, PathBuf}; use std::sync::Arc; use lazycell::LazyCell; use tracing::debug; use super::{BuildContext, BuildRunner, CompileKind, FileFlavor, Layout}; use crate::core::compiler::{CompileMode, CompileTarget, CrateType, FileType, Unit}; use crate::core::{Target, TargetKind, Workspace}; use crate::util::{self, CargoResult, StableHasher}; /// This is a generic version number that can be changed to make /// backwards-incompatible changes to any file structures in the output /// directory. For example, the fingerprint files or the build-script /// output files. /// /// Normally cargo updates ship with rustc updates which will /// cause a new hash due to the rustc version changing, but this allows /// cargo to be extra careful to deal with different versions of cargo that /// use the same rustc version. const METADATA_VERSION: u8 = 2; /// Uniquely identify a [`Unit`] under specific circumstances, see [`Metadata`] for more. #[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] pub struct UnitHash(u64); impl fmt::Display for UnitHash { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:016x}", self.0) } } impl fmt::Debug for UnitHash { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "UnitHash({:016x})", self.0) } } /// [`Metadata`] tracks several [`UnitHash`]s, including /// [`Metadata::unit_id`], [`Metadata::c_metadata`], and [`Metadata::c_extra_filename`]. /// /// We use a hash because it is an easy way to guarantee /// that all the inputs can be converted to a valid path. /// /// [`Metadata::unit_id`] is used to uniquely identify a unit in the build graph. /// This serves as a similar role as [`Metadata::c_extra_filename`] in that it uniquely identifies output /// on the filesystem except that its always present. /// /// [`Metadata::c_extra_filename`] is needed for cases like: /// - A project may depend on crate `A` and crate `B`, so the package name must be in the file name. /// - Similarly a project may depend on two versions of `A`, so the version must be in the file name. /// /// This also acts as the main layer of caching provided by Cargo /// so this must include all things that need to be distinguished in different parts of /// the same build. This is absolutely required or we override things before /// we get chance to use them. /// /// For example, we want to cache `cargo build` and `cargo doc` separately, so that running one /// does not invalidate the artifacts for the other. We do this by including [`CompileMode`] in the /// hash, thus the artifacts go in different folders and do not override each other. /// If we don't add something that we should have, for this reason, we get the /// correct output but rebuild more than is needed. /// /// Some things that need to be tracked to ensure the correct output should definitely *not* /// go in the `Metadata`. For example, the modification time of a file, should be tracked to make a /// rebuild when the file changes. However, it would be wasteful to include in the `Metadata`. The /// old artifacts are never going to be needed again. We can save space by just overwriting them. /// If we add something that we should not have, for this reason, we get the correct output but take /// more space than needed. This makes not including something in `Metadata` /// a form of cache invalidation. /// /// Note that the `Fingerprint` is in charge of tracking everything needed to determine if a /// rebuild is needed. /// /// [`Metadata::c_metadata`] is used for symbol mangling, because if you have two versions of /// the same crate linked together, their symbols need to be differentiated. /// /// You should avoid anything that would interfere with reproducible /// builds. For example, *any* absolute path should be avoided. This is one /// reason that `RUSTFLAGS` is not in [`Metadata::c_metadata`], because it often has /// absolute paths (like `--remap-path-prefix` which is fundamentally used for /// reproducible builds and has absolute paths in it). Also, in some cases the /// mangled symbols need to be stable between different builds with different /// settings. For example, profile-guided optimizations need to swap /// `RUSTFLAGS` between runs, but needs to keep the same symbol names. #[derive(Copy, Clone, Debug)] pub struct Metadata { unit_id: UnitHash, c_metadata: UnitHash, c_extra_filename: Option, } impl Metadata { /// A hash to identify a given [`Unit`] in the build graph pub fn unit_id(&self) -> UnitHash { self.unit_id } /// A hash to add to symbol naming through `-C metadata` pub fn c_metadata(&self) -> UnitHash { self.c_metadata } /// A hash to add to file names through `-C extra-filename` pub fn c_extra_filename(&self) -> Option { self.c_extra_filename } } /// Collection of information about the files emitted by the compiler, and the /// output directory structure. pub struct CompilationFiles<'a, 'gctx> { /// The target directory layout for the host (and target if it is the same as host). pub(super) host: Layout, /// The target directory layout for the target (if different from then host). pub(super) target: HashMap, /// Additional directory to include a copy of the outputs. export_dir: Option, /// The root targets requested by the user on the command line (does not /// include dependencies). roots: Vec, ws: &'a Workspace<'gctx>, /// Metadata hash to use for each unit. metas: HashMap, /// For each Unit, a list all files produced. outputs: HashMap>>>, } /// Info about a single file emitted by the compiler. #[derive(Debug)] pub struct OutputFile { /// Absolute path to the file that will be produced by the build process. pub path: PathBuf, /// If it should be linked into `target`, and what it should be called /// (e.g., without metadata). pub hardlink: Option, /// If `--artifact-dir` is specified, the absolute path to the exported file. pub export_path: Option, /// Type of the file (library / debug symbol / else). pub flavor: FileFlavor, } impl OutputFile { /// Gets the hard link if present; otherwise, returns the path. pub fn bin_dst(&self) -> &PathBuf { match self.hardlink { Some(ref link_dst) => link_dst, None => &self.path, } } } impl<'a, 'gctx: 'a> CompilationFiles<'a, 'gctx> { pub(super) fn new( build_runner: &BuildRunner<'a, 'gctx>, host: Layout, target: HashMap, ) -> CompilationFiles<'a, 'gctx> { let mut metas = HashMap::new(); for unit in &build_runner.bcx.roots { metadata_of(unit, build_runner, &mut metas); } let outputs = metas .keys() .cloned() .map(|unit| (unit, LazyCell::new())) .collect(); CompilationFiles { ws: build_runner.bcx.ws, host, target, export_dir: build_runner.bcx.build_config.export_dir.clone(), roots: build_runner.bcx.roots.clone(), metas, outputs, } } /// Returns the appropriate directory layout for either a plugin or not. pub fn layout(&self, kind: CompileKind) -> &Layout { match kind { CompileKind::Host => &self.host, CompileKind::Target(target) => &self.target[&target], } } /// Gets the metadata for the given unit. /// /// See [`Metadata`] and [`fingerprint`] module for more. /// /// [`fingerprint`]: super::super::fingerprint#fingerprints-and-metadata pub fn metadata(&self, unit: &Unit) -> Metadata { self.metas[unit] } /// Gets the short hash based only on the `PackageId`. /// Used for the metadata when `c_extra_filename` returns `None`. fn target_short_hash(&self, unit: &Unit) -> String { let hashable = unit.pkg.package_id().stable_hash(self.ws.root()); util::short_hash(&(METADATA_VERSION, hashable)) } /// Returns the directory where the artifacts for the given unit are /// initially created. pub fn out_dir(&self, unit: &Unit) -> PathBuf { // Docscrape units need to have doc/ set as the out_dir so sources for reverse-dependencies // will be put into doc/ and not into deps/ where the *.examples files are stored. if unit.mode.is_doc() || unit.mode.is_doc_scrape() { self.layout(unit.kind).doc().to_path_buf() } else if unit.mode.is_doc_test() { panic!("doc tests do not have an out dir"); } else if unit.target.is_custom_build() { self.build_script_dir(unit) } else if unit.target.is_example() { self.layout(unit.kind).build_examples().to_path_buf() } else if unit.artifact.is_true() { self.artifact_dir(unit) } else { self.deps_dir(unit).to_path_buf() } } /// Additional export directory from `--artifact-dir`. pub fn export_dir(&self) -> Option { self.export_dir.clone() } /// Directory name to use for a package in the form `NAME-HASH`. /// /// Note that some units may share the same directory, so care should be /// taken in those cases! fn pkg_dir(&self, unit: &Unit) -> String { let name = unit.pkg.package_id().name(); let meta = self.metas[unit]; if let Some(c_extra_filename) = meta.c_extra_filename() { format!("{}-{}", name, c_extra_filename) } else { format!("{}-{}", name, self.target_short_hash(unit)) } } /// Returns the final artifact path for the host (`/…/target/debug`) pub fn host_dest(&self) -> &Path { self.host.dest() } /// Returns the root of the build output tree for the host (`/…/target`) pub fn host_root(&self) -> &Path { self.host.root() } /// Returns the host `deps` directory path. pub fn host_deps(&self) -> &Path { self.host.deps() } /// Returns the directories where Rust crate dependencies are found for the /// specified unit. pub fn deps_dir(&self, unit: &Unit) -> &Path { self.layout(unit.kind).deps() } /// Directory where the fingerprint for the given unit should go. pub fn fingerprint_dir(&self, unit: &Unit) -> PathBuf { let dir = self.pkg_dir(unit); self.layout(unit.kind).fingerprint().join(dir) } /// Returns the path for a file in the fingerprint directory. /// /// The "prefix" should be something to distinguish the file from other /// files in the fingerprint directory. pub fn fingerprint_file_path(&self, unit: &Unit, prefix: &str) -> PathBuf { // Different targets need to be distinguished in the let kind = unit.target.kind().description(); let flavor = if unit.mode.is_any_test() { "test-" } else if unit.mode.is_doc() { "doc-" } else if unit.mode.is_run_custom_build() { "run-" } else { "" }; let name = format!("{}{}{}-{}", prefix, flavor, kind, unit.target.name()); self.fingerprint_dir(unit).join(name) } /// Path where compiler output is cached. pub fn message_cache_path(&self, unit: &Unit) -> PathBuf { self.fingerprint_file_path(unit, "output-") } /// Returns the directory where a compiled build script is stored. /// `/path/to/target/{debug,release}/build/PKG-HASH` pub fn build_script_dir(&self, unit: &Unit) -> PathBuf { assert!(unit.target.is_custom_build()); assert!(!unit.mode.is_run_custom_build()); assert!(self.metas.contains_key(unit)); let dir = self.pkg_dir(unit); self.layout(CompileKind::Host).build().join(dir) } /// Returns the directory for compiled artifacts files. /// `/path/to/target/{debug,release}/deps/artifact/KIND/PKG-HASH` fn artifact_dir(&self, unit: &Unit) -> PathBuf { assert!(self.metas.contains_key(unit)); assert!(unit.artifact.is_true()); let dir = self.pkg_dir(unit); let kind = match unit.target.kind() { TargetKind::Bin => "bin", TargetKind::Lib(lib_kinds) => match lib_kinds.as_slice() { &[CrateType::Cdylib] => "cdylib", &[CrateType::Staticlib] => "staticlib", invalid => unreachable!( "BUG: unexpected artifact library type(s): {:?} - these should have been split", invalid ), }, invalid => unreachable!( "BUG: {:?} are not supposed to be used as artifacts", invalid ), }; self.layout(unit.kind).artifact().join(dir).join(kind) } /// Returns the directory where information about running a build script /// is stored. /// `/path/to/target/{debug,release}/build/PKG-HASH` pub fn build_script_run_dir(&self, unit: &Unit) -> PathBuf { assert!(unit.target.is_custom_build()); assert!(unit.mode.is_run_custom_build()); let dir = self.pkg_dir(unit); self.layout(unit.kind).build().join(dir) } /// Returns the "`OUT_DIR`" directory for running a build script. /// `/path/to/target/{debug,release}/build/PKG-HASH/out` pub fn build_script_out_dir(&self, unit: &Unit) -> PathBuf { self.build_script_run_dir(unit).join("out") } /// Returns the path to the executable binary for the given bin target. /// /// This should only to be used when a `Unit` is not available. pub fn bin_link_for_target( &self, target: &Target, kind: CompileKind, bcx: &BuildContext<'_, '_>, ) -> CargoResult { assert!(target.is_bin()); let dest = self.layout(kind).dest(); let info = bcx.target_data.info(kind); let (file_types, _) = info .rustc_outputs( CompileMode::Build, &TargetKind::Bin, bcx.target_data.short_name(&kind), bcx.gctx, ) .expect("target must support `bin`"); let file_type = file_types .iter() .find(|file_type| file_type.flavor == FileFlavor::Normal) .expect("target must support `bin`"); Ok(dest.join(file_type.uplift_filename(target))) } /// Returns the filenames that the given unit will generate. /// /// Note: It is not guaranteed that all of the files will be generated. pub(super) fn outputs( &self, unit: &Unit, bcx: &BuildContext<'a, 'gctx>, ) -> CargoResult>> { self.outputs[unit] .try_borrow_with(|| self.calc_outputs(unit, bcx)) .map(Arc::clone) } /// Returns the path where the output for the given unit and `FileType` /// should be uplifted to. /// /// Returns `None` if the unit shouldn't be uplifted (for example, a /// dependent rlib). fn uplift_to(&self, unit: &Unit, file_type: &FileType, from_path: &Path) -> Option { // Tests, check, doc, etc. should not be uplifted. if unit.mode != CompileMode::Build || file_type.flavor == FileFlavor::Rmeta { return None; } // Artifact dependencies are never uplifted. if unit.artifact.is_true() { return None; } // - Binaries: The user always wants to see these, even if they are // implicitly built (for example for integration tests). // - dylibs: This ensures that the dynamic linker pulls in all the // latest copies (even if the dylib was built from a previous cargo // build). There are complex reasons for this, see #8139, #6167, #6162. // - Things directly requested from the command-line (the "roots"). // This one is a little questionable for rlibs (see #6131), but is // historically how Cargo has operated. This is primarily useful to // give the user access to staticlibs and cdylibs. if !unit.target.is_bin() && !unit.target.is_custom_build() && file_type.crate_type != Some(CrateType::Dylib) && !self.roots.contains(unit) { return None; } let filename = file_type.uplift_filename(&unit.target); let uplift_path = if unit.target.is_example() { // Examples live in their own little world. self.layout(unit.kind).examples().join(filename) } else if unit.target.is_custom_build() { self.build_script_dir(unit).join(filename) } else { self.layout(unit.kind).dest().join(filename) }; if from_path == uplift_path { // This can happen with things like examples that reside in the // same directory, do not have a metadata hash (like on Windows), // and do not have hyphens. return None; } Some(uplift_path) } /// Calculates the filenames that the given unit will generate. /// Should use [`CompilationFiles::outputs`] instead /// as it caches the result of this function. fn calc_outputs( &self, unit: &Unit, bcx: &BuildContext<'a, 'gctx>, ) -> CargoResult>> { let ret = match unit.mode { _ if unit.skip_non_compile_time_dep => { // This skips compilations so no outputs vec![] } CompileMode::Doc => { let path = if bcx.build_config.intent.wants_doc_json_output() { self.out_dir(unit) .join(format!("{}.json", unit.target.crate_name())) } else { self.out_dir(unit) .join(unit.target.crate_name()) .join("index.html") }; vec![OutputFile { path, hardlink: None, export_path: None, flavor: FileFlavor::Normal, }] } CompileMode::RunCustomBuild => { // At this time, this code path does not handle build script // outputs. vec![] } CompileMode::Doctest => { // Doctests are built in a temporary directory and then // deleted. There is the `--persist-doctests` unstable flag, // but Cargo does not know about that. vec![] } CompileMode::Docscrape => { // The file name needs to be stable across Cargo sessions. // This originally used unit.buildkey(), but that isn't stable, // so we use metadata instead (prefixed with name for debugging). let file_name = format!( "{}-{}.examples", unit.pkg.name(), self.metadata(unit).unit_id() ); let path = self.deps_dir(unit).join(file_name); vec![OutputFile { path, hardlink: None, export_path: None, flavor: FileFlavor::Normal, }] } CompileMode::Test | CompileMode::Build | CompileMode::Check { .. } => { let mut outputs = self.calc_outputs_rustc(unit, bcx)?; if bcx.build_config.sbom && bcx.gctx.cli_unstable().sbom { let sbom_files: Vec<_> = outputs .iter() .filter(|o| matches!(o.flavor, FileFlavor::Normal | FileFlavor::Linkable)) .map(|output| OutputFile { path: Self::append_sbom_suffix(&output.path), hardlink: output.hardlink.as_ref().map(Self::append_sbom_suffix), export_path: output.export_path.as_ref().map(Self::append_sbom_suffix), flavor: FileFlavor::Sbom, }) .collect(); outputs.extend(sbom_files.into_iter()); } outputs } }; debug!("Target filenames: {:?}", ret); Ok(Arc::new(ret)) } /// Append the SBOM suffix to the file name. fn append_sbom_suffix(link: &PathBuf) -> PathBuf { const SBOM_FILE_EXTENSION: &str = ".cargo-sbom.json"; let mut link_buf = link.clone().into_os_string(); link_buf.push(SBOM_FILE_EXTENSION); PathBuf::from(link_buf) } /// Computes the actual, full pathnames for all the files generated by rustc. /// /// The `OutputFile` also contains the paths where those files should be /// "uplifted" to. fn calc_outputs_rustc( &self, unit: &Unit, bcx: &BuildContext<'a, 'gctx>, ) -> CargoResult> { let out_dir = self.out_dir(unit); let info = bcx.target_data.info(unit.kind); let triple = bcx.target_data.short_name(&unit.kind); let (file_types, unsupported) = info.rustc_outputs(unit.mode, unit.target.kind(), triple, bcx.gctx)?; if file_types.is_empty() { if !unsupported.is_empty() { let unsupported_strs: Vec<_> = unsupported.iter().map(|ct| ct.as_str()).collect(); anyhow::bail!( "cannot produce {} for `{}` as the target `{}` \ does not support these crate types", unsupported_strs.join(", "), unit.pkg, triple, ) } anyhow::bail!( "cannot compile `{}` as the target `{}` does not \ support any of the output crate types", unit.pkg, triple, ); } // Convert FileType to OutputFile. let mut outputs = Vec::new(); for file_type in file_types { let meta = self.metas[unit]; let meta_opt = meta.c_extra_filename().map(|h| h.to_string()); let path = out_dir.join(file_type.output_filename(&unit.target, meta_opt.as_deref())); // If, the `different_binary_name` feature is enabled, the name of the hardlink will // be the name of the binary provided by the user in `Cargo.toml`. let hardlink = self.uplift_to(unit, &file_type, &path); let export_path = if unit.target.is_custom_build() { None } else { self.export_dir.as_ref().and_then(|export_dir| { hardlink .as_ref() .map(|hardlink| export_dir.join(hardlink.file_name().unwrap())) }) }; outputs.push(OutputFile { path, hardlink, export_path, flavor: file_type.flavor, }); } Ok(outputs) } } /// Gets the metadata hash for the given [`Unit`]. /// /// When a metadata hash doesn't exist for the given unit, /// this calls itself recursively to compute metadata hashes of all its dependencies. /// See [`compute_metadata`] for how a single metadata hash is computed. fn metadata_of<'a>( unit: &Unit, build_runner: &BuildRunner<'_, '_>, metas: &'a mut HashMap, ) -> &'a Metadata { if !metas.contains_key(unit) { let meta = compute_metadata(unit, build_runner, metas); metas.insert(unit.clone(), meta); for dep in build_runner.unit_deps(unit) { metadata_of(&dep.unit, build_runner, metas); } } &metas[unit] } /// Computes the metadata hash for the given [`Unit`]. fn compute_metadata( unit: &Unit, build_runner: &BuildRunner<'_, '_>, metas: &mut HashMap, ) -> Metadata { let bcx = &build_runner.bcx; let deps_metadata = build_runner .unit_deps(unit) .iter() .map(|dep| *metadata_of(&dep.unit, build_runner, metas)) .collect::>(); let use_extra_filename = use_extra_filename(bcx, unit); let mut shared_hasher = StableHasher::new(); METADATA_VERSION.hash(&mut shared_hasher); let ws_root = if unit.is_std { // SourceId for stdlib crates is an absolute path inside the sysroot. // Pass the sysroot as workspace root so that we hash a relative path. // This avoids the metadata hash changing depending on where the user installed rustc. &bcx.target_data.get_info(unit.kind).unwrap().sysroot } else { bcx.ws.root() }; // Unique metadata per (name, source, version) triple. This'll allow us // to pull crates from anywhere without worrying about conflicts. unit.pkg .package_id() .stable_hash(ws_root) .hash(&mut shared_hasher); // Also mix in enabled features to our metadata. This'll ensure that // when changing feature sets each lib is separately cached. unit.features.hash(&mut shared_hasher); // Throw in the profile we're compiling with. This helps caching // `panic=abort` and `panic=unwind` artifacts, additionally with various // settings like debuginfo and whatnot. unit.profile.hash(&mut shared_hasher); unit.mode.hash(&mut shared_hasher); build_runner.lto[unit].hash(&mut shared_hasher); // Artifacts compiled for the host should have a different // metadata piece than those compiled for the target, so make sure // we throw in the unit's `kind` as well. Use `fingerprint_hash` // so that the StableHash doesn't change based on the pathnames // of the custom target JSON spec files. unit.kind.fingerprint_hash().hash(&mut shared_hasher); // Finally throw in the target name/kind. This ensures that concurrent // compiles of targets in the same crate don't collide. unit.target.name().hash(&mut shared_hasher); unit.target.kind().hash(&mut shared_hasher); hash_rustc_version(bcx, &mut shared_hasher, unit); if build_runner.bcx.ws.is_member(&unit.pkg) { // This is primarily here for clippy. This ensures that the clippy // artifacts are separate from the `check` ones. if let Some(path) = &build_runner.bcx.rustc().workspace_wrapper { path.hash(&mut shared_hasher); } } // Seed the contents of `__CARGO_DEFAULT_LIB_METADATA` to the hasher if present. // This should be the release channel, to get a different hash for each channel. if let Ok(ref channel) = build_runner .bcx .gctx .get_env("__CARGO_DEFAULT_LIB_METADATA") { channel.hash(&mut shared_hasher); } // std units need to be kept separate from user dependencies. std crates // are differentiated in the Unit with `is_std` (for things like // `-Zforce-unstable-if-unmarked`), so they are always built separately. // This isn't strictly necessary for build dependencies which probably // don't need unstable support. A future experiment might be to set // `is_std` to false for build dependencies so that they can be shared // with user dependencies. unit.is_std.hash(&mut shared_hasher); // While we don't hash RUSTFLAGS because it may contain absolute paths that // hurts reproducibility, we track whether a unit's RUSTFLAGS is from host // config, so that we can generate a different metadata hash for runtime // and compile-time units. // // HACK: This is a temporary hack for fixing rust-lang/cargo#14253 // Need to find a long-term solution to replace this fragile workaround. // See https://github.com/rust-lang/cargo/pull/14432#discussion_r1725065350 if unit.kind.is_host() && !bcx.gctx.target_applies_to_host().unwrap_or_default() { let host_info = bcx.target_data.info(CompileKind::Host); let target_configs_are_different = unit.rustflags != host_info.rustflags || unit.rustdocflags != host_info.rustdocflags || bcx .target_data .target_config(CompileKind::Host) .links_overrides != unit.links_overrides; target_configs_are_different.hash(&mut shared_hasher); } let mut c_metadata_hasher = shared_hasher.clone(); // Mix in the target-metadata of all the dependencies of this target. let mut dep_c_metadata_hashes = deps_metadata .iter() .map(|m| m.c_metadata) .collect::>(); dep_c_metadata_hashes.sort(); dep_c_metadata_hashes.hash(&mut c_metadata_hasher); let mut c_extra_filename_hasher = shared_hasher.clone(); // Mix in the target-metadata of all the dependencies of this target. let mut dep_c_extra_filename_hashes = deps_metadata .iter() .map(|m| m.c_extra_filename) .collect::>(); dep_c_extra_filename_hashes.sort(); dep_c_extra_filename_hashes.hash(&mut c_extra_filename_hasher); // Avoid trashing the caches on RUSTFLAGS changing via `c_extra_filename` // // Limited to `c_extra_filename` to help with reproducible build / PGO issues. let default = Vec::new(); let extra_args = build_runner.bcx.extra_args_for(unit).unwrap_or(&default); if !has_remap_path_prefix(&extra_args) { extra_args.hash(&mut c_extra_filename_hasher); } if unit.mode.is_doc() || unit.mode.is_doc_scrape() { if !has_remap_path_prefix(&unit.rustdocflags) { unit.rustdocflags.hash(&mut c_extra_filename_hasher); } } else { if !has_remap_path_prefix(&unit.rustflags) { unit.rustflags.hash(&mut c_extra_filename_hasher); } } let c_metadata = UnitHash(Hasher::finish(&c_metadata_hasher)); let c_extra_filename = UnitHash(Hasher::finish(&c_extra_filename_hasher)); let unit_id = c_extra_filename; let c_extra_filename = use_extra_filename.then_some(c_extra_filename); Metadata { unit_id, c_metadata, c_extra_filename, } } /// HACK: Detect the *potential* presence of `--remap-path-prefix` /// /// As CLI parsing is contextual and dependent on the CLI definition to understand the context, we /// can't say for sure whether `--remap-path-prefix` is present, so we guess if anything looks like /// it. /// If we could, we'd strip it out for hashing. /// Instead, we use this to avoid hashing rustflags if it might be present to avoid the risk of taking /// a flag that is trying to make things reproducible and making things less reproducible by the /// `-Cextra-filename` showing up in the rlib, even with `split-debuginfo`. fn has_remap_path_prefix(args: &[String]) -> bool { args.iter() .any(|s| s.starts_with("--remap-path-prefix=") || s == "--remap-path-prefix") } /// Hash the version of rustc being used during the build process. fn hash_rustc_version(bcx: &BuildContext<'_, '_>, hasher: &mut StableHasher, unit: &Unit) { let vers = &bcx.rustc().version; if vers.pre.is_empty() || bcx.gctx.cli_unstable().separate_nightlies { // For stable, keep the artifacts separate. This helps if someone is // testing multiple versions, to avoid recompiles. Note though that for // cross-compiled builds the `host:` line of `verbose_version` is // omitted since rustc should produce the same output for each target // regardless of the host. for line in bcx.rustc().verbose_version.lines() { if unit.kind.is_host() || !line.starts_with("host: ") { line.hash(hasher); } } return; } // On "nightly"/"beta"/"dev"/etc, keep each "channel" separate. Don't hash // the date/git information, so that whenever someone updates "nightly", // they won't have a bunch of stale artifacts in the target directory. // // This assumes that the first segment is the important bit ("nightly", // "beta", "dev", etc.). Skip other parts like the `.3` in `-beta.3`. vers.pre.split('.').next().hash(hasher); // Keep "host" since some people switch hosts to implicitly change // targets, (like gnu vs musl or gnu vs msvc). In the future, we may want // to consider hashing `unit.kind.short_name()` instead. if unit.kind.is_host() { bcx.rustc().host.hash(hasher); } // None of the other lines are important. Currently they are: // binary: rustc <-- or "rustdoc" // commit-hash: 38114ff16e7856f98b2b4be7ab4cd29b38bed59a // commit-date: 2020-03-21 // host: x86_64-apple-darwin // release: 1.44.0-nightly // LLVM version: 9.0 // // The backend version ("LLVM version") might become more relevant in // the future when cranelift sees more use, and people want to switch // between different backends without recompiling. } /// Returns whether or not this unit should use a hash in the filename to make it unique. fn use_extra_filename(bcx: &BuildContext<'_, '_>, unit: &Unit) -> bool { if unit.mode.is_doc_test() || unit.mode.is_doc() { // Doc tests do not have metadata. return false; } if unit.mode.is_any_test() || unit.mode.is_check() { // These always use metadata. return true; } // No metadata in these cases: // // - dylibs: // - if any dylib names are encoded in executables, so they can't be renamed. // - TODO: Maybe use `-install-name` on macOS or `-soname` on other UNIX systems // to specify the dylib name to be used by the linker instead of the filename. // - Windows MSVC executables: The path to the PDB is embedded in the // executable, and we don't want the PDB path to include the hash in it. // - wasm32-unknown-emscripten executables: When using emscripten, the path to the // .wasm file is embedded in the .js file, so we don't want the hash in there. // // This is only done for local packages, as we don't expect to export // dependencies. // // The __CARGO_DEFAULT_LIB_METADATA env var is used to override this to // force metadata in the hash. This is only used for building libstd. For // example, if libstd is placed in a common location, we don't want a file // named /usr/lib/libstd.so which could conflict with other rustc // installs. In addition it prevents accidentally loading a libstd of a // different compiler at runtime. // See https://github.com/rust-lang/cargo/issues/3005 let short_name = bcx.target_data.short_name(&unit.kind); if (unit.target.is_dylib() || unit.target.is_cdylib() || (unit.target.is_executable() && short_name == "wasm32-unknown-emscripten") || (unit.target.is_executable() && short_name.contains("msvc"))) && unit.pkg.package_id().source_id().is_path() && bcx.gctx.get_env("__CARGO_DEFAULT_LIB_METADATA").is_err() { return false; } true } cargo-0.91.0/src/cargo/core/compiler/build_runner/mod.rs000064400000000000000000000727461046102023000212670ustar 00000000000000//! [`BuildRunner`] is the mutable state used during the build process. use std::collections::{BTreeSet, HashMap, HashSet}; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; use crate::core::PackageId; use crate::core::compiler::compilation::{self, UnitOutput}; use crate::core::compiler::{self, Unit, artifact}; use crate::util::cache_lock::CacheLockMode; use crate::util::errors::CargoResult; use anyhow::{Context as _, bail}; use filetime::FileTime; use itertools::Itertools; use jobserver::Client; use super::build_plan::BuildPlan; use super::custom_build::{self, BuildDeps, BuildScriptOutputs, BuildScripts}; use super::fingerprint::{Checksum, Fingerprint}; use super::job_queue::JobQueue; use super::layout::Layout; use super::lto::Lto; use super::unit_graph::UnitDep; use super::{ BuildContext, Compilation, CompileKind, CompileMode, Executor, FileFlavor, RustDocFingerprint, }; mod compilation_files; use self::compilation_files::CompilationFiles; pub use self::compilation_files::{Metadata, OutputFile, UnitHash}; /// Collection of all the stuff that is needed to perform a build. /// /// Different from the [`BuildContext`], `Context` is a _mutable_ state used /// throughout the entire build process. Everything is coordinated through this. /// /// [`BuildContext`]: crate::core::compiler::BuildContext pub struct BuildRunner<'a, 'gctx> { /// Mostly static information about the build task. pub bcx: &'a BuildContext<'a, 'gctx>, /// A large collection of information about the result of the entire compilation. pub compilation: Compilation<'gctx>, /// Output from build scripts, updated after each build script runs. pub build_script_outputs: Arc>, /// Dependencies (like rerun-if-changed) declared by a build script. /// This is *only* populated from the output from previous runs. /// If the build script hasn't ever been run, then it must be run. pub build_explicit_deps: HashMap, /// Fingerprints used to detect if a unit is out-of-date. pub fingerprints: HashMap>, /// Cache of file mtimes to reduce filesystem hits. pub mtime_cache: HashMap, /// Cache of file checksums to reduce filesystem reads. pub checksum_cache: HashMap, /// A set used to track which units have been compiled. /// A unit may appear in the job graph multiple times as a dependency of /// multiple packages, but it only needs to run once. pub compiled: HashSet, /// Linking information for each `Unit`. /// See `build_map` for details. pub build_scripts: HashMap>, /// Job server client to manage concurrency with other processes. pub jobserver: Client, /// "Primary" packages are the ones the user selected on the command-line /// with `-p` flags. If no flags are specified, then it is the defaults /// based on the current directory and the default workspace members. primary_packages: HashSet, /// An abstraction of the files and directories that will be generated by /// the compilation. This is `None` until after `unit_dependencies` has /// been computed. files: Option>, /// A set of units which are compiling rlibs and are expected to produce /// metadata files in addition to the rlib itself. rmeta_required: HashSet, /// Map of the LTO-status of each unit. This indicates what sort of /// compilation is happening (only object, only bitcode, both, etc), and is /// precalculated early on. pub lto: HashMap, /// Map of Doc/Docscrape units to metadata for their -Cmetadata flag. /// See `Context::find_metadata_units` for more details. pub metadata_for_doc_units: HashMap, /// Set of metadata of Docscrape units that fail before completion, e.g. /// because the target has a type error. This is in an Arc> /// because it is continuously updated as the job progresses. pub failed_scrape_units: Arc>>, } impl<'a, 'gctx> BuildRunner<'a, 'gctx> { pub fn new(bcx: &'a BuildContext<'a, 'gctx>) -> CargoResult { // Load up the jobserver that we'll use to manage our parallelism. This // is the same as the GNU make implementation of a jobserver, and // intentionally so! It's hoped that we can interact with GNU make and // all share the same jobserver. // // Note that if we don't have a jobserver in our environment then we // create our own, and we create it with `n` tokens, but immediately // acquire one, because one token is ourself, a running process. let jobserver = match bcx.gctx.jobserver_from_env() { Some(c) => c.clone(), None => { let client = Client::new(bcx.jobs() as usize).context("failed to create jobserver")?; client.acquire_raw()?; client } }; Ok(Self { bcx, compilation: Compilation::new(bcx)?, build_script_outputs: Arc::new(Mutex::new(BuildScriptOutputs::default())), fingerprints: HashMap::new(), mtime_cache: HashMap::new(), checksum_cache: HashMap::new(), compiled: HashSet::new(), build_scripts: HashMap::new(), build_explicit_deps: HashMap::new(), jobserver, primary_packages: HashSet::new(), files: None, rmeta_required: HashSet::new(), lto: HashMap::new(), metadata_for_doc_units: HashMap::new(), failed_scrape_units: Arc::new(Mutex::new(HashSet::new())), }) } /// Dry-run the compilation without actually running it. /// /// This is expected to collect information like the location of output artifacts. /// Please keep in sync with non-compilation part in [`BuildRunner::compile`]. pub fn dry_run(mut self) -> CargoResult> { let _lock = self .bcx .gctx .acquire_package_cache_lock(CacheLockMode::Shared)?; self.lto = super::lto::generate(self.bcx)?; self.prepare_units()?; self.prepare()?; self.check_collisions()?; for unit in &self.bcx.roots { self.collect_tests_and_executables(unit)?; } Ok(self.compilation) } /// Starts compilation, waits for it to finish, and returns information /// about the result of compilation. /// /// See [`ops::cargo_compile`] for a higher-level view of the compile process. /// /// [`ops::cargo_compile`]: crate::ops::cargo_compile #[tracing::instrument(skip_all)] pub fn compile(mut self, exec: &Arc) -> CargoResult> { // A shared lock is held during the duration of the build since rustc // needs to read from the `src` cache, and we don't want other // commands modifying the `src` cache while it is running. let _lock = self .bcx .gctx .acquire_package_cache_lock(CacheLockMode::Shared)?; let mut queue = JobQueue::new(self.bcx); let mut plan = BuildPlan::new(); let build_plan = self.bcx.build_config.build_plan; self.lto = super::lto::generate(self.bcx)?; self.prepare_units()?; self.prepare()?; custom_build::build_map(&mut self)?; self.check_collisions()?; self.compute_metadata_for_doc_units(); // We need to make sure that if there were any previous docs // already compiled, they were compiled with the same Rustc version that we're currently // using. Otherwise we must remove the `doc/` folder and compile again forcing a rebuild. // // This is important because the `.js`/`.html` & `.css` files that are generated by Rustc don't have // any versioning (See https://github.com/rust-lang/cargo/issues/8461). // Therefore, we can end up with weird bugs and behaviours if we mix different // versions of these files. if self.bcx.build_config.intent.is_doc() { RustDocFingerprint::check_rustdoc_fingerprint(&self)? } for unit in &self.bcx.roots { let force_rebuild = self.bcx.build_config.force_rebuild; super::compile(&mut self, &mut queue, &mut plan, unit, exec, force_rebuild)?; } // Now that we've got the full job queue and we've done all our // fingerprint analysis to determine what to run, bust all the memoized // fingerprint hashes to ensure that during the build they all get the // most up-to-date values. In theory we only need to bust hashes that // transitively depend on a dirty build script, but it shouldn't matter // that much for performance anyway. for fingerprint in self.fingerprints.values() { fingerprint.clear_memoized(); } // Now that we've figured out everything that we're going to do, do it! queue.execute(&mut self, &mut plan)?; if build_plan { plan.set_inputs(self.build_plan_inputs()?); plan.output_plan(self.bcx.gctx); } // Add `OUT_DIR` to env vars if unit has a build script. let units_with_build_script = &self .bcx .roots .iter() .filter(|unit| self.build_scripts.contains_key(unit)) .dedup_by(|x, y| x.pkg.package_id() == y.pkg.package_id()) .collect::>(); for unit in units_with_build_script { for dep in &self.bcx.unit_graph[unit] { if dep.unit.mode.is_run_custom_build() { let out_dir = self .files() .build_script_out_dir(&dep.unit) .display() .to_string(); let script_meta = self.get_run_build_script_metadata(&dep.unit); self.compilation .extra_env .entry(script_meta) .or_insert_with(Vec::new) .push(("OUT_DIR".to_string(), out_dir)); } } } // Collect the result of the build into `self.compilation`. for unit in &self.bcx.roots { self.collect_tests_and_executables(unit)?; // Collect information for `rustdoc --test`. if unit.mode.is_doc_test() { let mut unstable_opts = false; let mut args = compiler::extern_args(&self, unit, &mut unstable_opts)?; args.extend(compiler::lto_args(&self, unit)); args.extend(compiler::features_args(unit)); args.extend(compiler::check_cfg_args(unit)); let script_metas = self.find_build_script_metadatas(unit); if let Some(meta_vec) = script_metas.clone() { for meta in meta_vec { if let Some(output) = self.build_script_outputs.lock().unwrap().get(meta) { for cfg in &output.cfgs { args.push("--cfg".into()); args.push(cfg.into()); } for check_cfg in &output.check_cfgs { args.push("--check-cfg".into()); args.push(check_cfg.into()); } for (lt, arg) in &output.linker_args { if lt.applies_to(&unit.target, unit.mode) { args.push("-C".into()); args.push(format!("link-arg={}", arg).into()); } } } } } args.extend(unit.rustdocflags.iter().map(Into::into)); use super::MessageFormat; let format = match self.bcx.build_config.message_format { MessageFormat::Short => "short", MessageFormat::Human => "human", MessageFormat::Json { .. } => "json", }; args.push("--error-format".into()); args.push(format.into()); self.compilation.to_doc_test.push(compilation::Doctest { unit: unit.clone(), args, unstable_opts, linker: self.compilation.target_linker(unit.kind).clone(), script_metas, env: artifact::get_env(&self, self.unit_deps(unit))?, }); } super::output_depinfo(&mut self, unit)?; } for (script_meta, output) in self.build_script_outputs.lock().unwrap().iter() { self.compilation .extra_env .entry(*script_meta) .or_insert_with(Vec::new) .extend(output.env.iter().cloned()); for dir in output.library_paths.iter() { self.compilation .native_dirs .insert(dir.clone().into_path_buf()); } } Ok(self.compilation) } fn collect_tests_and_executables(&mut self, unit: &Unit) -> CargoResult<()> { for output in self.outputs(unit)?.iter() { if matches!( output.flavor, FileFlavor::DebugInfo | FileFlavor::Auxiliary | FileFlavor::Sbom ) { continue; } let bindst = output.bin_dst(); if unit.mode == CompileMode::Test { self.compilation .tests .push(self.unit_output(unit, &output.path)); } else if unit.target.is_executable() { self.compilation .binaries .push(self.unit_output(unit, bindst)); } else if unit.target.is_cdylib() && !self.compilation.cdylibs.iter().any(|uo| uo.unit == *unit) { self.compilation .cdylibs .push(self.unit_output(unit, bindst)); } } Ok(()) } /// Returns the executable for the specified unit (if any). pub fn get_executable(&mut self, unit: &Unit) -> CargoResult> { let is_binary = unit.target.is_executable(); let is_test = unit.mode.is_any_test(); if !unit.mode.generates_executable() || !(is_binary || is_test) { return Ok(None); } Ok(self .outputs(unit)? .iter() .find(|o| o.flavor == FileFlavor::Normal) .map(|output| output.bin_dst().clone())) } #[tracing::instrument(skip_all)] pub fn prepare_units(&mut self) -> CargoResult<()> { let dest = self.bcx.profiles.get_dir_name(); let host_layout = Layout::new(self.bcx.ws, None, &dest)?; let mut targets = HashMap::new(); for kind in self.bcx.all_kinds.iter() { if let CompileKind::Target(target) = *kind { let layout = Layout::new(self.bcx.ws, Some(target), &dest)?; targets.insert(target, layout); } } self.primary_packages .extend(self.bcx.roots.iter().map(|u| u.pkg.package_id())); self.compilation .root_crate_names .extend(self.bcx.roots.iter().map(|u| u.target.crate_name())); self.record_units_requiring_metadata(); let files = CompilationFiles::new(self, host_layout, targets); self.files = Some(files); Ok(()) } /// Prepare this context, ensuring that all filesystem directories are in /// place. #[tracing::instrument(skip_all)] pub fn prepare(&mut self) -> CargoResult<()> { self.files .as_mut() .unwrap() .host .prepare() .context("couldn't prepare build directories")?; for target in self.files.as_mut().unwrap().target.values_mut() { target .prepare() .context("couldn't prepare build directories")?; } let files = self.files.as_ref().unwrap(); for &kind in self.bcx.all_kinds.iter() { let layout = files.layout(kind); self.compilation .root_output .insert(kind, layout.dest().to_path_buf()); self.compilation .deps_output .insert(kind, layout.deps().to_path_buf()); } Ok(()) } pub fn files(&self) -> &CompilationFiles<'a, 'gctx> { self.files.as_ref().unwrap() } /// Returns the filenames that the given unit will generate. pub fn outputs(&self, unit: &Unit) -> CargoResult>> { self.files.as_ref().unwrap().outputs(unit, self.bcx) } /// Direct dependencies for the given unit. pub fn unit_deps(&self, unit: &Unit) -> &[UnitDep] { &self.bcx.unit_graph[unit] } /// Returns the `RunCustomBuild` Units associated with the given Unit. /// /// If the package does not have a build script, this returns None. pub fn find_build_script_units(&self, unit: &Unit) -> Option> { if unit.mode.is_run_custom_build() { return Some(vec![unit.clone()]); } let build_script_units: Vec = self.bcx.unit_graph[unit] .iter() .filter(|unit_dep| { unit_dep.unit.mode.is_run_custom_build() && unit_dep.unit.pkg.package_id() == unit.pkg.package_id() }) .map(|unit_dep| unit_dep.unit.clone()) .collect(); if build_script_units.is_empty() { None } else { Some(build_script_units) } } /// Returns the metadata hash for the `RunCustomBuild` Unit associated with /// the given unit. /// /// If the package does not have a build script, this returns None. pub fn find_build_script_metadatas(&self, unit: &Unit) -> Option> { self.find_build_script_units(unit).map(|units| { units .iter() .map(|u| self.get_run_build_script_metadata(u)) .collect() }) } /// Returns the metadata hash for a `RunCustomBuild` unit. pub fn get_run_build_script_metadata(&self, unit: &Unit) -> UnitHash { assert!(unit.mode.is_run_custom_build()); self.files().metadata(unit).unit_id() } /// Returns the list of SBOM output file paths for a given [`Unit`]. pub fn sbom_output_files(&self, unit: &Unit) -> CargoResult> { Ok(self .outputs(unit)? .iter() .filter(|o| o.flavor == FileFlavor::Sbom) .map(|o| o.path.clone()) .collect()) } pub fn is_primary_package(&self, unit: &Unit) -> bool { self.primary_packages.contains(&unit.pkg.package_id()) } /// Returns the list of filenames read by cargo to generate the [`BuildContext`] /// (all `Cargo.toml`, etc.). pub fn build_plan_inputs(&self) -> CargoResult> { // Keep sorted for consistency. let mut inputs = BTreeSet::new(); // Note: dev-deps are skipped if they are not present in the unit graph. for unit in self.bcx.unit_graph.keys() { inputs.insert(unit.pkg.manifest_path().to_path_buf()); } Ok(inputs.into_iter().collect()) } /// Returns a [`UnitOutput`] which represents some information about the /// output of a unit. pub fn unit_output(&self, unit: &Unit, path: &Path) -> UnitOutput { let script_metas = self.find_build_script_metadatas(unit); UnitOutput { unit: unit.clone(), path: path.to_path_buf(), script_metas, } } /// Check if any output file name collision happens. /// See for more. #[tracing::instrument(skip_all)] fn check_collisions(&self) -> CargoResult<()> { let mut output_collisions = HashMap::new(); let describe_collision = |unit: &Unit, other_unit: &Unit, path: &PathBuf| -> String { format!( "The {} target `{}` in package `{}` has the same output \ filename as the {} target `{}` in package `{}`.\n\ Colliding filename is: {}\n", unit.target.kind().description(), unit.target.name(), unit.pkg.package_id(), other_unit.target.kind().description(), other_unit.target.name(), other_unit.pkg.package_id(), path.display() ) }; let suggestion = "Consider changing their names to be unique or compiling them separately.\n\ This may become a hard error in the future; see \ ."; let rustdoc_suggestion = "This is a known bug where multiple crates with the same name use\n\ the same path; see ."; let report_collision = |unit: &Unit, other_unit: &Unit, path: &PathBuf, suggestion: &str| -> CargoResult<()> { if unit.target.name() == other_unit.target.name() { self.bcx.gctx.shell().warn(format!( "output filename collision.\n\ {}\ The targets should have unique names.\n\ {}", describe_collision(unit, other_unit, path), suggestion )) } else { self.bcx.gctx.shell().warn(format!( "output filename collision.\n\ {}\ The output filenames should be unique.\n\ {}\n\ If this looks unexpected, it may be a bug in Cargo. Please file a bug report at\n\ https://github.com/rust-lang/cargo/issues/ with as much information as you\n\ can provide.\n\ cargo {} running on `{}` target `{}`\n\ First unit: {:?}\n\ Second unit: {:?}", describe_collision(unit, other_unit, path), suggestion, crate::version(), self.bcx.host_triple(), self.bcx.target_data.short_name(&unit.kind), unit, other_unit)) } }; fn doc_collision_error(unit: &Unit, other_unit: &Unit) -> CargoResult<()> { bail!( "document output filename collision\n\ The {} `{}` in package `{}` has the same name as the {} `{}` in package `{}`.\n\ Only one may be documented at once since they output to the same path.\n\ Consider documenting only one, renaming one, \ or marking one with `doc = false` in Cargo.toml.", unit.target.kind().description(), unit.target.name(), unit.pkg, other_unit.target.kind().description(), other_unit.target.name(), other_unit.pkg, ); } let mut keys = self .bcx .unit_graph .keys() .filter(|unit| !unit.mode.is_run_custom_build()) .collect::>(); // Sort for consistent error messages. keys.sort_unstable(); // These are kept separate to retain compatibility with older // versions, which generated an error when there was a duplicate lib // or bin (but the old code did not check bin<->lib collisions). To // retain backwards compatibility, this only generates an error for // duplicate libs or duplicate bins (but not both). Ideally this // shouldn't be here, but since there isn't a complete workaround, // yet, this retains the old behavior. let mut doc_libs = HashMap::new(); let mut doc_bins = HashMap::new(); for unit in keys { if unit.mode.is_doc() && self.is_primary_package(unit) { // These situations have been an error since before 1.0, so it // is not a warning like the other situations. if unit.target.is_lib() { if let Some(prev) = doc_libs.insert((unit.target.crate_name(), unit.kind), unit) { doc_collision_error(unit, prev)?; } } else if let Some(prev) = doc_bins.insert((unit.target.crate_name(), unit.kind), unit) { doc_collision_error(unit, prev)?; } } for output in self.outputs(unit)?.iter() { if let Some(other_unit) = output_collisions.insert(output.path.clone(), unit) { if unit.mode.is_doc() { // See https://github.com/rust-lang/rust/issues/56169 // and https://github.com/rust-lang/rust/issues/61378 report_collision(unit, other_unit, &output.path, rustdoc_suggestion)?; } else { report_collision(unit, other_unit, &output.path, suggestion)?; } } if let Some(hardlink) = output.hardlink.as_ref() { if let Some(other_unit) = output_collisions.insert(hardlink.clone(), unit) { report_collision(unit, other_unit, hardlink, suggestion)?; } } if let Some(ref export_path) = output.export_path { if let Some(other_unit) = output_collisions.insert(export_path.clone(), unit) { self.bcx.gctx.shell().warn(format!( "`--artifact-dir` filename collision.\n\ {}\ The exported filenames should be unique.\n\ {}", describe_collision(unit, other_unit, export_path), suggestion ))?; } } } } Ok(()) } /// Records the list of units which are required to emit metadata. /// /// Units which depend only on the metadata of others requires the others to /// actually produce metadata, so we'll record that here. fn record_units_requiring_metadata(&mut self) { for (key, deps) in self.bcx.unit_graph.iter() { for dep in deps { if self.only_requires_rmeta(key, &dep.unit) { self.rmeta_required.insert(dep.unit.clone()); } } } } /// Returns whether when `parent` depends on `dep` if it only requires the /// metadata file from `dep`. pub fn only_requires_rmeta(&self, parent: &Unit, dep: &Unit) -> bool { // We're only a candidate for requiring an `rmeta` file if we // ourselves are building an rlib, !parent.requires_upstream_objects() && parent.mode == CompileMode::Build // Our dependency must also be built as an rlib, otherwise the // object code must be useful in some fashion && !dep.requires_upstream_objects() && dep.mode == CompileMode::Build } /// Returns whether when `unit` is built whether it should emit metadata as /// well because some compilations rely on that. pub fn rmeta_required(&self, unit: &Unit) -> bool { self.rmeta_required.contains(unit) } /// Finds metadata for Doc/Docscrape units. /// /// rustdoc needs a -Cmetadata flag in order to recognize StableCrateIds that refer to /// items in the crate being documented. The -Cmetadata flag used by reverse-dependencies /// will be the metadata of the Cargo unit that generated the current library's rmeta file, /// which should be a Check unit. /// /// If the current crate has reverse-dependencies, such a Check unit should exist, and so /// we use that crate's metadata. If not, we use the crate's Doc unit so at least examples /// scraped from the current crate can be used when documenting the current crate. #[tracing::instrument(skip_all)] pub fn compute_metadata_for_doc_units(&mut self) { for unit in self.bcx.unit_graph.keys() { if !unit.mode.is_doc() && !unit.mode.is_doc_scrape() { continue; } let matching_units = self .bcx .unit_graph .keys() .filter(|other| { unit.pkg == other.pkg && unit.target == other.target && !other.mode.is_doc_scrape() }) .collect::>(); let metadata_unit = matching_units .iter() .find(|other| other.mode.is_check()) .or_else(|| matching_units.iter().find(|other| other.mode.is_doc())) .unwrap_or(&unit); self.metadata_for_doc_units .insert(unit.clone(), self.files().metadata(metadata_unit)); } } } cargo-0.91.0/src/cargo/core/compiler/compilation.rs000064400000000000000000000442221046102023000203220ustar 00000000000000//! Type definitions for the result of a compilation. use std::collections::{BTreeSet, HashMap}; use std::ffi::{OsStr, OsString}; use std::path::PathBuf; use cargo_platform::CfgExpr; use cargo_util::{ProcessBuilder, paths}; use crate::core::Package; use crate::core::compiler::BuildContext; use crate::core::compiler::apply_env_config; use crate::core::compiler::{CompileKind, Unit, UnitHash}; use crate::util::{CargoResult, GlobalContext, context}; /// Represents the kind of process we are creating. #[derive(Debug)] enum ToolKind { /// See [`Compilation::rustc_process`]. Rustc, /// See [`Compilation::rustdoc_process`]. Rustdoc, /// See [`Compilation::host_process`]. HostProcess, /// See [`Compilation::target_process`]. TargetProcess, } impl ToolKind { fn is_rustc_tool(&self) -> bool { matches!(self, ToolKind::Rustc | ToolKind::Rustdoc) } } /// Structure with enough information to run `rustdoc --test`. pub struct Doctest { /// What's being doctested pub unit: Unit, /// Arguments needed to pass to rustdoc to run this test. pub args: Vec, /// Whether or not -Zunstable-options is needed. pub unstable_opts: bool, /// The -Clinker value to use. pub linker: Option, /// The script metadata, if this unit's package has a build script. /// /// This is used for indexing [`Compilation::extra_env`]. pub script_metas: Option>, /// Environment variables to set in the rustdoc process. pub env: HashMap, } /// Information about the output of a unit. #[derive(Ord, PartialOrd, Eq, PartialEq)] pub struct UnitOutput { /// The unit that generated this output. pub unit: Unit, /// Path to the unit's primary output (an executable or cdylib). pub path: PathBuf, /// The script metadata, if this unit's package has a build script. /// /// This is used for indexing [`Compilation::extra_env`]. pub script_metas: Option>, } /// A structure returning the result of a compilation. pub struct Compilation<'gctx> { /// An array of all tests created during this compilation. pub tests: Vec, /// An array of all binaries created. pub binaries: Vec, /// An array of all cdylibs created. pub cdylibs: Vec, /// The crate names of the root units specified on the command-line. pub root_crate_names: Vec, /// All directories for the output of native build commands. /// /// This is currently used to drive some entries which are added to the /// `LD_LIBRARY_PATH` as appropriate. /// /// The order should be deterministic. pub native_dirs: BTreeSet, /// Root output directory (for the local package's artifacts) pub root_output: HashMap, /// Output directory for rust dependencies. /// May be for the host or for a specific target. pub deps_output: HashMap, /// The path to libstd for each target sysroot_target_libdir: HashMap, /// Extra environment variables that were passed to compilations and should /// be passed to future invocations of programs. /// /// The key is the build script metadata for uniquely identifying the /// `RunCustomBuild` unit that generated these env vars. pub extra_env: HashMap>, /// Libraries to test with rustdoc. pub to_doc_test: Vec, /// The target host triple. pub host: String, gctx: &'gctx GlobalContext, /// Rustc process to be used by default rustc_process: ProcessBuilder, /// Rustc process to be used for workspace crates instead of `rustc_process` rustc_workspace_wrapper_process: ProcessBuilder, /// Optional rustc process to be used for primary crates instead of either `rustc_process` or /// `rustc_workspace_wrapper_process` primary_rustc_process: Option, target_runners: HashMap)>>, /// The linker to use for each host or target. target_linkers: HashMap>, /// The total number of warnings emitted by the compilation. pub warning_count: usize, } impl<'gctx> Compilation<'gctx> { pub fn new<'a>(bcx: &BuildContext<'a, 'gctx>) -> CargoResult> { let rustc_process = bcx.rustc().process(); let primary_rustc_process = bcx.build_config.primary_unit_rustc.clone(); let rustc_workspace_wrapper_process = bcx.rustc().workspace_process(); Ok(Compilation { native_dirs: BTreeSet::new(), root_output: HashMap::new(), deps_output: HashMap::new(), sysroot_target_libdir: get_sysroot_target_libdir(bcx)?, tests: Vec::new(), binaries: Vec::new(), cdylibs: Vec::new(), root_crate_names: Vec::new(), extra_env: HashMap::new(), to_doc_test: Vec::new(), gctx: bcx.gctx, host: bcx.host_triple().to_string(), rustc_process, rustc_workspace_wrapper_process, primary_rustc_process, target_runners: bcx .build_config .requested_kinds .iter() .chain(Some(&CompileKind::Host)) .map(|kind| Ok((*kind, target_runner(bcx, *kind)?))) .collect::>>()?, target_linkers: bcx .build_config .requested_kinds .iter() .chain(Some(&CompileKind::Host)) .map(|kind| Ok((*kind, target_linker(bcx, *kind)?))) .collect::>>()?, warning_count: 0, }) } /// Returns a [`ProcessBuilder`] for running `rustc`. /// /// `is_primary` is true if this is a "primary package", which means it /// was selected by the user on the command-line (such as with a `-p` /// flag), see [`crate::core::compiler::BuildRunner::primary_packages`]. /// /// `is_workspace` is true if this is a workspace member. pub fn rustc_process( &self, unit: &Unit, is_primary: bool, is_workspace: bool, ) -> CargoResult { let mut rustc = if is_primary && self.primary_rustc_process.is_some() { self.primary_rustc_process.clone().unwrap() } else if is_workspace { self.rustc_workspace_wrapper_process.clone() } else { self.rustc_process.clone() }; if self.gctx.extra_verbose() { rustc.display_env_vars(); } let cmd = fill_rustc_tool_env(rustc, unit); self.fill_env(cmd, &unit.pkg, None, unit.kind, ToolKind::Rustc) } /// Returns a [`ProcessBuilder`] for running `rustdoc`. pub fn rustdoc_process( &self, unit: &Unit, script_metas: Option<&Vec>, ) -> CargoResult { let mut rustdoc = ProcessBuilder::new(&*self.gctx.rustdoc()?); if self.gctx.extra_verbose() { rustdoc.display_env_vars(); } let cmd = fill_rustc_tool_env(rustdoc, unit); let mut cmd = self.fill_env(cmd, &unit.pkg, script_metas, unit.kind, ToolKind::Rustdoc)?; cmd.retry_with_argfile(true); unit.target.edition().cmd_edition_arg(&mut cmd); for crate_type in unit.target.rustc_crate_types() { cmd.arg("--crate-type").arg(crate_type.as_str()); } Ok(cmd) } /// Returns a [`ProcessBuilder`] appropriate for running a process for the /// host platform. /// /// This is currently only used for running build scripts. If you use this /// for anything else, please be extra careful on how environment /// variables are set! pub fn host_process>( &self, cmd: T, pkg: &Package, ) -> CargoResult { self.fill_env( ProcessBuilder::new(cmd), pkg, None, CompileKind::Host, ToolKind::HostProcess, ) } pub fn target_runner(&self, kind: CompileKind) -> Option<&(PathBuf, Vec)> { self.target_runners.get(&kind).and_then(|x| x.as_ref()) } /// Gets the user-specified linker for a particular host or target. pub fn target_linker(&self, kind: CompileKind) -> Option { self.target_linkers.get(&kind).and_then(|x| x.clone()) } /// Returns a [`ProcessBuilder`] appropriate for running a process for the /// target platform. This is typically used for `cargo run` and `cargo /// test`. /// /// `script_metas` is the metadata for the `RunCustomBuild` unit that this /// unit used for its build script. Use `None` if the package did not have /// a build script. pub fn target_process>( &self, cmd: T, kind: CompileKind, pkg: &Package, script_metas: Option<&Vec>, ) -> CargoResult { let builder = if let Some((runner, args)) = self.target_runner(kind) { let mut builder = ProcessBuilder::new(runner); builder.args(args); builder.arg(cmd); builder } else { ProcessBuilder::new(cmd) }; let tool_kind = ToolKind::TargetProcess; let mut builder = self.fill_env(builder, pkg, script_metas, kind, tool_kind)?; if let Some(client) = self.gctx.jobserver_from_env() { builder.inherit_jobserver(client); } Ok(builder) } /// Prepares a new process with an appropriate environment to run against /// the artifacts produced by the build process. /// /// The package argument is also used to configure environment variables as /// well as the working directory of the child process. fn fill_env( &self, mut cmd: ProcessBuilder, pkg: &Package, script_metas: Option<&Vec>, kind: CompileKind, tool_kind: ToolKind, ) -> CargoResult { let mut search_path = Vec::new(); if tool_kind.is_rustc_tool() { if matches!(tool_kind, ToolKind::Rustdoc) { // HACK: `rustdoc --test` not only compiles but executes doctests. // Ideally only execution phase should have search paths appended, // so the executions can find native libs just like other tests. // However, there is no way to separate these two phase, so this // hack is added for both phases. // TODO: handle doctest-xcompile search_path.extend(super::filter_dynamic_search_path( self.native_dirs.iter(), &self.root_output[&CompileKind::Host], )); } search_path.push(self.deps_output[&CompileKind::Host].clone()); } else { search_path.extend(super::filter_dynamic_search_path( self.native_dirs.iter(), &self.root_output[&kind], )); search_path.push(self.deps_output[&kind].clone()); search_path.push(self.root_output[&kind].clone()); // For build-std, we don't want to accidentally pull in any shared // libs from the sysroot that ships with rustc. This may not be // required (at least I cannot craft a situation where it // matters), but is here to be safe. if self.gctx.cli_unstable().build_std.is_none() || // Proc macros dynamically link to std, so set it anyway. pkg.proc_macro() { search_path.push(self.sysroot_target_libdir[&kind].clone()); } } let dylib_path = paths::dylib_path(); let dylib_path_is_empty = dylib_path.is_empty(); if dylib_path.starts_with(&search_path) { search_path = dylib_path; } else { search_path.extend(dylib_path.into_iter()); } if cfg!(target_os = "macos") && dylib_path_is_empty { // These are the defaults when DYLD_FALLBACK_LIBRARY_PATH isn't // set or set to an empty string. Since Cargo is explicitly setting // the value, make sure the defaults still work. if let Some(home) = self.gctx.get_env_os("HOME") { search_path.push(PathBuf::from(home).join("lib")); } search_path.push(PathBuf::from("/usr/local/lib")); search_path.push(PathBuf::from("/usr/lib")); } let search_path = paths::join_paths(&search_path, paths::dylib_path_envvar())?; cmd.env(paths::dylib_path_envvar(), &search_path); if let Some(meta_vec) = script_metas { for meta in meta_vec { if let Some(env) = self.extra_env.get(meta) { for (k, v) in env { cmd.env(k, v); } } } } let cargo_exe = self.gctx.cargo_exe()?; cmd.env(crate::CARGO_ENV, cargo_exe); // When adding new environment variables depending on // crate properties which might require rebuild upon change // consider adding the corresponding properties to the hash // in BuildContext::target_metadata() cmd.env("CARGO_MANIFEST_DIR", pkg.root()) .env("CARGO_MANIFEST_PATH", pkg.manifest_path()) .env("CARGO_PKG_VERSION_MAJOR", &pkg.version().major.to_string()) .env("CARGO_PKG_VERSION_MINOR", &pkg.version().minor.to_string()) .env("CARGO_PKG_VERSION_PATCH", &pkg.version().patch.to_string()) .env("CARGO_PKG_VERSION_PRE", pkg.version().pre.as_str()) .env("CARGO_PKG_VERSION", &pkg.version().to_string()) .env("CARGO_PKG_NAME", &*pkg.name()); for (key, value) in pkg.manifest().metadata().env_vars() { cmd.env(key, value.as_ref()); } cmd.cwd(pkg.root()); apply_env_config(self.gctx, &mut cmd)?; Ok(cmd) } } /// Prepares a `rustc_tool` process with additional environment variables /// that are only relevant in a context that has a unit fn fill_rustc_tool_env(mut cmd: ProcessBuilder, unit: &Unit) -> ProcessBuilder { if unit.target.is_executable() { let name = unit .target .binary_filename() .unwrap_or(unit.target.name().to_string()); cmd.env("CARGO_BIN_NAME", name); } cmd.env("CARGO_CRATE_NAME", unit.target.crate_name()); cmd } fn get_sysroot_target_libdir( bcx: &BuildContext<'_, '_>, ) -> CargoResult> { bcx.all_kinds .iter() .map(|&kind| { let Some(info) = bcx.target_data.get_info(kind) else { let target = match kind { CompileKind::Host => "host".to_owned(), CompileKind::Target(s) => s.short_name().to_owned(), }; let dependency = bcx .unit_graph .iter() .find_map(|(u, _)| (u.kind == kind).then_some(u.pkg.summary().package_id())) .unwrap(); anyhow::bail!( "could not find specification for target `{target}`.\n \ Dependency `{dependency}` requires to build for target `{target}`." ) }; Ok((kind, info.sysroot_target_libdir.clone())) }) .collect() } fn target_runner( bcx: &BuildContext<'_, '_>, kind: CompileKind, ) -> CargoResult)>> { let target = bcx.target_data.short_name(&kind); // try target.{}.runner let key = format!("target.{}.runner", target); if let Some(v) = bcx.gctx.get::>(&key)? { let path = v.path.resolve_program(bcx.gctx); return Ok(Some((path, v.args))); } // try target.'cfg(...)'.runner let target_cfg = bcx.target_data.info(kind).cfg(); let mut cfgs = bcx .gctx .target_cfgs()? .iter() .filter_map(|(key, cfg)| cfg.runner.as_ref().map(|runner| (key, runner))) .filter(|(key, _runner)| CfgExpr::matches_key(key, target_cfg)); let matching_runner = cfgs.next(); if let Some((key, runner)) = cfgs.next() { anyhow::bail!( "several matching instances of `target.'cfg(..)'.runner` in configurations\n\ first match `{}` located in {}\n\ second match `{}` located in {}", matching_runner.unwrap().0, matching_runner.unwrap().1.definition, key, runner.definition ); } Ok(matching_runner.map(|(_k, runner)| { ( runner.val.path.clone().resolve_program(bcx.gctx), runner.val.args.clone(), ) })) } /// Gets the user-specified linker for a particular host or target from the configuration. fn target_linker(bcx: &BuildContext<'_, '_>, kind: CompileKind) -> CargoResult> { // Try host.linker and target.{}.linker. if let Some(path) = bcx .target_data .target_config(kind) .linker .as_ref() .map(|l| l.val.clone().resolve_program(bcx.gctx)) { return Ok(Some(path)); } // Try target.'cfg(...)'.linker. let target_cfg = bcx.target_data.info(kind).cfg(); let mut cfgs = bcx .gctx .target_cfgs()? .iter() .filter_map(|(key, cfg)| cfg.linker.as_ref().map(|linker| (key, linker))) .filter(|(key, _linker)| CfgExpr::matches_key(key, target_cfg)); let matching_linker = cfgs.next(); if let Some((key, linker)) = cfgs.next() { anyhow::bail!( "several matching instances of `target.'cfg(..)'.linker` in configurations\n\ first match `{}` located in {}\n\ second match `{}` located in {}", matching_linker.unwrap().0, matching_linker.unwrap().1.definition, key, linker.definition ); } Ok(matching_linker.map(|(_k, linker)| linker.val.clone().resolve_program(bcx.gctx))) } cargo-0.91.0/src/cargo/core/compiler/compile_kind.rs000064400000000000000000000211221046102023000204330ustar 00000000000000//! Type definitions for cross-compilation. use crate::core::Target; use crate::util::errors::CargoResult; use crate::util::interning::InternedString; use crate::util::{GlobalContext, StableHasher, try_canonicalize}; use anyhow::Context as _; use serde::Serialize; use std::collections::BTreeSet; use std::fs; use std::hash::{Hash, Hasher}; use std::path::Path; /// Indicator for how a unit is being compiled. /// /// This is used primarily for organizing cross compilations vs host /// compilations, where cross compilations happen at the request of `--target` /// and host compilations happen for things like build scripts and procedural /// macros. #[derive(PartialEq, Eq, Hash, Debug, Clone, Copy, PartialOrd, Ord)] pub enum CompileKind { /// Attached to a unit that is compiled for the "host" system or otherwise /// is compiled without a `--target` flag. This is used for procedural /// macros and build scripts, or if the `--target` flag isn't passed. Host, /// Attached to a unit to be compiled for a particular target. This is used /// for units when the `--target` flag is passed. Target(CompileTarget), } /// Fallback behavior in the /// [`CompileKind::from_requested_targets_with_fallback`] function when /// no targets are specified. pub enum CompileKindFallback { /// The build configuration is consulted to find the default target, such as /// `$CARGO_BUILD_TARGET` or reading `build.target`. BuildConfig, /// Only the host should be returned when targets aren't explicitly /// specified. This is used by `cargo metadata` for example where "only /// host" has a special meaning in terms of the returned metadata. JustHost, } impl CompileKind { pub fn is_host(&self) -> bool { matches!(self, CompileKind::Host) } pub fn for_target(self, target: &Target) -> CompileKind { // Once we start compiling for the `Host` kind we continue doing so, but // if we are a `Target` kind and then we start compiling for a target // that needs to be on the host we lift ourselves up to `Host`. match self { CompileKind::Host => CompileKind::Host, CompileKind::Target(_) if target.for_host() => CompileKind::Host, CompileKind::Target(n) => CompileKind::Target(n), } } /// Creates a new list of `CompileKind` based on the requested list of /// targets. /// /// If no targets are given then this returns a single-element vector with /// `CompileKind::Host`. pub fn from_requested_targets( gctx: &GlobalContext, targets: &[String], ) -> CargoResult> { CompileKind::from_requested_targets_with_fallback( gctx, targets, CompileKindFallback::BuildConfig, ) } /// Same as [`CompileKind::from_requested_targets`] except that if `targets` /// doesn't explicitly mention anything the behavior of what to return is /// controlled by the `fallback` argument. pub fn from_requested_targets_with_fallback( gctx: &GlobalContext, targets: &[String], fallback: CompileKindFallback, ) -> CargoResult> { let dedup = |targets: &[String]| { Ok(targets .iter() .map(|value| Ok(CompileKind::Target(CompileTarget::new(value)?))) // First collect into a set to deduplicate any `--target` passed // more than once... .collect::>>()? // ... then generate a flat list for everything else to use. .into_iter() .collect()) }; if !targets.is_empty() { return dedup(targets); } let kinds = match (fallback, &gctx.build_config()?.target) { (_, None) | (CompileKindFallback::JustHost, _) => Ok(vec![CompileKind::Host]), (CompileKindFallback::BuildConfig, Some(build_target_config)) => { dedup(&build_target_config.values(gctx)?) } }; kinds } /// Hash used for fingerprinting. /// /// Metadata hashing uses the normal Hash trait, which does not /// differentiate on `.json` file contents. The fingerprint hash does /// check the contents. pub fn fingerprint_hash(&self) -> u64 { match self { CompileKind::Host => 0, CompileKind::Target(target) => target.fingerprint_hash(), } } } impl serde::ser::Serialize for CompileKind { fn serialize(&self, s: S) -> Result where S: serde::ser::Serializer, { match self { CompileKind::Host => None::<&str>.serialize(s), CompileKind::Target(t) => Some(t.name).serialize(s), } } } /// Abstraction for the representation of a compilation target that Cargo has. /// /// Compilation targets are one of two things right now: /// /// 1. A raw target string, like `x86_64-unknown-linux-gnu`. /// 2. The path to a JSON file, such as `/path/to/my-target.json`. /// /// Raw target strings are typically dictated by `rustc` itself and represent /// built-in targets. Custom JSON files are somewhat unstable, but supported /// here in Cargo. Note that for JSON target files this `CompileTarget` stores a /// full canonicalized path to the target. /// /// The main reason for this existence is to handle JSON target files where when /// we call rustc we pass full paths but when we use it for Cargo's purposes /// like naming directories or looking up configuration keys we only check the /// file stem of JSON target files. For built-in rustc targets this is just an /// uninterpreted string basically. #[derive(PartialEq, Eq, Hash, Debug, Clone, Copy, PartialOrd, Ord, Serialize)] pub struct CompileTarget { name: InternedString, } impl CompileTarget { pub fn new(name: &str) -> CargoResult { let name = name.trim(); if name.is_empty() { anyhow::bail!("target was empty"); } if !name.ends_with(".json") { return Ok(CompileTarget { name: name.into() }); } // If `name` ends in `.json` then it's likely a custom target // specification. Canonicalize the path to ensure that different builds // with different paths always produce the same result. let path = try_canonicalize(Path::new(name)) .with_context(|| format!("target path {:?} is not a valid file", name))?; let name = path .into_os_string() .into_string() .map_err(|_| anyhow::format_err!("target path is not valid unicode"))?; Ok(CompileTarget { name: name.into() }) } /// Returns the full unqualified name of this target, suitable for passing /// to `rustc` directly. /// /// Typically this is pretty much the same as `short_name`, but for the case /// of JSON target files this will be a full canonicalized path name for the /// current filesystem. pub fn rustc_target(&self) -> InternedString { self.name } /// Returns a "short" version of the target name suitable for usage within /// Cargo for configuration and such. /// /// This is typically the same as `rustc_target`, or the full name, but for /// JSON target files this returns just the file stem (e.g. `foo` out of /// `foo.json`) instead of the full path. pub fn short_name(&self) -> &str { // Flexible target specifications often point at json files, so if it // looks like we've got one of those just use the file stem (the file // name without ".json") as a short name for this target. Note that the // `unwrap()` here should never trigger since we have a nonempty name // and it starts as utf-8 so it's always utf-8 if self.name.ends_with(".json") { Path::new(&self.name).file_stem().unwrap().to_str().unwrap() } else { &self.name } } /// See [`CompileKind::fingerprint_hash`]. pub fn fingerprint_hash(&self) -> u64 { let mut hasher = StableHasher::new(); match self .name .ends_with(".json") .then(|| fs::read_to_string(self.name)) { Some(Ok(contents)) => { // This may have some performance concerns, since it is called // fairly often. If that ever seems worth fixing, consider // embedding this in `CompileTarget`. contents.hash(&mut hasher); } _ => { self.name.hash(&mut hasher); } } Hasher::finish(&hasher) } } cargo-0.91.0/src/cargo/core/compiler/crate_type.rs000064400000000000000000000121151046102023000201370ustar 00000000000000use std::fmt; /// Types of the output artifact that the compiler emits. /// Usually distributable or linkable either statically or dynamically. /// /// See . #[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum CrateType { Bin, Lib, Rlib, Dylib, Cdylib, Staticlib, ProcMacro, Other(String), } impl CrateType { pub fn as_str(&self) -> &str { match self { CrateType::Bin => "bin", CrateType::Lib => "lib", CrateType::Rlib => "rlib", CrateType::Dylib => "dylib", CrateType::Cdylib => "cdylib", CrateType::Staticlib => "staticlib", CrateType::ProcMacro => "proc-macro", CrateType::Other(s) => s, } } pub fn can_lto(&self) -> bool { match self { CrateType::Bin | CrateType::Staticlib | CrateType::Cdylib => true, CrateType::Lib | CrateType::Rlib | CrateType::Dylib | CrateType::ProcMacro | CrateType::Other(..) => false, } } pub fn is_linkable(&self) -> bool { match self { CrateType::Lib | CrateType::Rlib | CrateType::Dylib | CrateType::ProcMacro => true, CrateType::Bin | CrateType::Cdylib | CrateType::Staticlib | CrateType::Other(..) => { false } } } pub fn is_dynamic(&self) -> bool { match self { CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro => true, CrateType::Lib | CrateType::Rlib | CrateType::Bin | CrateType::Staticlib | CrateType::Other(..) => false, } } /// Returns whether production of this crate type requires the object files /// from dependencies to be available. /// /// See also [`TargetKind::requires_upstream_objects`]. /// /// [`TargetKind::requires_upstream_objects`]: crate::core::manifest::TargetKind::requires_upstream_objects pub fn requires_upstream_objects(&self) -> bool { // "lib" == "rlib" and is a compilation that doesn't actually // require upstream object files to exist, only upstream metadata // files. As a result, it doesn't require upstream artifacts !matches!(self, CrateType::Lib | CrateType::Rlib) // Everything else, however, is some form of "linkable output" or // something that requires upstream object files. } /// Returns whether production of this crate type could benefit from splitting metadata /// into a .rmeta file. /// /// See also [`TargetKind::benefits_from_no_embed_metadata`]. /// /// [`TargetKind::benefits_from_no_embed_metadata`]: crate::core::manifest::TargetKind::benefits_from_no_embed_metadata pub fn benefits_from_no_embed_metadata(&self) -> bool { match self { // rlib/libs generate .rmeta files for pipelined compilation. // If we also include metadata inside of them, we waste disk space, since the metadata // will be located both in the lib/rlib and the .rmeta file. CrateType::Lib | CrateType::Rlib | // Dylibs do not have to contain metadata when they are used as a runtime dependency. // If we split the metadata into a separate .rmeta file, the dylib file (that // can be shipped as a runtime dependency) can be smaller. CrateType::Dylib => true, // Proc macros contain metadata that specifies what macro functions are available in // it, but the metadata is typically very small. The metadata of proc macros is also // self-contained (unlike rlibs/dylibs), so let's not unnecessarily split it into // multiple files. CrateType::ProcMacro | // cdylib and staticlib produce artifacts that are used through the C ABI and do not // contain Rust-specific metadata. CrateType::Cdylib | CrateType::Staticlib | // Binaries also do not contain metadata CrateType::Bin | CrateType::Other(_) => false } } } impl fmt::Display for CrateType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.as_str().fmt(f) } } impl<'a> From<&'a String> for CrateType { fn from(s: &'a String) -> Self { match s.as_str() { "bin" => CrateType::Bin, "lib" => CrateType::Lib, "rlib" => CrateType::Rlib, "dylib" => CrateType::Dylib, "cdylib" => CrateType::Cdylib, "staticlib" => CrateType::Staticlib, "procmacro" => CrateType::ProcMacro, _ => CrateType::Other(s.clone()), } } } impl fmt::Debug for CrateType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.to_string().fmt(f) } } impl serde::Serialize for CrateType { fn serialize(&self, s: S) -> Result where S: serde::ser::Serializer, { self.to_string().serialize(s) } } cargo-0.91.0/src/cargo/core/compiler/custom_build.rs000064400000000000000000001630211046102023000204740ustar 00000000000000//! How to execute a build script and parse its output. //! //! ## Preparing a build script run //! //! A [build script] is an optional Rust script Cargo will run before building //! your package. As of this writing, two kinds of special [`Unit`]s will be //! constructed when there is a build script in a package. //! //! * Build script compilation --- This unit is generally the same as units //! that would compile other Cargo targets. It will recursively creates units //! of its dependencies. One biggest difference is that the [`Unit`] of //! compiling a build script is flagged as [`TargetKind::CustomBuild`]. //! * Build script execution --- During the construction of the [`UnitGraph`], //! Cargo inserts a [`Unit`] with [`CompileMode::RunCustomBuild`]. This unit //! depends on the unit of compiling the associated build script, to ensure //! the executable is available before running. The [`Work`] of running the //! build script is prepared in the function [`prepare`]. //! //! ## Running a build script //! //! When running a build script, Cargo is aware of the progress and the result //! of a build script. Standard output is the chosen interprocess communication //! between Cargo and build script processes. A set of strings is defined for //! that purpose. These strings, a.k.a. instructions, are interpreted by //! [`BuildOutput::parse`] and stored in [`BuildRunner::build_script_outputs`]. //! The entire execution work is constructed by [`build_work`]. //! //! [build script]: https://doc.rust-lang.org/nightly/cargo/reference/build-scripts.html //! [`TargetKind::CustomBuild`]: crate::core::manifest::TargetKind::CustomBuild //! [`UnitGraph`]: super::unit_graph::UnitGraph //! [`CompileMode::RunCustomBuild`]: crate::core::compiler::CompileMode::RunCustomBuild //! [instructions]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script use super::{BuildRunner, Job, Unit, Work, fingerprint, get_dynamic_search_path}; use crate::core::compiler::CompileMode; use crate::core::compiler::artifact; use crate::core::compiler::build_runner::UnitHash; use crate::core::compiler::fingerprint::DirtyReason; use crate::core::compiler::job_queue::JobState; use crate::core::{PackageId, Target, profiles::ProfileRoot}; use crate::util::errors::CargoResult; use crate::util::internal; use crate::util::machine_message::{self, Message}; use anyhow::{Context as _, bail}; use cargo_platform::Cfg; use cargo_util::paths; use cargo_util_schemas::manifest::RustVersion; use std::collections::hash_map::{Entry, HashMap}; use std::collections::{BTreeSet, HashSet}; use std::path::{Path, PathBuf}; use std::str::{self, FromStr}; use std::sync::{Arc, Mutex}; /// A build script instruction that tells Cargo to display an error after the /// build script has finished running. Read [the doc] for more. /// /// [the doc]: https://doc.rust-lang.org/nightly/cargo/reference/build-scripts.html#cargo-error const CARGO_ERROR_SYNTAX: &str = "cargo::error="; /// Deprecated: A build script instruction that tells Cargo to display a warning after the /// build script has finished running. Read [the doc] for more. /// /// [the doc]: https://doc.rust-lang.org/nightly/cargo/reference/build-scripts.html#cargo-warning const OLD_CARGO_WARNING_SYNTAX: &str = "cargo:warning="; /// A build script instruction that tells Cargo to display a warning after the /// build script has finished running. Read [the doc] for more. /// /// [the doc]: https://doc.rust-lang.org/nightly/cargo/reference/build-scripts.html#cargo-warning const NEW_CARGO_WARNING_SYNTAX: &str = "cargo::warning="; #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] pub enum Severity { Error, Warning, } pub type LogMessage = (Severity, String); /// Represents a path added to the library search path. /// /// We need to keep track of requests to add search paths within the cargo build directory /// separately from paths outside of Cargo. The reason is that we want to give precedence to linking /// against libraries within the Cargo build directory even if a similar library exists in the /// system (e.g. crate A adds `/usr/lib` to the search path and then a later build of crate B adds /// `target/debug/...` to satisfy its request to link against the library B that it built, but B is /// also found in `/usr/lib`). /// /// There's some nuance here because we want to preserve relative order of paths of the same type. /// For example, if the build process would in declaration order emit the following linker line: /// ```bash /// -L/usr/lib -Ltarget/debug/build/crate1/libs -L/lib -Ltarget/debug/build/crate2/libs) /// ``` /// /// we want the linker to actually receive: /// ```bash /// -Ltarget/debug/build/crate1/libs -Ltarget/debug/build/crate2/libs) -L/usr/lib -L/lib /// ``` /// /// so that the library search paths within the crate artifacts directory come first but retain /// relative ordering while the system library paths come after while still retaining relative /// ordering among them; ordering is the order they are emitted within the build process, /// not lexicographic order. /// /// WARNING: Even though this type implements PartialOrd + Ord, this is a lexicographic ordering. /// The linker line will require an explicit sorting algorithm. PartialOrd + Ord is derived because /// BuildOutput requires it but that ordering is different from the one for the linker search path, /// at least today. It may be worth reconsidering & perhaps it's ok if BuildOutput doesn't have /// a lexicographic ordering for the library_paths? I'm not sure the consequence of that. #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] pub enum LibraryPath { /// The path is pointing within the output folder of the crate and takes priority over /// external paths when passed to the linker. CargoArtifact(PathBuf), /// The path is pointing outside of the crate's build location. The linker will always /// receive such paths after `CargoArtifact`. External(PathBuf), } impl LibraryPath { fn new(p: PathBuf, script_out_dir: &Path) -> Self { let search_path = get_dynamic_search_path(&p); if search_path.starts_with(script_out_dir) { Self::CargoArtifact(p) } else { Self::External(p) } } pub fn into_path_buf(self) -> PathBuf { match self { LibraryPath::CargoArtifact(p) | LibraryPath::External(p) => p, } } } impl AsRef for LibraryPath { fn as_ref(&self) -> &PathBuf { match self { LibraryPath::CargoArtifact(p) | LibraryPath::External(p) => p, } } } /// Contains the parsed output of a custom build script. #[derive(Clone, Debug, Hash, Default, PartialEq, Eq, PartialOrd, Ord)] pub struct BuildOutput { /// Paths to pass to rustc with the `-L` flag. pub library_paths: Vec, /// Names and link kinds of libraries, suitable for the `-l` flag. pub library_links: Vec, /// Linker arguments suitable to be passed to `-C link-arg=` pub linker_args: Vec<(LinkArgTarget, String)>, /// Various `--cfg` flags to pass to the compiler. pub cfgs: Vec, /// Various `--check-cfg` flags to pass to the compiler. pub check_cfgs: Vec, /// Additional environment variables to run the compiler with. pub env: Vec<(String, String)>, /// Metadata to pass to the immediate dependencies. pub metadata: Vec<(String, String)>, /// Paths to trigger a rerun of this build script. /// May be absolute or relative paths (relative to package root). pub rerun_if_changed: Vec, /// Environment variables which, when changed, will cause a rebuild. pub rerun_if_env_changed: Vec, /// Errors and warnings generated by this build. /// /// These are only displayed if this is a "local" package, `-vv` is used, or /// there is a build error for any target in this package. Note that any log /// message of severity `Error` will by itself cause a build error, and will /// cause all log messages to be displayed. pub log_messages: Vec, } /// Map of packages to build script output. /// /// This initially starts out as empty. Overridden build scripts get /// inserted during `build_map`. The rest of the entries are added /// immediately after each build script runs. /// /// The [`UnitHash`] is the unique metadata hash for the `RunCustomBuild` Unit of /// the package. It needs a unique key, since the build script can be run /// multiple times with different profiles or features. We can't embed a /// `Unit` because this structure needs to be shareable between threads. #[derive(Default)] pub struct BuildScriptOutputs { outputs: HashMap, } /// Linking information for a `Unit`. /// /// See [`build_map`] for more details. #[derive(Default)] pub struct BuildScripts { /// List of build script outputs this Unit needs to include for linking. Each /// element is an index into `BuildScriptOutputs`. /// /// Cargo will use this `to_link` vector to add `-L` flags to compiles as we /// propagate them upwards towards the final build. Note, however, that we /// need to preserve the ordering of `to_link` to be topologically sorted. /// This will ensure that build scripts which print their paths properly will /// correctly pick up the files they generated (if there are duplicates /// elsewhere). /// /// To preserve this ordering, the (id, metadata) is stored in two places, once /// in the `Vec` and once in `seen_to_link` for a fast lookup. We maintain /// this as we're building interactively below to ensure that the memory /// usage here doesn't blow up too much. /// /// For more information, see #2354. pub to_link: Vec<(PackageId, UnitHash)>, /// This is only used while constructing `to_link` to avoid duplicates. seen_to_link: HashSet<(PackageId, UnitHash)>, /// Host-only dependencies that have build scripts. Each element is an /// index into `BuildScriptOutputs`. /// /// This is the set of transitive dependencies that are host-only /// (proc-macro, plugin, build-dependency) that contain a build script. /// Any `BuildOutput::library_paths` path relative to `target` will be /// added to `LD_LIBRARY_PATH` so that the compiler can find any dynamic /// libraries a build script may have generated. pub plugins: BTreeSet<(PackageId, UnitHash)>, } /// Dependency information as declared by a build script that might trigger /// a recompile of itself. #[derive(Debug)] pub struct BuildDeps { /// Absolute path to the file in the target directory that stores the /// output of the build script. pub build_script_output: PathBuf, /// Files that trigger a rebuild if they change. pub rerun_if_changed: Vec, /// Environment variables that trigger a rebuild if they change. pub rerun_if_env_changed: Vec, } /// Represents one of the instructions from `cargo::rustc-link-arg-*` build /// script instruction family. /// /// In other words, indicates targets that custom linker arguments applies to. /// /// See the [build script documentation][1] for more. /// /// [1]: https://doc.rust-lang.org/nightly/cargo/reference/build-scripts.html#cargorustc-link-argflag #[derive(Clone, Hash, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum LinkArgTarget { /// Represents `cargo::rustc-link-arg=FLAG`. All, /// Represents `cargo::rustc-cdylib-link-arg=FLAG`. Cdylib, /// Represents `cargo::rustc-link-arg-bins=FLAG`. Bin, /// Represents `cargo::rustc-link-arg-bin=BIN=FLAG`. SingleBin(String), /// Represents `cargo::rustc-link-arg-tests=FLAG`. Test, /// Represents `cargo::rustc-link-arg-benches=FLAG`. Bench, /// Represents `cargo::rustc-link-arg-examples=FLAG`. Example, } impl LinkArgTarget { /// Checks if this link type applies to a given [`Target`]. pub fn applies_to(&self, target: &Target, mode: CompileMode) -> bool { let is_test = mode.is_any_test(); match self { LinkArgTarget::All => true, LinkArgTarget::Cdylib => !is_test && target.is_cdylib(), LinkArgTarget::Bin => target.is_bin(), LinkArgTarget::SingleBin(name) => target.is_bin() && target.name() == name, LinkArgTarget::Test => target.is_test(), LinkArgTarget::Bench => target.is_bench(), LinkArgTarget::Example => target.is_exe_example(), } } } /// Prepares a `Work` that executes the target as a custom build script. #[tracing::instrument(skip_all)] pub fn prepare(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult { let metadata = build_runner.get_run_build_script_metadata(unit); if build_runner .build_script_outputs .lock() .unwrap() .contains_key(metadata) { // The output is already set, thus the build script is overridden. fingerprint::prepare_target(build_runner, unit, false) } else { build_work(build_runner, unit) } } /// Emits the output of a build script as a [`machine_message::BuildScript`] /// JSON string to standard output. fn emit_build_output( state: &JobState<'_, '_>, output: &BuildOutput, out_dir: &Path, package_id: PackageId, ) -> CargoResult<()> { let library_paths = output .library_paths .iter() .map(|l| l.as_ref().display().to_string()) .collect::>(); let msg = machine_message::BuildScript { package_id: package_id.to_spec(), linked_libs: &output.library_links, linked_paths: &library_paths, cfgs: &output.cfgs, env: &output.env, out_dir, } .to_json_string(); state.stdout(msg)?; Ok(()) } /// Constructs the unit of work of running a build script. /// /// The construction includes: /// /// * Set environment variables for the build script run. /// * Create the output dir (`OUT_DIR`) for the build script output. /// * Determine if the build script needs a re-run. /// * Run the build script and store its output. fn build_work(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult { assert!(unit.mode.is_run_custom_build()); let bcx = &build_runner.bcx; let dependencies = build_runner.unit_deps(unit); let build_script_unit = dependencies .iter() .find(|d| !d.unit.mode.is_run_custom_build() && d.unit.target.is_custom_build()) .map(|d| &d.unit) .expect("running a script not depending on an actual script"); let script_dir = build_runner.files().build_script_dir(build_script_unit); let script_out_dir = build_runner.files().build_script_out_dir(unit); let script_run_dir = build_runner.files().build_script_run_dir(unit); let build_plan = bcx.build_config.build_plan; let invocation_name = unit.buildkey(); if let Some(deps) = unit.pkg.manifest().metabuild() { prepare_metabuild(build_runner, build_script_unit, deps)?; } // Building the command to execute let to_exec = script_dir.join(unit.target.name()); // Start preparing the process to execute, starting out with some // environment variables. Note that the profile-related environment // variables are not set with this the build script's profile but rather the // package's library profile. // NOTE: if you add any profile flags, be sure to update // `Profiles::get_profile_run_custom_build` so that those flags get // carried over. let to_exec = to_exec.into_os_string(); let mut cmd = build_runner.compilation.host_process(to_exec, &unit.pkg)?; let debug = unit.profile.debuginfo.is_turned_on(); cmd.env("OUT_DIR", &script_out_dir) .env("CARGO_MANIFEST_DIR", unit.pkg.root()) .env("CARGO_MANIFEST_PATH", unit.pkg.manifest_path()) .env("NUM_JOBS", &bcx.jobs().to_string()) .env("TARGET", bcx.target_data.short_name(&unit.kind)) .env("DEBUG", debug.to_string()) .env("OPT_LEVEL", &unit.profile.opt_level) .env( "PROFILE", match unit.profile.root { ProfileRoot::Release => "release", ProfileRoot::Debug => "debug", }, ) .env("HOST", &bcx.host_triple()) .env("RUSTC", &bcx.rustc().path) .env("RUSTDOC", &*bcx.gctx.rustdoc()?) .inherit_jobserver(&build_runner.jobserver); // Find all artifact dependencies and make their file and containing directory discoverable using environment variables. for (var, value) in artifact::get_env(build_runner, dependencies)? { cmd.env(&var, value); } if let Some(linker) = &build_runner.compilation.target_linker(unit.kind) { cmd.env("RUSTC_LINKER", linker); } if let Some(links) = unit.pkg.manifest().links() { cmd.env("CARGO_MANIFEST_LINKS", links); } if let Some(trim_paths) = unit.profile.trim_paths.as_ref() { cmd.env("CARGO_TRIM_PATHS", trim_paths.to_string()); } // Be sure to pass along all enabled features for this package, this is the // last piece of statically known information that we have. for feat in &unit.features { cmd.env(&format!("CARGO_FEATURE_{}", super::envify(feat)), "1"); } let mut cfg_map = HashMap::new(); cfg_map.insert( "feature", unit.features.iter().map(|s| s.as_str()).collect::>(), ); for cfg in bcx.target_data.cfg(unit.kind) { match *cfg { Cfg::Name(ref n) => { cfg_map.insert(n.as_str(), Vec::new()); } Cfg::KeyPair(ref k, ref v) => { let values = cfg_map.entry(k.as_str()).or_default(); values.push(v.as_str()); } } } for (k, v) in cfg_map { if k == "debug_assertions" { // This cfg is always true and misleading, so avoid setting it. // That is because Cargo queries rustc without any profile settings. continue; } // FIXME: We should handle raw-idents somehow instead of predenting they // don't exist here let k = format!("CARGO_CFG_{}", super::envify(k)); cmd.env(&k, v.join(",")); } // Also inform the build script of the rustc compiler context. if let Some(wrapper) = bcx.rustc().wrapper.as_ref() { cmd.env("RUSTC_WRAPPER", wrapper); } else { cmd.env_remove("RUSTC_WRAPPER"); } cmd.env_remove("RUSTC_WORKSPACE_WRAPPER"); if build_runner.bcx.ws.is_member(&unit.pkg) { if let Some(wrapper) = bcx.rustc().workspace_wrapper.as_ref() { cmd.env("RUSTC_WORKSPACE_WRAPPER", wrapper); } } cmd.env("CARGO_ENCODED_RUSTFLAGS", unit.rustflags.join("\x1f")); cmd.env_remove("RUSTFLAGS"); if build_runner.bcx.ws.gctx().extra_verbose() { cmd.display_env_vars(); } // Gather the set of native dependencies that this package has along with // some other variables to close over. // // This information will be used at build-time later on to figure out which // sorts of variables need to be discovered at that time. let lib_deps = dependencies .iter() .filter_map(|dep| { if dep.unit.mode.is_run_custom_build() { let dep_metadata = build_runner.get_run_build_script_metadata(&dep.unit); Some(( dep.unit.pkg.manifest().links().unwrap().to_string(), dep.unit.pkg.package_id(), dep_metadata, )) } else { None } }) .collect::>(); let library_name = unit.pkg.library().map(|t| t.crate_name()); let pkg_descr = unit.pkg.to_string(); let build_script_outputs = Arc::clone(&build_runner.build_script_outputs); let id = unit.pkg.package_id(); let output_file = script_run_dir.join("output"); let err_file = script_run_dir.join("stderr"); let root_output_file = script_run_dir.join("root-output"); let host_target_root = build_runner.files().host_dest().to_path_buf(); let all = ( id, library_name.clone(), pkg_descr.clone(), Arc::clone(&build_script_outputs), output_file.clone(), script_out_dir.clone(), ); let build_scripts = build_runner.build_scripts.get(unit).cloned(); let json_messages = bcx.build_config.emit_json(); let extra_verbose = bcx.gctx.extra_verbose(); let (prev_output, prev_script_out_dir) = prev_build_output(build_runner, unit); let metadata_hash = build_runner.get_run_build_script_metadata(unit); paths::create_dir_all(&script_dir)?; paths::create_dir_all(&script_out_dir)?; let nightly_features_allowed = build_runner.bcx.gctx.nightly_features_allowed; let targets: Vec = unit.pkg.targets().to_vec(); let msrv = unit.pkg.rust_version().cloned(); // Need a separate copy for the fresh closure. let targets_fresh = targets.clone(); let msrv_fresh = msrv.clone(); let env_profile_name = unit.profile.name.to_uppercase(); let built_with_debuginfo = build_runner .bcx .unit_graph .get(unit) .and_then(|deps| deps.iter().find(|dep| dep.unit.target == unit.target)) .map(|dep| dep.unit.profile.debuginfo.is_turned_on()) .unwrap_or(false); // Prepare the unit of "dirty work" which will actually run the custom build // command. // // Note that this has to do some extra work just before running the command // to determine extra environment variables and such. let dirty = Work::new(move |state| { // Make sure that OUT_DIR exists. // // If we have an old build directory, then just move it into place, // otherwise create it! paths::create_dir_all(&script_out_dir) .context("failed to create script output directory for build command")?; // For all our native lib dependencies, pick up their metadata to pass // along to this custom build command. We're also careful to augment our // dynamic library search path in case the build script depended on any // native dynamic libraries. if !build_plan { let build_script_outputs = build_script_outputs.lock().unwrap(); for (name, dep_id, dep_metadata) in lib_deps { let script_output = build_script_outputs.get(dep_metadata).ok_or_else(|| { internal(format!( "failed to locate build state for env vars: {}/{}", dep_id, dep_metadata )) })?; let data = &script_output.metadata; for (key, value) in data.iter() { cmd.env( &format!("DEP_{}_{}", super::envify(&name), super::envify(key)), value, ); } } if let Some(build_scripts) = build_scripts { super::add_plugin_deps( &mut cmd, &build_script_outputs, &build_scripts, &host_target_root, )?; } } if build_plan { state.build_plan(invocation_name, cmd.clone(), Arc::new(Vec::new())); return Ok(()); } // And now finally, run the build command itself! state.running(&cmd); let timestamp = paths::set_invocation_time(&script_run_dir)?; let prefix = format!("[{} {}] ", id.name(), id.version()); let mut log_messages_in_case_of_panic = Vec::new(); let output = cmd .exec_with_streaming( &mut |stdout| { if let Some(error) = stdout.strip_prefix(CARGO_ERROR_SYNTAX) { log_messages_in_case_of_panic.push((Severity::Error, error.to_owned())); } if let Some(warning) = stdout .strip_prefix(OLD_CARGO_WARNING_SYNTAX) .or(stdout.strip_prefix(NEW_CARGO_WARNING_SYNTAX)) { log_messages_in_case_of_panic.push((Severity::Warning, warning.to_owned())); } if extra_verbose { state.stdout(format!("{}{}", prefix, stdout))?; } Ok(()) }, &mut |stderr| { if extra_verbose { state.stderr(format!("{}{}", prefix, stderr))?; } Ok(()) }, true, ) .with_context(|| { let mut build_error_context = format!("failed to run custom build command for `{}`", pkg_descr); // If we're opting into backtraces, mention that build dependencies' backtraces can // be improved by requesting debuginfo to be built, if we're not building with // debuginfo already. // // ALLOWED: Other tools like `rustc` might read it directly // through `std::env`. We should make their behavior consistent. #[allow(clippy::disallowed_methods)] if let Ok(show_backtraces) = std::env::var("RUST_BACKTRACE") { if !built_with_debuginfo && show_backtraces != "0" { build_error_context.push_str(&format!( "\n\ note: To improve backtraces for build dependencies, set the \ CARGO_PROFILE_{env_profile_name}_BUILD_OVERRIDE_DEBUG=true environment \ variable to enable debug information generation.", )); } } build_error_context }); // If the build failed if let Err(error) = output { insert_log_messages_in_build_outputs( build_script_outputs, id, metadata_hash, log_messages_in_case_of_panic, ); return Err(error); } // ... or it logged any errors else if log_messages_in_case_of_panic .iter() .any(|(severity, _)| *severity == Severity::Error) { insert_log_messages_in_build_outputs( build_script_outputs, id, metadata_hash, log_messages_in_case_of_panic, ); anyhow::bail!("build script logged errors"); } let output = output.unwrap(); // After the build command has finished running, we need to be sure to // remember all of its output so we can later discover precisely what it // was, even if we don't run the build command again (due to freshness). // // This is also the location where we provide feedback into the build // state informing what variables were discovered via our script as // well. paths::write(&output_file, &output.stdout)?; // This mtime shift allows Cargo to detect if a source file was // modified in the middle of the build. paths::set_file_time_no_err(output_file, timestamp); paths::write(&err_file, &output.stderr)?; paths::write(&root_output_file, paths::path2bytes(&script_out_dir)?)?; let parsed_output = BuildOutput::parse( &output.stdout, library_name, &pkg_descr, &script_out_dir, &script_out_dir, nightly_features_allowed, &targets, &msrv, )?; if json_messages { emit_build_output(state, &parsed_output, script_out_dir.as_path(), id)?; } build_script_outputs .lock() .unwrap() .insert(id, metadata_hash, parsed_output); Ok(()) }); // Now that we've prepared our work-to-do, we need to prepare the fresh work // itself to run when we actually end up just discarding what we calculated // above. let fresh = Work::new(move |state| { let (id, library_name, pkg_descr, build_script_outputs, output_file, script_out_dir) = all; let output = match prev_output { Some(output) => output, None => BuildOutput::parse_file( &output_file, library_name, &pkg_descr, &prev_script_out_dir, &script_out_dir, nightly_features_allowed, &targets_fresh, &msrv_fresh, )?, }; if json_messages { emit_build_output(state, &output, script_out_dir.as_path(), id)?; } build_script_outputs .lock() .unwrap() .insert(id, metadata_hash, output); Ok(()) }); let mut job = if build_runner.bcx.build_config.build_plan { Job::new_dirty(Work::noop(), DirtyReason::FreshBuild) } else { fingerprint::prepare_target(build_runner, unit, false)? }; if job.freshness().is_dirty() { job.before(dirty); } else { job.before(fresh); } Ok(job) } /// When a build script run fails, store only log messages, and nuke other /// outputs, as they are likely broken. fn insert_log_messages_in_build_outputs( build_script_outputs: Arc>, id: PackageId, metadata_hash: UnitHash, log_messages: Vec, ) { let build_output_with_only_log_messages = BuildOutput { log_messages, ..BuildOutput::default() }; build_script_outputs.lock().unwrap().insert( id, metadata_hash, build_output_with_only_log_messages, ); } impl BuildOutput { /// Like [`BuildOutput::parse`] but from a file path. pub fn parse_file( path: &Path, library_name: Option, pkg_descr: &str, script_out_dir_when_generated: &Path, script_out_dir: &Path, nightly_features_allowed: bool, targets: &[Target], msrv: &Option, ) -> CargoResult { let contents = paths::read_bytes(path)?; BuildOutput::parse( &contents, library_name, pkg_descr, script_out_dir_when_generated, script_out_dir, nightly_features_allowed, targets, msrv, ) } /// Parses the output instructions of a build script. /// /// * `pkg_descr` --- for error messages /// * `library_name` --- for determining if `RUSTC_BOOTSTRAP` should be allowed pub fn parse( input: &[u8], // Takes String instead of InternedString so passing `unit.pkg.name()` will give a compile error. library_name: Option, pkg_descr: &str, script_out_dir_when_generated: &Path, script_out_dir: &Path, nightly_features_allowed: bool, targets: &[Target], msrv: &Option, ) -> CargoResult { let mut library_paths = Vec::new(); let mut library_links = Vec::new(); let mut linker_args = Vec::new(); let mut cfgs = Vec::new(); let mut check_cfgs = Vec::new(); let mut env = Vec::new(); let mut metadata = Vec::new(); let mut rerun_if_changed = Vec::new(); let mut rerun_if_env_changed = Vec::new(); let mut log_messages = Vec::new(); let whence = format!("build script of `{}`", pkg_descr); // Old syntax: // cargo:rustc-flags=VALUE // cargo:KEY=VALUE (for other unreserved keys) // New syntax: // cargo::rustc-flags=VALUE // cargo::metadata=KEY=VALUE (for other unreserved keys) // Due to backwards compatibility, no new keys can be added to this old format. const RESERVED_PREFIXES: &[&str] = &[ "rustc-flags=", "rustc-link-lib=", "rustc-link-search=", "rustc-link-arg-cdylib=", "rustc-cdylib-link-arg=", "rustc-link-arg-bins=", "rustc-link-arg-bin=", "rustc-link-arg-tests=", "rustc-link-arg-benches=", "rustc-link-arg-examples=", "rustc-link-arg=", "rustc-cfg=", "rustc-check-cfg=", "rustc-env=", "warning=", "rerun-if-changed=", "rerun-if-env-changed=", ]; const DOCS_LINK_SUGGESTION: &str = "See https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script \ for more information about build script outputs."; fn has_reserved_prefix(flag: &str) -> bool { RESERVED_PREFIXES .iter() .any(|reserved_prefix| flag.starts_with(reserved_prefix)) } fn check_minimum_supported_rust_version_for_new_syntax( pkg_descr: &str, msrv: &Option, flag: &str, ) -> CargoResult<()> { if let Some(msrv) = msrv { let new_syntax_added_in = RustVersion::from_str("1.77.0")?; if !new_syntax_added_in.is_compatible_with(msrv.as_partial()) { let old_syntax_suggestion = if has_reserved_prefix(flag) { format!( "Switch to the old `cargo:{flag}` syntax (note the single colon).\n" ) } else if flag.starts_with("metadata=") { let old_format_flag = flag.strip_prefix("metadata=").unwrap(); format!( "Switch to the old `cargo:{old_format_flag}` syntax instead of `cargo::{flag}` (note the single colon).\n" ) } else { String::new() }; bail!( "the `cargo::` syntax for build script output instructions was added in \ Rust 1.77.0, but the minimum supported Rust version of `{pkg_descr}` is {msrv}.\n\ {old_syntax_suggestion}\ {DOCS_LINK_SUGGESTION}" ); } } Ok(()) } fn parse_directive<'a>( whence: &str, line: &str, data: &'a str, old_syntax: bool, ) -> CargoResult<(&'a str, &'a str)> { let mut iter = data.splitn(2, "="); let key = iter.next(); let value = iter.next(); match (key, value) { (Some(a), Some(b)) => Ok((a, b.trim_end())), _ => bail!( "invalid output in {whence}: `{line}`\n\ Expected a line with `{syntax}KEY=VALUE` with an `=` character, \ but none was found.\n\ {DOCS_LINK_SUGGESTION}", syntax = if old_syntax { "cargo:" } else { "cargo::" }, ), } } fn parse_metadata<'a>( whence: &str, line: &str, data: &'a str, old_syntax: bool, ) -> CargoResult<(&'a str, &'a str)> { let mut iter = data.splitn(2, "="); let key = iter.next(); let value = iter.next(); match (key, value) { (Some(a), Some(b)) => Ok((a, b.trim_end())), _ => bail!( "invalid output in {whence}: `{line}`\n\ Expected a line with `{syntax}KEY=VALUE` with an `=` character, \ but none was found.\n\ {DOCS_LINK_SUGGESTION}", syntax = if old_syntax { "cargo:" } else { "cargo::metadata=" }, ), } } for line in input.split(|b| *b == b'\n') { let line = match str::from_utf8(line) { Ok(line) => line.trim(), Err(..) => continue, }; let mut old_syntax = false; let (key, value) = if let Some(data) = line.strip_prefix("cargo::") { check_minimum_supported_rust_version_for_new_syntax(pkg_descr, msrv, data)?; // For instance, `cargo::rustc-flags=foo` or `cargo::metadata=foo=bar`. parse_directive(whence.as_str(), line, data, old_syntax)? } else if let Some(data) = line.strip_prefix("cargo:") { old_syntax = true; // For instance, `cargo:rustc-flags=foo`. if has_reserved_prefix(data) { parse_directive(whence.as_str(), line, data, old_syntax)? } else { // For instance, `cargo:foo=bar`. ("metadata", data) } } else { // Skip this line since it doesn't start with "cargo:" or "cargo::". continue; }; // This will rewrite paths if the target directory has been moved. let value = value.replace( script_out_dir_when_generated.to_str().unwrap(), script_out_dir.to_str().unwrap(), ); let syntax_prefix = if old_syntax { "cargo:" } else { "cargo::" }; macro_rules! check_and_add_target { ($target_kind: expr, $is_target_kind: expr, $link_type: expr) => { if !targets.iter().any(|target| $is_target_kind(target)) { bail!( "invalid instruction `{}{}` from {}\n\ The package {} does not have a {} target.", syntax_prefix, key, whence, pkg_descr, $target_kind ); } linker_args.push(($link_type, value)); }; } // Keep in sync with TargetConfig::parse_links_overrides. match key { "rustc-flags" => { let (paths, links) = BuildOutput::parse_rustc_flags(&value, &whence)?; library_links.extend(links.into_iter()); library_paths.extend( paths .into_iter() .map(|p| LibraryPath::new(p, script_out_dir)), ); } "rustc-link-lib" => library_links.push(value.to_string()), "rustc-link-search" => { library_paths.push(LibraryPath::new(PathBuf::from(value), script_out_dir)) } "rustc-link-arg-cdylib" | "rustc-cdylib-link-arg" => { if !targets.iter().any(|target| target.is_cdylib()) { log_messages.push(( Severity::Warning, format!( "{}{} was specified in the build script of {}, \ but that package does not contain a cdylib target\n\ \n\ Allowing this was an unintended change in the 1.50 \ release, and may become an error in the future. \ For more information, see \ .", syntax_prefix, key, pkg_descr ), )); } linker_args.push((LinkArgTarget::Cdylib, value)) } "rustc-link-arg-bins" => { check_and_add_target!("bin", Target::is_bin, LinkArgTarget::Bin); } "rustc-link-arg-bin" => { let (bin_name, arg) = value.split_once('=').ok_or_else(|| { anyhow::format_err!( "invalid instruction `{}{}={}` from {}\n\ The instruction should have the form {}{}=BIN=ARG", syntax_prefix, key, value, whence, syntax_prefix, key ) })?; if !targets .iter() .any(|target| target.is_bin() && target.name() == bin_name) { bail!( "invalid instruction `{}{}` from {}\n\ The package {} does not have a bin target with the name `{}`.", syntax_prefix, key, whence, pkg_descr, bin_name ); } linker_args.push(( LinkArgTarget::SingleBin(bin_name.to_owned()), arg.to_string(), )); } "rustc-link-arg-tests" => { check_and_add_target!("test", Target::is_test, LinkArgTarget::Test); } "rustc-link-arg-benches" => { check_and_add_target!("benchmark", Target::is_bench, LinkArgTarget::Bench); } "rustc-link-arg-examples" => { check_and_add_target!("example", Target::is_example, LinkArgTarget::Example); } "rustc-link-arg" => { linker_args.push((LinkArgTarget::All, value)); } "rustc-cfg" => cfgs.push(value.to_string()), "rustc-check-cfg" => check_cfgs.push(value.to_string()), "rustc-env" => { let (key, val) = BuildOutput::parse_rustc_env(&value, &whence)?; // Build scripts aren't allowed to set RUSTC_BOOTSTRAP. // See https://github.com/rust-lang/cargo/issues/7088. if key == "RUSTC_BOOTSTRAP" { // If RUSTC_BOOTSTRAP is already set, the user of Cargo knows about // bootstrap and still wants to override the channel. Give them a way to do // so, but still emit a warning that the current crate shouldn't be trying // to set RUSTC_BOOTSTRAP. // If this is a nightly build, setting RUSTC_BOOTSTRAP wouldn't affect the // behavior, so still only give a warning. // NOTE: cargo only allows nightly features on RUSTC_BOOTSTRAP=1, but we // want setting any value of RUSTC_BOOTSTRAP to downgrade this to a warning // (so that `RUSTC_BOOTSTRAP=library_name` will work) let rustc_bootstrap_allows = |name: Option<&str>| { let name = match name { // as of 2021, no binaries on crates.io use RUSTC_BOOTSTRAP, so // fine-grained opt-outs aren't needed. end-users can always use // RUSTC_BOOTSTRAP=1 from the top-level if it's really a problem. None => return false, Some(n) => n, }; // ALLOWED: the process of rustc bootstrapping reads this through // `std::env`. We should make the behavior consistent. Also, we // don't advertise this for bypassing nightly. #[allow(clippy::disallowed_methods)] std::env::var("RUSTC_BOOTSTRAP") .map_or(false, |var| var.split(',').any(|s| s == name)) }; if nightly_features_allowed || rustc_bootstrap_allows(library_name.as_deref()) { log_messages.push((Severity::Warning, format!("Cannot set `RUSTC_BOOTSTRAP={}` from {}.\n\ note: Crates cannot set `RUSTC_BOOTSTRAP` themselves, as doing so would subvert the stability guarantees of Rust for your project.", val, whence ))); } else { // Setting RUSTC_BOOTSTRAP would change the behavior of the crate. // Abort with an error. bail!( "Cannot set `RUSTC_BOOTSTRAP={}` from {}.\n\ note: Crates cannot set `RUSTC_BOOTSTRAP` themselves, as doing so would subvert the stability guarantees of Rust for your project.\n\ help: If you're sure you want to do this in your project, set the environment variable `RUSTC_BOOTSTRAP={}` before running cargo instead.", val, whence, library_name.as_deref().unwrap_or("1"), ); } } else { env.push((key, val)); } } "error" => log_messages.push((Severity::Error, value.to_string())), "warning" => log_messages.push((Severity::Warning, value.to_string())), "rerun-if-changed" => rerun_if_changed.push(PathBuf::from(value)), "rerun-if-env-changed" => rerun_if_env_changed.push(value.to_string()), "metadata" => { let (key, value) = parse_metadata(whence.as_str(), line, &value, old_syntax)?; metadata.push((key.to_owned(), value.to_owned())); } _ => bail!( "invalid output in {whence}: `{line}`\n\ Unknown key: `{key}`.\n\ {DOCS_LINK_SUGGESTION}", ), } } Ok(BuildOutput { library_paths, library_links, linker_args, cfgs, check_cfgs, env, metadata, rerun_if_changed, rerun_if_env_changed, log_messages, }) } /// Parses [`cargo::rustc-flags`] instruction. /// /// [`cargo::rustc-flags`]: https://doc.rust-lang.org/nightly/cargo/reference/build-scripts.html#cargorustc-flagsflags pub fn parse_rustc_flags( value: &str, whence: &str, ) -> CargoResult<(Vec, Vec)> { let value = value.trim(); let mut flags_iter = value .split(|c: char| c.is_whitespace()) .filter(|w| w.chars().any(|c| !c.is_whitespace())); let (mut library_paths, mut library_links) = (Vec::new(), Vec::new()); while let Some(flag) = flags_iter.next() { if flag.starts_with("-l") || flag.starts_with("-L") { // Check if this flag has no space before the value as is // common with tools like pkg-config // e.g. -L/some/dir/local/lib or -licui18n let (flag, mut value) = flag.split_at(2); if value.is_empty() { value = match flags_iter.next() { Some(v) => v, None => bail! { "Flag in rustc-flags has no value in {}: {}", whence, value }, } } match flag { "-l" => library_links.push(value.to_string()), "-L" => library_paths.push(PathBuf::from(value)), // This was already checked above _ => unreachable!(), }; } else { bail!( "Only `-l` and `-L` flags are allowed in {}: `{}`", whence, value ) } } Ok((library_paths, library_links)) } /// Parses [`cargo::rustc-env`] instruction. /// /// [`cargo::rustc-env`]: https://doc.rust-lang.org/nightly/cargo/reference/build-scripts.html#rustc-env pub fn parse_rustc_env(value: &str, whence: &str) -> CargoResult<(String, String)> { match value.split_once('=') { Some((n, v)) => Ok((n.to_owned(), v.to_owned())), _ => bail!("Variable rustc-env has no value in {whence}: {value}"), } } } /// Prepares the Rust script for the unstable feature [metabuild]. /// /// [metabuild]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#metabuild fn prepare_metabuild( build_runner: &BuildRunner<'_, '_>, unit: &Unit, deps: &[String], ) -> CargoResult<()> { let mut output = Vec::new(); let available_deps = build_runner.unit_deps(unit); // Filter out optional dependencies, and look up the actual lib name. let meta_deps: Vec<_> = deps .iter() .filter_map(|name| { available_deps .iter() .find(|d| d.unit.pkg.name().as_str() == name.as_str()) .map(|d| d.unit.target.crate_name()) }) .collect(); output.push("fn main() {\n".to_string()); for dep in &meta_deps { output.push(format!(" {}::metabuild();\n", dep)); } output.push("}\n".to_string()); let output = output.join(""); let path = unit .pkg .manifest() .metabuild_path(build_runner.bcx.ws.build_dir()); paths::create_dir_all(path.parent().unwrap())?; paths::write_if_changed(path, &output)?; Ok(()) } impl BuildDeps { /// Creates a build script dependency information from a previous /// build script output path and the content. pub fn new(output_file: &Path, output: Option<&BuildOutput>) -> BuildDeps { BuildDeps { build_script_output: output_file.to_path_buf(), rerun_if_changed: output .map(|p| &p.rerun_if_changed) .cloned() .unwrap_or_default(), rerun_if_env_changed: output .map(|p| &p.rerun_if_env_changed) .cloned() .unwrap_or_default(), } } } /// Computes several maps in [`BuildRunner`]. /// /// - [`build_scripts`]: A map that tracks which build scripts each package /// depends on. /// - [`build_explicit_deps`]: Dependency statements emitted by build scripts /// from a previous run. /// - [`build_script_outputs`]: Pre-populates this with any overridden build /// scripts. /// /// The important one here is [`build_scripts`], which for each `(package, /// metadata)` stores a [`BuildScripts`] object which contains a list of /// dependencies with build scripts that the unit should consider when linking. /// For example this lists all dependencies' `-L` flags which need to be /// propagated transitively. /// /// The given set of units to this function is the initial set of /// targets/profiles which are being built. /// /// [`build_scripts`]: BuildRunner::build_scripts /// [`build_explicit_deps`]: BuildRunner::build_explicit_deps /// [`build_script_outputs`]: BuildRunner::build_script_outputs pub fn build_map(build_runner: &mut BuildRunner<'_, '_>) -> CargoResult<()> { let mut ret = HashMap::new(); for unit in &build_runner.bcx.roots { build(&mut ret, build_runner, unit)?; } build_runner .build_scripts .extend(ret.into_iter().map(|(k, v)| (k, Arc::new(v)))); return Ok(()); // Recursive function to build up the map we're constructing. This function // memoizes all of its return values as it goes along. fn build<'a>( out: &'a mut HashMap, build_runner: &mut BuildRunner<'_, '_>, unit: &Unit, ) -> CargoResult<&'a BuildScripts> { // Do a quick pre-flight check to see if we've already calculated the // set of dependencies. if out.contains_key(unit) { return Ok(&out[unit]); } // If there is a build script override, pre-fill the build output. if unit.mode.is_run_custom_build() { if let Some(links) = unit.pkg.manifest().links() { if let Some(output) = unit.links_overrides.get(links) { let metadata = build_runner.get_run_build_script_metadata(unit); build_runner.build_script_outputs.lock().unwrap().insert( unit.pkg.package_id(), metadata, output.clone(), ); } } } let mut ret = BuildScripts::default(); // If a package has a build script, add itself as something to inspect for linking. if !unit.target.is_custom_build() && unit.pkg.has_custom_build() { let script_metas = build_runner .find_build_script_metadatas(unit) .expect("has_custom_build should have RunCustomBuild"); for script_meta in script_metas { add_to_link(&mut ret, unit.pkg.package_id(), script_meta); } } if unit.mode.is_run_custom_build() { parse_previous_explicit_deps(build_runner, unit); } // We want to invoke the compiler deterministically to be cache-friendly // to rustc invocation caching schemes, so be sure to generate the same // set of build script dependency orderings via sorting the targets that // come out of the `Context`. let mut dependencies: Vec = build_runner .unit_deps(unit) .iter() .map(|d| d.unit.clone()) .collect(); dependencies.sort_by_key(|u| u.pkg.package_id()); for dep_unit in dependencies.iter() { let dep_scripts = build(out, build_runner, dep_unit)?; if dep_unit.target.for_host() { ret.plugins.extend(dep_scripts.to_link.iter().cloned()); } else if dep_unit.target.is_linkable() { for &(pkg, metadata) in dep_scripts.to_link.iter() { add_to_link(&mut ret, pkg, metadata); } } } match out.entry(unit.clone()) { Entry::Vacant(entry) => Ok(entry.insert(ret)), Entry::Occupied(_) => panic!("cyclic dependencies in `build_map`"), } } // When adding an entry to 'to_link' we only actually push it on if the // script hasn't seen it yet (e.g., we don't push on duplicates). fn add_to_link(scripts: &mut BuildScripts, pkg: PackageId, metadata: UnitHash) { if scripts.seen_to_link.insert((pkg, metadata)) { scripts.to_link.push((pkg, metadata)); } } /// Load any dependency declarations from a previous build script run. fn parse_previous_explicit_deps(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) { let script_run_dir = build_runner.files().build_script_run_dir(unit); let output_file = script_run_dir.join("output"); let (prev_output, _) = prev_build_output(build_runner, unit); let deps = BuildDeps::new(&output_file, prev_output.as_ref()); build_runner.build_explicit_deps.insert(unit.clone(), deps); } } /// Returns the previous parsed `BuildOutput`, if any, from a previous /// execution. /// /// Also returns the directory containing the output, typically used later in /// processing. fn prev_build_output( build_runner: &mut BuildRunner<'_, '_>, unit: &Unit, ) -> (Option, PathBuf) { let script_out_dir = build_runner.files().build_script_out_dir(unit); let script_run_dir = build_runner.files().build_script_run_dir(unit); let root_output_file = script_run_dir.join("root-output"); let output_file = script_run_dir.join("output"); let prev_script_out_dir = paths::read_bytes(&root_output_file) .and_then(|bytes| paths::bytes2path(&bytes)) .unwrap_or_else(|_| script_out_dir.clone()); ( BuildOutput::parse_file( &output_file, unit.pkg.library().map(|t| t.crate_name()), &unit.pkg.to_string(), &prev_script_out_dir, &script_out_dir, build_runner.bcx.gctx.nightly_features_allowed, unit.pkg.targets(), &unit.pkg.rust_version().cloned(), ) .ok(), prev_script_out_dir, ) } impl BuildScriptOutputs { /// Inserts a new entry into the map. fn insert(&mut self, pkg_id: PackageId, metadata: UnitHash, parsed_output: BuildOutput) { match self.outputs.entry(metadata) { Entry::Vacant(entry) => { entry.insert(parsed_output); } Entry::Occupied(entry) => panic!( "build script output collision for {}/{}\n\ old={:?}\nnew={:?}", pkg_id, metadata, entry.get(), parsed_output ), } } /// Returns `true` if the given key already exists. fn contains_key(&self, metadata: UnitHash) -> bool { self.outputs.contains_key(&metadata) } /// Gets the build output for the given key. pub fn get(&self, meta: UnitHash) -> Option<&BuildOutput> { self.outputs.get(&meta) } /// Returns an iterator over all entries. pub fn iter(&self) -> impl Iterator { self.outputs.iter() } } cargo-0.91.0/src/cargo/core/compiler/fingerprint/dep_info.rs000064400000000000000000000652251046102023000221240ustar 00000000000000//! Types and functions managing dep-info files. //! For more, see [the documentation] in the `fingerprint` module. //! //! [the documentation]: crate::core::compiler::fingerprint#dep-info-files use std::collections::HashMap; use std::ffi::OsString; use std::fmt; use std::io; use std::io::Read; use std::path::Path; use std::path::PathBuf; use std::str; use std::str::FromStr; use std::sync::Arc; use anyhow::bail; use cargo_util::ProcessBuilder; use cargo_util::Sha256; use cargo_util::paths; use crate::CARGO_ENV; use crate::CargoResult; use crate::core::manifest::ManifestMetadata; /// The current format version of [`EncodedDepInfo`]. const CURRENT_ENCODED_DEP_INFO_VERSION: u8 = 1; /// The representation of the `.d` dep-info file generated by rustc #[derive(Default)] pub struct RustcDepInfo { /// The list of files that the main target in the dep-info file depends on. /// /// The optional checksums are parsed from the special `# checksum:...` comments. pub files: HashMap>, /// The list of environment variables we found that the rustc compilation /// depends on. /// /// The first element of the pair is the name of the env var and the second /// item is the value. `Some` means that the env var was set, and `None` /// means that the env var wasn't actually set and the compilation depends /// on it not being set. /// /// These are from the special `# env-var:...` comments. pub env: Vec<(String, Option)>, } /// Tells the associated path in [`EncodedDepInfo::files`] is relative to package root, /// target root, or absolute. #[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)] pub enum DepInfoPathType { /// src/, e.g. src/lib.rs PackageRootRelative, /// {build-dir}/debug/deps/lib... /// or an absolute path /.../sysroot/... BuildRootRelative, } /// Same as [`RustcDepInfo`] except avoids absolute paths as much as possible to /// allow moving around the target directory. /// /// This is also stored in an optimized format to make parsing it fast because /// Cargo will read it for crates on all future compilations. /// /// Currently the format looks like: /// /// ```text /// +--------+---------+------------+------------+---------------+---------------+ /// | marker | version | # of files | file paths | # of env vars | env var pairs | /// +--------+---------+------------+------------+---------------+---------------+ /// ``` /// /// Each field represents /// /// * _Marker_ --- A magic marker to ensure that older Cargoes, which only /// recognize format v0 (prior to checksum support in [`f4ca7390`]), do not /// proceed with parsing newer formats. Since [`EncodedDepInfo`] is merely /// an optimization, and to avoid adding complexity, Cargo recognizes only /// one version of [`CURRENT_ENCODED_DEP_INFO_VERSION`]. /// The current layout looks like this /// ```text /// +----------------------------+ /// | [0x01 0x00 0x00 0x00 0xff] | /// +----------------------------+ /// ``` /// These bytes will be interpreted as "one file tracked and an invalid /// [`DepInfoPathType`] variant with 255" by older Cargoes, causing them to /// stop parsing. This could prevent problematic parsing as noted in /// rust-lang/cargo#14712. /// * _Version_ --- The current format version. /// * _Number of files/envs_ --- A `u32` representing the number of things. /// * _File paths_ --- Zero or more paths of files the dep-info file depends on. /// Each path is encoded as the following: /// /// ```text /// +-----------+-------------+------------+---------------+-----------+-------+ /// | path type | len of path | path bytes | cksum exists? | file size | cksum | /// +-----------+-------------+------------+---------------+-----------+-------+ /// ``` /// * _Env var pairs_ --- Zero or more env vars the dep-info file depends on. /// Each env key-value pair is encoded as the following: /// ```text /// +------------+-----------+---------------+--------------+-------------+ /// | len of key | key bytes | value exists? | len of value | value bytes | /// +------------+-----------+---------------+--------------+-------------+ /// ``` /// /// [`f4ca7390`]: https://github.com/rust-lang/cargo/commit/f4ca739073185ea5e1148ff100bb4a06d3bf721d #[derive(Default, Debug, PartialEq, Eq)] pub struct EncodedDepInfo { pub files: Vec<(DepInfoPathType, PathBuf, Option<(u64, String)>)>, pub env: Vec<(String, Option)>, } impl EncodedDepInfo { pub fn parse(mut bytes: &[u8]) -> Option { let bytes = &mut bytes; read_magic_marker(bytes)?; let version = read_u8(bytes)?; if version != CURRENT_ENCODED_DEP_INFO_VERSION { return None; } let nfiles = read_usize(bytes)?; let mut files = Vec::with_capacity(nfiles); for _ in 0..nfiles { let ty = match read_u8(bytes)? { 0 => DepInfoPathType::PackageRootRelative, 1 => DepInfoPathType::BuildRootRelative, _ => return None, }; let path_bytes = read_bytes(bytes)?; let path = paths::bytes2path(path_bytes).ok()?; let has_checksum = read_bool(bytes)?; let checksum_info = has_checksum .then(|| { let file_len = read_u64(bytes); let checksum_string = read_bytes(bytes) .map(Vec::from) .and_then(|v| String::from_utf8(v).ok()); file_len.zip(checksum_string) }) .flatten(); files.push((ty, path, checksum_info)); } let nenv = read_usize(bytes)?; let mut env = Vec::with_capacity(nenv); for _ in 0..nenv { let key = str::from_utf8(read_bytes(bytes)?).ok()?.to_string(); let val = match read_u8(bytes)? { 0 => None, 1 => Some(str::from_utf8(read_bytes(bytes)?).ok()?.to_string()), _ => return None, }; env.push((key, val)); } return Some(EncodedDepInfo { files, env }); /// See [`EncodedDepInfo`] for why a magic marker exists. fn read_magic_marker(bytes: &mut &[u8]) -> Option<()> { let _size = read_usize(bytes)?; let path_type = read_u8(bytes)?; if path_type != u8::MAX { // Old depinfo. Give up parsing it. None } else { Some(()) } } fn read_usize(bytes: &mut &[u8]) -> Option { let ret = bytes.get(..4)?; *bytes = &bytes[4..]; Some(u32::from_le_bytes(ret.try_into().unwrap()) as usize) } fn read_u64(bytes: &mut &[u8]) -> Option { let ret = bytes.get(..8)?; *bytes = &bytes[8..]; Some(u64::from_le_bytes(ret.try_into().unwrap())) } fn read_bool(bytes: &mut &[u8]) -> Option { read_u8(bytes).map(|b| b != 0) } fn read_u8(bytes: &mut &[u8]) -> Option { let ret = *bytes.get(0)?; *bytes = &bytes[1..]; Some(ret) } fn read_bytes<'a>(bytes: &mut &'a [u8]) -> Option<&'a [u8]> { let n = read_usize(bytes)? as usize; let ret = bytes.get(..n)?; *bytes = &bytes[n..]; Some(ret) } } pub fn serialize(&self) -> CargoResult> { let mut ret = Vec::new(); let dst = &mut ret; write_magic_marker(dst); dst.push(CURRENT_ENCODED_DEP_INFO_VERSION); write_usize(dst, self.files.len()); for (ty, file, checksum_info) in self.files.iter() { match ty { DepInfoPathType::PackageRootRelative => dst.push(0), DepInfoPathType::BuildRootRelative => dst.push(1), } write_bytes(dst, paths::path2bytes(file)?); write_bool(dst, checksum_info.is_some()); if let Some((len, checksum)) = checksum_info { write_u64(dst, *len); write_bytes(dst, checksum); } } write_usize(dst, self.env.len()); for (key, val) in self.env.iter() { write_bytes(dst, key); match val { None => dst.push(0), Some(val) => { dst.push(1); write_bytes(dst, val); } } } return Ok(ret); /// See [`EncodedDepInfo`] for why a magic marker exists. /// /// There is an assumption that there is always at least a file. fn write_magic_marker(dst: &mut Vec) { write_usize(dst, 1); dst.push(u8::MAX); } fn write_bytes(dst: &mut Vec, val: impl AsRef<[u8]>) { let val = val.as_ref(); write_usize(dst, val.len()); dst.extend_from_slice(val); } fn write_usize(dst: &mut Vec, val: usize) { dst.extend(&u32::to_le_bytes(val as u32)); } fn write_u64(dst: &mut Vec, val: u64) { dst.extend(&u64::to_le_bytes(val)); } fn write_bool(dst: &mut Vec, val: bool) { dst.push(u8::from(val)); } } } /// Parses the dep-info file coming out of rustc into a Cargo-specific format. /// /// This function will parse `rustc_dep_info` as a makefile-style dep info to /// learn about the all files which a crate depends on. This is then /// re-serialized into the `cargo_dep_info` path in a Cargo-specific format. /// /// The `pkg_root` argument here is the absolute path to the directory /// containing `Cargo.toml` for this crate that was compiled. The paths listed /// in the rustc dep-info file may or may not be absolute but we'll want to /// consider all of them relative to the `root` specified. /// /// The `rustc_cwd` argument is the absolute path to the cwd of the compiler /// when it was invoked. /// /// If the `allow_package` argument is true, then package-relative paths are /// included. If it is false, then package-relative paths are skipped and /// ignored (typically used for registry or git dependencies where we assume /// the source never changes, and we don't want the cost of running `stat` on /// all those files). See the module-level docs for the note about /// `-Zbinary-dep-depinfo` for more details on why this is done. /// /// The serialized Cargo format will contain a list of files, all of which are /// relative if they're under `root`. or absolute if they're elsewhere. /// /// The `env_config` argument is a set of environment variables that are /// defined in `[env]` table of the `config.toml`. pub fn translate_dep_info( rustc_dep_info: &Path, cargo_dep_info: &Path, rustc_cwd: &Path, pkg_root: &Path, build_root: &Path, rustc_cmd: &ProcessBuilder, allow_package: bool, env_config: &Arc>, ) -> CargoResult<()> { let depinfo = parse_rustc_dep_info(rustc_dep_info)?; let build_root = crate::util::try_canonicalize(build_root)?; let pkg_root = crate::util::try_canonicalize(pkg_root)?; let mut on_disk_info = EncodedDepInfo::default(); on_disk_info.env = depinfo.env; // This is a bit of a tricky statement, but here we're *removing* the // dependency on environment variables that were defined specifically for // the command itself. Environment variables returned by `get_envs` includes // environment variables like: // // * `OUT_DIR` if applicable // * env vars added by a build script, if any // // The general idea here is that the dep info file tells us what, when // changed, should cause us to rebuild the crate. These environment // variables are synthesized by Cargo and/or the build script, and the // intention is that their values are tracked elsewhere for whether the // crate needs to be rebuilt. // // For example a build script says when it needs to be rerun and otherwise // it's assumed to produce the same output, so we're guaranteed that env // vars defined by the build script will always be the same unless the build // script itself reruns, in which case the crate will rerun anyway. // // For things like `OUT_DIR` it's a bit sketchy for now. Most of the time // that's used for code generation but this is technically buggy where if // you write a binary that does `println!("{}", env!("OUT_DIR"))` we won't // recompile that if you move the target directory. Hopefully that's not too // bad of an issue for now... // // This also includes `CARGO` since if the code is explicitly wanting to // know that path, it should be rebuilt if it changes. The CARGO path is // not tracked elsewhere in the fingerprint. // // For cargo#13280, We trace env vars that are defined in the `[env]` config table. on_disk_info.env.retain(|(key, _)| { ManifestMetadata::should_track(key) || env_config.contains_key(key) || !rustc_cmd.get_envs().contains_key(key) || key == CARGO_ENV }); let serialize_path = |file| { // The path may be absolute or relative, canonical or not. Make sure // it is canonicalized so we are comparing the same kinds of paths. let abs_file = rustc_cwd.join(file); // If canonicalization fails, just use the abs path. There is currently // a bug where --remap-path-prefix is affecting .d files, causing them // to point to non-existent paths. let canon_file = crate::util::try_canonicalize(&abs_file).unwrap_or_else(|_| abs_file.clone()); let (ty, path) = if let Ok(stripped) = canon_file.strip_prefix(&build_root) { (DepInfoPathType::BuildRootRelative, stripped) } else if let Ok(stripped) = canon_file.strip_prefix(&pkg_root) { if !allow_package { return None; } (DepInfoPathType::PackageRootRelative, stripped) } else { // It's definitely not target root relative, but this is an absolute path (since it was // joined to rustc_cwd) and as such re-joining it later to the target root will have no // effect. (DepInfoPathType::BuildRootRelative, &*abs_file) }; Some((ty, path.to_owned())) }; for (file, checksum_info) in depinfo.files { let Some((path_type, path)) = serialize_path(file) else { continue; }; on_disk_info.files.push(( path_type, path, checksum_info.map(|(len, checksum)| (len, checksum.to_string())), )); } paths::write(cargo_dep_info, on_disk_info.serialize()?)?; Ok(()) } /// Parse the `.d` dep-info file generated by rustc. pub fn parse_rustc_dep_info(rustc_dep_info: &Path) -> CargoResult { let contents = paths::read(rustc_dep_info)?; let mut ret = RustcDepInfo::default(); let mut found_deps = false; for line in contents.lines() { if let Some(rest) = line.strip_prefix("# env-dep:") { let mut parts = rest.splitn(2, '='); let Some(env_var) = parts.next() else { continue; }; let env_val = match parts.next() { Some(s) => Some(unescape_env(s)?), None => None, }; ret.env.push((unescape_env(env_var)?, env_val)); } else if let Some(pos) = line.find(": ") { if found_deps { continue; } found_deps = true; let mut deps = line[pos + 2..].split_whitespace(); while let Some(s) = deps.next() { let mut file = s.to_string(); while file.ends_with('\\') { file.pop(); file.push(' '); file.push_str(deps.next().ok_or_else(|| { crate::util::internal("malformed dep-info format, trailing \\") })?); } ret.files.entry(file.into()).or_default(); } } else if let Some(rest) = line.strip_prefix("# checksum:") { let mut parts = rest.splitn(3, ' '); let Some(checksum) = parts.next().map(Checksum::from_str).transpose()? else { continue; }; let Some(Ok(file_len)) = parts .next() .and_then(|s| s.strip_prefix("file_len:").map(|s| s.parse::())) else { continue; }; let Some(path) = parts.next().map(PathBuf::from) else { continue; }; ret.files.insert(path, Some((file_len, checksum))); } } return Ok(ret); // rustc tries to fit env var names and values all on a single line, which // means it needs to escape `\r` and `\n`. The escape syntax used is "\n" // which means that `\` also needs to be escaped. fn unescape_env(s: &str) -> CargoResult { let mut ret = String::with_capacity(s.len()); let mut chars = s.chars(); while let Some(c) = chars.next() { if c != '\\' { ret.push(c); continue; } match chars.next() { Some('\\') => ret.push('\\'), Some('n') => ret.push('\n'), Some('r') => ret.push('\r'), Some(c) => bail!("unknown escape character `{}`", c), None => bail!("unterminated escape character"), } } Ok(ret) } } /// Parses Cargo's internal [`EncodedDepInfo`] structure that was previously /// serialized to disk. /// /// Note that this is not rustc's `*.d` files. /// /// Also note that rustc's `*.d` files are translated to Cargo-specific /// `EncodedDepInfo` files after compilations have finished in /// [`translate_dep_info`]. /// /// Returns `None` if the file is corrupt or couldn't be read from disk. This /// indicates that the crate should likely be rebuilt. pub fn parse_dep_info( pkg_root: &Path, build_root: &Path, dep_info: &Path, ) -> CargoResult> { let Ok(data) = paths::read_bytes(dep_info) else { return Ok(None); }; let Some(info) = EncodedDepInfo::parse(&data) else { tracing::warn!("failed to parse cargo's dep-info at {:?}", dep_info); return Ok(None); }; let mut ret = RustcDepInfo::default(); ret.env = info.env; ret.files .extend(info.files.into_iter().map(|(ty, path, checksum_info)| { ( make_absolute_path(ty, pkg_root, build_root, path), checksum_info.and_then(|(file_len, checksum)| { Checksum::from_str(&checksum).ok().map(|c| (file_len, c)) }), ) })); Ok(Some(ret)) } fn make_absolute_path( ty: DepInfoPathType, pkg_root: &Path, build_root: &Path, path: PathBuf, ) -> PathBuf { match ty { DepInfoPathType::PackageRootRelative => pkg_root.join(path), // N.B. path might be absolute here in which case the join will have no effect DepInfoPathType::BuildRootRelative => build_root.join(path), } } /// Some algorithms are here to ensure compatibility with possible rustc outputs. /// The presence of an algorithm here is not a suggestion that it's fit for use. #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum ChecksumAlgo { Sha256, Blake3, } impl ChecksumAlgo { fn hash_len(&self) -> usize { match self { ChecksumAlgo::Sha256 | ChecksumAlgo::Blake3 => 32, } } } impl FromStr for ChecksumAlgo { type Err = InvalidChecksum; fn from_str(s: &str) -> Result { match s { "sha256" => Ok(Self::Sha256), "blake3" => Ok(Self::Blake3), _ => Err(InvalidChecksum::InvalidChecksumAlgo), } } } impl fmt::Display for ChecksumAlgo { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(match self { ChecksumAlgo::Sha256 => "sha256", ChecksumAlgo::Blake3 => "blake3", }) } } #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct Checksum { algo: ChecksumAlgo, /// If the algorithm uses fewer than 32 bytes, then the remaining bytes will be zero. value: [u8; 32], } impl Checksum { pub fn new(algo: ChecksumAlgo, value: [u8; 32]) -> Self { Self { algo, value } } pub fn compute(algo: ChecksumAlgo, contents: impl Read) -> Result { // Buffer size is the recommended amount to fully leverage SIMD instructions on AVX-512 as per // blake3 documentation. let mut buf = vec![0; 16 * 1024]; let mut ret = Self { algo, value: [0; 32], }; let len = algo.hash_len(); let value = &mut ret.value[..len]; fn digest( mut hasher: T, mut update: impl FnMut(&mut T, &[u8]), finish: impl FnOnce(T, &mut [u8]), mut contents: impl Read, buf: &mut [u8], value: &mut [u8], ) -> Result<(), io::Error> { loop { let bytes_read = contents.read(buf)?; if bytes_read == 0 { break; } update(&mut hasher, &buf[0..bytes_read]); } finish(hasher, value); Ok(()) } match algo { ChecksumAlgo::Sha256 => { digest( Sha256::new(), |h, b| { h.update(b); }, |mut h, out| out.copy_from_slice(&h.finish()), contents, &mut buf, value, )?; } ChecksumAlgo::Blake3 => { digest( blake3::Hasher::new(), |h, b| { h.update(b); }, |h, out| out.copy_from_slice(h.finalize().as_bytes()), contents, &mut buf, value, )?; } } Ok(ret) } pub fn algo(&self) -> ChecksumAlgo { self.algo } pub fn value(&self) -> &[u8; 32] { &self.value } } impl FromStr for Checksum { type Err = InvalidChecksum; fn from_str(s: &str) -> Result { let mut parts = s.split('='); let Some(algo) = parts.next().map(ChecksumAlgo::from_str).transpose()? else { return Err(InvalidChecksum::InvalidFormat); }; let Some(checksum) = parts.next() else { return Err(InvalidChecksum::InvalidFormat); }; let mut value = [0; 32]; if hex::decode_to_slice(checksum, &mut value[0..algo.hash_len()]).is_err() { return Err(InvalidChecksum::InvalidChecksum(algo)); } Ok(Self { algo, value }) } } impl fmt::Display for Checksum { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut checksum = [0; 64]; let hash_len = self.algo.hash_len(); hex::encode_to_slice(&self.value[0..hash_len], &mut checksum[0..(hash_len * 2)]) .map_err(|_| fmt::Error)?; write!( f, "{}={}", self.algo, str::from_utf8(&checksum[0..(hash_len * 2)]).unwrap_or_default() ) } } #[derive(Debug, thiserror::Error)] pub enum InvalidChecksum { #[error("algorithm portion incorrect, expected `sha256`, or `blake3`")] InvalidChecksumAlgo, #[error("expected {} hexadecimal digits in checksum portion", .0.hash_len() * 2)] InvalidChecksum(ChecksumAlgo), #[error("expected a string with format \"algorithm=hex_checksum\"")] InvalidFormat, } #[cfg(test)] mod encoded_dep_info { use super::*; #[track_caller] fn gen_test(checksum: bool) { let checksum = checksum.then_some((768, "c01efc669f09508b55eced32d3c88702578a7c3e".into())); let lib_rs = ( DepInfoPathType::BuildRootRelative, PathBuf::from("src/lib.rs"), checksum.clone(), ); let depinfo = EncodedDepInfo { files: vec![lib_rs.clone()], env: Vec::new(), }; let data = depinfo.serialize().unwrap(); assert_eq!(EncodedDepInfo::parse(&data).unwrap(), depinfo); let mod_rs = ( DepInfoPathType::BuildRootRelative, PathBuf::from("src/mod.rs"), checksum.clone(), ); let depinfo = EncodedDepInfo { files: vec![lib_rs.clone(), mod_rs.clone()], env: Vec::new(), }; let data = depinfo.serialize().unwrap(); assert_eq!(EncodedDepInfo::parse(&data).unwrap(), depinfo); let depinfo = EncodedDepInfo { files: vec![lib_rs, mod_rs], env: vec![ ("Gimli".into(), Some("Legolas".into())), ("Beren".into(), Some("Lúthien".into())), ], }; let data = depinfo.serialize().unwrap(); assert_eq!(EncodedDepInfo::parse(&data).unwrap(), depinfo); } #[test] fn round_trip() { gen_test(false); } #[test] fn round_trip_with_checksums() { gen_test(true); } #[test] fn path_type_is_u8_max() { #[rustfmt::skip] let data = [ 0x01, 0x00, 0x00, 0x00, 0xff, // magic marker CURRENT_ENCODED_DEP_INFO_VERSION, // version 0x01, 0x00, 0x00, 0x00, // # of files 0x00, // path type 0x04, 0x00, 0x00, 0x00, // len of path 0x72, 0x75, 0x73, 0x74, // path bytes ("rust") 0x00, // cksum exists? 0x00, 0x00, 0x00, 0x00, // # of env vars ]; // The current cargo doesn't recognize the magic marker. assert_eq!( EncodedDepInfo::parse(&data).unwrap(), EncodedDepInfo { files: vec![(DepInfoPathType::PackageRootRelative, "rust".into(), None)], env: Vec::new(), } ); } #[test] fn parse_v0_fingerprint_dep_info() { #[rustfmt::skip] let data = [ 0x01, 0x00, 0x00, 0x00, // # of files 0x00, // path type 0x04, 0x00, 0x00, 0x00, // len of path 0x72, 0x75, 0x73, 0x74, // path bytes: "rust" 0x00, 0x00, 0x00, 0x00, // # of env vars ]; // Cargo can't recognize v0 after `-Zchecksum-freshess` added. assert!(EncodedDepInfo::parse(&data).is_none()); } } cargo-0.91.0/src/cargo/core/compiler/fingerprint/dirty_reason.rs000064400000000000000000000274651046102023000230470ustar 00000000000000use std::fmt; use std::fmt::Debug; use super::*; use crate::core::Shell; /// Tells a better story of why a build is considered "dirty" that leads /// to a recompile. Usually constructed via [`Fingerprint::compare`]. /// /// [`Fingerprint::compare`]: super::Fingerprint::compare #[derive(Clone, Debug)] pub enum DirtyReason { RustcChanged, FeaturesChanged { old: String, new: String, }, DeclaredFeaturesChanged { old: String, new: String, }, TargetConfigurationChanged, PathToSourceChanged, ProfileConfigurationChanged, RustflagsChanged { old: Vec, new: Vec, }, ConfigSettingsChanged, CompileKindChanged, LocalLengthsChanged, PrecalculatedComponentsChanged { old: String, new: String, }, ChecksumUseChanged { old: bool, }, DepInfoOutputChanged { old: PathBuf, new: PathBuf, }, RerunIfChangedOutputFileChanged { old: PathBuf, new: PathBuf, }, RerunIfChangedOutputPathsChanged { old: Vec, new: Vec, }, EnvVarsChanged { old: String, new: String, }, EnvVarChanged { name: String, old_value: Option, new_value: Option, }, LocalFingerprintTypeChanged { old: &'static str, new: &'static str, }, NumberOfDependenciesChanged { old: usize, new: usize, }, UnitDependencyNameChanged { old: InternedString, new: InternedString, }, UnitDependencyInfoChanged { old_name: InternedString, old_fingerprint: u64, new_name: InternedString, new_fingerprint: u64, }, FsStatusOutdated(FsStatus), NothingObvious, Forced, /// First time to build something. FreshBuild, } trait ShellExt { fn dirty_because(&mut self, unit: &Unit, s: impl fmt::Display) -> CargoResult<()>; } impl ShellExt for Shell { fn dirty_because(&mut self, unit: &Unit, s: impl fmt::Display) -> CargoResult<()> { self.status("Dirty", format_args!("{}: {s}", &unit.pkg)) } } struct FileTimeDiff { old_time: FileTime, new_time: FileTime, } impl fmt::Display for FileTimeDiff { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let s_diff = self.new_time.seconds() - self.old_time.seconds(); if s_diff >= 1 { write!(f, "{:#}", jiff::SignedDuration::from_secs(s_diff)) } else { // format nanoseconds as it is, jiff would display ms, us and ns let ns_diff = self.new_time.nanoseconds() - self.old_time.nanoseconds(); write!(f, "{ns_diff}ns") } } } #[derive(Copy, Clone)] struct After { old_time: FileTime, new_time: FileTime, what: &'static str, } impl fmt::Display for After { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self { old_time, new_time, what, } = *self; let diff = FileTimeDiff { old_time, new_time }; write!(f, "{new_time}, {diff} after {what} at {old_time}") } } impl DirtyReason { /// Whether a build is dirty because it is a fresh build being kicked off. pub fn is_fresh_build(&self) -> bool { matches!(self, DirtyReason::FreshBuild) } fn after(old_time: FileTime, new_time: FileTime, what: &'static str) -> After { After { old_time, new_time, what, } } pub fn present_to(&self, s: &mut Shell, unit: &Unit, root: &Path) -> CargoResult<()> { match self { DirtyReason::RustcChanged => s.dirty_because(unit, "the toolchain changed"), DirtyReason::FeaturesChanged { .. } => { s.dirty_because(unit, "the list of features changed") } DirtyReason::DeclaredFeaturesChanged { .. } => { s.dirty_because(unit, "the list of declared features changed") } DirtyReason::TargetConfigurationChanged => { s.dirty_because(unit, "the target configuration changed") } DirtyReason::PathToSourceChanged => { s.dirty_because(unit, "the path to the source changed") } DirtyReason::ProfileConfigurationChanged => { s.dirty_because(unit, "the profile configuration changed") } DirtyReason::RustflagsChanged { .. } => s.dirty_because(unit, "the rustflags changed"), DirtyReason::ConfigSettingsChanged => { s.dirty_because(unit, "the config settings changed") } DirtyReason::CompileKindChanged => { s.dirty_because(unit, "the rustc compile kind changed") } DirtyReason::LocalLengthsChanged => { s.dirty_because(unit, "the local lengths changed")?; s.note( "this could happen because of added/removed `cargo::rerun-if` instructions in the build script", )?; Ok(()) } DirtyReason::PrecalculatedComponentsChanged { .. } => { s.dirty_because(unit, "the precalculated components changed") } DirtyReason::ChecksumUseChanged { old } => { if *old { s.dirty_because( unit, "the prior compilation used checksum freshness and this one does not", ) } else { s.dirty_because(unit, "checksum freshness requested, prior compilation did not use checksum freshness") } } DirtyReason::DepInfoOutputChanged { .. } => { s.dirty_because(unit, "the dependency info output changed") } DirtyReason::RerunIfChangedOutputFileChanged { .. } => { s.dirty_because(unit, "rerun-if-changed output file path changed") } DirtyReason::RerunIfChangedOutputPathsChanged { .. } => { s.dirty_because(unit, "the rerun-if-changed instructions changed") } DirtyReason::EnvVarsChanged { .. } => { s.dirty_because(unit, "the environment variables changed") } DirtyReason::EnvVarChanged { name, .. } => { s.dirty_because(unit, format_args!("the env variable {name} changed")) } DirtyReason::LocalFingerprintTypeChanged { .. } => { s.dirty_because(unit, "the local fingerprint type changed") } DirtyReason::NumberOfDependenciesChanged { old, new } => s.dirty_because( unit, format_args!("number of dependencies changed ({old} => {new})",), ), DirtyReason::UnitDependencyNameChanged { old, new } => s.dirty_because( unit, format_args!("name of dependency changed ({old} => {new})"), ), DirtyReason::UnitDependencyInfoChanged { .. } => { s.dirty_because(unit, "dependency info changed") } DirtyReason::FsStatusOutdated(status) => match status { FsStatus::Stale => s.dirty_because(unit, "stale, unknown reason"), FsStatus::StaleItem(item) => match item { StaleItem::MissingFile(missing_file) => { let file = missing_file.strip_prefix(root).unwrap_or(&missing_file); s.dirty_because( unit, format_args!("the file `{}` is missing", file.display()), ) } StaleItem::UnableToReadFile(file) => { let file = file.strip_prefix(root).unwrap_or(&file); s.dirty_because( unit, format_args!("the file `{}` could not be read", file.display()), ) } StaleItem::FailedToReadMetadata(file) => { let file = file.strip_prefix(root).unwrap_or(&file); s.dirty_because( unit, format_args!("couldn't read metadata for file `{}`", file.display()), ) } StaleItem::ChangedFile { stale, stale_mtime, reference_mtime, .. } => { let file = stale.strip_prefix(root).unwrap_or(&stale); let after = Self::after(*reference_mtime, *stale_mtime, "last build"); s.dirty_because( unit, format_args!("the file `{}` has changed ({after})", file.display()), ) } StaleItem::ChangedChecksum { source, stored_checksum, new_checksum, } => { let file = source.strip_prefix(root).unwrap_or(&source); s.dirty_because( unit, format_args!( "the file `{}` has changed (checksum didn't match, {stored_checksum} != {new_checksum})", file.display(), ), ) } StaleItem::FileSizeChanged { path, old_size, new_size, } => { let file = path.strip_prefix(root).unwrap_or(&path); s.dirty_because( unit, format_args!( "file size changed ({old_size} != {new_size}) for `{}`", file.display() ), ) } StaleItem::MissingChecksum(path) => { let file = path.strip_prefix(root).unwrap_or(&path); s.dirty_because( unit, format_args!("the checksum for file `{}` is missing", file.display()), ) } StaleItem::ChangedEnv { var, .. } => s.dirty_because( unit, format_args!("the environment variable {var} changed"), ), }, FsStatus::StaleDependency { name, dep_mtime, max_mtime, .. } => { let after = Self::after(*max_mtime, *dep_mtime, "last build"); s.dirty_because( unit, format_args!("the dependency {name} was rebuilt ({after})"), ) } FsStatus::StaleDepFingerprint { name } => { s.dirty_because(unit, format_args!("the dependency {name} was rebuilt")) } FsStatus::UpToDate { .. } => { unreachable!() } }, DirtyReason::NothingObvious => { // See comment in fingerprint compare method. s.dirty_because(unit, "the fingerprint comparison turned up nothing obvious") } DirtyReason::Forced => s.dirty_because(unit, "forced"), DirtyReason::FreshBuild => s.dirty_because(unit, "fresh build"), } } } cargo-0.91.0/src/cargo/core/compiler/fingerprint/mod.rs000064400000000000000000002564631046102023000211260ustar 00000000000000//! Tracks changes to determine if something needs to be recompiled. //! //! This module implements change-tracking so that Cargo can know whether or //! not something needs to be recompiled. A Cargo [`Unit`] can be either "dirty" //! (needs to be recompiled) or "fresh" (it does not need to be recompiled). //! //! ## Mechanisms affecting freshness //! //! There are several mechanisms that influence a Unit's freshness: //! //! - The [`Fingerprint`] is a hash, saved to the filesystem in the //! `.fingerprint` directory, that tracks information about the Unit. If the //! fingerprint is missing (such as the first time the unit is being //! compiled), then the unit is dirty. If any of the fingerprint fields //! change (like the name of the source file), then the Unit is considered //! dirty. //! //! The `Fingerprint` also tracks the fingerprints of all its dependencies, //! so a change in a dependency will propagate the "dirty" status up. //! //! - Filesystem mtime tracking is also used to check if a unit is dirty. //! See the section below on "Mtime comparison" for more details. There //! are essentially two parts to mtime tracking: //! //! 1. The mtime of a Unit's output files is compared to the mtime of all //! its dependencies' output file mtimes (see //! [`check_filesystem`]). If any output is missing, or is //! older than a dependency's output, then the unit is dirty. //! 2. The mtime of a Unit's source files is compared to the mtime of its //! dep-info file in the fingerprint directory (see [`find_stale_file`]). //! The dep-info file is used as an anchor to know when the last build of //! the unit was done. See the "dep-info files" section below for more //! details. If any input files are missing, or are newer than the //! dep-info, then the unit is dirty. //! //! - Alternatively if you're using the unstable feature `checksum-freshness` //! mtimes are ignored entirely in favor of comparing first the file size, and //! then the checksum with a known prior value emitted by rustc. Only nightly //! rustc will emit the needed metadata at the time of writing. This is dependent //! on the unstable feature `-Z checksum-hash-algorithm`. //! //! Note: Fingerprinting is not a perfect solution. Filesystem mtime tracking //! is notoriously imprecise and problematic. Only a small part of the //! environment is captured. This is a balance of performance, simplicity, and //! completeness. Sandboxing, hashing file contents, tracking every file //! access, environment variable, and network operation would ensure more //! reliable and reproducible builds at the cost of being complex, slow, and //! platform-dependent. //! //! ## Fingerprints and [`UnitHash`]s //! //! [`Metadata`] tracks several [`UnitHash`]s, including //! [`Metadata::unit_id`], [`Metadata::c_metadata`], and [`Metadata::c_extra_filename`]. //! See its documentation for more details. //! //! NOTE: Not all output files are isolated via filename hashes (like dylibs). //! The fingerprint directory uses a hash, but sometimes units share the same //! fingerprint directory (when they don't have Metadata) so care should be //! taken to handle this! //! //! Fingerprints and [`UnitHash`]s are similar, and track some of the same things. //! [`UnitHash`]s contains information that is required to keep Units separate. //! The Fingerprint includes additional information that should cause a //! recompile, but it is desired to reuse the same filenames. A comparison //! of what is tracked: //! //! Value | Fingerprint | `Metadata::unit_id` | `Metadata::c_metadata` | `Metadata::c_extra_filename` //! -------------------------------------------|-------------|---------------------|------------------------|---------- //! rustc | ✓ | ✓ | ✓ | ✓ //! [`Profile`] | ✓ | ✓ | ✓ | ✓ //! `cargo rustc` extra args | ✓ | ✓[^7] | | ✓[^7] //! [`CompileMode`] | ✓ | ✓ | ✓ | ✓ //! Target Name | ✓ | ✓ | ✓ | ✓ //! `TargetKind` (bin/lib/etc.) | ✓ | ✓ | ✓ | ✓ //! Enabled Features | ✓ | ✓ | ✓ | ✓ //! Declared Features | ✓ | | | //! Immediate dependency’s hashes | ✓[^1] | ✓ | ✓ | ✓ //! [`CompileKind`] (host/target) | ✓ | ✓ | ✓ | ✓ //! `__CARGO_DEFAULT_LIB_METADATA`[^4] | | ✓ | ✓ | ✓ //! `package_id` | | ✓ | ✓ | ✓ //! Target src path relative to ws | ✓ | | | //! Target flags (test/bench/for_host/edition) | ✓ | | | //! -C incremental=… flag | ✓ | | | //! mtime of sources | ✓[^3] | | | //! RUSTFLAGS/RUSTDOCFLAGS | ✓ | ✓[^7] | | ✓[^7] //! [`Lto`] flags | ✓ | ✓ | ✓ | ✓ //! config settings[^5] | ✓ | | | //! `is_std` | | ✓ | ✓ | ✓ //! `[lints]` table[^6] | ✓ | | | //! `[lints.rust.unexpected_cfgs.check-cfg]` | ✓ | | | //! //! [^1]: Bin dependencies are not included. //! //! [^3]: See below for details on mtime tracking. //! //! [^4]: `__CARGO_DEFAULT_LIB_METADATA` is set by rustbuild to embed the //! release channel (bootstrap/stable/beta/nightly) in libstd. //! //! [^5]: Config settings that are not otherwise captured anywhere else. //! Currently, this is only `doc.extern-map`. //! //! [^6]: Via [`Manifest::lint_rustflags`][crate::core::Manifest::lint_rustflags] //! //! [^7]: extra-flags and RUSTFLAGS are conditionally excluded when `--remap-path-prefix` is //! present to avoid breaking build reproducibility while we wait for trim-paths //! //! When deciding what should go in the Metadata vs the Fingerprint, consider //! that some files (like dylibs) do not have a hash in their filename. Thus, //! if a value changes, only the fingerprint will detect the change (consider, //! for example, swapping between different features). Fields that are only in //! Metadata generally aren't relevant to the fingerprint because they //! fundamentally change the output (like target vs host changes the directory //! where it is emitted). //! //! ## Fingerprint files //! //! Fingerprint information is stored in the //! `target/{debug,release}/.fingerprint/` directory. Each Unit is stored in a //! separate directory. Each Unit directory contains: //! //! - A file with a 16 hex-digit hash. This is the Fingerprint hash, used for //! quick loading and comparison. //! - A `.json` file that contains details about the Fingerprint. This is only //! used to log details about *why* a fingerprint is considered dirty. //! `CARGO_LOG=cargo::core::compiler::fingerprint=trace cargo build` can be //! used to display this log information. //! - A "dep-info" file which is a translation of rustc's `*.d` dep-info files //! to a Cargo-specific format that tweaks file names and is optimized for //! reading quickly. //! - An `invoked.timestamp` file whose filesystem mtime is updated every time //! the Unit is built. This is used for capturing the time when the build //! starts, to detect if files are changed in the middle of the build. See //! below for more details. //! //! Note that some units are a little different. A Unit for *running* a build //! script or for `rustdoc` does not have a dep-info file (it's not //! applicable). Build script `invoked.timestamp` files are in the build //! output directory. //! //! ## Fingerprint calculation //! //! After the list of Units has been calculated, the Units are added to the //! [`JobQueue`]. As each one is added, the fingerprint is calculated, and the //! dirty/fresh status is recorded. A closure is used to update the fingerprint //! on-disk when the Unit successfully finishes. The closure will recompute the //! Fingerprint based on the updated information. If the Unit fails to compile, //! the fingerprint is not updated. //! //! Fingerprints are cached in the [`BuildRunner`]. This makes computing //! Fingerprints faster, but also is necessary for properly updating //! dependency information. Since a Fingerprint includes the Fingerprints of //! all dependencies, when it is updated, by using `Arc` clones, it //! automatically picks up the updates to its dependencies. //! //! ### dep-info files //! //! Cargo has several kinds of "dep info" files: //! //! * dep-info files generated by `rustc`. //! * Fingerprint dep-info files translated from the first one. //! * dep-info for external build system integration. //! * Unstable `-Zbinary-dep-depinfo`. //! //! #### `rustc` dep-info files //! //! Cargo passes the `--emit=dep-info` flag to `rustc` so that `rustc` will //! generate a "dep info" file (with the `.d` extension). This is a //! Makefile-like syntax that includes all of the source files used to build //! the crate. This file is used by Cargo to know which files to check to see //! if the crate will need to be rebuilt. Example: //! //! ```makefile //! /path/to/target/debug/deps/cargo-b6219d178925203d: src/bin/main.rs src/bin/cargo/cli.rs # … etc. //! ``` //! //! #### Fingerprint dep-info files //! //! After `rustc` exits successfully, Cargo will read the first kind of dep //! info file and translate it into a binary format that is stored in the //! fingerprint directory ([`translate_dep_info`]). //! //! These are used to quickly scan for any changed files. The mtime of the //! fingerprint dep-info file itself is used as the reference for comparing the //! source files to determine if any of the source files have been modified //! (see [below](#mtime-comparison) for more detail). //! //! Note that Cargo parses the special `# env-var:...` comments in dep-info //! files to learn about environment variables that the rustc compile depends on. //! Cargo then later uses this to trigger a recompile if a referenced env var //! changes (even if the source didn't change). //! This also includes env vars generated from Cargo metadata like `CARGO_PKG_DESCRIPTION`. //! (See [`crate::core::manifest::ManifestMetadata`] //! //! #### dep-info files for build system integration. //! //! There is also a third dep-info file. Cargo will extend the file created by //! rustc with some additional information and saves this into the output //! directory. This is intended for build system integration. See the //! [`output_depinfo`] function for more detail. //! //! #### -Zbinary-dep-depinfo //! //! `rustc` has an experimental flag `-Zbinary-dep-depinfo`. This causes //! `rustc` to include binary files (like rlibs) in the dep-info file. This is //! primarily to support rustc development, so that Cargo can check the //! implicit dependency to the standard library (which lives in the sysroot). //! We want Cargo to recompile whenever the standard library rlib/dylibs //! change, and this is a generic mechanism to make that work. //! //! ### Mtime comparison //! //! The use of modification timestamps is the most common way a unit will be //! determined to be dirty or fresh between builds. There are many subtle //! issues and edge cases with mtime comparisons. This gives a high-level //! overview, but you'll need to read the code for the gritty details. Mtime //! handling is different for different unit kinds. The different styles are //! driven by the [`Fingerprint::local`] field, which is set based on the unit //! kind. //! //! The status of whether or not the mtime is "stale" or "up-to-date" is //! stored in [`Fingerprint::fs_status`]. //! //! All units will compare the mtime of its newest output file with the mtimes //! of the outputs of all its dependencies. If any output file is missing, //! then the unit is stale. If any dependency is newer, the unit is stale. //! //! #### Normal package mtime handling //! //! [`LocalFingerprint::CheckDepInfo`] is used for checking the mtime of //! packages. It compares the mtime of the input files (the source files) to //! the mtime of the dep-info file (which is written last after a build is //! finished). If the dep-info is missing, the unit is stale (it has never //! been built). The list of input files comes from the dep-info file. See the //! section above for details on dep-info files. //! //! Also note that although registry and git packages use [`CheckDepInfo`], none //! of their source files are included in the dep-info (see //! [`translate_dep_info`]), so for those kinds no mtime checking is done //! (unless `-Zbinary-dep-depinfo` is used). Repository and git packages are //! static, so there is no need to check anything. //! //! When a build is complete, the mtime of the dep-info file in the //! fingerprint directory is modified to rewind it to the time when the build //! started. This is done by creating an `invoked.timestamp` file when the //! build starts to capture the start time. The mtime is rewound to the start //! to handle the case where the user modifies a source file while a build is //! running. Cargo can't know whether or not the file was included in the //! build, so it takes a conservative approach of assuming the file was *not* //! included, and it should be rebuilt during the next build. //! //! #### Rustdoc mtime handling //! //! Rustdoc does not emit a dep-info file, so Cargo currently has a relatively //! simple system for detecting rebuilds. [`LocalFingerprint::Precalculated`] is //! used for rustdoc units. For registry packages, this is the package //! version. For git packages, it is the git hash. For path packages, it is //! a string of the mtime of the newest file in the package. //! //! There are some known bugs with how this works, so it should be improved at //! some point. //! //! #### Build script mtime handling //! //! Build script mtime handling runs in different modes. There is the "old //! style" where the build script does not emit any `rerun-if` directives. In //! this mode, Cargo will use [`LocalFingerprint::Precalculated`]. See the //! "rustdoc" section above how it works. //! //! In the new-style, each `rerun-if` directive is translated to the //! corresponding [`LocalFingerprint`] variant. The [`RerunIfChanged`] variant //! compares the mtime of the given filenames against the mtime of the //! "output" file. //! //! Similar to normal units, the build script "output" file mtime is rewound //! to the time just before the build script is executed to handle mid-build //! modifications. //! //! ## Considerations for inclusion in a fingerprint //! //! Over time we've realized a few items which historically were included in //! fingerprint hashings should not actually be included. Examples are: //! //! * Modification time values. We strive to never include a modification time //! inside a `Fingerprint` to get hashed into an actual value. While //! theoretically fine to do, in practice this causes issues with common //! applications like Docker. Docker, after a layer is built, will zero out //! the nanosecond part of all filesystem modification times. This means that //! the actual modification time is different for all build artifacts, which //! if we tracked the actual values of modification times would cause //! unnecessary recompiles. To fix this we instead only track paths which are //! relevant. These paths are checked dynamically to see if they're up to //! date, and the modification time doesn't make its way into the fingerprint //! hash. //! //! * Absolute path names. We strive to maintain a property where if you rename //! a project directory Cargo will continue to preserve all build artifacts //! and reuse the cache. This means that we can't ever hash an absolute path //! name. Instead we always hash relative path names and the "root" is passed //! in at runtime dynamically. Some of this is best effort, but the general //! idea is that we assume all accesses within a crate stay within that //! crate. //! //! These are pretty tricky to test for unfortunately, but we should have a good //! test suite nowadays and lord knows Cargo gets enough testing in the wild! //! //! ## Build scripts //! //! The *running* of a build script ([`CompileMode::RunCustomBuild`]) is treated //! significantly different than all other Unit kinds. It has its own function //! for calculating the Fingerprint ([`calculate_run_custom_build`]) and has some //! unique considerations. It does not track the same information as a normal //! Unit. The information tracked depends on the `rerun-if-changed` and //! `rerun-if-env-changed` statements produced by the build script. If the //! script does not emit either of these statements, the Fingerprint runs in //! "old style" mode where an mtime change of *any* file in the package will //! cause the build script to be re-run. Otherwise, the fingerprint *only* //! tracks the individual "rerun-if" items listed by the build script. //! //! The "rerun-if" statements from a *previous* build are stored in the build //! output directory in a file called `output`. Cargo parses this file when //! the Unit for that build script is prepared for the [`JobQueue`]. The //! Fingerprint code can then use that information to compute the Fingerprint //! and compare against the old fingerprint hash. //! //! Care must be taken with build script Fingerprints because the //! [`Fingerprint::local`] value may be changed after the build script runs //! (such as if the build script adds or removes "rerun-if" items). //! //! Another complication is if a build script is overridden. In that case, the //! fingerprint is the hash of the output of the override. //! //! ## Special considerations //! //! Registry dependencies do not track the mtime of files. This is because //! registry dependencies are not expected to change (if a new version is //! used, the Package ID will change, causing a rebuild). Cargo currently //! partially works with Docker caching. When a Docker image is built, it has //! normal mtime information. However, when a step is cached, the nanosecond //! portions of all files is zeroed out. Currently this works, but care must //! be taken for situations like these. //! //! HFS on macOS only supports 1 second timestamps. This causes a significant //! number of problems, particularly with Cargo's testsuite which does rapid //! builds in succession. Other filesystems have various degrees of //! resolution. //! //! Various weird filesystems (such as network filesystems) also can cause //! complications. Network filesystems may track the time on the server //! (except when the time is set manually such as with //! `filetime::set_file_times`). Not all filesystems support modifying the //! mtime. //! //! See the [`A-rebuild-detection`] label on the issue tracker for more. //! //! [`check_filesystem`]: Fingerprint::check_filesystem //! [`Metadata`]: crate::core::compiler::Metadata //! [`Metadata::unit_id`]: crate::core::compiler::Metadata::unit_id //! [`Metadata::c_metadata`]: crate::core::compiler::Metadata::c_metadata //! [`Metadata::c_extra_filename`]: crate::core::compiler::Metadata::c_extra_filename //! [`UnitHash`]: crate::core::compiler::UnitHash //! [`Profile`]: crate::core::profiles::Profile //! [`CompileMode`]: crate::core::compiler::CompileMode //! [`Lto`]: crate::core::compiler::Lto //! [`CompileKind`]: crate::core::compiler::CompileKind //! [`JobQueue`]: super::job_queue::JobQueue //! [`output_depinfo`]: super::output_depinfo() //! [`CheckDepInfo`]: LocalFingerprint::CheckDepInfo //! [`RerunIfChanged`]: LocalFingerprint::RerunIfChanged //! [`CompileMode::RunCustomBuild`]: crate::core::compiler::CompileMode::RunCustomBuild //! [`A-rebuild-detection`]: https://github.com/rust-lang/cargo/issues?q=is%3Aissue+is%3Aopen+label%3AA-rebuild-detection mod dep_info; mod dirty_reason; use std::collections::hash_map::{Entry, HashMap}; use std::env; use std::ffi::OsString; use std::fs; use std::fs::File; use std::hash::{self, Hash, Hasher}; use std::io::{self}; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; use std::time::SystemTime; use anyhow::Context as _; use anyhow::format_err; use cargo_util::paths; use filetime::FileTime; use serde::de; use serde::ser; use serde::{Deserialize, Serialize}; use tracing::{debug, info}; use crate::core::Package; use crate::core::compiler::unit_graph::UnitDep; use crate::util; use crate::util::errors::CargoResult; use crate::util::interning::InternedString; use crate::util::{StableHasher, internal, path_args}; use crate::{CARGO_ENV, GlobalContext}; use super::custom_build::BuildDeps; use super::{BuildContext, BuildRunner, FileFlavor, Job, Unit, Work}; pub use self::dep_info::Checksum; pub use self::dep_info::parse_dep_info; pub use self::dep_info::parse_rustc_dep_info; pub use self::dep_info::translate_dep_info; pub use self::dirty_reason::DirtyReason; /// Determines if a [`Unit`] is up-to-date, and if not prepares necessary work to /// update the persisted fingerprint. /// /// This function will inspect `Unit`, calculate a fingerprint for it, and then /// return an appropriate [`Job`] to run. The returned `Job` will be a noop if /// `unit` is considered "fresh", or if it was previously built and cached. /// Otherwise the `Job` returned will write out the true fingerprint to the /// filesystem, to be executed after the unit's work has completed. /// /// The `force` flag is a way to force the `Job` to be "dirty", or always /// update the fingerprint. **Beware using this flag** because it does not /// transitively propagate throughout the dependency graph, it only forces this /// one unit which is very unlikely to be what you want unless you're /// exclusively talking about top-level units. #[tracing::instrument( skip(build_runner, unit), fields(package_id = %unit.pkg.package_id(), target = unit.target.name()) )] pub fn prepare_target( build_runner: &mut BuildRunner<'_, '_>, unit: &Unit, force: bool, ) -> CargoResult { let bcx = build_runner.bcx; let loc = build_runner.files().fingerprint_file_path(unit, ""); debug!("fingerprint at: {}", loc.display()); // Figure out if this unit is up to date. After calculating the fingerprint // compare it to an old version, if any, and attempt to print diagnostic // information about failed comparisons to aid in debugging. let fingerprint = calculate(build_runner, unit)?; let mtime_on_use = build_runner.bcx.gctx.cli_unstable().mtime_on_use; let dirty_reason = compare_old_fingerprint(unit, &loc, &*fingerprint, mtime_on_use, force); let Some(dirty_reason) = dirty_reason else { return Ok(Job::new_fresh()); }; // We're going to rebuild, so ensure the source of the crate passes all // verification checks before we build it. // // The `Source::verify` method is intended to allow sources to execute // pre-build checks to ensure that the relevant source code is all // up-to-date and as expected. This is currently used primarily for // directory sources which will use this hook to perform an integrity check // on all files in the source to ensure they haven't changed. If they have // changed then an error is issued. let source_id = unit.pkg.package_id().source_id(); let sources = bcx.packages.sources(); let source = sources .get(source_id) .ok_or_else(|| internal("missing package source"))?; source.verify(unit.pkg.package_id())?; // Clear out the old fingerprint file if it exists. This protects when // compilation is interrupted leaving a corrupt file. For example, a // project with a lib.rs and integration test (two units): // // 1. Build the library and integration test. // 2. Make a change to lib.rs (NOT the integration test). // 3. Build the integration test, hit Ctrl-C while linking. With gcc, this // will leave behind an incomplete executable (zero size, or partially // written). NOTE: The library builds successfully, it is the linking // of the integration test that we are interrupting. // 4. Build the integration test again. // // Without the following line, then step 3 will leave a valid fingerprint // on the disk. Then step 4 will think the integration test is "fresh" // because: // // - There is a valid fingerprint hash on disk (written in step 1). // - The mtime of the output file (the corrupt integration executable // written in step 3) is newer than all of its dependencies. // - The mtime of the integration test fingerprint dep-info file (written // in step 1) is newer than the integration test's source files, because // we haven't modified any of its source files. // // But the executable is corrupt and needs to be rebuilt. Clearing the // fingerprint at step 3 ensures that Cargo never mistakes a partially // written output as up-to-date. if loc.exists() { // Truncate instead of delete so that compare_old_fingerprint will // still log the reason for the fingerprint failure instead of just // reporting "failed to read fingerprint" during the next build if // this build fails. paths::write(&loc, b"")?; } let write_fingerprint = if unit.mode.is_run_custom_build() { // For build scripts the `local` field of the fingerprint may change // while we're executing it. For example it could be in the legacy // "consider everything a dependency mode" and then we switch to "deps // are explicitly specified" mode. // // To handle this movement we need to regenerate the `local` field of a // build script's fingerprint after it's executed. We do this by // using the `build_script_local_fingerprints` function which returns a // thunk we can invoke on a foreign thread to calculate this. let build_script_outputs = Arc::clone(&build_runner.build_script_outputs); let metadata = build_runner.get_run_build_script_metadata(unit); let (gen_local, _overridden) = build_script_local_fingerprints(build_runner, unit)?; let output_path = build_runner.build_explicit_deps[unit] .build_script_output .clone(); Work::new(move |_| { let outputs = build_script_outputs.lock().unwrap(); let output = outputs .get(metadata) .expect("output must exist after running"); let deps = BuildDeps::new(&output_path, Some(output)); // FIXME: it's basically buggy that we pass `None` to `call_box` // here. See documentation on `build_script_local_fingerprints` // below for more information. Despite this just try to proceed and // hobble along if it happens to return `Some`. if let Some(new_local) = (gen_local)(&deps, None)? { *fingerprint.local.lock().unwrap() = new_local; } write_fingerprint(&loc, &fingerprint) }) } else { Work::new(move |_| write_fingerprint(&loc, &fingerprint)) }; Ok(Job::new_dirty(write_fingerprint, dirty_reason)) } /// Dependency edge information for fingerprints. This is generated for each /// dependency and is stored in a [`Fingerprint`]. #[derive(Clone)] struct DepFingerprint { /// The hash of the package id that this dependency points to pkg_id: u64, /// The crate name we're using for this dependency, which if we change we'll /// need to recompile! name: InternedString, /// Whether or not this dependency is flagged as a public dependency or not. public: bool, /// Whether or not this dependency is an rmeta dependency or a "full" /// dependency. In the case of an rmeta dependency our dependency edge only /// actually requires the rmeta from what we depend on, so when checking /// mtime information all files other than the rmeta can be ignored. only_requires_rmeta: bool, /// The dependency's fingerprint we recursively point to, containing all the /// other hash information we'd otherwise need. fingerprint: Arc, } /// A fingerprint can be considered to be a "short string" representing the /// state of a world for a package. /// /// If a fingerprint ever changes, then the package itself needs to be /// recompiled. Inputs to the fingerprint include source code modifications, /// compiler flags, compiler version, etc. This structure is not simply a /// `String` due to the fact that some fingerprints cannot be calculated lazily. /// /// Path sources, for example, use the mtime of the corresponding dep-info file /// as a fingerprint (all source files must be modified *before* this mtime). /// This dep-info file is not generated, however, until after the crate is /// compiled. As a result, this structure can be thought of as a fingerprint /// to-be. The actual value can be calculated via [`hash_u64()`], but the operation /// may fail as some files may not have been generated. /// /// Note that dependencies are taken into account for fingerprints because rustc /// requires that whenever an upstream crate is recompiled that all downstream /// dependents are also recompiled. This is typically tracked through /// [`DependencyQueue`], but it also needs to be retained here because Cargo can /// be interrupted while executing, losing the state of the [`DependencyQueue`] /// graph. /// /// [`hash_u64()`]: crate::core::compiler::fingerprint::Fingerprint::hash_u64 /// [`DependencyQueue`]: crate::util::DependencyQueue #[derive(Serialize, Deserialize)] pub struct Fingerprint { /// Hash of the version of `rustc` used. rustc: u64, /// Sorted list of cfg features enabled. features: String, /// Sorted list of all the declared cfg features. declared_features: String, /// Hash of the `Target` struct, including the target name, /// package-relative source path, edition, etc. target: u64, /// Hash of the [`Profile`], [`CompileMode`], and any extra flags passed via /// `cargo rustc` or `cargo rustdoc`. /// /// [`Profile`]: crate::core::profiles::Profile /// [`CompileMode`]: crate::core::compiler::CompileMode profile: u64, /// Hash of the path to the base source file. This is relative to the /// workspace root for path members, or absolute for other sources. path: u64, /// Fingerprints of dependencies. deps: Vec, /// Information about the inputs that affect this Unit (such as source /// file mtimes or build script environment variables). local: Mutex>, /// Cached hash of the [`Fingerprint`] struct. Used to improve performance /// for hashing. #[serde(skip)] memoized_hash: Mutex>, /// RUSTFLAGS/RUSTDOCFLAGS environment variable value (or config value). rustflags: Vec, /// Hash of various config settings that change how things are compiled. config: u64, /// The rustc target. This is only relevant for `.json` files, otherwise /// the metadata hash segregates the units. compile_kind: u64, /// Description of whether the filesystem status for this unit is up to date /// or should be considered stale. #[serde(skip)] fs_status: FsStatus, /// Files, relative to `target_root`, that are produced by the step that /// this `Fingerprint` represents. This is used to detect when the whole /// fingerprint is out of date if this is missing, or if previous /// fingerprints output files are regenerated and look newer than this one. #[serde(skip)] outputs: Vec, } /// Indication of the status on the filesystem for a particular unit. #[derive(Clone, Default, Debug)] pub enum FsStatus { /// This unit is to be considered stale, even if hash information all /// matches. #[default] Stale, /// File system inputs have changed (or are missing), or there were /// changes to the environment variables that affect this unit. See /// the variants of [`StaleItem`] for more information. StaleItem(StaleItem), /// A dependency was stale. StaleDependency { name: InternedString, dep_mtime: FileTime, max_mtime: FileTime, }, /// A dependency was stale. StaleDepFingerprint { name: InternedString }, /// This unit is up-to-date. All outputs and their corresponding mtime are /// listed in the payload here for other dependencies to compare against. UpToDate { mtimes: HashMap }, } impl FsStatus { fn up_to_date(&self) -> bool { match self { FsStatus::UpToDate { .. } => true, FsStatus::Stale | FsStatus::StaleItem(_) | FsStatus::StaleDependency { .. } | FsStatus::StaleDepFingerprint { .. } => false, } } } impl Serialize for DepFingerprint { fn serialize(&self, ser: S) -> Result where S: ser::Serializer, { ( &self.pkg_id, &self.name, &self.public, &self.fingerprint.hash_u64(), ) .serialize(ser) } } impl<'de> Deserialize<'de> for DepFingerprint { fn deserialize(d: D) -> Result where D: de::Deserializer<'de>, { let (pkg_id, name, public, hash) = <(u64, String, bool, u64)>::deserialize(d)?; Ok(DepFingerprint { pkg_id, name: name.into(), public, fingerprint: Arc::new(Fingerprint { memoized_hash: Mutex::new(Some(hash)), ..Fingerprint::new() }), // This field is never read since it's only used in // `check_filesystem` which isn't used by fingerprints loaded from // disk. only_requires_rmeta: false, }) } } /// A `LocalFingerprint` represents something that we use to detect direct /// changes to a `Fingerprint`. /// /// This is where we track file information, env vars, etc. This /// `LocalFingerprint` struct is hashed and if the hash changes will force a /// recompile of any fingerprint it's included into. Note that the "local" /// terminology comes from the fact that it only has to do with one crate, and /// `Fingerprint` tracks the transitive propagation of fingerprint changes. /// /// Note that because this is hashed its contents are carefully managed. Like /// mentioned in the above module docs, we don't want to hash absolute paths or /// mtime information. /// /// Also note that a `LocalFingerprint` is used in `check_filesystem` to detect /// when the filesystem contains stale information (based on mtime currently). /// The paths here don't change much between compilations but they're used as /// inputs when we probe the filesystem looking at information. #[derive(Debug, Serialize, Deserialize, Hash)] enum LocalFingerprint { /// This is a precalculated fingerprint which has an opaque string we just /// hash as usual. This variant is primarily used for rustdoc where we /// don't have a dep-info file to compare against. /// /// This is also used for build scripts with no `rerun-if-*` statements, but /// that's overall a mistake and causes bugs in Cargo. We shouldn't use this /// for build scripts. Precalculated(String), /// This is used for crate compilations. The `dep_info` file is a relative /// path anchored at `target_root(...)` to the dep-info file that Cargo /// generates (which is a custom serialization after parsing rustc's own /// `dep-info` output). /// /// The `dep_info` file, when present, also lists a number of other files /// for us to look at. If any of those files are newer than this file then /// we need to recompile. /// /// If the `checksum` bool is true then the `dep_info` file is expected to /// contain file checksums instead of file mtimes. CheckDepInfo { dep_info: PathBuf, checksum: bool }, /// This represents a nonempty set of `rerun-if-changed` annotations printed /// out by a build script. The `output` file is a relative file anchored at /// `target_root(...)` which is the actual output of the build script. That /// output has already been parsed and the paths printed out via /// `rerun-if-changed` are listed in `paths`. The `paths` field is relative /// to `pkg.root()` /// /// This is considered up-to-date if all of the `paths` are older than /// `output`, otherwise we need to recompile. RerunIfChanged { output: PathBuf, paths: Vec, }, /// This represents a single `rerun-if-env-changed` annotation printed by a /// build script. The exact env var and value are hashed here. There's no /// filesystem dependence here, and if the values are changed the hash will /// change forcing a recompile. RerunIfEnvChanged { var: String, val: Option }, } /// See [`FsStatus::StaleItem`]. #[derive(Clone, Debug)] pub enum StaleItem { MissingFile(PathBuf), UnableToReadFile(PathBuf), FailedToReadMetadata(PathBuf), FileSizeChanged { path: PathBuf, old_size: u64, new_size: u64, }, ChangedFile { reference: PathBuf, reference_mtime: FileTime, stale: PathBuf, stale_mtime: FileTime, }, ChangedChecksum { source: PathBuf, stored_checksum: Checksum, new_checksum: Checksum, }, MissingChecksum(PathBuf), ChangedEnv { var: String, previous: Option, current: Option, }, } impl LocalFingerprint { /// Read the environment variable of the given env `key`, and creates a new /// [`LocalFingerprint::RerunIfEnvChanged`] for it. The `env_config` is used firstly /// to check if the env var is set in the config system as some envs need to be overridden. /// If not, it will fallback to `std::env::var`. /// // TODO: `std::env::var` is allowed at this moment. Should figure out // if it makes sense if permitting to read env from the env snapshot. #[allow(clippy::disallowed_methods)] fn from_env>( key: K, env_config: &Arc>, ) -> LocalFingerprint { let key = key.as_ref(); let var = key.to_owned(); let val = if let Some(val) = env_config.get(key) { val.to_str().map(ToOwned::to_owned) } else { env::var(key).ok() }; LocalFingerprint::RerunIfEnvChanged { var, val } } /// Checks dynamically at runtime if this `LocalFingerprint` has a stale /// item inside of it. /// /// The main purpose of this function is to handle two different ways /// fingerprints can be invalidated: /// /// * One is a dependency listed in rustc's dep-info files is invalid. Note /// that these could either be env vars or files. We check both here. /// /// * Another is the `rerun-if-changed` directive from build scripts. This /// is where we'll find whether files have actually changed fn find_stale_item( &self, mtime_cache: &mut HashMap, checksum_cache: &mut HashMap, pkg: &Package, build_root: &Path, cargo_exe: &Path, gctx: &GlobalContext, ) -> CargoResult> { let pkg_root = pkg.root(); match self { // We need to parse `dep_info`, learn about the crate's dependencies. // // For each env var we see if our current process's env var still // matches, and for each file we see if any of them are newer than // the `dep_info` file itself whose mtime represents the start of // rustc. LocalFingerprint::CheckDepInfo { dep_info, checksum } => { let dep_info = build_root.join(dep_info); let Some(info) = parse_dep_info(pkg_root, build_root, &dep_info)? else { return Ok(Some(StaleItem::MissingFile(dep_info))); }; for (key, previous) in info.env.iter() { if let Some(value) = pkg.manifest().metadata().env_var(key.as_str()) { if Some(value.as_ref()) == previous.as_deref() { continue; } } let current = if key == CARGO_ENV { Some(cargo_exe.to_str().ok_or_else(|| { format_err!( "cargo exe path {} must be valid UTF-8", cargo_exe.display() ) })?) } else { if let Some(value) = gctx.env_config()?.get(key) { value.to_str() } else { gctx.get_env(key).ok() } }; if current == previous.as_deref() { continue; } return Ok(Some(StaleItem::ChangedEnv { var: key.clone(), previous: previous.clone(), current: current.map(Into::into), })); } if *checksum { Ok(find_stale_file( mtime_cache, checksum_cache, &dep_info, info.files.iter().map(|(file, checksum)| (file, *checksum)), *checksum, )) } else { Ok(find_stale_file( mtime_cache, checksum_cache, &dep_info, info.files.into_keys().map(|p| (p, None)), *checksum, )) } } // We need to verify that no paths listed in `paths` are newer than // the `output` path itself, or the last time the build script ran. LocalFingerprint::RerunIfChanged { output, paths } => Ok(find_stale_file( mtime_cache, checksum_cache, &build_root.join(output), paths.iter().map(|p| (pkg_root.join(p), None)), false, )), // These have no dependencies on the filesystem, and their values // are included natively in the `Fingerprint` hash so nothing // tocheck for here. LocalFingerprint::RerunIfEnvChanged { .. } => Ok(None), LocalFingerprint::Precalculated(..) => Ok(None), } } fn kind(&self) -> &'static str { match self { LocalFingerprint::Precalculated(..) => "precalculated", LocalFingerprint::CheckDepInfo { .. } => "dep-info", LocalFingerprint::RerunIfChanged { .. } => "rerun-if-changed", LocalFingerprint::RerunIfEnvChanged { .. } => "rerun-if-env-changed", } } } impl Fingerprint { fn new() -> Fingerprint { Fingerprint { rustc: 0, target: 0, profile: 0, path: 0, features: String::new(), declared_features: String::new(), deps: Vec::new(), local: Mutex::new(Vec::new()), memoized_hash: Mutex::new(None), rustflags: Vec::new(), config: 0, compile_kind: 0, fs_status: FsStatus::Stale, outputs: Vec::new(), } } /// For performance reasons fingerprints will memoize their own hash, but /// there's also internal mutability with its `local` field which can /// change, for example with build scripts, during a build. /// /// This method can be used to bust all memoized hashes just before a build /// to ensure that after a build completes everything is up-to-date. pub fn clear_memoized(&self) { *self.memoized_hash.lock().unwrap() = None; } fn hash_u64(&self) -> u64 { if let Some(s) = *self.memoized_hash.lock().unwrap() { return s; } let ret = util::hash_u64(self); *self.memoized_hash.lock().unwrap() = Some(ret); ret } /// Compares this fingerprint with an old version which was previously /// serialized to filesystem. /// /// The purpose of this is exclusively to produce a diagnostic message /// [`DirtyReason`], indicating why we're recompiling something. fn compare(&self, old: &Fingerprint) -> DirtyReason { if self.rustc != old.rustc { return DirtyReason::RustcChanged; } if self.features != old.features { return DirtyReason::FeaturesChanged { old: old.features.clone(), new: self.features.clone(), }; } if self.declared_features != old.declared_features { return DirtyReason::DeclaredFeaturesChanged { old: old.declared_features.clone(), new: self.declared_features.clone(), }; } if self.target != old.target { return DirtyReason::TargetConfigurationChanged; } if self.path != old.path { return DirtyReason::PathToSourceChanged; } if self.profile != old.profile { return DirtyReason::ProfileConfigurationChanged; } if self.rustflags != old.rustflags { return DirtyReason::RustflagsChanged { old: old.rustflags.clone(), new: self.rustflags.clone(), }; } if self.config != old.config { return DirtyReason::ConfigSettingsChanged; } if self.compile_kind != old.compile_kind { return DirtyReason::CompileKindChanged; } let my_local = self.local.lock().unwrap(); let old_local = old.local.lock().unwrap(); if my_local.len() != old_local.len() { return DirtyReason::LocalLengthsChanged; } for (new, old) in my_local.iter().zip(old_local.iter()) { match (new, old) { (LocalFingerprint::Precalculated(a), LocalFingerprint::Precalculated(b)) => { if a != b { return DirtyReason::PrecalculatedComponentsChanged { old: b.to_string(), new: a.to_string(), }; } } ( LocalFingerprint::CheckDepInfo { dep_info: adep, checksum: checksum_a, }, LocalFingerprint::CheckDepInfo { dep_info: bdep, checksum: checksum_b, }, ) => { if adep != bdep { return DirtyReason::DepInfoOutputChanged { old: bdep.clone(), new: adep.clone(), }; } if checksum_a != checksum_b { return DirtyReason::ChecksumUseChanged { old: *checksum_b }; } } ( LocalFingerprint::RerunIfChanged { output: aout, paths: apaths, }, LocalFingerprint::RerunIfChanged { output: bout, paths: bpaths, }, ) => { if aout != bout { return DirtyReason::RerunIfChangedOutputFileChanged { old: bout.clone(), new: aout.clone(), }; } if apaths != bpaths { return DirtyReason::RerunIfChangedOutputPathsChanged { old: bpaths.clone(), new: apaths.clone(), }; } } ( LocalFingerprint::RerunIfEnvChanged { var: akey, val: avalue, }, LocalFingerprint::RerunIfEnvChanged { var: bkey, val: bvalue, }, ) => { if *akey != *bkey { return DirtyReason::EnvVarsChanged { old: bkey.clone(), new: akey.clone(), }; } if *avalue != *bvalue { return DirtyReason::EnvVarChanged { name: akey.clone(), old_value: bvalue.clone(), new_value: avalue.clone(), }; } } (a, b) => { return DirtyReason::LocalFingerprintTypeChanged { old: b.kind(), new: a.kind(), }; } } } if self.deps.len() != old.deps.len() { return DirtyReason::NumberOfDependenciesChanged { old: old.deps.len(), new: self.deps.len(), }; } for (a, b) in self.deps.iter().zip(old.deps.iter()) { if a.name != b.name { return DirtyReason::UnitDependencyNameChanged { old: b.name, new: a.name, }; } if a.fingerprint.hash_u64() != b.fingerprint.hash_u64() { return DirtyReason::UnitDependencyInfoChanged { new_name: a.name, new_fingerprint: a.fingerprint.hash_u64(), old_name: b.name, old_fingerprint: b.fingerprint.hash_u64(), }; } } if !self.fs_status.up_to_date() { return DirtyReason::FsStatusOutdated(self.fs_status.clone()); } // This typically means some filesystem modifications happened or // something transitive was odd. In general we should strive to provide // a better error message than this, so if you see this message a lot it // likely means this method needs to be updated! DirtyReason::NothingObvious } /// Dynamically inspect the local filesystem to update the `fs_status` field /// of this `Fingerprint`. /// /// This function is used just after a `Fingerprint` is constructed to check /// the local state of the filesystem and propagate any dirtiness from /// dependencies up to this unit as well. This function assumes that the /// unit starts out as [`FsStatus::Stale`] and then it will optionally switch /// it to `UpToDate` if it can. fn check_filesystem( &mut self, mtime_cache: &mut HashMap, checksum_cache: &mut HashMap, pkg: &Package, build_root: &Path, cargo_exe: &Path, gctx: &GlobalContext, ) -> CargoResult<()> { assert!(!self.fs_status.up_to_date()); let pkg_root = pkg.root(); let mut mtimes = HashMap::new(); // Get the `mtime` of all outputs. Optionally update their mtime // afterwards based on the `mtime_on_use` flag. Afterwards we want the // minimum mtime as it's the one we'll be comparing to inputs and // dependencies. for output in self.outputs.iter() { let Ok(mtime) = paths::mtime(output) else { // This path failed to report its `mtime`. It probably doesn't // exists, so leave ourselves as stale and bail out. let item = StaleItem::FailedToReadMetadata(output.clone()); self.fs_status = FsStatus::StaleItem(item); return Ok(()); }; assert!(mtimes.insert(output.clone(), mtime).is_none()); } let opt_max = mtimes.iter().max_by_key(|kv| kv.1); let Some((max_path, max_mtime)) = opt_max else { // We had no output files. This means we're an overridden build // script and we're just always up to date because we aren't // watching the filesystem. self.fs_status = FsStatus::UpToDate { mtimes }; return Ok(()); }; debug!( "max output mtime for {:?} is {:?} {}", pkg_root, max_path, max_mtime ); for dep in self.deps.iter() { let dep_mtimes = match &dep.fingerprint.fs_status { FsStatus::UpToDate { mtimes } => mtimes, // If our dependency is stale, so are we, so bail out. FsStatus::Stale | FsStatus::StaleItem(_) | FsStatus::StaleDependency { .. } | FsStatus::StaleDepFingerprint { .. } => { self.fs_status = FsStatus::StaleDepFingerprint { name: dep.name }; return Ok(()); } }; // If our dependency edge only requires the rmeta file to be present // then we only need to look at that one output file, otherwise we // need to consider all output files to see if we're out of date. let (dep_path, dep_mtime) = if dep.only_requires_rmeta { dep_mtimes .iter() .find(|(path, _mtime)| { path.extension().and_then(|s| s.to_str()) == Some("rmeta") }) .expect("failed to find rmeta") } else { match dep_mtimes.iter().max_by_key(|kv| kv.1) { Some(dep_mtime) => dep_mtime, // If our dependencies is up to date and has no filesystem // interactions, then we can move on to the next dependency. None => continue, } }; debug!( "max dep mtime for {:?} is {:?} {}", pkg_root, dep_path, dep_mtime ); // If the dependency is newer than our own output then it was // recompiled previously. We transitively become stale ourselves in // that case, so bail out. // // Note that this comparison should probably be `>=`, not `>`, but // for a discussion of why it's `>` see the discussion about #5918 // below in `find_stale`. if dep_mtime > max_mtime { info!( "dependency on `{}` is newer than we are {} > {} {:?}", dep.name, dep_mtime, max_mtime, pkg_root ); self.fs_status = FsStatus::StaleDependency { name: dep.name, dep_mtime: *dep_mtime, max_mtime: *max_mtime, }; return Ok(()); } } // If we reached this far then all dependencies are up to date. Check // all our `LocalFingerprint` information to see if we have any stale // files for this package itself. If we do find something log a helpful // message and bail out so we stay stale. for local in self.local.get_mut().unwrap().iter() { if let Some(item) = local.find_stale_item( mtime_cache, checksum_cache, pkg, build_root, cargo_exe, gctx, )? { item.log(); self.fs_status = FsStatus::StaleItem(item); return Ok(()); } } // Everything was up to date! Record such. self.fs_status = FsStatus::UpToDate { mtimes }; debug!("filesystem up-to-date {:?}", pkg_root); Ok(()) } } impl hash::Hash for Fingerprint { fn hash(&self, h: &mut H) { let Fingerprint { rustc, ref features, ref declared_features, target, path, profile, ref deps, ref local, config, compile_kind, ref rustflags, .. } = *self; let local = local.lock().unwrap(); ( rustc, features, declared_features, target, path, profile, &*local, config, compile_kind, rustflags, ) .hash(h); h.write_usize(deps.len()); for DepFingerprint { pkg_id, name, public, fingerprint, only_requires_rmeta: _, // static property, no need to hash } in deps { pkg_id.hash(h); name.hash(h); public.hash(h); // use memoized dep hashes to avoid exponential blowup h.write_u64(fingerprint.hash_u64()); } } } impl DepFingerprint { fn new( build_runner: &mut BuildRunner<'_, '_>, parent: &Unit, dep: &UnitDep, ) -> CargoResult { let fingerprint = calculate(build_runner, &dep.unit)?; // We need to be careful about what we hash here. We have a goal of // supporting renaming a project directory and not rebuilding // everything. To do that, however, we need to make sure that the cwd // doesn't make its way into any hashes, and one source of that is the // `SourceId` for `path` packages. // // We already have a requirement that `path` packages all have unique // names (sort of for this same reason), so if the package source is a // `path` then we just hash the name, but otherwise we hash the full // id as it won't change when the directory is renamed. let pkg_id = if dep.unit.pkg.package_id().source_id().is_path() { util::hash_u64(dep.unit.pkg.package_id().name()) } else { util::hash_u64(dep.unit.pkg.package_id()) }; Ok(DepFingerprint { pkg_id, name: dep.extern_crate_name, public: dep.public, fingerprint, only_requires_rmeta: build_runner.only_requires_rmeta(parent, &dep.unit), }) } } impl StaleItem { /// Use the `log` crate to log a hopefully helpful message in diagnosing /// what file is considered stale and why. This is intended to be used in /// conjunction with `CARGO_LOG` to determine why Cargo is recompiling /// something. Currently there's no user-facing usage of this other than /// that. fn log(&self) { match self { StaleItem::MissingFile(path) => { info!("stale: missing {:?}", path); } StaleItem::UnableToReadFile(path) => { info!("stale: unable to read {:?}", path); } StaleItem::FailedToReadMetadata(path) => { info!("stale: couldn't read metadata {:?}", path); } StaleItem::ChangedFile { reference, reference_mtime, stale, stale_mtime, } => { info!("stale: changed {:?}", stale); info!(" (vs) {:?}", reference); info!(" {:?} < {:?}", reference_mtime, stale_mtime); } StaleItem::FileSizeChanged { path, new_size, old_size, } => { info!("stale: changed {:?}", path); info!("prior file size {old_size}"); info!(" new file size {new_size}"); } StaleItem::ChangedChecksum { source, stored_checksum, new_checksum, } => { info!("stale: changed {:?}", source); info!("prior checksum {stored_checksum}"); info!(" new checksum {new_checksum}"); } StaleItem::MissingChecksum(path) => { info!("stale: no prior checksum {:?}", path); } StaleItem::ChangedEnv { var, previous, current, } => { info!("stale: changed env {:?}", var); info!(" {:?} != {:?}", previous, current); } } } } /// Calculates the fingerprint for a [`Unit`]. /// /// This fingerprint is used by Cargo to learn about when information such as: /// /// * A non-path package changes (changes version, changes revision, etc). /// * Any dependency changes /// * The compiler changes /// * The set of features a package is built with changes /// * The profile a target is compiled with changes (e.g., opt-level changes) /// * Any other compiler flags change that will affect the result /// /// Information like file modification time is only calculated for path /// dependencies. fn calculate(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult> { // This function is slammed quite a lot, so the result is memoized. if let Some(s) = build_runner.fingerprints.get(unit) { return Ok(Arc::clone(s)); } let mut fingerprint = if unit.mode.is_run_custom_build() { calculate_run_custom_build(build_runner, unit)? } else if unit.mode.is_doc_test() { panic!("doc tests do not fingerprint"); } else { calculate_normal(build_runner, unit)? }; // After we built the initial `Fingerprint` be sure to update the // `fs_status` field of it. let build_root = build_root(build_runner); let cargo_exe = build_runner.bcx.gctx.cargo_exe()?; fingerprint.check_filesystem( &mut build_runner.mtime_cache, &mut build_runner.checksum_cache, &unit.pkg, &build_root, cargo_exe, build_runner.bcx.gctx, )?; let fingerprint = Arc::new(fingerprint); build_runner .fingerprints .insert(unit.clone(), Arc::clone(&fingerprint)); Ok(fingerprint) } /// Calculate a fingerprint for a "normal" unit, or anything that's not a build /// script. This is an internal helper of [`calculate`], don't call directly. fn calculate_normal( build_runner: &mut BuildRunner<'_, '_>, unit: &Unit, ) -> CargoResult { let deps = { // Recursively calculate the fingerprint for all of our dependencies. // // Skip fingerprints of binaries because they don't actually induce a // recompile, they're just dependencies in the sense that they need to be // built. The only exception here are artifact dependencies, // which is an actual dependency that needs a recompile. // // Create Vec since mutable build_runner is needed in closure. let deps = Vec::from(build_runner.unit_deps(unit)); let mut deps = deps .into_iter() .filter(|dep| !dep.unit.target.is_bin() || dep.unit.artifact.is_true()) .map(|dep| DepFingerprint::new(build_runner, unit, &dep)) .collect::>>()?; deps.sort_by(|a, b| a.pkg_id.cmp(&b.pkg_id)); deps }; // Afterwards calculate our own fingerprint information. let build_root = build_root(build_runner); let is_any_doc_gen = unit.mode.is_doc() || unit.mode.is_doc_scrape(); let rustdoc_depinfo_enabled = build_runner.bcx.gctx.cli_unstable().rustdoc_depinfo; let local = if is_any_doc_gen && !rustdoc_depinfo_enabled { // rustdoc does not have dep-info files. let fingerprint = pkg_fingerprint(build_runner.bcx, &unit.pkg).with_context(|| { format!( "failed to determine package fingerprint for documenting {}", unit.pkg ) })?; vec![LocalFingerprint::Precalculated(fingerprint)] } else { let dep_info = dep_info_loc(build_runner, unit); let dep_info = dep_info.strip_prefix(&build_root).unwrap().to_path_buf(); vec![LocalFingerprint::CheckDepInfo { dep_info, checksum: build_runner.bcx.gctx.cli_unstable().checksum_freshness, }] }; // Figure out what the outputs of our unit is, and we'll be storing them // into the fingerprint as well. let outputs = build_runner .outputs(unit)? .iter() .filter(|output| { !matches!( output.flavor, FileFlavor::DebugInfo | FileFlavor::Auxiliary | FileFlavor::Sbom ) }) .map(|output| output.path.clone()) .collect(); // Fill out a bunch more information that we'll be tracking typically // hashed to take up less space on disk as we just need to know when things // change. let extra_flags = if unit.mode.is_doc() || unit.mode.is_doc_scrape() { &unit.rustdocflags } else { &unit.rustflags } .to_vec(); let profile_hash = util::hash_u64(( &unit.profile, unit.mode, build_runner.bcx.extra_args_for(unit), build_runner.lto[unit], unit.pkg.manifest().lint_rustflags(), )); let mut config = StableHasher::new(); if let Some(linker) = build_runner.compilation.target_linker(unit.kind) { linker.hash(&mut config); } if unit.mode.is_doc() && build_runner.bcx.gctx.cli_unstable().rustdoc_map { if let Ok(map) = build_runner.bcx.gctx.doc_extern_map() { map.hash(&mut config); } } if let Some(allow_features) = &build_runner.bcx.gctx.cli_unstable().allow_features { allow_features.hash(&mut config); } let compile_kind = unit.kind.fingerprint_hash(); let mut declared_features = unit.pkg.summary().features().keys().collect::>(); declared_features.sort(); // to avoid useless rebuild if the user orders it's features // differently Ok(Fingerprint { rustc: util::hash_u64(&build_runner.bcx.rustc().verbose_version), target: util::hash_u64(&unit.target), profile: profile_hash, // Note that .0 is hashed here, not .1 which is the cwd. That doesn't // actually affect the output artifact so there's no need to hash it. path: util::hash_u64(path_args(build_runner.bcx.ws, unit).0), features: format!("{:?}", unit.features), declared_features: format!("{declared_features:?}"), deps, local: Mutex::new(local), memoized_hash: Mutex::new(None), config: Hasher::finish(&config), compile_kind, rustflags: extra_flags, fs_status: FsStatus::Stale, outputs, }) } /// Calculate a fingerprint for an "execute a build script" unit. This is an /// internal helper of [`calculate`], don't call directly. fn calculate_run_custom_build( build_runner: &mut BuildRunner<'_, '_>, unit: &Unit, ) -> CargoResult { assert!(unit.mode.is_run_custom_build()); // Using the `BuildDeps` information we'll have previously parsed and // inserted into `build_explicit_deps` built an initial snapshot of the // `LocalFingerprint` list for this build script. If we previously executed // the build script this means we'll be watching files and env vars. // Otherwise if we haven't previously executed it we'll just start watching // the whole crate. let (gen_local, overridden) = build_script_local_fingerprints(build_runner, unit)?; let deps = &build_runner.build_explicit_deps[unit]; let local = (gen_local)( deps, Some(&|| { const IO_ERR_MESSAGE: &str = "\ An I/O error happened. Please make sure you can access the file. By default, if your project contains a build script, cargo scans all files in it to determine whether a rebuild is needed. If you don't expect to access the file, specify `rerun-if-changed` in your build script. See https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-changed for more information."; pkg_fingerprint(build_runner.bcx, &unit.pkg).map_err(|err| { let mut message = format!("failed to determine package fingerprint for build script for {}", unit.pkg); if err.root_cause().is::() { message = format!("{}\n{}", message, IO_ERR_MESSAGE) } err.context(message) }) }), )? .unwrap(); let output = deps.build_script_output.clone(); // Include any dependencies of our execution, which is typically just the // compilation of the build script itself. (if the build script changes we // should be rerun!). Note though that if we're an overridden build script // we have no dependencies so no need to recurse in that case. let deps = if overridden { // Overridden build scripts don't need to track deps. vec![] } else { // Create Vec since mutable build_runner is needed in closure. let deps = Vec::from(build_runner.unit_deps(unit)); deps.into_iter() .map(|dep| DepFingerprint::new(build_runner, unit, &dep)) .collect::>>()? }; let rustflags = unit.rustflags.to_vec(); Ok(Fingerprint { local: Mutex::new(local), rustc: util::hash_u64(&build_runner.bcx.rustc().verbose_version), deps, outputs: if overridden { Vec::new() } else { vec![output] }, rustflags, // Most of the other info is blank here as we don't really include it // in the execution of the build script, but... this may be a latent // bug in Cargo. ..Fingerprint::new() }) } /// Get ready to compute the [`LocalFingerprint`] values /// for a [`RunCustomBuild`] unit. /// /// This function has, what's on the surface, a seriously wonky interface. /// You'll call this function and it'll return a closure and a boolean. The /// boolean is pretty simple in that it indicates whether the `unit` has been /// overridden via `.cargo/config.toml`. The closure is much more complicated. /// /// This closure is intended to capture any local state necessary to compute /// the `LocalFingerprint` values for this unit. It is `Send` and `'static` to /// be sent to other threads as well (such as when we're executing build /// scripts). That deduplication is the rationale for the closure at least. /// /// The arguments to the closure are a bit weirder, though, and I'll apologize /// in advance for the weirdness too. The first argument to the closure is a /// `&BuildDeps`. This is the parsed version of a build script, and when Cargo /// starts up this is cached from previous runs of a build script. After a /// build script executes the output file is reparsed and passed in here. /// /// The second argument is the weirdest, it's *optionally* a closure to /// call [`pkg_fingerprint`]. The `pkg_fingerprint` requires access to /// "source map" located in `Context`. That's very non-`'static` and /// non-`Send`, so it can't be used on other threads, such as when we invoke /// this after a build script has finished. The `Option` allows us to for sure /// calculate it on the main thread at the beginning, and then swallow the bug /// for now where a worker thread after a build script has finished doesn't /// have access. Ideally there would be no second argument or it would be more /// "first class" and not an `Option` but something that can be sent between /// threads. In any case, it's a bug for now. /// /// This isn't the greatest of interfaces, and if there's suggestions to /// improve please do so! /// /// FIXME(#6779) - see all the words above /// /// [`RunCustomBuild`]: crate::core::compiler::CompileMode::RunCustomBuild fn build_script_local_fingerprints( build_runner: &mut BuildRunner<'_, '_>, unit: &Unit, ) -> CargoResult<( Box< dyn FnOnce( &BuildDeps, Option<&dyn Fn() -> CargoResult>, ) -> CargoResult>> + Send, >, bool, )> { assert!(unit.mode.is_run_custom_build()); // First up, if this build script is entirely overridden, then we just // return the hash of what we overrode it with. This is the easy case! if let Some(fingerprint) = build_script_override_fingerprint(build_runner, unit) { debug!("override local fingerprints deps {}", unit.pkg); return Ok(( Box::new( move |_: &BuildDeps, _: Option<&dyn Fn() -> CargoResult>| { Ok(Some(vec![fingerprint])) }, ), true, // this is an overridden build script )); } // ... Otherwise this is a "real" build script and we need to return a real // closure. Our returned closure classifies the build script based on // whether it prints `rerun-if-*`. If it *doesn't* print this it's where the // magical second argument comes into play, which fingerprints a whole // package. Remember that the fact that this is an `Option` is a bug, but a // longstanding bug, in Cargo. Recent refactorings just made it painfully // obvious. let pkg_root = unit.pkg.root().to_path_buf(); let build_dir = build_root(build_runner); let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?); let calculate = move |deps: &BuildDeps, pkg_fingerprint: Option<&dyn Fn() -> CargoResult>| { if deps.rerun_if_changed.is_empty() && deps.rerun_if_env_changed.is_empty() { match pkg_fingerprint { // FIXME: this is somewhat buggy with respect to docker and // weird filesystems. The `Precalculated` variant // constructed below will, for `path` dependencies, contain // a stringified version of the mtime for the local crate. // This violates one of the things we describe in this // module's doc comment, never hashing mtimes. We should // figure out a better scheme where a package fingerprint // may be a string (like for a registry) or a list of files // (like for a path dependency). Those list of files would // be stored here rather than the mtime of them. Some(f) => { let s = f()?; debug!( "old local fingerprints deps {:?} precalculated={:?}", pkg_root, s ); return Ok(Some(vec![LocalFingerprint::Precalculated(s)])); } None => return Ok(None), } } // Ok so now we're in "new mode" where we can have files listed as // dependencies as well as env vars listed as dependencies. Process // them all here. Ok(Some(local_fingerprints_deps( deps, &build_dir, &pkg_root, &env_config, ))) }; // Note that `false` == "not overridden" Ok((Box::new(calculate), false)) } /// Create a [`LocalFingerprint`] for an overridden build script. /// Returns None if it is not overridden. fn build_script_override_fingerprint( build_runner: &mut BuildRunner<'_, '_>, unit: &Unit, ) -> Option { // Build script output is only populated at this stage when it is // overridden. let build_script_outputs = build_runner.build_script_outputs.lock().unwrap(); let metadata = build_runner.get_run_build_script_metadata(unit); // Returns None if it is not overridden. let output = build_script_outputs.get(metadata)?; let s = format!( "overridden build state with hash: {}", util::hash_u64(output) ); Some(LocalFingerprint::Precalculated(s)) } /// Compute the [`LocalFingerprint`] values for a [`RunCustomBuild`] unit for /// non-overridden new-style build scripts only. This is only used when `deps` /// is already known to have a nonempty `rerun-if-*` somewhere. /// /// [`RunCustomBuild`]: crate::core::compiler::CompileMode::RunCustomBuild fn local_fingerprints_deps( deps: &BuildDeps, build_root: &Path, pkg_root: &Path, env_config: &Arc>, ) -> Vec { debug!("new local fingerprints deps {:?}", pkg_root); let mut local = Vec::new(); if !deps.rerun_if_changed.is_empty() { // Note that like the module comment above says we are careful to never // store an absolute path in `LocalFingerprint`, so ensure that we strip // absolute prefixes from them. let output = deps .build_script_output .strip_prefix(build_root) .unwrap() .to_path_buf(); let paths = deps .rerun_if_changed .iter() .map(|p| p.strip_prefix(pkg_root).unwrap_or(p).to_path_buf()) .collect(); local.push(LocalFingerprint::RerunIfChanged { output, paths }); } local.extend( deps.rerun_if_env_changed .iter() .map(|s| LocalFingerprint::from_env(s, env_config)), ); local } /// Writes the short fingerprint hash value to `` /// and logs detailed JSON information to `.json`. fn write_fingerprint(loc: &Path, fingerprint: &Fingerprint) -> CargoResult<()> { debug_assert_ne!(fingerprint.rustc, 0); // fingerprint::new().rustc == 0, make sure it doesn't make it to the file system. // This is mostly so outside tools can reliably find out what rust version this file is for, // as we can use the full hash. let hash = fingerprint.hash_u64(); debug!("write fingerprint ({:x}) : {}", hash, loc.display()); paths::write(loc, util::to_hex(hash).as_bytes())?; let json = serde_json::to_string(fingerprint).unwrap(); if cfg!(debug_assertions) { let f: Fingerprint = serde_json::from_str(&json).unwrap(); assert_eq!(f.hash_u64(), hash); } paths::write(&loc.with_extension("json"), json.as_bytes())?; Ok(()) } /// Prepare for work when a package starts to build pub fn prepare_init(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<()> { let new1 = build_runner.files().fingerprint_dir(unit); // Doc tests have no output, thus no fingerprint. if !new1.exists() && !unit.mode.is_doc_test() { paths::create_dir_all(&new1)?; } Ok(()) } /// Returns the location that the dep-info file will show up at /// for the [`Unit`] specified. pub fn dep_info_loc(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> PathBuf { build_runner.files().fingerprint_file_path(unit, "dep-") } /// Returns an absolute path that build directory. /// All paths are rewritten to be relative to this. fn build_root(build_runner: &BuildRunner<'_, '_>) -> PathBuf { build_runner.bcx.ws.build_dir().into_path_unlocked() } /// Reads the value from the old fingerprint hash file and compare. /// /// If dirty, it then restores the detailed information /// from the fingerprint JSON file, and provides an rich dirty reason. fn compare_old_fingerprint( unit: &Unit, old_hash_path: &Path, new_fingerprint: &Fingerprint, mtime_on_use: bool, forced: bool, ) -> Option { if mtime_on_use { // update the mtime so other cleaners know we used it let t = FileTime::from_system_time(SystemTime::now()); debug!("mtime-on-use forcing {:?} to {}", old_hash_path, t); paths::set_file_time_no_err(old_hash_path, t); } let compare = _compare_old_fingerprint(old_hash_path, new_fingerprint); match compare.as_ref() { Ok(None) => {} Ok(Some(reason)) => { info!( "fingerprint dirty for {}/{:?}/{:?}", unit.pkg, unit.mode, unit.target, ); info!(" dirty: {reason:?}"); } Err(e) => { info!( "fingerprint error for {}/{:?}/{:?}", unit.pkg, unit.mode, unit.target, ); info!(" err: {e:?}"); } } match compare { Ok(None) if forced => Some(DirtyReason::Forced), Ok(reason) => reason, Err(_) => Some(DirtyReason::FreshBuild), } } fn _compare_old_fingerprint( old_hash_path: &Path, new_fingerprint: &Fingerprint, ) -> CargoResult> { let old_fingerprint_short = paths::read(old_hash_path)?; let new_hash = new_fingerprint.hash_u64(); if util::to_hex(new_hash) == old_fingerprint_short && new_fingerprint.fs_status.up_to_date() { return Ok(None); } let old_fingerprint_json = paths::read(&old_hash_path.with_extension("json"))?; let old_fingerprint: Fingerprint = serde_json::from_str(&old_fingerprint_json) .with_context(|| internal("failed to deserialize json"))?; // Fingerprint can be empty after a failed rebuild (see comment in prepare_target). if !old_fingerprint_short.is_empty() { debug_assert_eq!( util::to_hex(old_fingerprint.hash_u64()), old_fingerprint_short ); } Ok(Some(new_fingerprint.compare(&old_fingerprint))) } /// Calculates the fingerprint of a unit thats contains no dep-info files. fn pkg_fingerprint(bcx: &BuildContext<'_, '_>, pkg: &Package) -> CargoResult { let source_id = pkg.package_id().source_id(); let sources = bcx.packages.sources(); let source = sources .get(source_id) .ok_or_else(|| internal("missing package source"))?; source.fingerprint(pkg) } /// The `reference` file is considered as "stale" if any file from `paths` has a newer mtime. fn find_stale_file( mtime_cache: &mut HashMap, checksum_cache: &mut HashMap, reference: &Path, paths: I, use_checksums: bool, ) -> Option where I: IntoIterator)>, P: AsRef, { let Ok(reference_mtime) = paths::mtime(reference) else { return Some(StaleItem::MissingFile(reference.to_path_buf())); }; let skipable_dirs = if let Ok(cargo_home) = home::cargo_home() { let skipable_dirs: Vec<_> = ["git", "registry"] .into_iter() .map(|subfolder| cargo_home.join(subfolder)) .collect(); Some(skipable_dirs) } else { None }; for (path, prior_checksum) in paths { let path = path.as_ref(); // Assuming anything in cargo_home/{git, registry} is immutable // (see also #9455 about marking the src directory readonly) which avoids rebuilds when CI // caches $CARGO_HOME/registry/{index, cache} and $CARGO_HOME/git/db across runs, keeping // the content the same but changing the mtime. if let Some(ref skipable_dirs) = skipable_dirs { if skipable_dirs.iter().any(|dir| path.starts_with(dir)) { continue; } } if use_checksums { let Some((file_len, prior_checksum)) = prior_checksum else { return Some(StaleItem::MissingChecksum(path.to_path_buf())); }; let path_buf = path.to_path_buf(); let path_checksum = match checksum_cache.entry(path_buf) { Entry::Occupied(o) => *o.get(), Entry::Vacant(v) => { let Ok(current_file_len) = fs::metadata(&path).map(|m| m.len()) else { return Some(StaleItem::FailedToReadMetadata(path.to_path_buf())); }; if current_file_len != file_len { return Some(StaleItem::FileSizeChanged { path: path.to_path_buf(), new_size: current_file_len, old_size: file_len, }); } let Ok(file) = File::open(path) else { return Some(StaleItem::MissingFile(path.to_path_buf())); }; let Ok(checksum) = Checksum::compute(prior_checksum.algo(), file) else { return Some(StaleItem::UnableToReadFile(path.to_path_buf())); }; *v.insert(checksum) } }; if path_checksum == prior_checksum { continue; } return Some(StaleItem::ChangedChecksum { source: path.to_path_buf(), stored_checksum: prior_checksum, new_checksum: path_checksum, }); } else { let path_mtime = match mtime_cache.entry(path.to_path_buf()) { Entry::Occupied(o) => *o.get(), Entry::Vacant(v) => { let Ok(mtime) = paths::mtime_recursive(path) else { return Some(StaleItem::MissingFile(path.to_path_buf())); }; *v.insert(mtime) } }; // TODO: fix #5918. // Note that equal mtimes should be considered "stale". For filesystems with // not much timestamp precision like 1s this is would be a conservative approximation // to handle the case where a file is modified within the same second after // a build starts. We want to make sure that incremental rebuilds pick that up! // // For filesystems with nanosecond precision it's been seen in the wild that // its "nanosecond precision" isn't really nanosecond-accurate. It turns out that // kernels may cache the current time so files created at different times actually // list the same nanosecond precision. Some digging on #5919 picked up that the // kernel caches the current time between timer ticks, which could mean that if // a file is updated at most 10ms after a build starts then Cargo may not // pick up the build changes. // // All in all, an equality check here would be a conservative assumption that, // if equal, files were changed just after a previous build finished. // Unfortunately this became problematic when (in #6484) cargo switch to more accurately // measuring the start time of builds. if path_mtime <= reference_mtime { continue; } return Some(StaleItem::ChangedFile { reference: reference.to_path_buf(), reference_mtime, stale: path.to_path_buf(), stale_mtime: path_mtime, }); } } debug!( "all paths up-to-date relative to {:?} mtime={}", reference, reference_mtime ); None } cargo-0.91.0/src/cargo/core/compiler/future_incompat.rs000064400000000000000000000435321046102023000212130ustar 00000000000000//! Support for [future-incompatible warning reporting][1]. //! //! Here is an overview of how Cargo handles future-incompatible reports. //! //! ## Receive reports from the compiler //! //! When receiving a compiler message during a build, if it is effectively //! a [`FutureIncompatReport`], Cargo gathers and forwards it as a //! `Message::FutureIncompatReport` to the main thread. //! //! To have the correct layout of structures for deserializing a report //! emitted by the compiler, most of structure definitions, for example //! [`FutureIncompatReport`], are copied either partially or entirely from //! [compiler/rustc_errors/src/json.rs][2] in rust-lang/rust repository. //! //! ## Persist reports on disk //! //! When a build comes to an end, by calling [`save_and_display_report`] //! Cargo saves the report on disk, and displays it directly if requested //! via command line or configuration. The information of the on-disk file can //! be found in [`FUTURE_INCOMPAT_FILE`]. //! //! During the persistent process, Cargo will attempt to query the source of //! each package emitting the report, for the sake of providing an upgrade //! information as a solution to fix the incompatibility. //! //! ## Display reports to users //! //! Users can run `cargo report future-incompat` to retrieve a report. This is //! done by [`OnDiskReports::load`]. Cargo simply prints reports to the //! standard output. //! //! [1]: https://doc.rust-lang.org/nightly/cargo/reference/future-incompat-report.html //! [2]: https://github.com/rust-lang/rust/blob/9bb6e60d1f1360234aae90c97964c0fa5524f141/compiler/rustc_errors/src/json.rs#L312-L315 use crate::core::compiler::BuildContext; use crate::core::{Dependency, PackageId, Workspace}; use crate::sources::SourceConfigMap; use crate::sources::source::QueryKind; use crate::util::CargoResult; use crate::util::cache_lock::CacheLockMode; use anyhow::{Context, bail, format_err}; use serde::{Deserialize, Serialize}; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::fmt::Write as _; use std::io::{Read, Write}; use std::task::Poll; pub const REPORT_PREAMBLE: &str = "\ The following warnings were discovered during the build. These warnings are an indication that the packages contain code that will become an error in a future release of Rust. These warnings typically cover changes to close soundness problems, unintended or undocumented behavior, or critical problems that cannot be fixed in a backwards-compatible fashion, and are not expected to be in wide use. Each warning should contain a link for more information on what the warning means and how to resolve it. "; /// Current version of the on-disk format. const ON_DISK_VERSION: u32 = 0; /// The future incompatibility report, emitted by the compiler as a JSON message. #[derive(serde::Deserialize)] pub struct FutureIncompatReport { pub future_incompat_report: Vec, } /// Structure used for collecting reports in-memory. pub struct FutureIncompatReportPackage { pub package_id: PackageId, /// Whether or not this is a local package, or a remote dependency. pub is_local: bool, pub items: Vec, } /// A single future-incompatible warning emitted by rustc. #[derive(Serialize, Deserialize)] pub struct FutureBreakageItem { /// The date at which this lint will become an error. /// Currently unused pub future_breakage_date: Option, /// The original diagnostic emitted by the compiler pub diagnostic: Diagnostic, } /// A diagnostic emitted by the compiler as a JSON message. /// We only care about the 'rendered' field #[derive(Serialize, Deserialize)] pub struct Diagnostic { pub rendered: String, pub level: String, } /// The filename in the top-level `build-dir` directory where we store /// the report const FUTURE_INCOMPAT_FILE: &str = ".future-incompat-report.json"; /// Max number of reports to save on disk. const MAX_REPORTS: usize = 5; /// The structure saved to disk containing the reports. #[derive(Serialize, Deserialize)] pub struct OnDiskReports { /// A schema version number, to handle older cargo's from trying to read /// something that they don't understand. version: u32, /// The report ID to use for the next report to save. next_id: u32, /// Available reports. reports: Vec, } /// A single report for a given compilation session. #[derive(Serialize, Deserialize)] struct OnDiskReport { /// Unique reference to the report for the `--id` CLI flag. id: u32, /// A message describing suggestions for fixing the /// reported issues suggestion_message: String, /// Report, suitable for printing to the console. /// Maps package names to the corresponding report /// We use a `BTreeMap` so that the iteration order /// is stable across multiple runs of `cargo` per_package: BTreeMap, } impl Default for OnDiskReports { fn default() -> OnDiskReports { OnDiskReports { version: ON_DISK_VERSION, next_id: 1, reports: Vec::new(), } } } impl OnDiskReports { /// Saves a new report returning its id pub fn save_report( mut self, ws: &Workspace<'_>, suggestion_message: String, per_package: BTreeMap, ) -> u32 { if let Some(existing_id) = self.has_report(&per_package) { return existing_id; } let report = OnDiskReport { id: self.next_id, suggestion_message, per_package, }; let saved_id = report.id; self.next_id += 1; self.reports.push(report); if self.reports.len() > MAX_REPORTS { self.reports.remove(0); } let on_disk = serde_json::to_vec(&self).unwrap(); if let Err(e) = ws .build_dir() .open_rw_exclusive_create( FUTURE_INCOMPAT_FILE, ws.gctx(), "Future incompatibility report", ) .and_then(|file| { let mut file = file.file(); file.set_len(0)?; file.write_all(&on_disk)?; Ok(()) }) { crate::display_warning_with_error( "failed to write on-disk future incompatible report", &e, &mut ws.gctx().shell(), ); } saved_id } /// Returns the ID of a report if it is already on disk. fn has_report(&self, rendered_per_package: &BTreeMap) -> Option { self.reports .iter() .find(|existing| &existing.per_package == rendered_per_package) .map(|report| report.id) } /// Loads the on-disk reports. pub fn load(ws: &Workspace<'_>) -> CargoResult { let report_file = match ws.build_dir().open_ro_shared( FUTURE_INCOMPAT_FILE, ws.gctx(), "Future incompatible report", ) { Ok(r) => r, Err(e) => { if let Some(io_err) = e.downcast_ref::() { if io_err.kind() == std::io::ErrorKind::NotFound { bail!("no reports are currently available"); } } return Err(e); } }; let mut file_contents = String::new(); report_file .file() .read_to_string(&mut file_contents) .context("failed to read report")?; let on_disk_reports: OnDiskReports = serde_json::from_str(&file_contents).context("failed to load report")?; if on_disk_reports.version != ON_DISK_VERSION { bail!("unable to read reports; reports were saved from a future version of Cargo"); } Ok(on_disk_reports) } /// Returns the most recent report ID. pub fn last_id(&self) -> u32 { self.reports.last().map(|r| r.id).unwrap() } /// Returns an ANSI-styled report pub fn get_report(&self, id: u32, package: Option<&str>) -> CargoResult { let report = self.reports.iter().find(|r| r.id == id).ok_or_else(|| { let available = itertools::join(self.reports.iter().map(|r| r.id), ", "); format_err!( "could not find report with ID {}\n\ Available IDs are: {}", id, available ) })?; let mut to_display = report.suggestion_message.clone(); to_display += "\n"; let package_report = if let Some(package) = package { report .per_package .get(package) .ok_or_else(|| { format_err!( "could not find package with ID `{}`\n Available packages are: {}\n Omit the `--package` flag to display a report for all packages", package, itertools::join(report.per_package.keys(), ", ") ) })? .to_string() } else { report .per_package .values() .cloned() .collect::>() .join("\n") }; to_display += &package_report; Ok(to_display) } } fn render_report(per_package_reports: &[FutureIncompatReportPackage]) -> BTreeMap { let mut report: BTreeMap = BTreeMap::new(); for per_package in per_package_reports { let package_spec = format!( "{}@{}", per_package.package_id.name(), per_package.package_id.version() ); let rendered = report.entry(package_spec).or_default(); rendered.push_str(&format!( "The package `{}` currently triggers the following future incompatibility lints:\n", per_package.package_id )); for item in &per_package.items { rendered.extend( item.diagnostic .rendered .lines() .map(|l| format!("> {}\n", l)), ); } } report } /// Returns a user-readable message explaining which of /// the packages in `package_ids` have updates available. /// This is best-effort - if an error occurs, `None` will be returned. fn get_updates(ws: &Workspace<'_>, package_ids: &BTreeSet) -> Option { // This in general ignores all errors since this is opportunistic. let _lock = ws .gctx() .acquire_package_cache_lock(CacheLockMode::DownloadExclusive) .ok()?; // Create a set of updated registry sources. let map = SourceConfigMap::new(ws.gctx()).ok()?; let mut package_ids: BTreeSet<_> = package_ids .iter() .filter(|pkg_id| pkg_id.source_id().is_registry()) .collect(); let source_ids: HashSet<_> = package_ids .iter() .map(|pkg_id| pkg_id.source_id()) .collect(); let mut sources: HashMap<_, _> = source_ids .into_iter() .filter_map(|sid| { let source = map.load(sid, &HashSet::new()).ok()?; Some((sid, source)) }) .collect(); // Query the sources for new versions, mapping `package_ids` into `summaries`. let mut summaries = Vec::new(); while !package_ids.is_empty() { package_ids.retain(|&pkg_id| { let Some(source) = sources.get_mut(&pkg_id.source_id()) else { return false; }; let Ok(dep) = Dependency::parse(pkg_id.name(), None, pkg_id.source_id()) else { return false; }; match source.query_vec(&dep, QueryKind::Exact) { Poll::Ready(Ok(sum)) => { summaries.push((pkg_id, sum)); false } Poll::Ready(Err(_)) => false, Poll::Pending => true, } }); for (_, source) in sources.iter_mut() { source.block_until_ready().ok()?; } } let mut updates = String::new(); for (pkg_id, summaries) in summaries { let mut updated_versions: Vec<_> = summaries .iter() .map(|summary| summary.as_summary().version()) .filter(|version| *version > pkg_id.version()) .collect(); updated_versions.sort(); if !updated_versions.is_empty() { let updated_versions = itertools::join(updated_versions, ", "); writeln!( updates, "{} has the following newer versions available: {}", pkg_id, updated_versions ) .unwrap(); } } Some(updates) } /// Writes a future-incompat report to disk, using the per-package /// reports gathered during the build. If requested by the user, /// a message is also displayed in the build output. pub fn save_and_display_report( bcx: &BuildContext<'_, '_>, per_package_future_incompat_reports: &[FutureIncompatReportPackage], ) { let should_display_message = match bcx.gctx.future_incompat_config() { Ok(config) => config.should_display_message(), Err(e) => { crate::display_warning_with_error( "failed to read future-incompat config from disk", &e, &mut bcx.gctx.shell(), ); true } }; if per_package_future_incompat_reports.is_empty() { // Explicitly passing a command-line flag overrides // `should_display_message` from the config file if bcx.build_config.future_incompat_report { drop( bcx.gctx .shell() .note("0 dependencies had future-incompatible warnings"), ); } return; } let current_reports = match OnDiskReports::load(bcx.ws) { Ok(r) => r, Err(e) => { tracing::debug!( "saving future-incompatible reports failed to load current reports: {:?}", e ); OnDiskReports::default() } }; let rendered_report = render_report(per_package_future_incompat_reports); // If the report is already on disk, then it will reuse the same ID, // otherwise prepare for the next ID. let report_id = current_reports .has_report(&rendered_report) .unwrap_or(current_reports.next_id); // Get a list of unique and sorted package name/versions. let package_ids: BTreeSet<_> = per_package_future_incompat_reports .iter() .map(|r| r.package_id) .collect(); let package_vers: Vec<_> = package_ids.iter().map(|pid| pid.to_string()).collect(); if should_display_message || bcx.build_config.future_incompat_report { drop(bcx.gctx.shell().warn(&format!( "the following packages contain code that will be rejected by a future \ version of Rust: {}", package_vers.join(", ") ))); } let updated_versions = get_updates(bcx.ws, &package_ids).unwrap_or(String::new()); let update_message = if !updated_versions.is_empty() { format!( " - Some affected dependencies have newer versions available. You may want to consider updating them to a newer version to see if the issue has been fixed. {updated_versions}\n", updated_versions = updated_versions ) } else { String::new() }; let upstream_info = package_ids .iter() .map(|package_id| { let manifest = bcx.packages.get_one(*package_id).unwrap().manifest(); format!( " - {package_spec} - Repository: {url} - Detailed warning command: `cargo report future-incompatibilities --id {id} --package {package_spec}`", package_spec = format!("{}@{}", package_id.name(), package_id.version()), url = manifest .metadata() .repository .as_deref() .unwrap_or(""), id = report_id, ) }) .collect::>() .join("\n"); let all_is_local = per_package_future_incompat_reports .iter() .all(|report| report.is_local); let suggestion_message = if all_is_local { String::new() } else { format!( " To solve this problem, you can try the following approaches: {update_message}\ - If the issue is not solved by updating the dependencies, a fix has to be implemented by those dependencies. You can help with that by notifying the maintainers of this problem (e.g. by creating a bug report) or by proposing a fix to the maintainers (e.g. by creating a pull request): {upstream_info} - If waiting for an upstream fix is not an option, you can use the `[patch]` section in `Cargo.toml` to use your own version of the dependency. For more information, see: https://doc.rust-lang.org/cargo/reference/overriding-dependencies.html#the-patch-section ", upstream_info = upstream_info, update_message = update_message, ) }; let saved_report_id = current_reports.save_report(bcx.ws, suggestion_message.clone(), rendered_report); if bcx.build_config.future_incompat_report { if !suggestion_message.is_empty() { drop(bcx.gctx.shell().note(&suggestion_message)); } drop(bcx.gctx.shell().note(&format!( "this report can be shown with `cargo report \ future-incompatibilities --id {}`", saved_report_id ))); } else if should_display_message { drop(bcx.gctx.shell().note(&format!( "to see what the problems were, use the option \ `--future-incompat-report`, or run `cargo report \ future-incompatibilities --id {}`", saved_report_id ))); } } cargo-0.91.0/src/cargo/core/compiler/job_queue/job.rs000064400000000000000000000056531046102023000205410ustar 00000000000000//! See [`Job`] and [`Work`]. use std::fmt; use std::mem; use super::JobState; use crate::core::compiler::fingerprint::DirtyReason; use crate::util::CargoResult; /// Represents a unit of [`Work`] with a [`Freshness`] for caller /// to determine whether to re-execute or not. pub struct Job { work: Work, fresh: Freshness, } /// The basic unit of work. /// /// Each proc should send its description before starting. /// It should send either once or close immediately. pub struct Work { inner: Box) -> CargoResult<()> + Send>, } impl Work { /// Creates a unit of work. pub fn new(f: F) -> Work where F: FnOnce(&JobState<'_, '_>) -> CargoResult<()> + Send + 'static, { Work { inner: Box::new(f) } } /// Creates a unit of work that does nothing. pub fn noop() -> Work { Work::new(|_| Ok(())) } /// Consumes this work by running it. pub fn call(self, tx: &JobState<'_, '_>) -> CargoResult<()> { (self.inner)(tx) } /// Creates a new unit of work that chains `next` after ourself. pub fn then(self, next: Work) -> Work { Work::new(move |state| { self.call(state)?; next.call(state) }) } } impl Job { /// Creates a new job that does nothing. pub fn new_fresh() -> Job { Job { work: Work::noop(), fresh: Freshness::Fresh, } } /// Creates a new job representing a unit of work. pub fn new_dirty(work: Work, dirty_reason: DirtyReason) -> Job { Job { work, fresh: Freshness::Dirty(dirty_reason), } } /// Consumes this job by running it, returning the result of the /// computation. pub fn run(self, state: &JobState<'_, '_>) -> CargoResult<()> { self.work.call(state) } /// Returns whether this job was fresh/dirty, where "fresh" means we're /// likely to perform just some small bookkeeping where "dirty" means we'll /// probably do something slow like invoke rustc. pub fn freshness(&self) -> &Freshness { &self.fresh } /// Chains the given work by putting it in front of our own unit of work. pub fn before(&mut self, next: Work) { let prev = mem::replace(&mut self.work, Work::noop()); self.work = next.then(prev); } } impl fmt::Debug for Job { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Job {{ ... }}") } } /// Indication of the freshness of a package. /// /// A fresh package does not necessarily need to be rebuilt (unless a dependency /// was also rebuilt), and a dirty package must always be rebuilt. #[derive(Debug, Clone)] pub enum Freshness { Fresh, Dirty(DirtyReason), } impl Freshness { pub fn is_dirty(&self) -> bool { matches!(self, Freshness::Dirty(_)) } pub fn is_fresh(&self) -> bool { matches!(self, Freshness::Fresh) } } cargo-0.91.0/src/cargo/core/compiler/job_queue/job_state.rs000064400000000000000000000164011046102023000217320ustar 00000000000000//! See [`JobState`]. use std::{cell::Cell, marker, sync::Arc}; use cargo_util::ProcessBuilder; use crate::CargoResult; use crate::core::compiler::build_runner::OutputFile; use crate::core::compiler::future_incompat::FutureBreakageItem; use crate::util::Queue; use super::{Artifact, DiagDedupe, Job, JobId, Message}; /// A `JobState` is constructed by `JobQueue::run` and passed to `Job::run`. It includes everything /// necessary to communicate between the main thread and the execution of the job. /// /// The job may execute on either a dedicated thread or the main thread. If the job executes on the /// main thread, the `output` field must be set to prevent a deadlock. pub struct JobState<'a, 'gctx> { /// Channel back to the main thread to coordinate messages and such. /// /// When the `output` field is `Some`, care must be taken to avoid calling `push_bounded` on /// the message queue to prevent a deadlock. messages: Arc>, /// Normally output is sent to the job queue with backpressure. When the job is fresh /// however we need to immediately display the output to prevent a deadlock as the /// output messages are processed on the same thread as they are sent from. `output` /// defines where to output in this case. /// /// Currently the [`Shell`] inside [`GlobalContext`] is wrapped in a `RefCell` and thus can't /// be passed between threads. This means that it isn't possible for multiple output messages /// to be interleaved. In the future, it may be wrapped in a `Mutex` instead. In this case /// interleaving is still prevented as the lock would be held for the whole printing of an /// output message. /// /// [`Shell`]: crate::core::Shell /// [`GlobalContext`]: crate::GlobalContext output: Option<&'a DiagDedupe<'gctx>>, /// The job id that this state is associated with, used when sending /// messages back to the main thread. id: JobId, /// Whether or not we're expected to have a call to `rmeta_produced`. Once /// that method is called this is dynamically set to `false` to prevent /// sending a double message later on. rmeta_required: Cell, // Historical versions of Cargo made use of the `'a` argument here, so to // leave the door open to future refactorings keep it here. _marker: marker::PhantomData<&'a ()>, } impl<'a, 'gctx> JobState<'a, 'gctx> { pub(super) fn new( id: JobId, messages: Arc>, output: Option<&'a DiagDedupe<'gctx>>, rmeta_required: bool, ) -> Self { Self { id, messages, output, rmeta_required: Cell::new(rmeta_required), _marker: marker::PhantomData, } } pub fn running(&self, cmd: &ProcessBuilder) { self.messages.push(Message::Run(self.id, cmd.to_string())); } pub fn build_plan( &self, module_name: String, cmd: ProcessBuilder, filenames: Arc>, ) { self.messages .push(Message::BuildPlanMsg(module_name, cmd, filenames)); } pub fn stdout(&self, stdout: String) -> CargoResult<()> { if let Some(dedupe) = self.output { writeln!(dedupe.gctx.shell().out(), "{}", stdout)?; } else { self.messages.push_bounded(Message::Stdout(stdout)); } Ok(()) } pub fn stderr(&self, stderr: String) -> CargoResult<()> { if let Some(dedupe) = self.output { let mut shell = dedupe.gctx.shell(); shell.print_ansi_stderr(stderr.as_bytes())?; shell.err().write_all(b"\n")?; } else { self.messages.push_bounded(Message::Stderr(stderr)); } Ok(()) } /// See [`Message::Diagnostic`] and [`Message::WarningCount`]. pub fn emit_diag(&self, level: &str, diag: String, fixable: bool) -> CargoResult<()> { if let Some(dedupe) = self.output { let emitted = dedupe.emit_diag(&diag)?; if level == "warning" { self.messages.push(Message::WarningCount { id: self.id, emitted, fixable, }); } } else { self.messages.push_bounded(Message::Diagnostic { id: self.id, level: level.to_string(), diag, fixable, }); } Ok(()) } /// See [`Message::Warning`]. pub fn warning(&self, warning: String) { self.messages.push_bounded(Message::Warning { id: self.id, warning, }); } /// A method used to signal to the coordinator thread that the rmeta file /// for an rlib has been produced. This is only called for some rmeta /// builds when required, and can be called at any time before a job ends. /// This should only be called once because a metadata file can only be /// produced once! pub fn rmeta_produced(&self) { self.rmeta_required.set(false); self.messages .push(Message::Finish(self.id, Artifact::Metadata, Ok(()))); } /// Drives a [`Job`] to finish. This ensures that a [`Message::Finish`] is /// sent even if our job panics. pub(super) fn run_to_finish(self, job: Job) { let mut sender = FinishOnDrop { messages: &self.messages, id: self.id, result: None, }; sender.result = Some(job.run(&self)); // If the `rmeta_required` wasn't consumed but it was set // previously, then we either have: // // 1. The `job` didn't do anything because it was "fresh". // 2. The `job` returned an error and didn't reach the point where // it called `rmeta_produced`. // 3. We forgot to call `rmeta_produced` and there's a bug in Cargo. // // Ruling out the third, the other two are pretty common for 2 // we'll just naturally abort the compilation operation but for 1 // we need to make sure that the metadata is flagged as produced so // send a synthetic message here. if self.rmeta_required.get() && sender.result.as_ref().unwrap().is_ok() { self.messages .push(Message::Finish(self.id, Artifact::Metadata, Ok(()))); } // Use a helper struct with a `Drop` implementation to guarantee // that a `Finish` message is sent even if our job panics. We // shouldn't panic unless there's a bug in Cargo, so we just need // to make sure nothing hangs by accident. struct FinishOnDrop<'a> { messages: &'a Queue, id: JobId, result: Option>, } impl Drop for FinishOnDrop<'_> { fn drop(&mut self) { let result = self .result .take() .unwrap_or_else(|| Err(anyhow::format_err!("worker panicked"))); self.messages .push(Message::Finish(self.id, Artifact::All, result)); } } } pub fn future_incompat_report(&self, report: Vec) { self.messages .push(Message::FutureIncompatReport(self.id, report)); } } cargo-0.91.0/src/cargo/core/compiler/job_queue/mod.rs000064400000000000000000001425331046102023000205450ustar 00000000000000//! Management of the interaction between the main `cargo` and all spawned jobs. //! //! ## Overview //! //! This module implements a job queue. A job here represents a unit of work, //! which is roughly a rustc invocation, a build script run, or just a no-op. //! The job queue primarily handles the following things: //! //! * Spawns concurrent jobs. Depending on its [`Freshness`], a job could be //! either executed on a spawned thread or ran on the same thread to avoid //! the threading overhead. //! * Controls the number of concurrency. It allocates and manages [`jobserver`] //! tokens to each spawned off rustc and build scripts. //! * Manages the communication between the main `cargo` process and its //! spawned jobs. Those [`Message`]s are sent over a [`Queue`] shared //! across threads. //! * Schedules the execution order of each [`Job`]. Priorities are determined //! when calling [`JobQueue::enqueue`] to enqueue a job. The scheduling is //! relatively rudimentary and could likely be improved. //! //! A rough outline of building a queue and executing jobs is: //! //! 1. [`JobQueue::new`] to simply create one queue. //! 2. [`JobQueue::enqueue`] to add new jobs onto the queue. //! 3. Consumes the queue and executes all jobs via [`JobQueue::execute`]. //! //! The primary loop happens insides [`JobQueue::execute`], which is effectively //! [`DrainState::drain_the_queue`]. [`DrainState`] is, as its name tells, //! the running state of the job queue getting drained. //! //! ## Jobserver //! //! As of Feb. 2023, Cargo and rustc have a relatively simple jobserver //! relationship with each other. They share a single jobserver amongst what //! is potentially hundreds of threads of work on many-cored systems. //! The jobserver could come from either the environment (e.g., from a `make` //! invocation), or from Cargo creating its own jobserver server if there is no //! jobserver to inherit from. //! //! Cargo wants to complete the build as quickly as possible, fully saturating //! all cores (as constrained by the `-j=N`) parameter. Cargo also must not spawn //! more than N threads of work: the total amount of tokens we have floating //! around must always be limited to N. //! //! It is not really possible to optimally choose which crate should build //! first or last; nor is it possible to decide whether to give an additional //! token to rustc first or rather spawn a new crate of work. The algorithm in //! Cargo prioritizes spawning as many crates (i.e., rustc processes) as //! possible. In short, the jobserver relationship among Cargo and rustc //! processes is **1 `cargo` to N `rustc`**. Cargo knows nothing beyond rustc //! processes in terms of parallelism[^parallel-rustc]. //! //! We integrate with the [jobserver] crate, originating from GNU make //! [POSIX jobserver], to make sure that build scripts which use make to //! build C code can cooperate with us on the number of used tokens and //! avoid overfilling the system we're on. //! //! ## Scheduling //! //! The current scheduling algorithm is not really polished. It is simply based //! on a dependency graph [`DependencyQueue`]. We continue adding nodes onto //! the graph until we finalize it. When the graph gets finalized, it finds the //! sum of the cost of each dependencies of each node, including transitively. //! The sum of dependency cost turns out to be the cost of each given node. //! //! At the time being, the cost is just passed as a fixed placeholder in //! [`JobQueue::enqueue`]. In the future, we could explore more possibilities //! around it. For instance, we start persisting timing information for each //! build somewhere. For a subsequent build, we can look into the historical //! data and perform a PGO-like optimization to prioritize jobs, making a build //! fully pipelined. //! //! ## Message queue //! //! Each spawned thread running a process uses the message queue [`Queue`] to //! send messages back to the main thread (the one running `cargo`). //! The main thread coordinates everything, and handles printing output. //! //! It is important to be careful which messages use [`push`] vs [`push_bounded`]. //! `push` is for priority messages (like tokens, or "finished") where the //! sender shouldn't block. We want to handle those so real work can proceed //! ASAP. //! //! `push_bounded` is only for messages being printed to stdout/stderr. Being //! bounded prevents a flood of messages causing a large amount of memory //! being used. //! //! `push` also avoids blocking which helps avoid deadlocks. For example, when //! the diagnostic server thread is dropped, it waits for the thread to exit. //! But if the thread is blocked on a full queue, and there is a critical //! error, the drop will deadlock. This should be fixed at some point in the //! future. The jobserver thread has a similar problem, though it will time //! out after 1 second. //! //! To access the message queue, each running `Job` is given its own [`JobState`], //! containing everything it needs to communicate with the main thread. //! //! See [`Message`] for all available message kinds. //! //! [^parallel-rustc]: In fact, `jobserver` that Cargo uses also manages the //! allocation of tokens to rustc beyond the implicit token each rustc owns //! (i.e., the ones used for parallel LLVM work and parallel rustc threads). //! See also ["Rust Compiler Development Guide: Parallel Compilation"] //! and [this comment][rustc-codegen] in rust-lang/rust. //! //! ["Rust Compiler Development Guide: Parallel Compilation"]: https://rustc-dev-guide.rust-lang.org/parallel-rustc.html //! [rustc-codegen]: https://github.com/rust-lang/rust/blob/5423745db8b434fcde54888b35f518f00cce00e4/compiler/rustc_codegen_ssa/src/back/write.rs#L1204-L1217 //! [jobserver]: https://docs.rs/jobserver //! [POSIX jobserver]: https://www.gnu.org/software/make/manual/html_node/POSIX-Jobserver.html //! [`push`]: Queue::push //! [`push_bounded`]: Queue::push_bounded mod job; mod job_state; use std::cell::RefCell; use std::collections::{HashMap, HashSet}; use std::fmt::Write as _; use std::io; use std::path::{Path, PathBuf}; use std::sync::Arc; use std::thread::{self, Scope}; use std::time::Duration; use anyhow::{Context as _, format_err}; use cargo_util::ProcessBuilder; use jobserver::{Acquired, HelperThread}; use semver::Version; use tracing::{debug, trace}; pub use self::job::Freshness::{self, Dirty, Fresh}; pub use self::job::{Job, Work}; pub use self::job_state::JobState; use super::build_runner::OutputFile; use super::custom_build::Severity; use super::timings::Timings; use super::{BuildContext, BuildPlan, BuildRunner, CompileMode, Unit}; use crate::core::compiler::descriptive_pkg_name; use crate::core::compiler::future_incompat::{ self, FutureBreakageItem, FutureIncompatReportPackage, }; use crate::core::resolver::ResolveBehavior; use crate::core::{PackageId, Shell, TargetKind}; use crate::util::CargoResult; use crate::util::context::WarningHandling; use crate::util::diagnostic_server::{self, DiagnosticPrinter}; use crate::util::errors::AlreadyPrintedError; use crate::util::machine_message::{self, Message as _}; use crate::util::{self, internal}; use crate::util::{DependencyQueue, GlobalContext, Progress, ProgressStyle, Queue}; /// This structure is backed by the `DependencyQueue` type and manages the /// queueing of compilation steps for each package. Packages enqueue units of /// work and then later on the entire graph is converted to `DrainState` and /// executed. pub struct JobQueue<'gctx> { queue: DependencyQueue, counts: HashMap, timings: Timings<'gctx>, } /// This structure is backed by the `DependencyQueue` type and manages the /// actual compilation step of each package. Packages enqueue units of work and /// then later on the entire graph is processed and compiled. /// /// It is created from `JobQueue` when we have fully assembled the crate graph /// (i.e., all package dependencies are known). struct DrainState<'gctx> { // This is the length of the DependencyQueue when starting out total_units: usize, queue: DependencyQueue, messages: Arc>, /// Diagnostic deduplication support. diag_dedupe: DiagDedupe<'gctx>, /// Count of warnings, used to print a summary after the job succeeds warning_count: HashMap, active: HashMap, compiled: HashSet, documented: HashSet, scraped: HashSet, counts: HashMap, progress: Progress<'gctx>, next_id: u32, timings: Timings<'gctx>, /// Tokens that are currently owned by this Cargo, and may be "associated" /// with a rustc process. They may also be unused, though if so will be /// dropped on the next loop iteration. /// /// Note that the length of this may be zero, but we will still spawn work, /// as we share the implicit token given to this Cargo process with a /// single rustc process. tokens: Vec, /// The list of jobs that we have not yet started executing, but have /// retrieved from the `queue`. We eagerly pull jobs off the main queue to /// allow us to request jobserver tokens pretty early. pending_queue: Vec<(Unit, Job, usize)>, print: DiagnosticPrinter<'gctx>, /// How many jobs we've finished finished: usize, per_package_future_incompat_reports: Vec, } /// Count of warnings, used to print a summary after the job succeeds #[derive(Default)] pub struct WarningCount { /// total number of warnings pub total: usize, /// number of warnings that were suppressed because they /// were duplicates of a previous warning pub duplicates: usize, /// number of fixable warnings set to `NotAllowed` /// if any errors have been seen ofr the current /// target pub fixable: FixableWarnings, } impl WarningCount { /// If an error is seen this should be called /// to set `fixable` to `NotAllowed` fn disallow_fixable(&mut self) { self.fixable = FixableWarnings::NotAllowed; } /// Checks fixable if warnings are allowed /// fixable warnings are allowed if no /// errors have been seen for the current /// target. If an error was seen `fixable` /// will be `NotAllowed`. fn fixable_allowed(&self) -> bool { match &self.fixable { FixableWarnings::NotAllowed => false, _ => true, } } } /// Used to keep track of how many fixable warnings there are /// and if fixable warnings are allowed #[derive(Default)] pub enum FixableWarnings { NotAllowed, #[default] Zero, Positive(usize), } pub struct ErrorsDuringDrain { pub count: usize, } struct ErrorToHandle { error: anyhow::Error, /// This field is true for "interesting" errors and false for "mundane" /// errors. If false, we print the above error only if it's the first one /// encountered so far while draining the job queue. /// /// At most places that an error is propagated, we set this to false to /// avoid scenarios where Cargo might end up spewing tons of redundant error /// messages. For example if an i/o stream got closed somewhere, we don't /// care about individually reporting every thread that it broke; just the /// first is enough. /// /// The exception where `print_always` is true is that we do report every /// instance of a rustc invocation that failed with diagnostics. This /// corresponds to errors from `Message::Finish`. print_always: bool, } impl From for ErrorToHandle where anyhow::Error: From, { fn from(error: E) -> Self { ErrorToHandle { error: anyhow::Error::from(error), print_always: false, } } } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct JobId(pub u32); impl std::fmt::Display for JobId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) } } /// Handler for deduplicating diagnostics. struct DiagDedupe<'gctx> { seen: RefCell>, gctx: &'gctx GlobalContext, } impl<'gctx> DiagDedupe<'gctx> { fn new(gctx: &'gctx GlobalContext) -> Self { DiagDedupe { seen: RefCell::new(HashSet::new()), gctx, } } /// Emits a diagnostic message. /// /// Returns `true` if the message was emitted, or `false` if it was /// suppressed for being a duplicate. fn emit_diag(&self, diag: &str) -> CargoResult { let h = util::hash_u64(diag); if !self.seen.borrow_mut().insert(h) { return Ok(false); } let mut shell = self.gctx.shell(); shell.print_ansi_stderr(diag.as_bytes())?; shell.err().write_all(b"\n")?; Ok(true) } } /// Possible artifacts that can be produced by compilations, used as edge values /// in the dependency graph. /// /// As edge values we can have multiple kinds of edges depending on one node, /// for example some units may only depend on the metadata for an rlib while /// others depend on the full rlib. This `Artifact` enum is used to distinguish /// this case and track the progress of compilations as they proceed. #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] enum Artifact { /// A generic placeholder for "depends on everything run by a step" and /// means that we can't start the next compilation until the previous has /// finished entirely. All, /// A node indicating that we only depend on the metadata of a compilation, /// but the compilation is typically also producing an rlib. We can start /// our step, however, before the full rlib is available. Metadata, } enum Message { Run(JobId, String), BuildPlanMsg(String, ProcessBuilder, Arc>), Stdout(String), Stderr(String), // This is for general stderr output from subprocesses Diagnostic { id: JobId, level: String, diag: String, fixable: bool, }, // This handles duplicate output that is suppressed, for showing // only a count of duplicate messages instead WarningCount { id: JobId, emitted: bool, fixable: bool, }, // This is for warnings generated by Cargo's interpretation of the // subprocess output, e.g. scrape-examples prints a warning if a // unit fails to be scraped Warning { id: JobId, warning: String, }, FixDiagnostic(diagnostic_server::Message), Token(io::Result), Finish(JobId, Artifact, CargoResult<()>), FutureIncompatReport(JobId, Vec), } impl<'gctx> JobQueue<'gctx> { pub fn new(bcx: &BuildContext<'_, 'gctx>) -> JobQueue<'gctx> { JobQueue { queue: DependencyQueue::new(), counts: HashMap::new(), timings: Timings::new(bcx, &bcx.roots), } } pub fn enqueue( &mut self, build_runner: &BuildRunner<'_, 'gctx>, unit: &Unit, job: Job, ) -> CargoResult<()> { let dependencies = build_runner.unit_deps(unit); let mut queue_deps = dependencies .iter() .filter(|dep| { // Binaries aren't actually needed to *compile* tests, just to run // them, so we don't include this dependency edge in the job graph. // But we shouldn't filter out dependencies being scraped for Rustdoc. (!dep.unit.target.is_test() && !dep.unit.target.is_bin()) || dep.unit.artifact.is_true() || dep.unit.mode.is_doc_scrape() }) .map(|dep| { // Handle the case here where our `unit -> dep` dependency may // only require the metadata, not the full compilation to // finish. Use the tables in `build_runner` to figure out what // kind of artifact is associated with this dependency. let artifact = if build_runner.only_requires_rmeta(unit, &dep.unit) { Artifact::Metadata } else { Artifact::All }; (dep.unit.clone(), artifact) }) .collect::>(); // This is somewhat tricky, but we may need to synthesize some // dependencies for this target if it requires full upstream // compilations to have completed. Because of pipelining, some // dependency edges may be `Metadata` due to the above clause (as // opposed to everything being `All`). For example consider: // // a (binary) // └ b (lib) // └ c (lib) // // Here the dependency edge from B to C will be `Metadata`, and the // dependency edge from A to B will be `All`. For A to be compiled, // however, it currently actually needs the full rlib of C. This means // that we need to synthesize a dependency edge for the dependency graph // from A to C. That's done here. // // This will walk all dependencies of the current target, and if any of // *their* dependencies are `Metadata` then we depend on the `All` of // the target as well. This should ensure that edges changed to // `Metadata` propagate upwards `All` dependencies to anything that // transitively contains the `Metadata` edge. if unit.requires_upstream_objects() { for dep in dependencies { depend_on_deps_of_deps(build_runner, &mut queue_deps, dep.unit.clone()); } fn depend_on_deps_of_deps( build_runner: &BuildRunner<'_, '_>, deps: &mut HashMap, unit: Unit, ) { for dep in build_runner.unit_deps(&unit) { if deps.insert(dep.unit.clone(), Artifact::All).is_none() { depend_on_deps_of_deps(build_runner, deps, dep.unit.clone()); } } } } // For now we use a fixed placeholder value for the cost of each unit, but // in the future this could be used to allow users to provide hints about // relative expected costs of units, or this could be automatically set in // a smarter way using timing data from a previous compilation. self.queue.queue(unit.clone(), job, queue_deps, 100); *self.counts.entry(unit.pkg.package_id()).or_insert(0) += 1; Ok(()) } /// Executes all jobs necessary to build the dependency graph. /// /// This function will spawn off `config.jobs()` workers to build all of the /// necessary dependencies, in order. Freshness is propagated as far as /// possible along each dependency chain. #[tracing::instrument(skip_all)] pub fn execute( mut self, build_runner: &mut BuildRunner<'_, '_>, plan: &mut BuildPlan, ) -> CargoResult<()> { self.queue.queue_finished(); let progress = Progress::with_style("Building", ProgressStyle::Ratio, build_runner.bcx.gctx); let state = DrainState { total_units: self.queue.len(), queue: self.queue, // 100 here is somewhat arbitrary. It is a few screenfulls of // output, and hopefully at most a few megabytes of memory for // typical messages. If you change this, please update the test // caching_large_output, too. messages: Arc::new(Queue::new(100)), diag_dedupe: DiagDedupe::new(build_runner.bcx.gctx), warning_count: HashMap::new(), active: HashMap::new(), compiled: HashSet::new(), documented: HashSet::new(), scraped: HashSet::new(), counts: self.counts, progress, next_id: 0, timings: self.timings, tokens: Vec::new(), pending_queue: Vec::new(), print: DiagnosticPrinter::new( build_runner.bcx.gctx, &build_runner.bcx.rustc().workspace_wrapper, ), finished: 0, per_package_future_incompat_reports: Vec::new(), }; // Create a helper thread for acquiring jobserver tokens let messages = state.messages.clone(); let helper = build_runner .jobserver .clone() .into_helper_thread(move |token| { messages.push(Message::Token(token)); }) .context("failed to create helper thread for jobserver management")?; // Create a helper thread to manage the diagnostics for rustfix if // necessary. let messages = state.messages.clone(); // It is important that this uses `push` instead of `push_bounded` for // now. If someone wants to fix this to be bounded, the `drop` // implementation needs to be changed to avoid possible deadlocks. let _diagnostic_server = build_runner .bcx .build_config .rustfix_diagnostic_server .borrow_mut() .take() .map(move |srv| srv.start(move |msg| messages.push(Message::FixDiagnostic(msg)))); thread::scope(move |scope| { match state.drain_the_queue(build_runner, plan, scope, &helper) { Some(err) => Err(err), None => Ok(()), } }) } } impl<'gctx> DrainState<'gctx> { fn spawn_work_if_possible<'s>( &mut self, build_runner: &mut BuildRunner<'_, '_>, jobserver_helper: &HelperThread, scope: &'s Scope<'s, '_>, ) -> CargoResult<()> { // Dequeue as much work as we can, learning about everything // possible that can run. Note that this is also the point where we // start requesting job tokens. Each job after the first needs to // request a token. while let Some((unit, job, priority)) = self.queue.dequeue() { // We want to keep the pieces of work in the `pending_queue` sorted // by their priorities, and insert the current job at its correctly // sorted position: following the lower priority jobs, and the ones // with the same priority (since they were dequeued before the // current one, we also keep that relation). let idx = self .pending_queue .partition_point(|&(_, _, p)| p <= priority); self.pending_queue.insert(idx, (unit, job, priority)); if self.active.len() + self.pending_queue.len() > 1 { jobserver_helper.request_token(); } } // Now that we've learned of all possible work that we can execute // try to spawn it so long as we've got a jobserver token which says // we're able to perform some parallel work. // The `pending_queue` is sorted in ascending priority order, and we // remove items from its end to schedule the highest priority items // sooner. while self.has_extra_tokens() && !self.pending_queue.is_empty() { let (unit, job, _) = self.pending_queue.pop().unwrap(); *self.counts.get_mut(&unit.pkg.package_id()).unwrap() -= 1; if !build_runner.bcx.build_config.build_plan { // Print out some nice progress information. // NOTE: An error here will drop the job without starting it. // That should be OK, since we want to exit as soon as // possible during an error. self.note_working_on( build_runner.bcx.gctx, build_runner.bcx.ws.root(), &unit, job.freshness(), )?; } self.run(&unit, job, build_runner, scope); } Ok(()) } fn has_extra_tokens(&self) -> bool { self.active.len() < self.tokens.len() + 1 } fn handle_event( &mut self, build_runner: &mut BuildRunner<'_, '_>, plan: &mut BuildPlan, event: Message, ) -> Result<(), ErrorToHandle> { let warning_handling = build_runner.bcx.gctx.warning_handling()?; match event { Message::Run(id, cmd) => { build_runner .bcx .gctx .shell() .verbose(|c| c.status("Running", &cmd))?; self.timings.unit_start(id, self.active[&id].clone()); } Message::BuildPlanMsg(module_name, cmd, filenames) => { plan.update(&module_name, &cmd, &filenames)?; } Message::Stdout(out) => { writeln!(build_runner.bcx.gctx.shell().out(), "{}", out)?; } Message::Stderr(err) => { let mut shell = build_runner.bcx.gctx.shell(); shell.print_ansi_stderr(err.as_bytes())?; shell.err().write_all(b"\n")?; } Message::Diagnostic { id, level, diag, fixable, } => { let emitted = self.diag_dedupe.emit_diag(&diag)?; if level == "warning" { self.bump_warning_count(id, emitted, fixable); } if level == "error" { let cnts = self.warning_count.entry(id).or_default(); // If there is an error, the `cargo fix` message should not show cnts.disallow_fixable(); } } Message::Warning { id, warning } => { if warning_handling != WarningHandling::Allow { build_runner.bcx.gctx.shell().warn(warning)?; } self.bump_warning_count(id, true, false); } Message::WarningCount { id, emitted, fixable, } => { self.bump_warning_count(id, emitted, fixable); } Message::FixDiagnostic(msg) => { self.print.print(&msg)?; } Message::Finish(id, artifact, result) => { let unit = match artifact { // If `id` has completely finished we remove it // from the `active` map ... Artifact::All => { trace!("end: {:?}", id); self.finished += 1; self.report_warning_count( build_runner, id, &build_runner.bcx.rustc().workspace_wrapper, ); self.active.remove(&id).unwrap() } // ... otherwise if it hasn't finished we leave it // in there as we'll get another `Finish` later on. Artifact::Metadata => { trace!("end (meta): {:?}", id); self.active[&id].clone() } }; debug!("end ({:?}): {:?}", unit, result); match result { Ok(()) => self.finish(id, &unit, artifact, build_runner)?, Err(_) if build_runner.bcx.unit_can_fail_for_docscraping(&unit) => { build_runner .failed_scrape_units .lock() .unwrap() .insert(build_runner.files().metadata(&unit).unit_id()); self.queue.finish(&unit, &artifact); } Err(error) => { let show_warnings = true; self.emit_log_messages(&unit, build_runner, show_warnings)?; self.back_compat_notice(build_runner, &unit)?; return Err(ErrorToHandle { error, print_always: true, }); } } } Message::FutureIncompatReport(id, items) => { let unit = &self.active[&id]; let package_id = unit.pkg.package_id(); let is_local = unit.is_local(); self.per_package_future_incompat_reports .push(FutureIncompatReportPackage { package_id, is_local, items, }); } Message::Token(acquired_token) => { let token = acquired_token.context("failed to acquire jobserver token")?; self.tokens.push(token); } } Ok(()) } // This will also tick the progress bar as appropriate fn wait_for_events(&mut self) -> Vec { // Drain all events at once to avoid displaying the progress bar // unnecessarily. If there's no events we actually block waiting for // an event, but we keep a "heartbeat" going to allow `record_cpu` // to run above to calculate CPU usage over time. To do this we // listen for a message with a timeout, and on timeout we run the // previous parts of the loop again. let mut events = self.messages.try_pop_all(); if events.is_empty() { loop { self.tick_progress(); self.tokens.truncate(self.active.len() - 1); match self.messages.pop(Duration::from_millis(500)) { Some(message) => { events.push(message); break; } None => continue, } } } events } /// This is the "main" loop, where Cargo does all work to run the /// compiler. /// /// This returns an Option to prevent the use of `?` on `Result` types /// because it is important for the loop to carefully handle errors. fn drain_the_queue<'s>( mut self, build_runner: &mut BuildRunner<'_, '_>, plan: &mut BuildPlan, scope: &'s Scope<'s, '_>, jobserver_helper: &HelperThread, ) -> Option { trace!("queue: {:#?}", self.queue); // Iteratively execute the entire dependency graph. Each turn of the // loop starts out by scheduling as much work as possible (up to the // maximum number of parallel jobs we have tokens for). A local queue // is maintained separately from the main dependency queue as one // dequeue may actually dequeue quite a bit of work (e.g., 10 binaries // in one package). // // After a job has finished we update our internal state if it was // successful and otherwise wait for pending work to finish if it failed // and then immediately return (or keep going, if requested by the build // config). let mut errors = ErrorsDuringDrain { count: 0 }; // CAUTION! Do not use `?` or break out of the loop early. Every error // must be handled in such a way that the loop is still allowed to // drain event messages. loop { if errors.count == 0 || build_runner.bcx.build_config.keep_going { if let Err(e) = self.spawn_work_if_possible(build_runner, jobserver_helper, scope) { self.handle_error(&mut build_runner.bcx.gctx.shell(), &mut errors, e); } } // If after all that we're not actually running anything then we're // done! if self.active.is_empty() { break; } // And finally, before we block waiting for the next event, drop any // excess tokens we may have accidentally acquired. Due to how our // jobserver interface is architected we may acquire a token that we // don't actually use, and if this happens just relinquish it back // to the jobserver itself. for event in self.wait_for_events() { if let Err(event_err) = self.handle_event(build_runner, plan, event) { self.handle_error(&mut build_runner.bcx.gctx.shell(), &mut errors, event_err); } } } self.progress.clear(); let profile_name = build_runner.bcx.build_config.requested_profile; // NOTE: this may be a bit inaccurate, since this may not display the // profile for what was actually built. Profile overrides can change // these settings, and in some cases different targets are built with // different profiles. To be accurate, it would need to collect a // list of Units built, and maybe display a list of the different // profiles used. However, to keep it simple and compatible with old // behavior, we just display what the base profile is. let profile = build_runner.bcx.profiles.base_profile(); let mut opt_type = String::from(if profile.opt_level.as_str() == "0" { "unoptimized" } else { "optimized" }); if profile.debuginfo.is_turned_on() { opt_type += " + debuginfo"; } let time_elapsed = util::elapsed(build_runner.bcx.gctx.creation_time().elapsed()); if let Err(e) = self.timings.finished(build_runner, &errors.to_error()) { self.handle_error(&mut build_runner.bcx.gctx.shell(), &mut errors, e); } if build_runner.bcx.build_config.emit_json() { let mut shell = build_runner.bcx.gctx.shell(); let msg = machine_message::BuildFinished { success: errors.count == 0, } .to_json_string(); if let Err(e) = writeln!(shell.out(), "{}", msg) { self.handle_error(&mut shell, &mut errors, e); } } if let Some(error) = errors.to_error() { // Any errors up to this point have already been printed via the // `display_error` inside `handle_error`. Some(anyhow::Error::new(AlreadyPrintedError::new(error))) } else if self.queue.is_empty() && self.pending_queue.is_empty() { let profile_link = build_runner.bcx.gctx.shell().err_hyperlink( "https://doc.rust-lang.org/cargo/reference/profiles.html#default-profiles", ); let message = format!( "{profile_link}`{profile_name}` profile [{opt_type}]{profile_link:#} target(s) in {time_elapsed}", ); if !build_runner.bcx.build_config.build_plan { // It doesn't really matter if this fails. let _ = build_runner.bcx.gctx.shell().status("Finished", message); future_incompat::save_and_display_report( build_runner.bcx, &self.per_package_future_incompat_reports, ); } None } else { debug!("queue: {:#?}", self.queue); Some(internal("finished with jobs still left in the queue")) } } fn handle_error( &mut self, shell: &mut Shell, err_state: &mut ErrorsDuringDrain, new_err: impl Into, ) { let new_err = new_err.into(); if new_err.print_always || err_state.count == 0 { crate::display_error(&new_err.error, shell); if err_state.count == 0 && !self.active.is_empty() { self.progress.indicate_error(); let _ = shell.warn("build failed, waiting for other jobs to finish..."); } err_state.count += 1; } else { tracing::warn!("{:?}", new_err.error); } } // This also records CPU usage and marks concurrency; we roughly want to do // this as often as we spin on the events receiver (at least every 500ms or // so). fn tick_progress(&mut self) { // Record some timing information if `--timings` is enabled, and // this'll end up being a noop if we're not recording this // information. self.timings.mark_concurrency( self.active.len(), self.pending_queue.len(), self.queue.len(), ); self.timings.record_cpu(); let active_names = self .active .values() .map(|u| self.name_for_progress(u)) .collect::>(); let _ = self.progress.tick_now( self.finished, self.total_units, &format!(": {}", active_names.join(", ")), ); } fn name_for_progress(&self, unit: &Unit) -> String { let pkg_name = unit.pkg.name(); let target_name = unit.target.name(); match unit.mode { CompileMode::Doc { .. } => format!("{}(doc)", pkg_name), CompileMode::RunCustomBuild => format!("{}(build)", pkg_name), CompileMode::Test | CompileMode::Check { test: true } => match unit.target.kind() { TargetKind::Lib(_) => format!("{}(test)", target_name), TargetKind::CustomBuild => panic!("cannot test build script"), TargetKind::Bin => format!("{}(bin test)", target_name), TargetKind::Test => format!("{}(test)", target_name), TargetKind::Bench => format!("{}(bench)", target_name), TargetKind::ExampleBin | TargetKind::ExampleLib(_) => { format!("{}(example test)", target_name) } }, _ => match unit.target.kind() { TargetKind::Lib(_) => pkg_name.to_string(), TargetKind::CustomBuild => format!("{}(build.rs)", pkg_name), TargetKind::Bin => format!("{}(bin)", target_name), TargetKind::Test => format!("{}(test)", target_name), TargetKind::Bench => format!("{}(bench)", target_name), TargetKind::ExampleBin | TargetKind::ExampleLib(_) => { format!("{}(example)", target_name) } }, } } /// Executes a job. /// /// Fresh jobs block until finished (which should be very fast!), Dirty /// jobs will spawn a thread in the background and return immediately. fn run<'s>( &mut self, unit: &Unit, job: Job, build_runner: &BuildRunner<'_, '_>, scope: &'s Scope<'s, '_>, ) { let id = JobId(self.next_id); self.next_id = self.next_id.checked_add(1).unwrap(); debug!("start {}: {:?}", id, unit); assert!(self.active.insert(id, unit.clone()).is_none()); let messages = self.messages.clone(); let is_fresh = job.freshness().is_fresh(); let rmeta_required = build_runner.rmeta_required(unit); let doit = move |diag_dedupe| { let state = JobState::new(id, messages, diag_dedupe, rmeta_required); state.run_to_finish(job); }; match is_fresh { true => { self.timings.add_fresh(); // Running a fresh job on the same thread is often much faster than spawning a new // thread to run the job. doit(Some(&self.diag_dedupe)); } false => { self.timings.add_dirty(); scope.spawn(move || doit(None)); } } } fn emit_log_messages( &self, unit: &Unit, build_runner: &mut BuildRunner<'_, '_>, show_warnings: bool, ) -> CargoResult<()> { let outputs = build_runner.build_script_outputs.lock().unwrap(); let Some(metadata_vec) = build_runner.find_build_script_metadatas(unit) else { return Ok(()); }; let bcx = &mut build_runner.bcx; for metadata in metadata_vec { if let Some(output) = outputs.get(metadata) { if !output.log_messages.is_empty() && (show_warnings || output .log_messages .iter() .any(|(severity, _)| *severity == Severity::Error)) { let msg_with_package = |msg: &str| format!("{}@{}: {}", unit.pkg.name(), unit.pkg.version(), msg); for (severity, message) in output.log_messages.iter() { match severity { Severity::Error => { bcx.gctx.shell().error(msg_with_package(message))?; } Severity::Warning => { bcx.gctx.shell().warn(msg_with_package(message))?; } } } } } } Ok(()) } fn bump_warning_count(&mut self, id: JobId, emitted: bool, fixable: bool) { let cnts = self.warning_count.entry(id).or_default(); cnts.total += 1; if !emitted { cnts.duplicates += 1; // Don't add to fixable if it's already been emitted } else if fixable { // Do not add anything to the fixable warning count if // is `NotAllowed` since that indicates there was an // error while building this `Unit` if cnts.fixable_allowed() { cnts.fixable = match cnts.fixable { FixableWarnings::NotAllowed => FixableWarnings::NotAllowed, FixableWarnings::Zero => FixableWarnings::Positive(1), FixableWarnings::Positive(fixable) => FixableWarnings::Positive(fixable + 1), }; } } } /// Displays a final report of the warnings emitted by a particular job. fn report_warning_count( &mut self, runner: &mut BuildRunner<'_, '_>, id: JobId, rustc_workspace_wrapper: &Option, ) { let gctx = runner.bcx.gctx; let count = match self.warning_count.get(&id) { // An error could add an entry for a `Unit` // with 0 warnings but having fixable // warnings be disallowed Some(count) if count.total > 0 => count, None | Some(_) => return, }; runner.compilation.warning_count += count.total; let unit = &self.active[&id]; let mut message = descriptive_pkg_name(&unit.pkg.name(), &unit.target, &unit.mode); message.push_str(" generated "); match count.total { 1 => message.push_str("1 warning"), n => { let _ = write!(message, "{} warnings", n); } }; match count.duplicates { 0 => {} 1 => message.push_str(" (1 duplicate)"), n => { let _ = write!(message, " ({} duplicates)", n); } } // Only show the `cargo fix` message if its a local `Unit` if unit.is_local() { // Do not show this if there are any errors or no fixable warnings if let FixableWarnings::Positive(fixable) = count.fixable { // `cargo fix` doesn't have an option for custom builds if !unit.target.is_custom_build() { // To make sure the correct command is shown for `clippy` we // check if `RUSTC_WORKSPACE_WRAPPER` is set and pointing towards // `clippy-driver`. let clippy = std::ffi::OsStr::new("clippy-driver"); let command = match rustc_workspace_wrapper.as_ref().and_then(|x| x.file_stem()) { Some(wrapper) if wrapper == clippy => "cargo clippy --fix", _ => "cargo fix", }; let mut args = { let named = unit.target.description_named(); // if its a lib we need to add the package to fix if unit.target.is_lib() { format!("{} -p {}", named, unit.pkg.name()) } else { named } }; if unit.mode.is_rustc_test() && !(unit.target.is_test() || unit.target.is_bench()) { args.push_str(" --tests"); } let mut suggestions = format!("{} suggestion", fixable); if fixable > 1 { suggestions.push_str("s") } let _ = write!( message, " (run `{command} --{args}` to apply {suggestions})" ); } } } // Errors are ignored here because it is tricky to handle them // correctly, and they aren't important. let _ = gctx.shell().warn(message); } fn finish( &mut self, id: JobId, unit: &Unit, artifact: Artifact, build_runner: &mut BuildRunner<'_, '_>, ) -> CargoResult<()> { if unit.mode.is_run_custom_build() { self.emit_log_messages( unit, build_runner, unit.show_warnings(build_runner.bcx.gctx), )?; } let unlocked = self.queue.finish(unit, &artifact); match artifact { Artifact::All => self.timings.unit_finished(id, unlocked), Artifact::Metadata => self.timings.unit_rmeta_finished(id, unlocked), } Ok(()) } // This isn't super trivial because we don't want to print loads and // loads of information to the console, but we also want to produce a // faithful representation of what's happening. This is somewhat nuanced // as a package can start compiling *very* early on because of custom // build commands and such. // // In general, we try to print "Compiling" for the first nontrivial task // run for a package, regardless of when that is. We then don't print // out any more information for a package after we've printed it once. fn note_working_on( &mut self, gctx: &GlobalContext, ws_root: &Path, unit: &Unit, fresh: &Freshness, ) -> CargoResult<()> { if (self.compiled.contains(&unit.pkg.package_id()) && !unit.mode.is_doc() && !unit.mode.is_doc_scrape()) || (self.documented.contains(&unit.pkg.package_id()) && unit.mode.is_doc()) || (self.scraped.contains(&unit.pkg.package_id()) && unit.mode.is_doc_scrape()) { return Ok(()); } match fresh { // Any dirty stage which runs at least one command gets printed as // being a compiled package. Dirty(dirty_reason) => { if !dirty_reason.is_fresh_build() { gctx.shell() .verbose(|shell| dirty_reason.present_to(shell, unit, ws_root))?; } if unit.mode.is_doc() { self.documented.insert(unit.pkg.package_id()); gctx.shell().status("Documenting", &unit.pkg)?; } else if unit.mode.is_doc_test() { // Skip doc test. } else if unit.mode.is_doc_scrape() { self.scraped.insert(unit.pkg.package_id()); gctx.shell().status("Scraping", &unit.pkg)?; } else { self.compiled.insert(unit.pkg.package_id()); if unit.mode.is_check() { gctx.shell().status("Checking", &unit.pkg)?; } else { gctx.shell().status("Compiling", &unit.pkg)?; } } } Fresh => { // If doc test are last, only print "Fresh" if nothing has been printed. if self.counts[&unit.pkg.package_id()] == 0 && !(unit.mode.is_doc_test() && self.compiled.contains(&unit.pkg.package_id())) { self.compiled.insert(unit.pkg.package_id()); gctx.shell().verbose(|c| c.status("Fresh", &unit.pkg))?; } } } Ok(()) } fn back_compat_notice( &self, build_runner: &BuildRunner<'_, '_>, unit: &Unit, ) -> CargoResult<()> { if unit.pkg.name() != "diesel" || unit.pkg.version() >= &Version::new(1, 4, 8) || build_runner.bcx.ws.resolve_behavior() == ResolveBehavior::V1 || !unit.pkg.package_id().source_id().is_registry() || !unit.features.is_empty() { return Ok(()); } if !build_runner .bcx .unit_graph .keys() .any(|unit| unit.pkg.name() == "diesel" && !unit.features.is_empty()) { return Ok(()); } build_runner.bcx.gctx.shell().note( "\ This error may be due to an interaction between diesel and Cargo's new feature resolver. Try updating to diesel 1.4.8 to fix this error. ", )?; Ok(()) } } impl ErrorsDuringDrain { fn to_error(&self) -> Option { match self.count { 0 => None, 1 => Some(format_err!("1 job failed")), n => Some(format_err!("{} jobs failed", n)), } } } cargo-0.91.0/src/cargo/core/compiler/layout.rs000064400000000000000000000243601046102023000173220ustar 00000000000000//! Management of the directory layout of a build //! //! The directory layout is a little tricky at times, hence a separate file to //! house this logic. The current layout looks like this: //! //! ```text //! # This is the root directory for all output, the top-level package //! # places all of its output here. //! target/ //! //! # Cache of `rustc -Vv` output for performance. //! .rustc-info.json //! //! # All final artifacts are linked into this directory from `deps`. //! # Note that named profiles will soon be included as separate directories //! # here. They have a restricted format, similar to Rust identifiers, so //! # Cargo-specific directories added in the future should use some prefix //! # like `.` to avoid name collisions. //! debug/ # or release/ //! //! # File used to lock the directory to prevent multiple cargo processes //! # from using it at the same time. //! .cargo-lock //! //! # Hidden directory that holds all of the fingerprint files for all //! # packages //! .fingerprint/ //! # Each package is in a separate directory. //! # Note that different target kinds have different filename prefixes. //! $pkgname-$META/ //! # Set of source filenames for this package. //! dep-lib-$targetname //! # Timestamp when this package was last built. //! invoked.timestamp //! # The fingerprint hash. //! lib-$targetname //! # Detailed information used for logging the reason why //! # something is being recompiled. //! lib-$targetname.json //! # The console output from the compiler. This is cached //! # so that warnings can be redisplayed for "fresh" units. //! output-lib-$targetname //! //! # This is the root directory for all rustc artifacts except build //! # scripts, examples, and test and bench executables. Almost every //! # artifact should have a metadata hash added to its filename to //! # prevent collisions. One notable exception is dynamic libraries. //! deps/ //! //! # Each artifact dependency gets in its own directory. //! /artifact/$pkgname-$META/$kind //! //! # Root directory for all compiled examples. //! examples/ //! //! # Directory used to store incremental data for the compiler (when //! # incremental is enabled. //! incremental/ //! //! # This is the location at which the output of all custom build //! # commands are rooted. //! build/ //! //! # Each package gets its own directory where its build script and //! # script output are placed //! $pkgname-$META/ # For the build script itself. //! # The build script executable (name may be changed by user). //! build-script-build-$META //! # Hard link to build-script-build-$META. //! build-script-build //! # Dependency information generated by rustc. //! build-script-build-$META.d //! # Debug information, depending on platform and profile //! # settings. //! //! //! # The package shows up twice with two different metadata hashes. //! $pkgname-$META/ # For the output of the build script. //! # Timestamp when the build script was last executed. //! invoked.timestamp //! # Directory where script can output files ($OUT_DIR). //! out/ //! # Output from the build script. //! output //! # Path to `out`, used to help when the target directory is //! # moved. //! root-output //! # Stderr output from the build script. //! stderr //! //! # Output from rustdoc //! doc/ //! //! # Used by `cargo package` and `cargo publish` to build a `.crate` file. //! package/ //! //! # Experimental feature for generated build scripts. //! .metabuild/ //! ``` //! //! When cross-compiling, the layout is the same, except it appears in //! `target/$TRIPLE`. use crate::core::Workspace; use crate::core::compiler::CompileTarget; use crate::util::{CargoResult, FileLock}; use cargo_util::paths; use std::path::{Path, PathBuf}; /// Contains the paths of all target output locations. /// /// See module docs for more information. pub struct Layout { /// The root directory: `/path/to/target`. /// If cross compiling: `/path/to/target/$TRIPLE`. root: PathBuf, /// The final artifact destination: `$root/debug` (or `release`). dest: PathBuf, /// The directory with rustc artifacts: `$dest/deps` deps: PathBuf, /// The directory for build scripts: `$dest/build` build: PathBuf, /// The directory for artifacts, i.e. binaries, cdylibs, staticlibs: `$dest/deps/artifact` artifact: PathBuf, /// The directory for incremental files: `$dest/incremental` incremental: PathBuf, /// The directory for fingerprints: `$dest/.fingerprint` fingerprint: PathBuf, /// The directory for examples: `$dest/examples` examples: PathBuf, /// The directory for pre-uplifted examples: `$build-dir/debug/examples` build_examples: PathBuf, /// The directory for rustdoc output: `$root/doc` doc: PathBuf, /// The directory for temporary data of integration tests and benches: `$dest/tmp` tmp: PathBuf, /// The lockfile for a build (`.cargo-lock`). Will be unlocked when this /// struct is `drop`ped. _lock: FileLock, /// Same as `_lock` but for the build directory. /// /// Will be `None` when the build-dir and target-dir are the same path as we cannot /// lock the same path twice. _build_lock: Option, } impl Layout { /// Calculate the paths for build output, lock the build directory, and return as a Layout. /// /// This function will block if the directory is already locked. /// /// `dest` should be the final artifact directory name. Currently either /// "debug" or "release". pub fn new( ws: &Workspace<'_>, target: Option, dest: &str, ) -> CargoResult { let mut root = ws.target_dir(); let mut build_root = ws.build_dir(); if let Some(target) = target { root.push(target.short_name()); build_root.push(target.short_name()); } let build_dest = build_root.join(dest); let dest = root.join(dest); // If the root directory doesn't already exist go ahead and create it // here. Use this opportunity to exclude it from backups as well if the // system supports it since this is a freshly created folder. // paths::create_dir_all_excluded_from_backups_atomic(root.as_path_unlocked())?; if root != build_root { paths::create_dir_all_excluded_from_backups_atomic(build_root.as_path_unlocked())?; } // Now that the excluded from backups target root is created we can create the // actual destination (sub)subdirectory. paths::create_dir_all(dest.as_path_unlocked())?; // For now we don't do any more finer-grained locking on the artifact // directory, so just lock the entire thing for the duration of this // compile. let lock = dest.open_rw_exclusive_create(".cargo-lock", ws.gctx(), "build directory")?; let build_lock = if root != build_root { Some(build_dest.open_rw_exclusive_create( ".cargo-lock", ws.gctx(), "build directory", )?) } else { None }; let root = root.into_path_unlocked(); let build_root = build_root.into_path_unlocked(); let dest = dest.into_path_unlocked(); let build_dest = build_dest.as_path_unlocked(); let deps = build_dest.join("deps"); let artifact = deps.join("artifact"); Ok(Layout { deps, build: build_dest.join("build"), artifact, incremental: build_dest.join("incremental"), fingerprint: build_dest.join(".fingerprint"), examples: dest.join("examples"), build_examples: build_dest.join("examples"), doc: root.join("doc"), tmp: build_root.join("tmp"), root, dest, _lock: lock, _build_lock: build_lock, }) } /// Makes sure all directories stored in the Layout exist on the filesystem. pub fn prepare(&mut self) -> CargoResult<()> { paths::create_dir_all(&self.deps)?; paths::create_dir_all(&self.incremental)?; paths::create_dir_all(&self.fingerprint)?; paths::create_dir_all(&self.examples)?; paths::create_dir_all(&self.build_examples)?; paths::create_dir_all(&self.build)?; Ok(()) } /// Fetch the destination path for final artifacts (`/…/target/debug`). pub fn dest(&self) -> &Path { &self.dest } /// Fetch the deps path. pub fn deps(&self) -> &Path { &self.deps } /// Fetch the examples path. pub fn examples(&self) -> &Path { &self.examples } /// Fetch the build examples path. pub fn build_examples(&self) -> &Path { &self.build_examples } /// Fetch the doc path. pub fn doc(&self) -> &Path { &self.doc } /// Fetch the root path (`/…/target`). pub fn root(&self) -> &Path { &self.root } /// Fetch the incremental path. pub fn incremental(&self) -> &Path { &self.incremental } /// Fetch the fingerprint path. pub fn fingerprint(&self) -> &Path { &self.fingerprint } /// Fetch the build script path. pub fn build(&self) -> &Path { &self.build } /// Fetch the artifact path. pub fn artifact(&self) -> &Path { &self.artifact } /// Create and return the tmp path. pub fn prepare_tmp(&self) -> CargoResult<&Path> { paths::create_dir_all(&self.tmp)?; Ok(&self.tmp) } } cargo-0.91.0/src/cargo/core/compiler/links.rs000064400000000000000000000047421046102023000171270ustar 00000000000000use super::unit_graph::UnitGraph; use crate::core::resolver::errors::describe_path; use crate::core::{PackageId, Resolve}; use crate::util::errors::CargoResult; use std::collections::{HashMap, HashSet}; /// Validates [`package.links`] field in the manifest file does not conflict /// between packages. /// /// NOTE: This is the *old* links validator. Links are usually validated in the /// resolver. However, the `links` field was added to the index in early 2018 /// (see [rust-lang/cargo#4978]). However, `links` has been around since 2014, /// so there are still many crates in the index that don't have `links` /// properly set in the index (over 600 at the time of this writing in 2019). /// This can probably be removed at some point in the future, though it might /// be worth considering fixing the index. /// /// [rust-lang/cargo#4978]: https://github.com/rust-lang/cargo/pull/4978 /// [`package.links`]: https://doc.rust-lang.org/nightly/cargo/reference/build-scripts.html#the-links-manifest-key pub fn validate_links(resolve: &Resolve, unit_graph: &UnitGraph) -> CargoResult<()> { let mut validated: HashSet = HashSet::new(); let mut links: HashMap = HashMap::new(); let mut units: Vec<_> = unit_graph.keys().collect(); // Sort primarily to make testing easier. units.sort_unstable(); for unit in units { if !validated.insert(unit.pkg.package_id()) { continue; } let Some(lib) = unit.pkg.manifest().links() else { continue; }; if let Some(&prev) = links.get(lib) { let prev_path = resolve .path_to_top(&prev) .into_iter() .map(|(p, d)| (p, d.and_then(|d| d.iter().next()))); let pkg = unit.pkg.package_id(); let path = resolve .path_to_top(&pkg) .into_iter() .map(|(p, d)| (p, d.and_then(|d| d.iter().next()))); anyhow::bail!( "multiple packages link to native library `{}`, \ but a native library can be linked only once\n\ \n\ {}\nlinks to native library `{}`\n\ \n\ {}\nalso links to native library `{}`", lib, describe_path(prev_path), lib, describe_path(path), lib ) } links.insert(lib.to_string(), unit.pkg.package_id()); } Ok(()) } cargo-0.91.0/src/cargo/core/compiler/lto.rs000064400000000000000000000206461046102023000166060ustar 00000000000000use crate::core::compiler::{BuildContext, CompileMode, CrateType, Unit}; use crate::core::profiles; use crate::util::interning::InternedString; use crate::util::errors::CargoResult; use std::collections::hash_map::{Entry, HashMap}; /// Possible ways to run rustc and request various parts of [LTO]. /// /// Variant | Flag | Object Code | Bitcode /// -------------------|------------------------|-------------|-------- /// `Run` | `-C lto=foo` | n/a | n/a /// `Off` | `-C lto=off` | n/a | n/a /// `OnlyBitcode` | `-C linker-plugin-lto` | | ✓ /// `ObjectAndBitcode` | | ✓ | ✓ /// `OnlyObject` | `-C embed-bitcode=no` | ✓ | /// /// [LTO]: https://doc.rust-lang.org/nightly/cargo/reference/profiles.html#lto #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum Lto { /// LTO is run for this rustc, and it's `-Clto=foo`. If the given value is /// None, that corresponds to `-Clto` with no argument, which means do /// "fat" LTO. Run(Option), /// LTO has been explicitly listed as "off". This means no thin-local-LTO, /// no LTO anywhere, I really mean it! Off, /// This rustc invocation only needs to produce bitcode (it is *only* used /// for LTO), there's no need to produce object files, so we can pass /// `-Clinker-plugin-lto` OnlyBitcode, /// This rustc invocation needs to embed bitcode in object files. This means /// that object files may be used for a normal link, and the crate may be /// loaded for LTO later, so both are required. ObjectAndBitcode, /// This should not include bitcode. This is primarily to reduce disk /// space usage. OnlyObject, } pub fn generate(bcx: &BuildContext<'_, '_>) -> CargoResult> { let mut map = HashMap::new(); for unit in bcx.roots.iter() { let root_lto = match unit.profile.lto { // LTO not requested, no need for bitcode. profiles::Lto::Bool(false) => Lto::OnlyObject, profiles::Lto::Off => Lto::Off, _ => { let crate_types = unit.target.rustc_crate_types(); if unit.target.for_host() { Lto::OnlyObject } else if needs_object(&crate_types) { lto_when_needs_object(&crate_types) } else { // This may or may not participate in LTO, let's start // with the minimum requirements. This may be expanded in // `calculate` below if necessary. Lto::OnlyBitcode } } }; calculate(bcx, &mut map, unit, root_lto)?; } Ok(map) } /// Whether or not any of these crate types need object code. fn needs_object(crate_types: &[CrateType]) -> bool { crate_types.iter().any(|k| k.can_lto() || k.is_dynamic()) } /// Lto setting to use when this unit needs object code. fn lto_when_needs_object(crate_types: &[CrateType]) -> Lto { if crate_types.iter().all(|ct| *ct == CrateType::Dylib) { // A dylib whose parent is running LTO. rustc currently // doesn't support LTO with dylibs, so bitcode is not // needed. Lto::OnlyObject } else { // Mixed rlib with a dylib or cdylib whose parent is running LTO. This // needs both: bitcode for the rlib (for LTO) and object code for the // dylib. Lto::ObjectAndBitcode } } fn calculate( bcx: &BuildContext<'_, '_>, map: &mut HashMap, unit: &Unit, parent_lto: Lto, ) -> CargoResult<()> { let crate_types = match unit.mode { // Note: Doctest ignores LTO, but for now we'll compute it as-if it is // a Bin, in case it is ever supported in the future. CompileMode::Test | CompileMode::Doctest => vec![CrateType::Bin], // Notes on other modes: // - Check: Treat as the underlying type, it doesn't really matter. // - Doc: LTO is N/A for the Doc unit itself since rustdoc does not // support codegen flags. We still compute the dependencies, which // are mostly `Check`. // - RunCustomBuild is ignored because it is always "for_host". _ => unit.target.rustc_crate_types(), }; // LTO can only be performed if *all* of the crate types support it. // For example, a cdylib/rlib combination won't allow LTO. let all_lto_types = crate_types.iter().all(CrateType::can_lto); // Compute the LTO based on the profile, and what our parent requires. let lto = if unit.target.for_host() { // Disable LTO for host builds since we only really want to perform LTO // for the final binary, and LTO on plugins/build scripts/proc macros is // largely not desired. Lto::OnlyObject } else if all_lto_types { // Note that this ignores the `parent_lto` because this isn't a // linkable crate type; this unit is not being embedded in the parent. match unit.profile.lto { profiles::Lto::Named(s) => Lto::Run(Some(s)), profiles::Lto::Off => Lto::Off, profiles::Lto::Bool(true) => Lto::Run(None), profiles::Lto::Bool(false) => Lto::OnlyObject, } } else { match (parent_lto, needs_object(&crate_types)) { // An rlib whose parent is running LTO, we only need bitcode. (Lto::Run(_), false) => Lto::OnlyBitcode, // LTO when something needs object code. (Lto::Run(_), true) | (Lto::OnlyBitcode, true) => lto_when_needs_object(&crate_types), // LTO is disabled, continue to disable it. (Lto::Off, _) => Lto::Off, // If this doesn't have any requirements, or the requirements are // already satisfied, then stay with our parent. (_, false) | (Lto::OnlyObject, true) | (Lto::ObjectAndBitcode, true) => parent_lto, } }; // Merge the computed LTO. If this unit appears multiple times in the // graph, the merge may expand the requirements. let merged_lto = match map.entry(unit.clone()) { // If we haven't seen this unit before then insert our value and keep // going. Entry::Vacant(v) => *v.insert(lto), Entry::Occupied(mut v) => { let result = match (lto, v.get()) { // No change in requirements. (Lto::OnlyBitcode, Lto::OnlyBitcode) => Lto::OnlyBitcode, (Lto::OnlyObject, Lto::OnlyObject) => Lto::OnlyObject, // Once we're running LTO we keep running LTO. We should always // calculate the same thing here each iteration because if we // see this twice then it means, for example, two unit tests // depend on a binary, which is normal. (Lto::Run(s), _) | (_, &Lto::Run(s)) => Lto::Run(s), // Off means off! This has the same reasoning as `Lto::Run`. (Lto::Off, _) | (_, Lto::Off) => Lto::Off, // Once a target has requested both, that's the maximal amount // of work that can be done, so we just keep doing that work. (Lto::ObjectAndBitcode, _) | (_, Lto::ObjectAndBitcode) => Lto::ObjectAndBitcode, // Upgrade so that both requirements can be met. // // This is where the trickiness happens. This unit needs // bitcode and the previously calculated value for this unit // says it didn't need bitcode (or vice versa). This means that // we're a shared dependency between some targets which require // LTO and some which don't. This means that instead of being // either only-objects or only-bitcode we have to embed both in // rlibs (used for different compilations), so we switch to // including both. (Lto::OnlyObject, Lto::OnlyBitcode) | (Lto::OnlyBitcode, Lto::OnlyObject) => { Lto::ObjectAndBitcode } }; // No need to recurse if we calculated the same value as before. if result == *v.get() { return Ok(()); } v.insert(result); result } }; for dep in &bcx.unit_graph[unit] { calculate(bcx, map, &dep.unit, merged_lto)?; } Ok(()) } cargo-0.91.0/src/cargo/core/compiler/mod.rs000064400000000000000000002445221046102023000165700ustar 00000000000000//! # Interact with the compiler //! //! If you consider [`ops::cargo_compile::compile`] as a `rustc` driver but on //! Cargo side, this module is kinda the `rustc_interface` for that merits. //! It contains all the interaction between Cargo and the rustc compiler, //! from preparing the context for the entire build process, to scheduling //! and executing each unit of work (e.g. running `rustc`), to managing and //! caching the output artifact of a build. //! //! However, it hasn't yet exposed a clear definition of each phase or session, //! like what rustc has done[^1]. Also, no one knows if Cargo really needs that. //! To be pragmatic, here we list a handful of items you may want to learn: //! //! * [`BuildContext`] is a static context containing all information you need //! before a build gets started. //! * [`BuildRunner`] is the center of the world, coordinating a running build and //! collecting information from it. //! * [`custom_build`] is the home of build script executions and output parsing. //! * [`fingerprint`] not only defines but also executes a set of rules to //! determine if a re-compile is needed. //! * [`job_queue`] is where the parallelism, job scheduling, and communication //! machinery happen between Cargo and the compiler. //! * [`layout`] defines and manages output artifacts of a build in the filesystem. //! * [`unit_dependencies`] is for building a dependency graph for compilation //! from a result of dependency resolution. //! * [`Unit`] contains sufficient information to build something, usually //! turning into a compiler invocation in a later phase. //! //! [^1]: Maybe [`-Zbuild-plan`](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-plan) //! was designed to serve that purpose but still [in flux](https://github.com/rust-lang/cargo/issues/7614). //! //! [`ops::cargo_compile::compile`]: crate::ops::compile pub mod artifact; mod build_config; pub(crate) mod build_context; mod build_plan; pub(crate) mod build_runner; mod compilation; mod compile_kind; mod crate_type; mod custom_build; pub(crate) mod fingerprint; pub mod future_incompat; pub(crate) mod job_queue; pub(crate) mod layout; mod links; mod lto; mod output_depinfo; mod output_sbom; pub mod rustdoc; pub mod standard_lib; mod timings; mod unit; pub mod unit_dependencies; pub mod unit_graph; use std::borrow::Cow; use std::collections::{HashMap, HashSet}; use std::env; use std::ffi::{OsStr, OsString}; use std::fmt::Display; use std::fs::{self, File}; use std::io::{BufRead, BufWriter, Write}; use std::path::{Path, PathBuf}; use std::sync::Arc; use anyhow::{Context as _, Error}; use lazycell::LazyCell; use tracing::{debug, instrument, trace}; pub use self::build_config::UserIntent; pub use self::build_config::{BuildConfig, CompileMode, MessageFormat, TimingOutput}; pub use self::build_context::{ BuildContext, FileFlavor, FileType, RustDocFingerprint, RustcTargetData, TargetInfo, }; use self::build_plan::BuildPlan; pub use self::build_runner::{BuildRunner, Metadata, UnitHash}; pub use self::compilation::{Compilation, Doctest, UnitOutput}; pub use self::compile_kind::{CompileKind, CompileKindFallback, CompileTarget}; pub use self::crate_type::CrateType; pub use self::custom_build::LinkArgTarget; pub use self::custom_build::{BuildOutput, BuildScriptOutputs, BuildScripts, LibraryPath}; pub(crate) use self::fingerprint::DirtyReason; pub use self::job_queue::Freshness; use self::job_queue::{Job, JobQueue, JobState, Work}; pub(crate) use self::layout::Layout; pub use self::lto::Lto; use self::output_depinfo::output_depinfo; use self::output_sbom::build_sbom; use self::unit_graph::UnitDep; use crate::core::compiler::future_incompat::FutureIncompatReport; pub use crate::core::compiler::unit::{Unit, UnitInterner}; use crate::core::manifest::TargetSourcePath; use crate::core::profiles::{PanicStrategy, Profile, StripInner}; use crate::core::{Feature, PackageId, Target, Verbosity}; use crate::util::context::WarningHandling; use crate::util::errors::{CargoResult, VerboseError}; use crate::util::interning::InternedString; use crate::util::machine_message::{self, Message}; use crate::util::{add_path_args, internal}; use cargo_util::{ProcessBuilder, ProcessError, paths}; use cargo_util_schemas::manifest::TomlDebugInfo; use cargo_util_schemas::manifest::TomlTrimPaths; use cargo_util_schemas::manifest::TomlTrimPathsValue; use rustfix::diagnostics::Applicability; const RUSTDOC_CRATE_VERSION_FLAG: &str = "--crate-version"; /// A glorified callback for executing calls to rustc. Rather than calling rustc /// directly, we'll use an `Executor`, giving clients an opportunity to intercept /// the build calls. pub trait Executor: Send + Sync + 'static { /// Called after a rustc process invocation is prepared up-front for a given /// unit of work (may still be modified for runtime-known dependencies, when /// the work is actually executed). fn init(&self, _build_runner: &BuildRunner<'_, '_>, _unit: &Unit) {} /// In case of an `Err`, Cargo will not continue with the build process for /// this package. fn exec( &self, cmd: &ProcessBuilder, id: PackageId, target: &Target, mode: CompileMode, on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>, on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>, ) -> CargoResult<()>; /// Queried when queuing each unit of work. If it returns true, then the /// unit will always be rebuilt, independent of whether it needs to be. fn force_rebuild(&self, _unit: &Unit) -> bool { false } } /// A `DefaultExecutor` calls rustc without doing anything else. It is Cargo's /// default behaviour. #[derive(Copy, Clone)] pub struct DefaultExecutor; impl Executor for DefaultExecutor { #[instrument(name = "rustc", skip_all, fields(package = id.name().as_str(), process = cmd.to_string()))] fn exec( &self, cmd: &ProcessBuilder, id: PackageId, _target: &Target, _mode: CompileMode, on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>, on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>, ) -> CargoResult<()> { cmd.exec_with_streaming(on_stdout_line, on_stderr_line, false) .map(drop) } } /// Builds up and enqueue a list of pending jobs onto the `job` queue. /// /// Starting from the `unit`, this function recursively calls itself to build /// all jobs for dependencies of the `unit`. Each of these jobs represents /// compiling a particular package. /// /// Note that **no actual work is executed as part of this**, that's all done /// next as part of [`JobQueue::execute`] function which will run everything /// in order with proper parallelism. #[tracing::instrument(skip(build_runner, jobs, plan, exec))] fn compile<'gctx>( build_runner: &mut BuildRunner<'_, 'gctx>, jobs: &mut JobQueue<'gctx>, plan: &mut BuildPlan, unit: &Unit, exec: &Arc, force_rebuild: bool, ) -> CargoResult<()> { let bcx = build_runner.bcx; let build_plan = bcx.build_config.build_plan; if !build_runner.compiled.insert(unit.clone()) { return Ok(()); } // If we are in `--compile-time-deps` and the given unit is not a compile time // dependency, skip compling the unit and jumps to dependencies, which still // have chances to be compile time dependencies if !unit.skip_non_compile_time_dep { // Build up the work to be done to compile this unit, enqueuing it once // we've got everything constructed. fingerprint::prepare_init(build_runner, unit)?; let job = if unit.mode.is_run_custom_build() { custom_build::prepare(build_runner, unit)? } else if unit.mode.is_doc_test() { // We run these targets later, so this is just a no-op for now. Job::new_fresh() } else if build_plan { Job::new_dirty( rustc(build_runner, unit, &exec.clone())?, DirtyReason::FreshBuild, ) } else { let force = exec.force_rebuild(unit) || force_rebuild; let mut job = fingerprint::prepare_target(build_runner, unit, force)?; job.before(if job.freshness().is_dirty() { let work = if unit.mode.is_doc() || unit.mode.is_doc_scrape() { rustdoc(build_runner, unit)? } else { rustc(build_runner, unit, exec)? }; work.then(link_targets(build_runner, unit, false)?) } else { // We always replay the output cache, // since it might contain future-incompat-report messages let show_diagnostics = unit.show_warnings(bcx.gctx) && build_runner.bcx.gctx.warning_handling()? != WarningHandling::Allow; let work = replay_output_cache( unit.pkg.package_id(), PathBuf::from(unit.pkg.manifest_path()), &unit.target, build_runner.files().message_cache_path(unit), build_runner.bcx.build_config.message_format, show_diagnostics, ); // Need to link targets on both the dirty and fresh. work.then(link_targets(build_runner, unit, true)?) }); job }; jobs.enqueue(build_runner, unit, job)?; } // Be sure to compile all dependencies of this target as well. let deps = Vec::from(build_runner.unit_deps(unit)); // Create vec due to mutable borrow. for dep in deps { compile(build_runner, jobs, plan, &dep.unit, exec, false)?; } if build_plan { plan.add(build_runner, unit)?; } Ok(()) } /// Generates the warning message used when fallible doc-scrape units fail, /// either for rustdoc or rustc. fn make_failed_scrape_diagnostic( build_runner: &BuildRunner<'_, '_>, unit: &Unit, top_line: impl Display, ) -> String { let manifest_path = unit.pkg.manifest_path(); let relative_manifest_path = manifest_path .strip_prefix(build_runner.bcx.ws.root()) .unwrap_or(&manifest_path); format!( "\ {top_line} Try running with `--verbose` to see the error message. If an example should not be scanned, then consider adding `doc-scrape-examples = false` to its `[[example]]` definition in {}", relative_manifest_path.display() ) } /// Creates a unit of work invoking `rustc` for building the `unit`. fn rustc( build_runner: &mut BuildRunner<'_, '_>, unit: &Unit, exec: &Arc, ) -> CargoResult { let mut rustc = prepare_rustc(build_runner, unit)?; let build_plan = build_runner.bcx.build_config.build_plan; let name = unit.pkg.name(); let buildkey = unit.buildkey(); let outputs = build_runner.outputs(unit)?; let root = build_runner.files().out_dir(unit); // Prepare the native lib state (extra `-L` and `-l` flags). let build_script_outputs = Arc::clone(&build_runner.build_script_outputs); let current_id = unit.pkg.package_id(); let manifest_path = PathBuf::from(unit.pkg.manifest_path()); let build_scripts = build_runner.build_scripts.get(unit).cloned(); // If we are a binary and the package also contains a library, then we // don't pass the `-l` flags. let pass_l_flag = unit.target.is_lib() || !unit.pkg.targets().iter().any(|t| t.is_lib()); let dep_info_name = if let Some(c_extra_filename) = build_runner.files().metadata(unit).c_extra_filename() { format!("{}-{}.d", unit.target.crate_name(), c_extra_filename) } else { format!("{}.d", unit.target.crate_name()) }; let rustc_dep_info_loc = root.join(dep_info_name); let dep_info_loc = fingerprint::dep_info_loc(build_runner, unit); let mut output_options = OutputOptions::new(build_runner, unit); let package_id = unit.pkg.package_id(); let target = Target::clone(&unit.target); let mode = unit.mode; exec.init(build_runner, unit); let exec = exec.clone(); let root_output = build_runner.files().host_dest().to_path_buf(); let build_dir = build_runner.bcx.ws.build_dir().into_path_unlocked(); let pkg_root = unit.pkg.root().to_path_buf(); let cwd = rustc .get_cwd() .unwrap_or_else(|| build_runner.bcx.gctx.cwd()) .to_path_buf(); let fingerprint_dir = build_runner.files().fingerprint_dir(unit); let script_metadatas = build_runner.find_build_script_metadatas(unit); let is_local = unit.is_local(); let artifact = unit.artifact; let sbom_files = build_runner.sbom_output_files(unit)?; let sbom = build_sbom(build_runner, unit)?; let hide_diagnostics_for_scrape_unit = build_runner.bcx.unit_can_fail_for_docscraping(unit) && !matches!( build_runner.bcx.gctx.shell().verbosity(), Verbosity::Verbose ); let failed_scrape_diagnostic = hide_diagnostics_for_scrape_unit.then(|| { // If this unit is needed for doc-scraping, then we generate a diagnostic that // describes the set of reverse-dependencies that cause the unit to be needed. let target_desc = unit.target.description_named(); let mut for_scrape_units = build_runner .bcx .scrape_units_have_dep_on(unit) .into_iter() .map(|unit| unit.target.description_named()) .collect::>(); for_scrape_units.sort(); let for_scrape_units = for_scrape_units.join(", "); make_failed_scrape_diagnostic(build_runner, unit, format_args!("failed to check {target_desc} in package `{name}` as a prerequisite for scraping examples from: {for_scrape_units}")) }); if hide_diagnostics_for_scrape_unit { output_options.show_diagnostics = false; } let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?); return Ok(Work::new(move |state| { // Artifacts are in a different location than typical units, // hence we must assure the crate- and target-dependent // directory is present. if artifact.is_true() { paths::create_dir_all(&root)?; } // Only at runtime have we discovered what the extra -L and -l // arguments are for native libraries, so we process those here. We // also need to be sure to add any -L paths for our plugins to the // dynamic library load path as a plugin's dynamic library may be // located somewhere in there. // Finally, if custom environment variables have been produced by // previous build scripts, we include them in the rustc invocation. if let Some(build_scripts) = build_scripts { let script_outputs = build_script_outputs.lock().unwrap(); if !build_plan { add_native_deps( &mut rustc, &script_outputs, &build_scripts, pass_l_flag, &target, current_id, mode, )?; add_plugin_deps(&mut rustc, &script_outputs, &build_scripts, &root_output)?; } add_custom_flags(&mut rustc, &script_outputs, script_metadatas)?; } for output in outputs.iter() { // If there is both an rmeta and rlib, rustc will prefer to use the // rlib, even if it is older. Therefore, we must delete the rlib to // force using the new rmeta. if output.path.extension() == Some(OsStr::new("rmeta")) { let dst = root.join(&output.path).with_extension("rlib"); if dst.exists() { paths::remove_file(&dst)?; } } // Some linkers do not remove the executable, but truncate and modify it. // That results in the old hard-link being modified even after renamed. // We delete the old artifact here to prevent this behavior from confusing users. // See rust-lang/cargo#8348. if output.hardlink.is_some() && output.path.exists() { _ = paths::remove_file(&output.path).map_err(|e| { tracing::debug!( "failed to delete previous output file `{:?}`: {e:?}", output.path ); }); } } state.running(&rustc); let timestamp = paths::set_invocation_time(&fingerprint_dir)?; if build_plan { state.build_plan(buildkey, rustc.clone(), outputs.clone()); } else { for file in sbom_files { tracing::debug!("writing sbom to {}", file.display()); let outfile = BufWriter::new(paths::create(&file)?); serde_json::to_writer(outfile, &sbom)?; } let result = exec .exec( &rustc, package_id, &target, mode, &mut |line| on_stdout_line(state, line, package_id, &target), &mut |line| { on_stderr_line( state, line, package_id, &manifest_path, &target, &mut output_options, ) }, ) .map_err(|e| { if output_options.errors_seen == 0 { // If we didn't expect an error, do not require --verbose to fail. // This is intended to debug // https://github.com/rust-lang/crater/issues/733, where we are seeing // Cargo exit unsuccessfully while seeming to not show any errors. e } else { verbose_if_simple_exit_code(e) } }) .with_context(|| { // adapted from rustc_errors/src/lib.rs let warnings = match output_options.warnings_seen { 0 => String::new(), 1 => "; 1 warning emitted".to_string(), count => format!("; {} warnings emitted", count), }; let errors = match output_options.errors_seen { 0 => String::new(), 1 => " due to 1 previous error".to_string(), count => format!(" due to {} previous errors", count), }; let name = descriptive_pkg_name(&name, &target, &mode); format!("could not compile {name}{errors}{warnings}") }); if let Err(e) = result { if let Some(diagnostic) = failed_scrape_diagnostic { state.warning(diagnostic); } return Err(e); } // Exec should never return with success *and* generate an error. debug_assert_eq!(output_options.errors_seen, 0); } if rustc_dep_info_loc.exists() { fingerprint::translate_dep_info( &rustc_dep_info_loc, &dep_info_loc, &cwd, &pkg_root, &build_dir, &rustc, // Do not track source files in the fingerprint for registry dependencies. is_local, &env_config, ) .with_context(|| { internal(format!( "could not parse/generate dep info at: {}", rustc_dep_info_loc.display() )) })?; // This mtime shift allows Cargo to detect if a source file was // modified in the middle of the build. paths::set_file_time_no_err(dep_info_loc, timestamp); } Ok(()) })); // Add all relevant `-L` and `-l` flags from dependencies (now calculated and // present in `state`) to the command provided. fn add_native_deps( rustc: &mut ProcessBuilder, build_script_outputs: &BuildScriptOutputs, build_scripts: &BuildScripts, pass_l_flag: bool, target: &Target, current_id: PackageId, mode: CompileMode, ) -> CargoResult<()> { let mut library_paths = vec![]; for key in build_scripts.to_link.iter() { let output = build_script_outputs.get(key.1).ok_or_else(|| { internal(format!( "couldn't find build script output for {}/{}", key.0, key.1 )) })?; library_paths.extend(output.library_paths.iter()); } // NOTE: This very intentionally does not use the derived ord from LibraryPath because we need to // retain relative ordering within the same type (i.e. not lexicographic). The use of a stable sort // is also important here because it ensures that paths of the same type retain the same relative // ordering (for an unstable sort to work here, the list would need to retain the idx of each element // and then sort by that idx when the type is equivalent. library_paths.sort_by_key(|p| match p { LibraryPath::CargoArtifact(_) => 0, LibraryPath::External(_) => 1, }); for path in library_paths.iter() { rustc.arg("-L").arg(path.as_ref()); } for key in build_scripts.to_link.iter() { let output = build_script_outputs.get(key.1).ok_or_else(|| { internal(format!( "couldn't find build script output for {}/{}", key.0, key.1 )) })?; if key.0 == current_id { if pass_l_flag { for name in output.library_links.iter() { rustc.arg("-l").arg(name); } } } for (lt, arg) in &output.linker_args { // There was an unintentional change where cdylibs were // allowed to be passed via transitive dependencies. This // clause should have been kept in the `if` block above. For // now, continue allowing it for cdylib only. // See https://github.com/rust-lang/cargo/issues/9562 if lt.applies_to(target, mode) && (key.0 == current_id || *lt == LinkArgTarget::Cdylib) { rustc.arg("-C").arg(format!("link-arg={}", arg)); } } } Ok(()) } } fn verbose_if_simple_exit_code(err: Error) -> Error { // If a signal on unix (`code == None`) or an abnormal termination // on Windows (codes like `0xC0000409`), don't hide the error details. match err .downcast_ref::() .as_ref() .and_then(|perr| perr.code) { Some(n) if cargo_util::is_simple_exit_code(n) => VerboseError::new(err).into(), _ => err, } } /// Link the compiled target (often of form `foo-{metadata_hash}`) to the /// final target. This must happen during both "Fresh" and "Compile". fn link_targets( build_runner: &mut BuildRunner<'_, '_>, unit: &Unit, fresh: bool, ) -> CargoResult { let bcx = build_runner.bcx; let outputs = build_runner.outputs(unit)?; let export_dir = build_runner.files().export_dir(); let package_id = unit.pkg.package_id(); let manifest_path = PathBuf::from(unit.pkg.manifest_path()); let profile = unit.profile.clone(); let unit_mode = unit.mode; let features = unit.features.iter().map(|s| s.to_string()).collect(); let json_messages = bcx.build_config.emit_json(); let executable = build_runner.get_executable(unit)?; let mut target = Target::clone(&unit.target); if let TargetSourcePath::Metabuild = target.src_path() { // Give it something to serialize. let path = unit .pkg .manifest() .metabuild_path(build_runner.bcx.ws.build_dir()); target.set_src_path(TargetSourcePath::Path(path)); } Ok(Work::new(move |state| { // If we're a "root crate", e.g., the target of this compilation, then we // hard link our outputs out of the `deps` directory into the directory // above. This means that `cargo build` will produce binaries in // `target/debug` which one probably expects. let mut destinations = vec![]; for output in outputs.iter() { let src = &output.path; // This may have been a `cargo rustc` command which changes the // output, so the source may not actually exist. if !src.exists() { continue; } let Some(dst) = output.hardlink.as_ref() else { destinations.push(src.clone()); continue; }; destinations.push(dst.clone()); paths::link_or_copy(src, dst)?; if let Some(ref path) = output.export_path { let export_dir = export_dir.as_ref().unwrap(); paths::create_dir_all(export_dir)?; paths::link_or_copy(src, path)?; } } if json_messages { let debuginfo = match profile.debuginfo.into_inner() { TomlDebugInfo::None => machine_message::ArtifactDebuginfo::Int(0), TomlDebugInfo::Limited => machine_message::ArtifactDebuginfo::Int(1), TomlDebugInfo::Full => machine_message::ArtifactDebuginfo::Int(2), TomlDebugInfo::LineDirectivesOnly => { machine_message::ArtifactDebuginfo::Named("line-directives-only") } TomlDebugInfo::LineTablesOnly => { machine_message::ArtifactDebuginfo::Named("line-tables-only") } }; let art_profile = machine_message::ArtifactProfile { opt_level: profile.opt_level.as_str(), debuginfo: Some(debuginfo), debug_assertions: profile.debug_assertions, overflow_checks: profile.overflow_checks, test: unit_mode.is_any_test(), }; let msg = machine_message::Artifact { package_id: package_id.to_spec(), manifest_path, target: &target, profile: art_profile, features, filenames: destinations, executable, fresh, } .to_json_string(); state.stdout(msg)?; } Ok(()) })) } // For all plugin dependencies, add their -L paths (now calculated and present // in `build_script_outputs`) to the dynamic library load path for the command // to execute. fn add_plugin_deps( rustc: &mut ProcessBuilder, build_script_outputs: &BuildScriptOutputs, build_scripts: &BuildScripts, root_output: &Path, ) -> CargoResult<()> { let var = paths::dylib_path_envvar(); let search_path = rustc.get_env(var).unwrap_or_default(); let mut search_path = env::split_paths(&search_path).collect::>(); for (pkg_id, metadata) in &build_scripts.plugins { let output = build_script_outputs .get(*metadata) .ok_or_else(|| internal(format!("couldn't find libs for plugin dep {}", pkg_id)))?; search_path.append(&mut filter_dynamic_search_path( output.library_paths.iter().map(AsRef::as_ref), root_output, )); } let search_path = paths::join_paths(&search_path, var)?; rustc.env(var, &search_path); Ok(()) } fn get_dynamic_search_path(path: &Path) -> &Path { match path.to_str().and_then(|s| s.split_once("=")) { Some(("native" | "crate" | "dependency" | "framework" | "all", path)) => Path::new(path), _ => path, } } // Determine paths to add to the dynamic search path from -L entries // // Strip off prefixes like "native=" or "framework=" and filter out directories // **not** inside our output directory since they are likely spurious and can cause // clashes with system shared libraries (issue #3366). fn filter_dynamic_search_path<'a, I>(paths: I, root_output: &Path) -> Vec where I: Iterator, { let mut search_path = vec![]; for dir in paths { let dir = get_dynamic_search_path(dir); if dir.starts_with(&root_output) { search_path.push(dir.to_path_buf()); } else { debug!( "Not including path {} in runtime library search path because it is \ outside target root {}", dir.display(), root_output.display() ); } } search_path } /// Prepares flags and environments we can compute for a `rustc` invocation /// before the job queue starts compiling any unit. /// /// This builds a static view of the invocation. Flags depending on the /// completion of other units will be added later in runtime, such as flags /// from build scripts. fn prepare_rustc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult { let gctx = build_runner.bcx.gctx; let is_primary = build_runner.is_primary_package(unit); let is_workspace = build_runner.bcx.ws.is_member(&unit.pkg); let mut base = build_runner .compilation .rustc_process(unit, is_primary, is_workspace)?; build_base_args(build_runner, &mut base, unit)?; if unit.pkg.manifest().is_embedded() { if !gctx.cli_unstable().script { anyhow::bail!( "parsing `{}` requires `-Zscript`", unit.pkg.manifest_path().display() ); } base.arg("-Z").arg("crate-attr=feature(frontmatter)"); } base.inherit_jobserver(&build_runner.jobserver); build_deps_args(&mut base, build_runner, unit)?; add_cap_lints(build_runner.bcx, unit, &mut base); if let Some(args) = build_runner.bcx.extra_args_for(unit) { base.args(args); } base.args(&unit.rustflags); if gctx.cli_unstable().binary_dep_depinfo { base.arg("-Z").arg("binary-dep-depinfo"); } if build_runner.bcx.gctx.cli_unstable().checksum_freshness { base.arg("-Z").arg("checksum-hash-algorithm=blake3"); } if is_primary { base.env("CARGO_PRIMARY_PACKAGE", "1"); let file_list = std::env::join_paths(build_runner.sbom_output_files(unit)?)?; base.env("CARGO_SBOM_PATH", file_list); } if unit.target.is_test() || unit.target.is_bench() { let tmp = build_runner.files().layout(unit.kind).prepare_tmp()?; base.env("CARGO_TARGET_TMPDIR", tmp.display().to_string()); } Ok(base) } /// Prepares flags and environments we can compute for a `rustdoc` invocation /// before the job queue starts compiling any unit. /// /// This builds a static view of the invocation. Flags depending on the /// completion of other units will be added later in runtime, such as flags /// from build scripts. fn prepare_rustdoc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult { let bcx = build_runner.bcx; // script_metadata is not needed here, it is only for tests. let mut rustdoc = build_runner.compilation.rustdoc_process(unit, None)?; if unit.pkg.manifest().is_embedded() { if !bcx.gctx.cli_unstable().script { anyhow::bail!( "parsing `{}` requires `-Zscript`", unit.pkg.manifest_path().display() ); } rustdoc.arg("-Z").arg("crate-attr=feature(frontmatter)"); } rustdoc.inherit_jobserver(&build_runner.jobserver); let crate_name = unit.target.crate_name(); rustdoc.arg("--crate-name").arg(&crate_name); add_path_args(bcx.ws, unit, &mut rustdoc); add_cap_lints(bcx, unit, &mut rustdoc); if let CompileKind::Target(target) = unit.kind { rustdoc.arg("--target").arg(target.rustc_target()); } let doc_dir = build_runner.files().out_dir(unit); rustdoc.arg("-o").arg(&doc_dir); rustdoc.args(&features_args(unit)); rustdoc.args(&check_cfg_args(unit)); add_error_format_and_color(build_runner, &mut rustdoc); add_allow_features(build_runner, &mut rustdoc); if build_runner.bcx.gctx.cli_unstable().rustdoc_depinfo { // toolchain-shared-resources is required for keeping the shared styling resources // invocation-specific is required for keeping the original rustdoc emission let mut arg = OsString::from("--emit=toolchain-shared-resources,invocation-specific,dep-info="); arg.push(rustdoc_dep_info_loc(build_runner, unit)); rustdoc.arg(arg); if build_runner.bcx.gctx.cli_unstable().checksum_freshness { rustdoc.arg("-Z").arg("checksum-hash-algorithm=blake3"); } rustdoc.arg("-Zunstable-options"); } if let Some(trim_paths) = unit.profile.trim_paths.as_ref() { trim_paths_args_rustdoc(&mut rustdoc, build_runner, unit, trim_paths)?; } rustdoc.args(unit.pkg.manifest().lint_rustflags()); let metadata = build_runner.metadata_for_doc_units[unit]; rustdoc .arg("-C") .arg(format!("metadata={}", metadata.c_metadata())); if unit.mode.is_doc_scrape() { debug_assert!(build_runner.bcx.scrape_units.contains(unit)); if unit.target.is_test() { rustdoc.arg("--scrape-tests"); } rustdoc.arg("-Zunstable-options"); rustdoc .arg("--scrape-examples-output-path") .arg(scrape_output_path(build_runner, unit)?); // Only scrape example for items from crates in the workspace, to reduce generated file size for pkg in build_runner.bcx.packages.packages() { let names = pkg .targets() .iter() .map(|target| target.crate_name()) .collect::>(); for name in names { rustdoc.arg("--scrape-examples-target-crate").arg(name); } } } if should_include_scrape_units(build_runner.bcx, unit) { rustdoc.arg("-Zunstable-options"); } build_deps_args(&mut rustdoc, build_runner, unit)?; rustdoc::add_root_urls(build_runner, unit, &mut rustdoc)?; rustdoc::add_output_format(build_runner, &mut rustdoc)?; if let Some(args) = build_runner.bcx.extra_args_for(unit) { rustdoc.args(args); } rustdoc.args(&unit.rustdocflags); if !crate_version_flag_already_present(&rustdoc) { append_crate_version_flag(unit, &mut rustdoc); } Ok(rustdoc) } /// Creates a unit of work invoking `rustdoc` for documenting the `unit`. fn rustdoc(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult { let mut rustdoc = prepare_rustdoc(build_runner, unit)?; let crate_name = unit.target.crate_name(); let doc_dir = build_runner.files().out_dir(unit); // Create the documentation directory ahead of time as rustdoc currently has // a bug where concurrent invocations will race to create this directory if // it doesn't already exist. paths::create_dir_all(&doc_dir)?; let target_desc = unit.target.description_named(); let name = unit.pkg.name(); let build_script_outputs = Arc::clone(&build_runner.build_script_outputs); let package_id = unit.pkg.package_id(); let manifest_path = PathBuf::from(unit.pkg.manifest_path()); let target = Target::clone(&unit.target); let rustdoc_dep_info_loc = rustdoc_dep_info_loc(build_runner, unit); let dep_info_loc = fingerprint::dep_info_loc(build_runner, unit); let build_dir = build_runner.bcx.ws.build_dir().into_path_unlocked(); let pkg_root = unit.pkg.root().to_path_buf(); let cwd = rustdoc .get_cwd() .unwrap_or_else(|| build_runner.bcx.gctx.cwd()) .to_path_buf(); let fingerprint_dir = build_runner.files().fingerprint_dir(unit); let is_local = unit.is_local(); let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?); let rustdoc_depinfo_enabled = build_runner.bcx.gctx.cli_unstable().rustdoc_depinfo; let mut output_options = OutputOptions::new(build_runner, unit); let script_metadatas = build_runner.find_build_script_metadatas(unit); let scrape_outputs = if should_include_scrape_units(build_runner.bcx, unit) { Some( build_runner .bcx .scrape_units .iter() .map(|unit| { Ok(( build_runner.files().metadata(unit).unit_id(), scrape_output_path(build_runner, unit)?, )) }) .collect::>>()?, ) } else { None }; let failed_scrape_units = Arc::clone(&build_runner.failed_scrape_units); let hide_diagnostics_for_scrape_unit = build_runner.bcx.unit_can_fail_for_docscraping(unit) && !matches!( build_runner.bcx.gctx.shell().verbosity(), Verbosity::Verbose ); let failed_scrape_diagnostic = hide_diagnostics_for_scrape_unit.then(|| { make_failed_scrape_diagnostic( build_runner, unit, format_args!("failed to scan {target_desc} in package `{name}` for example code usage"), ) }); if hide_diagnostics_for_scrape_unit { output_options.show_diagnostics = false; } Ok(Work::new(move |state| { add_custom_flags( &mut rustdoc, &build_script_outputs.lock().unwrap(), script_metadatas, )?; // Add the output of scraped examples to the rustdoc command. // This action must happen after the unit's dependencies have finished, // because some of those deps may be Docscrape units which have failed. // So we dynamically determine which `--with-examples` flags to pass here. if let Some(scrape_outputs) = scrape_outputs { let failed_scrape_units = failed_scrape_units.lock().unwrap(); for (metadata, output_path) in &scrape_outputs { if !failed_scrape_units.contains(metadata) { rustdoc.arg("--with-examples").arg(output_path); } } } let crate_dir = doc_dir.join(&crate_name); if crate_dir.exists() { // Remove output from a previous build. This ensures that stale // files for removed items are removed. debug!("removing pre-existing doc directory {:?}", crate_dir); paths::remove_dir_all(crate_dir)?; } state.running(&rustdoc); let timestamp = paths::set_invocation_time(&fingerprint_dir)?; let result = rustdoc .exec_with_streaming( &mut |line| on_stdout_line(state, line, package_id, &target), &mut |line| { on_stderr_line( state, line, package_id, &manifest_path, &target, &mut output_options, ) }, false, ) .map_err(verbose_if_simple_exit_code) .with_context(|| format!("could not document `{}`", name)); if let Err(e) = result { if let Some(diagnostic) = failed_scrape_diagnostic { state.warning(diagnostic); } return Err(e); } if rustdoc_depinfo_enabled && rustdoc_dep_info_loc.exists() { fingerprint::translate_dep_info( &rustdoc_dep_info_loc, &dep_info_loc, &cwd, &pkg_root, &build_dir, &rustdoc, // Should we track source file for doc gen? is_local, &env_config, ) .with_context(|| { internal(format_args!( "could not parse/generate dep info at: {}", rustdoc_dep_info_loc.display() )) })?; // This mtime shift allows Cargo to detect if a source file was // modified in the middle of the build. paths::set_file_time_no_err(dep_info_loc, timestamp); } Ok(()) })) } // The --crate-version flag could have already been passed in RUSTDOCFLAGS // or as an extra compiler argument for rustdoc fn crate_version_flag_already_present(rustdoc: &ProcessBuilder) -> bool { rustdoc.get_args().any(|flag| { flag.to_str() .map_or(false, |flag| flag.starts_with(RUSTDOC_CRATE_VERSION_FLAG)) }) } fn append_crate_version_flag(unit: &Unit, rustdoc: &mut ProcessBuilder) { rustdoc .arg(RUSTDOC_CRATE_VERSION_FLAG) .arg(unit.pkg.version().to_string()); } /// Adds [`--cap-lints`] to the command to execute. /// /// [`--cap-lints`]: https://doc.rust-lang.org/nightly/rustc/lints/levels.html#capping-lints fn add_cap_lints(bcx: &BuildContext<'_, '_>, unit: &Unit, cmd: &mut ProcessBuilder) { // If this is an upstream dep we don't want warnings from, turn off all // lints. if !unit.show_warnings(bcx.gctx) { cmd.arg("--cap-lints").arg("allow"); // If this is an upstream dep but we *do* want warnings, make sure that they // don't fail compilation. } else if !unit.is_local() { cmd.arg("--cap-lints").arg("warn"); } } /// Forwards [`-Zallow-features`] if it is set for cargo. /// /// [`-Zallow-features`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#allow-features fn add_allow_features(build_runner: &BuildRunner<'_, '_>, cmd: &mut ProcessBuilder) { if let Some(allow) = &build_runner.bcx.gctx.cli_unstable().allow_features { use std::fmt::Write; let mut arg = String::from("-Zallow-features="); for f in allow { let _ = write!(&mut arg, "{f},"); } cmd.arg(arg.trim_end_matches(',')); } } /// Adds [`--error-format`] to the command to execute. /// /// Cargo always uses JSON output. This has several benefits, such as being /// easier to parse, handles changing formats (for replaying cached messages), /// ensures atomic output (so messages aren't interleaved), allows for /// intercepting messages like rmeta artifacts, etc. rustc includes a /// "rendered" field in the JSON message with the message properly formatted, /// which Cargo will extract and display to the user. /// /// [`--error-format`]: https://doc.rust-lang.org/nightly/rustc/command-line-arguments.html#--error-format-control-how-errors-are-produced fn add_error_format_and_color(build_runner: &BuildRunner<'_, '_>, cmd: &mut ProcessBuilder) { cmd.arg("--error-format=json"); let mut json = String::from("--json=diagnostic-rendered-ansi,artifacts,future-incompat"); match build_runner.bcx.build_config.message_format { MessageFormat::Short | MessageFormat::Json { short: true, .. } => { json.push_str(",diagnostic-short"); } _ => {} } cmd.arg(json); let gctx = build_runner.bcx.gctx; if let Some(width) = gctx.shell().err_width().diagnostic_terminal_width() { cmd.arg(format!("--diagnostic-width={width}")); } } /// Adds essential rustc flags and environment variables to the command to execute. fn build_base_args( build_runner: &BuildRunner<'_, '_>, cmd: &mut ProcessBuilder, unit: &Unit, ) -> CargoResult<()> { assert!(!unit.mode.is_run_custom_build()); let bcx = build_runner.bcx; let Profile { ref opt_level, codegen_backend, codegen_units, debuginfo, debug_assertions, split_debuginfo, overflow_checks, rpath, ref panic, incremental, strip, rustflags: profile_rustflags, trim_paths, hint_mostly_unused: profile_hint_mostly_unused, .. } = unit.profile.clone(); let hints = unit.pkg.hints().cloned().unwrap_or_default(); let test = unit.mode.is_any_test(); let warn = |msg: &str| { bcx.gctx.shell().warn(format!( "{}@{}: {msg}", unit.pkg.package_id().name(), unit.pkg.package_id().version() )) }; let unit_capped_warn = |msg: &str| { if unit.show_warnings(bcx.gctx) { warn(msg) } else { Ok(()) } }; cmd.arg("--crate-name").arg(&unit.target.crate_name()); let edition = unit.target.edition(); edition.cmd_edition_arg(cmd); add_path_args(bcx.ws, unit, cmd); add_error_format_and_color(build_runner, cmd); add_allow_features(build_runner, cmd); let mut contains_dy_lib = false; if !test { for crate_type in &unit.target.rustc_crate_types() { cmd.arg("--crate-type").arg(crate_type.as_str()); contains_dy_lib |= crate_type == &CrateType::Dylib; } } if unit.mode.is_check() { cmd.arg("--emit=dep-info,metadata"); } else if build_runner.bcx.gctx.cli_unstable().no_embed_metadata { // Nightly rustc supports the -Zembed-metadata=no flag, which tells it to avoid including // full metadata in rlib/dylib artifacts, to save space on disk. In this case, metadata // will only be stored in .rmeta files. // When we use this flag, we should also pass --emit=metadata to all artifacts that // contain useful metadata (rlib/dylib/proc macros), so that a .rmeta file is actually // generated. If we didn't do this, the full metadata would not get written anywhere. // However, we do not want to pass --emit=metadata to artifacts that never produce useful // metadata, such as binaries, because that would just unnecessarily create empty .rmeta // files on disk. if unit.benefits_from_no_embed_metadata() { cmd.arg("--emit=dep-info,metadata,link"); cmd.args(&["-Z", "embed-metadata=no"]); } else { cmd.arg("--emit=dep-info,link"); } } else { // If we don't use -Zembed-metadata=no, we emit .rmeta files only for rlib outputs. // This metadata may be used in this session for a pipelined compilation, or it may // be used in a future Cargo session as part of a pipelined compile. if !unit.requires_upstream_objects() { cmd.arg("--emit=dep-info,metadata,link"); } else { cmd.arg("--emit=dep-info,link"); } } let prefer_dynamic = (unit.target.for_host() && !unit.target.is_custom_build()) || (contains_dy_lib && !build_runner.is_primary_package(unit)); if prefer_dynamic { cmd.arg("-C").arg("prefer-dynamic"); } if opt_level.as_str() != "0" { cmd.arg("-C").arg(&format!("opt-level={}", opt_level)); } if *panic != PanicStrategy::Unwind { cmd.arg("-C").arg(format!("panic={}", panic)); } cmd.args(<o_args(build_runner, unit)); if let Some(backend) = codegen_backend { cmd.arg("-Z").arg(&format!("codegen-backend={}", backend)); } if let Some(n) = codegen_units { cmd.arg("-C").arg(&format!("codegen-units={}", n)); } let debuginfo = debuginfo.into_inner(); // Shorten the number of arguments if possible. if debuginfo != TomlDebugInfo::None { cmd.arg("-C").arg(format!("debuginfo={debuginfo}")); // This is generally just an optimization on build time so if we don't // pass it then it's ok. The values for the flag (off, packed, unpacked) // may be supported or not depending on the platform, so availability is // checked per-value. For example, at the time of writing this code, on // Windows the only stable valid value for split-debuginfo is "packed", // while on Linux "unpacked" is also stable. if let Some(split) = split_debuginfo { if build_runner .bcx .target_data .info(unit.kind) .supports_debuginfo_split(split) { cmd.arg("-C").arg(format!("split-debuginfo={split}")); } } } if let Some(trim_paths) = trim_paths { trim_paths_args(cmd, build_runner, unit, &trim_paths)?; } cmd.args(unit.pkg.manifest().lint_rustflags()); cmd.args(&profile_rustflags); // `-C overflow-checks` is implied by the setting of `-C debug-assertions`, // so we only need to provide `-C overflow-checks` if it differs from // the value of `-C debug-assertions` we would provide. if opt_level.as_str() != "0" { if debug_assertions { cmd.args(&["-C", "debug-assertions=on"]); if !overflow_checks { cmd.args(&["-C", "overflow-checks=off"]); } } else if overflow_checks { cmd.args(&["-C", "overflow-checks=on"]); } } else if !debug_assertions { cmd.args(&["-C", "debug-assertions=off"]); if overflow_checks { cmd.args(&["-C", "overflow-checks=on"]); } } else if !overflow_checks { cmd.args(&["-C", "overflow-checks=off"]); } if test && unit.target.harness() { cmd.arg("--test"); // Cargo has historically never compiled `--test` binaries with // `panic=abort` because the `test` crate itself didn't support it. // Support is now upstream, however, but requires an unstable flag to be // passed when compiling the test. We require, in Cargo, an unstable // flag to pass to rustc, so register that here. Eventually this flag // will simply not be needed when the behavior is stabilized in the Rust // compiler itself. if *panic == PanicStrategy::Abort { cmd.arg("-Z").arg("panic-abort-tests"); } } else if test { cmd.arg("--cfg").arg("test"); } cmd.args(&features_args(unit)); cmd.args(&check_cfg_args(unit)); let meta = build_runner.files().metadata(unit); cmd.arg("-C") .arg(&format!("metadata={}", meta.c_metadata())); if let Some(c_extra_filename) = meta.c_extra_filename() { cmd.arg("-C") .arg(&format!("extra-filename=-{c_extra_filename}")); } if rpath { cmd.arg("-C").arg("rpath"); } cmd.arg("--out-dir") .arg(&build_runner.files().out_dir(unit)); fn opt(cmd: &mut ProcessBuilder, key: &str, prefix: &str, val: Option<&OsStr>) { if let Some(val) = val { let mut joined = OsString::from(prefix); joined.push(val); cmd.arg(key).arg(joined); } } if let CompileKind::Target(n) = unit.kind { cmd.arg("--target").arg(n.rustc_target()); } opt( cmd, "-C", "linker=", build_runner .compilation .target_linker(unit.kind) .as_ref() .map(|s| s.as_ref()), ); if incremental { let dir = build_runner .files() .layout(unit.kind) .incremental() .as_os_str(); opt(cmd, "-C", "incremental=", Some(dir)); } let pkg_hint_mostly_unused = match hints.mostly_unused { None => None, Some(toml::Value::Boolean(b)) => Some(b), Some(v) => { unit_capped_warn(&format!( "ignoring unsupported value type ({}) for 'hints.mostly-unused', which expects a boolean", v.type_str() ))?; None } }; if profile_hint_mostly_unused .or(pkg_hint_mostly_unused) .unwrap_or(false) { if bcx.gctx.cli_unstable().profile_hint_mostly_unused { cmd.arg("-Zhint-mostly-unused"); } else { if profile_hint_mostly_unused.is_some() { // Profiles come from the top-level unit, so we don't use `unit_capped_warn` here. warn( "ignoring 'hint-mostly-unused' profile option, pass `-Zprofile-hint-mostly-unused` to enable it", )?; } else if pkg_hint_mostly_unused.is_some() { unit_capped_warn( "ignoring 'hints.mostly-unused', pass `-Zprofile-hint-mostly-unused` to enable it", )?; } } } let strip = strip.into_inner(); if strip != StripInner::None { cmd.arg("-C").arg(format!("strip={}", strip)); } if unit.is_std { // -Zforce-unstable-if-unmarked prevents the accidental use of // unstable crates within the sysroot (such as "extern crate libc" or // any non-public crate in the sysroot). // // RUSTC_BOOTSTRAP allows unstable features on stable. cmd.arg("-Z") .arg("force-unstable-if-unmarked") .env("RUSTC_BOOTSTRAP", "1"); } // Add `CARGO_BIN_EXE_` environment variables for building tests. if unit.target.is_test() || unit.target.is_bench() { for bin_target in unit .pkg .manifest() .targets() .iter() .filter(|target| target.is_bin()) { let exe_path = build_runner.files().bin_link_for_target( bin_target, unit.kind, build_runner.bcx, )?; let name = bin_target .binary_filename() .unwrap_or(bin_target.name().to_string()); let key = format!("CARGO_BIN_EXE_{}", name); cmd.env(&key, exe_path); } } Ok(()) } /// All active features for the unit passed as `--cfg features=`. fn features_args(unit: &Unit) -> Vec { let mut args = Vec::with_capacity(unit.features.len() * 2); for feat in &unit.features { args.push(OsString::from("--cfg")); args.push(OsString::from(format!("feature=\"{}\"", feat))); } args } /// Like [`trim_paths_args`] but for rustdoc invocations. fn trim_paths_args_rustdoc( cmd: &mut ProcessBuilder, build_runner: &BuildRunner<'_, '_>, unit: &Unit, trim_paths: &TomlTrimPaths, ) -> CargoResult<()> { match trim_paths { // rustdoc supports diagnostics trimming only. TomlTrimPaths::Values(values) if !values.contains(&TomlTrimPathsValue::Diagnostics) => { return Ok(()); } _ => {} } // feature gate was checked during manifest/config parsing. cmd.arg("-Zunstable-options"); // Order of `--remap-path-prefix` flags is important for `-Zbuild-std`. // We want to show `/rustc//library/std` instead of `std-0.0.0`. cmd.arg(package_remap(build_runner, unit)); cmd.arg(build_dir_remap(build_runner)); cmd.arg(sysroot_remap(build_runner, unit)); Ok(()) } /// Generates the `--remap-path-scope` and `--remap-path-prefix` for [RFC 3127]. /// See also unstable feature [`-Ztrim-paths`]. /// /// [RFC 3127]: https://rust-lang.github.io/rfcs/3127-trim-paths.html /// [`-Ztrim-paths`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#profile-trim-paths-option fn trim_paths_args( cmd: &mut ProcessBuilder, build_runner: &BuildRunner<'_, '_>, unit: &Unit, trim_paths: &TomlTrimPaths, ) -> CargoResult<()> { if trim_paths.is_none() { return Ok(()); } // feature gate was checked during manifest/config parsing. cmd.arg("-Zunstable-options"); cmd.arg(format!("-Zremap-path-scope={trim_paths}")); // Order of `--remap-path-prefix` flags is important for `-Zbuild-std`. // We want to show `/rustc//library/std` instead of `std-0.0.0`. cmd.arg(package_remap(build_runner, unit)); cmd.arg(build_dir_remap(build_runner)); cmd.arg(sysroot_remap(build_runner, unit)); Ok(()) } /// Path prefix remap rules for sysroot. /// /// This remap logic aligns with rustc: /// fn sysroot_remap(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OsString { let mut remap = OsString::from("--remap-path-prefix="); remap.push({ // See also `detect_sysroot_src_path()`. let mut sysroot = build_runner.bcx.target_data.info(unit.kind).sysroot.clone(); sysroot.push("lib"); sysroot.push("rustlib"); sysroot.push("src"); sysroot.push("rust"); sysroot }); remap.push("="); remap.push("/rustc/"); if let Some(commit_hash) = build_runner.bcx.rustc().commit_hash.as_ref() { remap.push(commit_hash); } else { remap.push(build_runner.bcx.rustc().version.to_string()); } remap } /// Path prefix remap rules for dependencies. /// /// * Git dependencies: remove `~/.cargo/git/checkouts` prefix. /// * Registry dependencies: remove `~/.cargo/registry/src` prefix. /// * Others (e.g. path dependencies): /// * relative paths to workspace root if inside the workspace directory. /// * otherwise remapped to `-`. fn package_remap(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OsString { let pkg_root = unit.pkg.root(); let ws_root = build_runner.bcx.ws.root(); let mut remap = OsString::from("--remap-path-prefix="); let source_id = unit.pkg.package_id().source_id(); if source_id.is_git() { remap.push( build_runner .bcx .gctx .git_checkouts_path() .as_path_unlocked(), ); remap.push("="); } else if source_id.is_registry() { remap.push( build_runner .bcx .gctx .registry_source_path() .as_path_unlocked(), ); remap.push("="); } else if pkg_root.strip_prefix(ws_root).is_ok() { remap.push(ws_root); remap.push("=."); // remap to relative rustc work dir explicitly } else { remap.push(pkg_root); remap.push("="); remap.push(unit.pkg.name()); remap.push("-"); remap.push(unit.pkg.version().to_string()); } remap } /// Remap all paths pointing to `build.build-dir`, /// i.e., `[BUILD_DIR]/debug/deps/foo-[HASH].dwo` would be remapped to /// `/cargo/build-dir/debug/deps/foo-[HASH].dwo` /// (note the `/cargo/build-dir` prefix). /// /// This covers scenarios like: /// /// * Build script generated code. For example, a build script may call `file!` /// macros, and the associated crate uses [`include!`] to include the expanded /// [`file!`] macro in-place via the `OUT_DIR` environment. /// * On Linux, `DW_AT_GNU_dwo_name` that contains paths to split debuginfo /// files (dwp and dwo). fn build_dir_remap(build_runner: &BuildRunner<'_, '_>) -> OsString { let build_dir = build_runner.bcx.ws.build_dir(); let mut remap = OsString::from("--remap-path-prefix="); remap.push(build_dir.as_path_unlocked()); remap.push("=/cargo/build-dir"); remap } /// Generates the `--check-cfg` arguments for the `unit`. fn check_cfg_args(unit: &Unit) -> Vec { // The routine below generates the --check-cfg arguments. Our goals here are to // enable the checking of conditionals and pass the list of declared features. // // In the simplified case, it would resemble something like this: // // --check-cfg=cfg() --check-cfg=cfg(feature, values(...)) // // but having `cfg()` is redundant with the second argument (as well-known names // and values are implicitly enabled when one or more `--check-cfg` argument is // passed) so we don't emit it and just pass: // // --check-cfg=cfg(feature, values(...)) // // This way, even if there are no declared features, the config `feature` will // still be expected, meaning users would get "unexpected value" instead of name. // This wasn't always the case, see rust-lang#119930 for some details. let gross_cap_estimation = unit.pkg.summary().features().len() * 7 + 25; let mut arg_feature = OsString::with_capacity(gross_cap_estimation); arg_feature.push("cfg(feature, values("); for (i, feature) in unit.pkg.summary().features().keys().enumerate() { if i != 0 { arg_feature.push(", "); } arg_feature.push("\""); arg_feature.push(feature); arg_feature.push("\""); } arg_feature.push("))"); // In addition to the package features, we also include the `test` cfg (since // compiler-team#785, as to be able to someday apply yt conditionally), as well // the `docsrs` cfg from the docs.rs service. // // We include `docsrs` here (in Cargo) instead of rustc, since there is a much closer // relationship between Cargo and docs.rs than rustc and docs.rs. In particular, all // users of docs.rs use Cargo, but not all users of rustc (like Rust-for-Linux) use docs.rs. vec![ OsString::from("--check-cfg"), OsString::from("cfg(docsrs,test)"), OsString::from("--check-cfg"), arg_feature, ] } /// Adds LTO related codegen flags. fn lto_args(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> Vec { let mut result = Vec::new(); let mut push = |arg: &str| { result.push(OsString::from("-C")); result.push(OsString::from(arg)); }; match build_runner.lto[unit] { lto::Lto::Run(None) => push("lto"), lto::Lto::Run(Some(s)) => push(&format!("lto={}", s)), lto::Lto::Off => { push("lto=off"); push("embed-bitcode=no"); } lto::Lto::ObjectAndBitcode => {} // this is rustc's default lto::Lto::OnlyBitcode => push("linker-plugin-lto"), lto::Lto::OnlyObject => push("embed-bitcode=no"), } result } /// Adds dependency-relevant rustc flags and environment variables /// to the command to execute, such as [`-L`] and [`--extern`]. /// /// [`-L`]: https://doc.rust-lang.org/nightly/rustc/command-line-arguments.html#-l-add-a-directory-to-the-library-search-path /// [`--extern`]: https://doc.rust-lang.org/nightly/rustc/command-line-arguments.html#--extern-specify-where-an-external-library-is-located fn build_deps_args( cmd: &mut ProcessBuilder, build_runner: &BuildRunner<'_, '_>, unit: &Unit, ) -> CargoResult<()> { let bcx = build_runner.bcx; cmd.arg("-L").arg(&{ let mut deps = OsString::from("dependency="); deps.push(build_runner.files().deps_dir(unit)); deps }); // Be sure that the host path is also listed. This'll ensure that proc macro // dependencies are correctly found (for reexported macros). if !unit.kind.is_host() { cmd.arg("-L").arg(&{ let mut deps = OsString::from("dependency="); deps.push(build_runner.files().host_deps()); deps }); } let deps = build_runner.unit_deps(unit); // If there is not one linkable target but should, rustc fails later // on if there is an `extern crate` for it. This may turn into a hard // error in the future (see PR #4797). if !deps .iter() .any(|dep| !dep.unit.mode.is_doc() && dep.unit.target.is_linkable()) { if let Some(dep) = deps.iter().find(|dep| { !dep.unit.mode.is_doc() && dep.unit.target.is_lib() && !dep.unit.artifact.is_true() }) { bcx.gctx.shell().warn(format!( "The package `{}` \ provides no linkable target. The compiler might raise an error while compiling \ `{}`. Consider adding 'dylib' or 'rlib' to key `crate-type` in `{}`'s \ Cargo.toml. This warning might turn into a hard error in the future.", dep.unit.target.crate_name(), unit.target.crate_name(), dep.unit.target.crate_name() ))?; } } let mut unstable_opts = false; for dep in deps { if dep.unit.mode.is_run_custom_build() { cmd.env( "OUT_DIR", &build_runner.files().build_script_out_dir(&dep.unit), ); } } for arg in extern_args(build_runner, unit, &mut unstable_opts)? { cmd.arg(arg); } for (var, env) in artifact::get_env(build_runner, deps)? { cmd.env(&var, env); } // This will only be set if we're already using a feature // requiring nightly rust if unstable_opts { cmd.arg("-Z").arg("unstable-options"); } Ok(()) } /// Adds extra rustc flags and environment variables collected from the output /// of a build-script to the command to execute, include custom environment /// variables and `cfg`. fn add_custom_flags( cmd: &mut ProcessBuilder, build_script_outputs: &BuildScriptOutputs, metadata_vec: Option>, ) -> CargoResult<()> { if let Some(metadata_vec) = metadata_vec { for metadata in metadata_vec { if let Some(output) = build_script_outputs.get(metadata) { for cfg in output.cfgs.iter() { cmd.arg("--cfg").arg(cfg); } for check_cfg in &output.check_cfgs { cmd.arg("--check-cfg").arg(check_cfg); } for (name, value) in output.env.iter() { cmd.env(name, value); } } } } Ok(()) } /// Generates a list of `--extern` arguments. pub fn extern_args( build_runner: &BuildRunner<'_, '_>, unit: &Unit, unstable_opts: &mut bool, ) -> CargoResult> { let mut result = Vec::new(); let deps = build_runner.unit_deps(unit); let no_embed_metadata = build_runner.bcx.gctx.cli_unstable().no_embed_metadata; // Closure to add one dependency to `result`. let mut link_to = |dep: &UnitDep, extern_crate_name: InternedString, noprelude: bool| -> CargoResult<()> { let mut value = OsString::new(); let mut opts = Vec::new(); let is_public_dependency_enabled = unit .pkg .manifest() .unstable_features() .require(Feature::public_dependency()) .is_ok() || build_runner.bcx.gctx.cli_unstable().public_dependency; if !dep.public && unit.target.is_lib() && is_public_dependency_enabled { opts.push("priv"); *unstable_opts = true; } if noprelude { opts.push("noprelude"); *unstable_opts = true; } if !opts.is_empty() { value.push(opts.join(",")); value.push(":"); } value.push(extern_crate_name.as_str()); value.push("="); let mut pass = |file| { let mut value = value.clone(); value.push(file); result.push(OsString::from("--extern")); result.push(value); }; let outputs = build_runner.outputs(&dep.unit)?; if build_runner.only_requires_rmeta(unit, &dep.unit) || dep.unit.mode.is_check() { // Example: rlib dependency for an rlib, rmeta is all that is required. let output = outputs .iter() .find(|output| output.flavor == FileFlavor::Rmeta) .expect("failed to find rmeta dep for pipelined dep"); pass(&output.path); } else { // Example: a bin needs `rlib` for dependencies, it cannot use rmeta. for output in outputs.iter() { if output.flavor == FileFlavor::Linkable { pass(&output.path); } // If we use -Zembed-metadata=no, we also need to pass the path to the // corresponding .rmeta file to the linkable artifact, because the // normal dependency (rlib) doesn't contain the full metadata. else if no_embed_metadata && output.flavor == FileFlavor::Rmeta { pass(&output.path); } } } Ok(()) }; for dep in deps { if dep.unit.target.is_linkable() && !dep.unit.mode.is_doc() { link_to(dep, dep.extern_crate_name, dep.noprelude)?; } } if unit.target.proc_macro() { // Automatically import `proc_macro`. result.push(OsString::from("--extern")); result.push(OsString::from("proc_macro")); } Ok(result) } fn envify(s: &str) -> String { s.chars() .flat_map(|c| c.to_uppercase()) .map(|c| if c == '-' { '_' } else { c }) .collect() } /// Configuration of the display of messages emitted by the compiler, /// e.g. diagnostics, warnings, errors, and message caching. struct OutputOptions { /// What format we're emitting from Cargo itself. format: MessageFormat, /// Where to write the JSON messages to support playback later if the unit /// is fresh. The file is created lazily so that in the normal case, lots /// of empty files are not created. If this is None, the output will not /// be cached (such as when replaying cached messages). cache_cell: Option<(PathBuf, LazyCell)>, /// If `true`, display any diagnostics. /// Other types of JSON messages are processed regardless /// of the value of this flag. /// /// This is used primarily for cache replay. If you build with `-vv`, the /// cache will be filled with diagnostics from dependencies. When the /// cache is replayed without `-vv`, we don't want to show them. show_diagnostics: bool, /// Tracks the number of warnings we've seen so far. warnings_seen: usize, /// Tracks the number of errors we've seen so far. errors_seen: usize, } impl OutputOptions { fn new(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OutputOptions { let path = build_runner.files().message_cache_path(unit); // Remove old cache, ignore ENOENT, which is the common case. drop(fs::remove_file(&path)); let cache_cell = Some((path, LazyCell::new())); let show_diagnostics = build_runner.bcx.gctx.warning_handling().unwrap_or_default() != WarningHandling::Allow; OutputOptions { format: build_runner.bcx.build_config.message_format, cache_cell, show_diagnostics, warnings_seen: 0, errors_seen: 0, } } } fn on_stdout_line( state: &JobState<'_, '_>, line: &str, _package_id: PackageId, _target: &Target, ) -> CargoResult<()> { state.stdout(line.to_string())?; Ok(()) } fn on_stderr_line( state: &JobState<'_, '_>, line: &str, package_id: PackageId, manifest_path: &std::path::Path, target: &Target, options: &mut OutputOptions, ) -> CargoResult<()> { if on_stderr_line_inner(state, line, package_id, manifest_path, target, options)? { // Check if caching is enabled. if let Some((path, cell)) = &mut options.cache_cell { // Cache the output, which will be replayed later when Fresh. let f = cell.try_borrow_mut_with(|| paths::create(path))?; debug_assert!(!line.contains('\n')); f.write_all(line.as_bytes())?; f.write_all(&[b'\n'])?; } } Ok(()) } /// Returns true if the line should be cached. fn on_stderr_line_inner( state: &JobState<'_, '_>, line: &str, package_id: PackageId, manifest_path: &std::path::Path, target: &Target, options: &mut OutputOptions, ) -> CargoResult { // We primarily want to use this function to process JSON messages from // rustc. The compiler should always print one JSON message per line, and // otherwise it may have other output intermingled (think RUST_LOG or // something like that), so skip over everything that doesn't look like a // JSON message. if !line.starts_with('{') { state.stderr(line.to_string())?; return Ok(true); } let mut compiler_message: Box = match serde_json::from_str(line) { Ok(msg) => msg, // If the compiler produced a line that started with `{` but it wasn't // valid JSON, maybe it wasn't JSON in the first place! Forward it along // to stderr. Err(e) => { debug!("failed to parse json: {:?}", e); state.stderr(line.to_string())?; return Ok(true); } }; let count_diagnostic = |level, options: &mut OutputOptions| { if level == "warning" { options.warnings_seen += 1; } else if level == "error" { options.errors_seen += 1; } }; if let Ok(report) = serde_json::from_str::(compiler_message.get()) { for item in &report.future_incompat_report { count_diagnostic(&*item.diagnostic.level, options); } state.future_incompat_report(report.future_incompat_report); return Ok(true); } // Depending on what we're emitting from Cargo itself, we figure out what to // do with this JSON message. match options.format { // In the "human" output formats (human/short) or if diagnostic messages // from rustc aren't being included in the output of Cargo's JSON // messages then we extract the diagnostic (if present) here and handle // it ourselves. MessageFormat::Human | MessageFormat::Short | MessageFormat::Json { render_diagnostics: true, .. } => { #[derive(serde::Deserialize)] struct CompilerMessage<'a> { // `rendered` contains escape sequences, which can't be // zero-copy deserialized by serde_json. // See https://github.com/serde-rs/json/issues/742 rendered: String, #[serde(borrow)] message: Cow<'a, str>, #[serde(borrow)] level: Cow<'a, str>, children: Vec, } // A partial rustfix::diagnostics::Diagnostic. We deserialize only a // subset of the fields because rustc's output can be extremely // deeply nested JSON in pathological cases involving macro // expansion. Rustfix's Diagnostic struct is recursive containing a // field `children: Vec`, and it can cause deserialization to // hit serde_json's default recursion limit, or overflow the stack // if we turn that off. Cargo only cares about the 1 field listed // here. #[derive(serde::Deserialize)] struct PartialDiagnostic { spans: Vec, } // A partial rustfix::diagnostics::DiagnosticSpan. #[derive(serde::Deserialize)] struct PartialDiagnosticSpan { suggestion_applicability: Option, } if let Ok(mut msg) = serde_json::from_str::>(compiler_message.get()) { if msg.message.starts_with("aborting due to") || msg.message.ends_with("warning emitted") || msg.message.ends_with("warnings emitted") { // Skip this line; we'll print our own summary at the end. return Ok(true); } // state.stderr will add a newline if msg.rendered.ends_with('\n') { msg.rendered.pop(); } let rendered = msg.rendered; if options.show_diagnostics { let machine_applicable: bool = msg .children .iter() .map(|child| { child .spans .iter() .filter_map(|span| span.suggestion_applicability) .any(|app| app == Applicability::MachineApplicable) }) .any(|b| b); count_diagnostic(&msg.level, options); state.emit_diag(&msg.level, rendered, machine_applicable)?; } return Ok(true); } } // Remove color information from the rendered string if color is not // enabled. Cargo always asks for ANSI colors from rustc. This allows // cached replay to enable/disable colors without re-invoking rustc. MessageFormat::Json { ansi: false, .. } => { #[derive(serde::Deserialize, serde::Serialize)] struct CompilerMessage<'a> { rendered: String, #[serde(flatten, borrow)] other: std::collections::BTreeMap, serde_json::Value>, } if let Ok(mut error) = serde_json::from_str::>(compiler_message.get()) { error.rendered = anstream::adapter::strip_str(&error.rendered).to_string(); let new_line = serde_json::to_string(&error)?; compiler_message = serde_json::value::RawValue::from_string(new_line)?; } } // If ansi colors are desired then we should be good to go! We can just // pass through this message as-is. MessageFormat::Json { ansi: true, .. } => {} } // We always tell rustc to emit messages about artifacts being produced. // These messages feed into pipelined compilation, as well as timing // information. // // Look for a matching directive and inform Cargo internally that a // metadata file has been produced. #[derive(serde::Deserialize)] struct ArtifactNotification<'a> { #[serde(borrow)] artifact: Cow<'a, str>, } if let Ok(artifact) = serde_json::from_str::>(compiler_message.get()) { trace!("found directive from rustc: `{}`", artifact.artifact); if artifact.artifact.ends_with(".rmeta") { debug!("looks like metadata finished early!"); state.rmeta_produced(); } return Ok(false); } // And failing all that above we should have a legitimate JSON diagnostic // from the compiler, so wrap it in an external Cargo JSON message // indicating which package it came from and then emit it. if !options.show_diagnostics { return Ok(true); } #[derive(serde::Deserialize)] struct CompilerMessage<'a> { #[serde(borrow)] message: Cow<'a, str>, #[serde(borrow)] level: Cow<'a, str>, } if let Ok(msg) = serde_json::from_str::>(compiler_message.get()) { if msg.message.starts_with("aborting due to") || msg.message.ends_with("warning emitted") || msg.message.ends_with("warnings emitted") { // Skip this line; we'll print our own summary at the end. return Ok(true); } count_diagnostic(&msg.level, options); } let msg = machine_message::FromCompiler { package_id: package_id.to_spec(), manifest_path, target, message: compiler_message, } .to_json_string(); // Switch json lines from rustc/rustdoc that appear on stderr to stdout // instead. We want the stdout of Cargo to always be machine parseable as // stderr has our colorized human-readable messages. state.stdout(msg)?; Ok(true) } /// Creates a unit of work that replays the cached compiler message. /// /// Usually used when a job is fresh and doesn't need to recompile. fn replay_output_cache( package_id: PackageId, manifest_path: PathBuf, target: &Target, path: PathBuf, format: MessageFormat, show_diagnostics: bool, ) -> Work { let target = target.clone(); let mut options = OutputOptions { format, cache_cell: None, show_diagnostics, warnings_seen: 0, errors_seen: 0, }; Work::new(move |state| { if !path.exists() { // No cached output, probably didn't emit anything. return Ok(()); } // We sometimes have gigabytes of output from the compiler, so avoid // loading it all into memory at once, as that can cause OOM where // otherwise there would be none. let file = paths::open(&path)?; let mut reader = std::io::BufReader::new(file); let mut line = String::new(); loop { let length = reader.read_line(&mut line)?; if length == 0 { break; } let trimmed = line.trim_end_matches(&['\n', '\r'][..]); on_stderr_line( state, trimmed, package_id, &manifest_path, &target, &mut options, )?; line.clear(); } Ok(()) }) } /// Provides a package name with descriptive target information, /// e.g., '`foo` (bin "bar" test)', '`foo` (lib doctest)'. fn descriptive_pkg_name(name: &str, target: &Target, mode: &CompileMode) -> String { let desc_name = target.description_named(); let mode = if mode.is_rustc_test() && !(target.is_test() || target.is_bench()) { " test" } else if mode.is_doc_test() { " doctest" } else if mode.is_doc() { " doc" } else { "" }; format!("`{name}` ({desc_name}{mode})") } /// Applies environment variables from config `[env]` to [`ProcessBuilder`]. pub(crate) fn apply_env_config( gctx: &crate::GlobalContext, cmd: &mut ProcessBuilder, ) -> CargoResult<()> { for (key, value) in gctx.env_config()?.iter() { // never override a value that has already been set by cargo if cmd.get_envs().contains_key(key) { continue; } cmd.env(key, value); } Ok(()) } /// Checks if there are some scrape units waiting to be processed. fn should_include_scrape_units(bcx: &BuildContext<'_, '_>, unit: &Unit) -> bool { unit.mode.is_doc() && bcx.scrape_units.len() > 0 && bcx.ws.unit_needs_doc_scrape(unit) } /// Gets the file path of function call information output from `rustdoc`. fn scrape_output_path(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult { assert!(unit.mode.is_doc() || unit.mode.is_doc_scrape()); build_runner .outputs(unit) .map(|outputs| outputs[0].path.clone()) } /// Gets the dep-info file emitted by rustdoc. fn rustdoc_dep_info_loc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> PathBuf { let mut loc = build_runner.files().fingerprint_file_path(unit, ""); loc.set_extension("d"); loc } cargo-0.91.0/src/cargo/core/compiler/output_depinfo.rs000064400000000000000000000154071046102023000210530ustar 00000000000000//! dep-info files for external build system integration. //! See [`output_depinfo`] for more. use cargo_util::paths::normalize_path; use std::collections::{BTreeSet, HashSet}; use std::io::{BufWriter, Write}; use std::path::{Path, PathBuf}; use super::{BuildRunner, FileFlavor, Unit, fingerprint}; use crate::util::{CargoResult, internal}; use cargo_util::paths; use tracing::debug; /// Bacially just normalizes a given path and converts it to a string. fn render_filename>(path: P, basedir: Option<&str>) -> CargoResult { fn wrap_path(path: &Path) -> CargoResult { path.to_str() .ok_or_else(|| internal(format!("path `{:?}` not utf-8", path))) .map(|f| f.replace(" ", "\\ ")) } let path = path.as_ref(); if let Some(basedir) = basedir { let norm_path = normalize_path(path); let norm_basedir = normalize_path(basedir.as_ref()); match norm_path.strip_prefix(norm_basedir) { Ok(relpath) => wrap_path(relpath), _ => wrap_path(path), } } else { wrap_path(path) } } /// Collects all dependencies of the `unit` for the output dep info file. /// /// Dependencies will be stored in `deps`, including: /// /// * dependencies from [fingerprint dep-info] /// * paths from `rerun-if-changed` build script instruction /// * ...and traverse transitive dependencies recursively /// /// [fingerprint dep-info]: super::fingerprint#fingerprint-dep-info-files fn add_deps_for_unit( deps: &mut BTreeSet, build_runner: &mut BuildRunner<'_, '_>, unit: &Unit, visited: &mut HashSet, ) -> CargoResult<()> { if !visited.insert(unit.clone()) { return Ok(()); } // units representing the execution of a build script don't actually // generate a dep info file, so we just keep on going below if !unit.mode.is_run_custom_build() { // Add dependencies from rustc dep-info output (stored in fingerprint directory) let dep_info_loc = fingerprint::dep_info_loc(build_runner, unit); if let Some(paths) = fingerprint::parse_dep_info( unit.pkg.root(), build_runner.files().host_root(), &dep_info_loc, )? { for path in paths.files.into_keys() { deps.insert(path); } } else { debug!( "can't find dep_info for {:?} {}", unit.pkg.package_id(), unit.target ); return Err(internal("dep_info missing")); } } // Add rerun-if-changed dependencies if let Some(metadata_vec) = build_runner.find_build_script_metadatas(unit) { for metadata in metadata_vec { if let Some(output) = build_runner .build_script_outputs .lock() .unwrap() .get(metadata) { for path in &output.rerun_if_changed { // The paths we have saved from the unit are of arbitrary relativeness and may be // relative to the crate root of the dependency. let path = unit.pkg.root().join(path); deps.insert(path); } } } } // Recursively traverse all transitive dependencies let unit_deps = Vec::from(build_runner.unit_deps(unit)); // Create vec due to mutable borrow. for dep in unit_deps { if dep.unit.is_local() { add_deps_for_unit(deps, build_runner, &dep.unit, visited)?; } } Ok(()) } /// Save a `.d` dep-info file for the given unit. This is the third kind of /// dep-info mentioned in [`fingerprint`] module. /// /// Argument `unit` is expected to be the root unit, which will be uplifted. /// /// Cargo emits its own dep-info files in the output directory. This is /// only done for every "uplifted" artifact. These are intended to be used /// with external build systems so that they can detect if Cargo needs to be /// re-executed. /// /// It includes all the entries from the `rustc` dep-info file, and extends it /// with any `rerun-if-changed` entries from build scripts. It also includes /// sources from any path dependencies. Registry dependencies are not included /// under the assumption that changes to them can be detected via changes to /// `Cargo.lock`. /// /// [`fingerprint`]: super::fingerprint#dep-info-files pub fn output_depinfo(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<()> { let bcx = build_runner.bcx; let mut deps = BTreeSet::new(); let mut visited = HashSet::new(); let success = add_deps_for_unit(&mut deps, build_runner, unit, &mut visited).is_ok(); let basedir_string; let basedir = match bcx.gctx.build_config()?.dep_info_basedir.clone() { Some(value) => { basedir_string = value .resolve_path(bcx.gctx) .as_os_str() .to_str() .ok_or_else(|| anyhow::format_err!("build.dep-info-basedir path not utf-8"))? .to_string(); Some(basedir_string.as_str()) } None => None, }; let deps = deps .iter() .map(|f| render_filename(f, basedir)) .collect::>>()?; for output in build_runner.outputs(unit)?.iter().filter(|o| { !matches!( o.flavor, FileFlavor::DebugInfo | FileFlavor::Auxiliary | FileFlavor::Sbom ) }) { if let Some(ref link_dst) = output.hardlink { let output_path = link_dst.with_extension("d"); if success { let target_fn = render_filename(link_dst, basedir)?; // If nothing changed don't recreate the file which could alter // its mtime if let Ok(previous) = fingerprint::parse_rustc_dep_info(&output_path) { if previous .files .iter() .map(|(path, _checksum)| path) .eq(deps.iter().map(Path::new)) { continue; } } // Otherwise write it all out let mut outfile = BufWriter::new(paths::create(output_path)?); write!(outfile, "{}:", target_fn)?; for dep in &deps { write!(outfile, " {}", dep)?; } writeln!(outfile)?; // dep-info generation failed, so delete output file. This will // usually cause the build system to always rerun the build // rule, which is correct if inefficient. } else if output_path.exists() { paths::remove_file(output_path)?; } } } Ok(()) } cargo-0.91.0/src/cargo/core/compiler/output_sbom.rs000064400000000000000000000134431046102023000203650ustar 00000000000000//! cargo-sbom precursor files for external tools to create SBOM files from. //! See [`build_sbom_graph`] for more. use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::path::PathBuf; use cargo_util_schemas::core::PackageIdSpec; use itertools::Itertools; use serde::Serialize; use crate::CargoResult; use crate::core::TargetKind; use crate::util::Rustc; use crate::util::interning::InternedString; use super::{BuildRunner, CompileMode, Unit}; /// Typed version of a SBOM format version number. #[derive(Serialize, Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)] pub struct SbomFormatVersion(u32); #[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Serialize)] #[serde(rename_all = "snake_case")] enum SbomDependencyType { /// A dependency linked to the artifact produced by this unit. Normal, /// A dependency needed to run the build for this unit (e.g. a build script or proc-macro). /// The dependency is not linked to the artifact produced by this unit. Build, } #[derive(Serialize, Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)] struct SbomIndex(usize); #[derive(Serialize, Clone, Debug)] #[serde(rename_all = "snake_case")] struct SbomDependency { index: SbomIndex, kind: SbomDependencyType, } #[derive(Serialize, Clone, Debug)] #[serde(rename_all = "snake_case")] struct SbomCrate { id: PackageIdSpec, features: Vec, dependencies: Vec, kind: TargetKind, } impl SbomCrate { pub fn new(unit: &Unit) -> Self { let package_id = unit.pkg.package_id().to_spec(); let features = unit.features.iter().map(|f| f.to_string()).collect_vec(); Self { id: package_id, features, dependencies: Vec::new(), kind: unit.target.kind().clone(), } } } #[derive(Serialize, Clone)] #[serde(rename_all = "snake_case")] struct SbomRustc { version: String, wrapper: Option, workspace_wrapper: Option, commit_hash: Option, host: String, verbose_version: String, } impl From<&Rustc> for SbomRustc { fn from(rustc: &Rustc) -> Self { Self { version: rustc.version.to_string(), wrapper: rustc.wrapper.clone(), workspace_wrapper: rustc.workspace_wrapper.clone(), commit_hash: rustc.commit_hash.clone(), host: rustc.host.to_string(), verbose_version: rustc.verbose_version.clone(), } } } #[derive(Serialize)] #[serde(rename_all = "snake_case")] pub struct Sbom { version: SbomFormatVersion, root: SbomIndex, crates: Vec, rustc: SbomRustc, target: InternedString, } /// Build an [`Sbom`] for the given [`Unit`]. pub fn build_sbom(build_runner: &BuildRunner<'_, '_>, root: &Unit) -> CargoResult { let bcx = build_runner.bcx; let rustc: SbomRustc = bcx.rustc().into(); let mut crates = Vec::new(); let sbom_graph = build_sbom_graph(build_runner, root); // Build set of indicies for each node in the graph for fast lookup. let indicies: HashMap<&Unit, SbomIndex> = sbom_graph .keys() .enumerate() .map(|(i, dep)| (*dep, SbomIndex(i))) .collect(); // Add a item to the crates list for each node in the graph. for (unit, edges) in sbom_graph { let mut krate = SbomCrate::new(unit); for (dep, kind) in edges { krate.dependencies.push(SbomDependency { index: indicies[dep], kind: kind, }); } crates.push(krate); } let target = match root.kind { super::CompileKind::Host => build_runner.bcx.host_triple(), super::CompileKind::Target(target) => target.rustc_target(), }; Ok(Sbom { version: SbomFormatVersion(1), crates, root: indicies[root], rustc, target, }) } /// List all dependencies, including transitive ones. A dependency can also appear multiple times /// if it's using different settings, e.g. profile, features or crate versions. /// /// Returns a graph of dependencies. fn build_sbom_graph<'a>( build_runner: &'a BuildRunner<'_, '_>, root: &'a Unit, ) -> BTreeMap<&'a Unit, BTreeSet<(&'a Unit, SbomDependencyType)>> { tracing::trace!("building sbom graph for {}", root.pkg.package_id()); let mut queue = Vec::new(); let mut sbom_graph: BTreeMap<&Unit, BTreeSet<(&Unit, SbomDependencyType)>> = BTreeMap::new(); let mut visited = HashSet::new(); // Search to collect all dependencies of the root unit. queue.push((root, root, false)); while let Some((node, parent, is_build_dep)) = queue.pop() { let dependencies = sbom_graph.entry(parent).or_default(); for dep in build_runner.unit_deps(node) { let dep = &dep.unit; let (next_parent, next_is_build_dep) = if dep.mode == CompileMode::RunCustomBuild { // Nodes in the SBOM graph for building/running build scripts are moved on to their parent as build dependencies. (parent, true) } else { // Proc-macros and build scripts are marked as build dependencies. let dep_type = match is_build_dep || dep.target.proc_macro() { false => SbomDependencyType::Normal, true => SbomDependencyType::Build, }; dependencies.insert((dep, dep_type)); tracing::trace!( "adding sbom edge {} -> {} ({:?})", parent.pkg.package_id(), dep.pkg.package_id(), dep_type, ); (dep, false) }; if visited.insert(dep) { queue.push((dep, next_parent, next_is_build_dep)); } } } sbom_graph } cargo-0.91.0/src/cargo/core/compiler/rustdoc.rs000064400000000000000000000261611046102023000174710ustar 00000000000000//! Utilities for building with rustdoc. use crate::core::compiler::build_runner::BuildRunner; use crate::core::compiler::unit::Unit; use crate::core::compiler::{BuildContext, CompileKind}; use crate::sources::CRATES_IO_REGISTRY; use crate::util::errors::{CargoResult, internal}; use cargo_util::ProcessBuilder; use std::collections::HashMap; use std::collections::HashSet; use std::fmt; use std::hash; use url::Url; const DOCS_RS_URL: &'static str = "https://docs.rs/"; /// Mode used for `std`. This is for unstable feature [`-Zrustdoc-map`][1]. /// /// [1]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#rustdoc-map #[derive(Debug, Hash)] pub enum RustdocExternMode { /// Use a local `file://` URL. Local, /// Use a remote URL to (default). Remote, /// An arbitrary URL. Url(String), } impl From for RustdocExternMode { fn from(s: String) -> RustdocExternMode { match s.as_ref() { "local" => RustdocExternMode::Local, "remote" => RustdocExternMode::Remote, _ => RustdocExternMode::Url(s), } } } impl fmt::Display for RustdocExternMode { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { RustdocExternMode::Local => "local".fmt(f), RustdocExternMode::Remote => "remote".fmt(f), RustdocExternMode::Url(s) => s.fmt(f), } } } impl<'de> serde::de::Deserialize<'de> for RustdocExternMode { fn deserialize(deserializer: D) -> Result where D: serde::de::Deserializer<'de>, { let s = String::deserialize(deserializer)?; Ok(s.into()) } } /// A map of registry names to URLs where documentations are hosted. /// This is for unstable feature [`-Zrustdoc-map`][1]. /// /// [1]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#rustdoc-map #[derive(serde::Deserialize, Debug)] #[serde(default)] pub struct RustdocExternMap { #[serde(deserialize_with = "default_crates_io_to_docs_rs")] /// * Key is the registry name in the configuration `[registries.]`. /// * Value is the URL where the documentation is hosted. registries: HashMap, std: Option, } impl Default for RustdocExternMap { fn default() -> Self { Self { registries: HashMap::from([(CRATES_IO_REGISTRY.into(), DOCS_RS_URL.into())]), std: None, } } } fn default_crates_io_to_docs_rs<'de, D: serde::Deserializer<'de>>( de: D, ) -> Result, D::Error> { use serde::Deserialize; let mut registries = HashMap::deserialize(de)?; if !registries.contains_key(CRATES_IO_REGISTRY) { registries.insert(CRATES_IO_REGISTRY.into(), DOCS_RS_URL.into()); } Ok(registries) } impl hash::Hash for RustdocExternMap { fn hash(&self, into: &mut H) { self.std.hash(into); for (key, value) in &self.registries { key.hash(into); value.hash(into); } } } /// Recursively generate html root url for all units and their children. /// /// This is needed because in case there is a reexport of foreign reexport, you /// need to have information about grand-children deps level (deps of your deps). fn build_all_urls( build_runner: &BuildRunner<'_, '_>, rustdoc: &mut ProcessBuilder, unit: &Unit, name2url: &HashMap<&String, Url>, map: &RustdocExternMap, unstable_opts: &mut bool, seen: &mut HashSet, ) { for dep in build_runner.unit_deps(unit) { if !seen.insert(dep.unit.clone()) { continue; } if !dep.unit.target.is_linkable() || dep.unit.mode.is_doc() { continue; } for (registry, location) in &map.registries { let sid = dep.unit.pkg.package_id().source_id(); let matches_registry = || -> bool { if !sid.is_registry() { return false; } if sid.is_crates_io() { return registry == CRATES_IO_REGISTRY; } if let Some(index_url) = name2url.get(registry) { return index_url == sid.url(); } false }; if matches_registry() { let mut url = location.clone(); if !url.contains("{pkg_name}") && !url.contains("{version}") { if !url.ends_with('/') { url.push('/'); } url.push_str("{pkg_name}/{version}/"); } let url = url .replace("{pkg_name}", &dep.unit.pkg.name()) .replace("{version}", &dep.unit.pkg.version().to_string()); rustdoc.arg("--extern-html-root-url"); rustdoc.arg(format!("{}={}", dep.unit.target.crate_name(), url)); *unstable_opts = true; } } build_all_urls( build_runner, rustdoc, &dep.unit, name2url, map, unstable_opts, seen, ); } } /// Adds unstable flag [`--extern-html-root-url`][1] to the given `rustdoc` /// invocation. This is for unstable feature [`-Zrustdoc-map`][2]. /// /// [1]: https://doc.rust-lang.org/nightly/rustdoc/unstable-features.html#--extern-html-root-url-control-how-rustdoc-links-to-non-local-crates /// [2]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#rustdoc-map pub fn add_root_urls( build_runner: &BuildRunner<'_, '_>, unit: &Unit, rustdoc: &mut ProcessBuilder, ) -> CargoResult<()> { let gctx = build_runner.bcx.gctx; if !gctx.cli_unstable().rustdoc_map { tracing::debug!("`doc.extern-map` ignored, requires -Zrustdoc-map flag"); return Ok(()); } let map = gctx.doc_extern_map()?; let mut unstable_opts = false; // Collect mapping of registry name -> index url. let name2url: HashMap<&String, Url> = map .registries .keys() .filter_map(|name| { if let Ok(index_url) = gctx.get_registry_index(name) { Some((name, index_url)) } else { tracing::warn!( "`doc.extern-map.{}` specifies a registry that is not defined", name ); None } }) .collect(); build_all_urls( build_runner, rustdoc, unit, &name2url, map, &mut unstable_opts, &mut HashSet::new(), ); let std_url = match &map.std { None | Some(RustdocExternMode::Remote) => None, Some(RustdocExternMode::Local) => { let sysroot = &build_runner.bcx.target_data.info(CompileKind::Host).sysroot; let html_root = sysroot.join("share").join("doc").join("rust").join("html"); if html_root.exists() { let url = Url::from_file_path(&html_root).map_err(|()| { internal(format!( "`{}` failed to convert to URL", html_root.display() )) })?; Some(url.to_string()) } else { tracing::warn!( "`doc.extern-map.std` is \"local\", but local docs don't appear to exist at {}", html_root.display() ); None } } Some(RustdocExternMode::Url(s)) => Some(s.to_string()), }; if let Some(url) = std_url { for name in &["std", "core", "alloc", "proc_macro"] { rustdoc.arg("--extern-html-root-url"); rustdoc.arg(format!("{}={}", name, url)); unstable_opts = true; } } if unstable_opts { rustdoc.arg("-Zunstable-options"); } Ok(()) } /// Adds unstable flag [`--output-format`][1] to the given `rustdoc` /// invocation. This is for unstable feature [`-Zunstable-features`]. /// /// [1]: https://doc.rust-lang.org/nightly/rustdoc/unstable-features.html?highlight=output-format#-w--output-format-output-format pub fn add_output_format( build_runner: &BuildRunner<'_, '_>, rustdoc: &mut ProcessBuilder, ) -> CargoResult<()> { let gctx = build_runner.bcx.gctx; if !gctx.cli_unstable().unstable_options { tracing::debug!("`unstable-options` is ignored, required -Zunstable-options flag"); return Ok(()); } if build_runner.bcx.build_config.intent.wants_doc_json_output() { rustdoc.arg("-Zunstable-options"); rustdoc.arg("--output-format=json"); } Ok(()) } /// Indicates whether a target should have examples scraped from it by rustdoc. /// Configured within Cargo.toml and only for unstable feature /// [`-Zrustdoc-scrape-examples`][1]. /// /// [1]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#scrape-examples #[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Copy)] pub enum RustdocScrapeExamples { Enabled, Disabled, Unset, } impl RustdocScrapeExamples { pub fn is_enabled(&self) -> bool { matches!(self, RustdocScrapeExamples::Enabled) } pub fn is_unset(&self) -> bool { matches!(self, RustdocScrapeExamples::Unset) } } impl BuildContext<'_, '_> { /// Returns the set of [`Docscrape`] units that have a direct dependency on `unit`. /// /// [`RunCustomBuild`] units are excluded because we allow failures /// from type checks but not build script executions. /// A plain old `cargo doc` would just die if a build script execution fails, /// there is no reason for `-Zrustdoc-scrape-examples` to keep going. /// /// [`Docscrape`]: crate::core::compiler::CompileMode::Docscrape /// [`RunCustomBuild`]: crate::core::compiler::CompileMode::Docscrape pub fn scrape_units_have_dep_on<'a>(&'a self, unit: &'a Unit) -> Vec<&'a Unit> { self.scrape_units .iter() .filter(|scrape_unit| { self.unit_graph[scrape_unit] .iter() .any(|dep| &dep.unit == unit && !dep.unit.mode.is_run_custom_build()) }) .collect() } /// Returns true if this unit is needed for doing doc-scraping and is also /// allowed to fail without killing the build. pub fn unit_can_fail_for_docscraping(&self, unit: &Unit) -> bool { // If the unit is not a Docscrape unit, e.g. a Lib target that is // checked to scrape an Example target, then we need to get the doc-scrape-examples // configuration for the reverse-dependent Example target. let for_scrape_units = if unit.mode.is_doc_scrape() { vec![unit] } else { self.scrape_units_have_dep_on(unit) }; if for_scrape_units.is_empty() { false } else { // All Docscrape units must have doc-scrape-examples unset. If any are true, // then the unit is not allowed to fail. for_scrape_units .iter() .all(|unit| unit.target.doc_scrape_examples().is_unset()) } } } cargo-0.91.0/src/cargo/core/compiler/standard_lib.rs000064400000000000000000000213141046102023000204270ustar 00000000000000//! Code for building the standard library. use crate::core::compiler::UnitInterner; use crate::core::compiler::unit_dependencies::IsArtifact; use crate::core::compiler::{CompileKind, CompileMode, RustcTargetData, Unit}; use crate::core::profiles::{Profiles, UnitFor}; use crate::core::resolver::HasDevUnits; use crate::core::resolver::features::{CliFeatures, FeaturesFor, ResolvedFeatures}; use crate::core::{PackageId, PackageSet, Resolve, Workspace}; use crate::ops::{self, Packages}; use crate::util::errors::CargoResult; use std::collections::{HashMap, HashSet}; use std::path::PathBuf; use super::BuildConfig; fn std_crates<'a>(crates: &'a [String], default: &'static str, units: &[Unit]) -> HashSet<&'a str> { let mut crates = HashSet::from_iter(crates.iter().map(|s| s.as_str())); // This is a temporary hack until there is a more principled way to // declare dependencies in Cargo.toml. if crates.is_empty() { crates.insert(default); } if crates.contains("std") { crates.insert("core"); crates.insert("alloc"); crates.insert("proc_macro"); crates.insert("panic_unwind"); crates.insert("compiler_builtins"); // Only build libtest if it looks like it is needed (libtest depends on libstd) // If we know what units we're building, we can filter for libtest depending on the jobs. if units .iter() .any(|unit| unit.mode.is_rustc_test() && unit.target.harness()) { crates.insert("test"); } } else if crates.contains("core") { crates.insert("compiler_builtins"); } crates } /// Resolve the standard library dependencies. /// /// * `crates` is the arg value from `-Zbuild-std`. pub fn resolve_std<'gctx>( ws: &Workspace<'gctx>, target_data: &mut RustcTargetData<'gctx>, build_config: &BuildConfig, crates: &[String], kinds: &[CompileKind], ) -> CargoResult<(PackageSet<'gctx>, Resolve, ResolvedFeatures)> { if build_config.build_plan { ws.gctx() .shell() .warn("-Zbuild-std does not currently fully support --build-plan")?; } let src_path = detect_sysroot_src_path(target_data)?; let std_ws_manifest_path = src_path.join("Cargo.toml"); let gctx = ws.gctx(); // TODO: Consider doing something to enforce --locked? Or to prevent the // lock file from being written, such as setting ephemeral. let mut std_ws = Workspace::new(&std_ws_manifest_path, gctx)?; // Don't require optional dependencies in this workspace, aka std's own // `[dev-dependencies]`. No need for us to generate a `Resolve` which has // those included because we'll never use them anyway. std_ws.set_require_optional_deps(false); let specs = { // If there is anything looks like needing std, resolve with it. // If not, we assume only `core` maye be needed, as `core the most fundamental crate. // // This may need a UI overhaul if `build-std` wants to fully support multi-targets. let maybe_std = kinds .iter() .any(|kind| target_data.info(*kind).maybe_support_std()); let mut crates = std_crates(crates, if maybe_std { "std" } else { "core" }, &[]); // `sysroot` is not in the default set because it is optional, but it needs // to be part of the resolve in case we do need it or `libtest`. crates.insert("sysroot"); let specs = Packages::Packages(crates.into_iter().map(Into::into).collect()); specs.to_package_id_specs(&std_ws)? }; let features = match &gctx.cli_unstable().build_std_features { Some(list) => list.clone(), None => vec![ "panic-unwind".to_string(), "backtrace".to_string(), "default".to_string(), ], }; let cli_features = CliFeatures::from_command_line( &features, /*all_features*/ false, /*uses_default_features*/ false, )?; let dry_run = false; let mut resolve = ops::resolve_ws_with_opts( &std_ws, target_data, &build_config.requested_kinds, &cli_features, &specs, HasDevUnits::No, crate::core::resolver::features::ForceAllTargets::No, dry_run, )?; debug_assert_eq!(resolve.specs_and_features.len(), 1); Ok(( resolve.pkg_set, resolve.targeted_resolve, resolve .specs_and_features .pop() .expect("resolve should have a single spec with resolved features") .resolved_features, )) } /// Generates a map of root units for the standard library for each kind requested. /// /// * `crates` is the arg value from `-Zbuild-std`. /// * `units` is the root units of the build. pub fn generate_std_roots( crates: &[String], units: &[Unit], std_resolve: &Resolve, std_features: &ResolvedFeatures, kinds: &[CompileKind], package_set: &PackageSet<'_>, interner: &UnitInterner, profiles: &Profiles, target_data: &RustcTargetData<'_>, ) -> CargoResult>> { // Generate a map of Units for each kind requested. let mut ret = HashMap::new(); let (maybe_std, maybe_core): (Vec<&CompileKind>, Vec<_>) = kinds .iter() .partition(|kind| target_data.info(**kind).maybe_support_std()); for (default_crate, kinds) in [("core", maybe_core), ("std", maybe_std)] { if kinds.is_empty() { continue; } generate_roots( &mut ret, default_crate, crates, units, std_resolve, std_features, &kinds, package_set, interner, profiles, target_data, )?; } Ok(ret) } fn generate_roots( ret: &mut HashMap>, default: &'static str, crates: &[String], units: &[Unit], std_resolve: &Resolve, std_features: &ResolvedFeatures, kinds: &[&CompileKind], package_set: &PackageSet<'_>, interner: &UnitInterner, profiles: &Profiles, target_data: &RustcTargetData<'_>, ) -> CargoResult<()> { let std_ids = std_crates(crates, default, units) .iter() .map(|crate_name| std_resolve.query(crate_name)) .collect::>>()?; let std_pkgs = package_set.get_many(std_ids)?; for pkg in std_pkgs { let lib = pkg .targets() .iter() .find(|t| t.is_lib()) .expect("std has a lib"); // I don't think we need to bother with Check here, the difference // in time is minimal, and the difference in caching is // significant. let mode = CompileMode::Build; let features = std_features.activated_features(pkg.package_id(), FeaturesFor::NormalOrDev); for kind in kinds { let kind = **kind; let list = ret.entry(kind).or_insert_with(Vec::new); let unit_for = UnitFor::new_normal(kind); let profile = profiles.get_profile( pkg.package_id(), /*is_member*/ false, /*is_local*/ false, unit_for, kind, ); list.push(interner.intern( pkg, lib, profile, kind, mode, features.clone(), target_data.info(kind).rustflags.clone(), target_data.info(kind).rustdocflags.clone(), target_data.target_config(kind).links_overrides.clone(), /*is_std*/ true, /*dep_hash*/ 0, IsArtifact::No, None, false, )); } } Ok(()) } fn detect_sysroot_src_path(target_data: &RustcTargetData<'_>) -> CargoResult { if let Some(s) = target_data.gctx.get_env_os("__CARGO_TESTS_ONLY_SRC_ROOT") { return Ok(s.into()); } // NOTE: This is temporary until we figure out how to acquire the source. let src_path = target_data .info(CompileKind::Host) .sysroot .join("lib") .join("rustlib") .join("src") .join("rust") .join("library"); let lock = src_path.join("Cargo.lock"); if !lock.exists() { let msg = format!( "{:?} does not exist, unable to build with the standard \ library, try:\n rustup component add rust-src", lock ); match target_data.gctx.get_env("RUSTUP_TOOLCHAIN") { Ok(rustup_toolchain) => { anyhow::bail!("{} --toolchain {}", msg, rustup_toolchain); } Err(_) => { anyhow::bail!(msg); } } } Ok(src_path) } cargo-0.91.0/src/cargo/core/compiler/timings.js000064400000000000000000000400601046102023000174420ustar 00000000000000// Position of the vertical axis. const X_LINE = 50; // General-use margin size. const MARGIN = 5; // Position of the horizontal axis, relative to the bottom. const Y_LINE = 35; // Minimum distance between time tick labels. const MIN_TICK_DIST = 50; // Radius for rounded rectangle corners. const RADIUS = 3; // Height of unit boxes. const BOX_HEIGHT = 25; // Distance between Y tick marks on the unit graph. const Y_TICK_DIST = BOX_HEIGHT + 2; // Rects used for mouseover detection. // Objects of {x, y, x2, y2, i} where `i` is the index into UNIT_DATA. let HIT_BOXES = []; // Index into UNIT_DATA of the last unit hovered over by mouse. let LAST_HOVER = null; // Key is unit index, value is {x, y, width, rmeta_x} of the box. let UNIT_COORDS = {}; // Map of unit index to the index it was unlocked by. let REVERSE_UNIT_DEPS = {}; let REVERSE_UNIT_RMETA_DEPS = {}; const MIN_GRAPH_WIDTH = 200; const MAX_GRAPH_WIDTH = 4096; // How many pixels per second is added by each scale value const SCALE_PIXELS_PER_SEC = 8; function scale_to_graph_width(scale) { // The scale corresponds to `SCALE_PIXELS_PER_SEC` pixels per seconds. // We thus multiply it by that, and the total duration, to get the graph width. const width = scale * SCALE_PIXELS_PER_SEC * DURATION; // We then cap the size of the graph. It is hard to view if it is too large, and // browsers may not render a large graph because it takes too much memory. // 4096 is still ridiculously large, and probably won't render on mobile // browsers, but should be ok for many desktop environments. // Also use a minimum width of 200. return Math.max(MIN_GRAPH_WIDTH, Math.min(MAX_GRAPH_WIDTH, width)); } // This function performs the reverse of `scale_to_graph_width`. function width_to_graph_scale(width) { const maxWidth = Math.min(MAX_GRAPH_WIDTH, width); const minWidth = Math.max(MIN_GRAPH_WIDTH, width); const trimmedWidth = Math.max(minWidth, Math.min(maxWidth, width)); const scale = Math.round(trimmedWidth / (DURATION * SCALE_PIXELS_PER_SEC)); return Math.max(1, scale); } // Init scale value and limits based on the client's window width and min/max graph width. const scaleElement = document.getElementById('scale'); scaleElement.min = width_to_graph_scale(MIN_GRAPH_WIDTH); scaleElement.max = width_to_graph_scale(MAX_GRAPH_WIDTH); scaleElement.value = width_to_graph_scale(window.innerWidth * 0.75); // Colors from css const getCssColor = name => getComputedStyle(document.body).getPropertyValue(name); const TEXT_COLOR = getCssColor('--text'); const BG_COLOR = getCssColor('--background'); const CANVAS_BG = getCssColor('--canvas-background'); const AXES_COLOR = getCssColor('--canvas-axes'); const GRID_COLOR = getCssColor('--canvas-grid'); const BLOCK_COLOR = getCssColor('--canvas-block'); const CUSTOM_BUILD_COLOR = getCssColor('--canvas-custom-build'); const NOT_CUSTOM_BUILD_COLOR = getCssColor('--canvas-not-custom-build'); const DEP_LINE_COLOR = getCssColor('--canvas-dep-line'); const DEP_LINE_HIGHLIGHTED_COLOR = getCssColor('--canvas-dep-line-highlighted'); const CPU_COLOR = getCssColor('--canvas-cpu'); for (let n=0; n unit.duration >= min_time); const graph_height = Y_TICK_DIST * units.length; const {ctx, graph_width, canvas_width, canvas_height, px_per_sec} = draw_graph_axes('pipeline-graph', graph_height); const container = document.getElementById('pipeline-container'); container.style.width = canvas_width; container.style.height = canvas_height; // Canvas for hover highlights. This is a separate layer to improve performance. const linectx = setup_canvas('pipeline-graph-lines', canvas_width, canvas_height); linectx.clearRect(0, 0, canvas_width, canvas_height); ctx.strokeStyle = AXES_COLOR; // Draw Y tick marks. for (let n=1; n 1 ? `${unit.name} (v${unit.version})${unit.target}` : `${unit.name}${unit.target}`; const label = `${labelName}: ${unit.duration}s`; const text_info = ctx.measureText(label); const label_x = Math.min(x + 5.0, canvas_width - text_info.width - X_LINE); ctx.fillText(label, label_x, y + BOX_HEIGHT / 2); draw_dep_lines(ctx, unit.i, false); } ctx.restore(); } // Draws lines from the given unit to the units it unlocks. function draw_dep_lines(ctx, unit_idx, highlighted) { const unit = UNIT_DATA[unit_idx]; const {x, y, rmeta_x} = UNIT_COORDS[unit_idx]; ctx.save(); for (const unlocked of unit.unlocked_units) { draw_one_dep_line(ctx, x, y, unlocked, highlighted); } for (const unlocked of unit.unlocked_rmeta_units) { draw_one_dep_line(ctx, rmeta_x, y, unlocked, highlighted); } ctx.restore(); } function draw_one_dep_line(ctx, from_x, from_y, to_unit, highlighted) { if (to_unit in UNIT_COORDS) { let {x: u_x, y: u_y} = UNIT_COORDS[to_unit]; ctx.strokeStyle = highlighted ? DEP_LINE_HIGHLIGHTED_COLOR: DEP_LINE_COLOR; ctx.setLineDash([2]); ctx.beginPath(); ctx.moveTo(from_x, from_y+BOX_HEIGHT/2); ctx.lineTo(from_x-5, from_y+BOX_HEIGHT/2); ctx.lineTo(from_x-5, u_y+BOX_HEIGHT/2); ctx.lineTo(u_x, u_y+BOX_HEIGHT/2); ctx.stroke(); } } function render_timing_graph() { if (CONCURRENCY_DATA.length == 0) { return; } const HEIGHT = 400; const AXIS_HEIGHT = HEIGHT - MARGIN - Y_LINE; const TOP_MARGIN = 10; const GRAPH_HEIGHT = AXIS_HEIGHT - TOP_MARGIN; const {canvas_width, graph_width, ctx} = draw_graph_axes('timing-graph', AXIS_HEIGHT); // Draw Y tick marks and labels. let max_v = 0; for (c of CONCURRENCY_DATA) { max_v = Math.max(max_v, c.active, c.waiting, c.inactive); } const px_per_v = GRAPH_HEIGHT / max_v; const {step, tick_dist, num_ticks} = split_ticks(max_v, px_per_v, GRAPH_HEIGHT); ctx.textAlign = 'end'; for (n=0; n 1) { ctx.beginPath(); ctx.fillStyle = cpuFillStyle; let bottomLeft = coord(CPU_USAGE[0][0], 0); ctx.moveTo(bottomLeft.x, bottomLeft.y); for (let i=0; i < CPU_USAGE.length; i++) { let [time, usage] = CPU_USAGE[i]; let {x, y} = coord(time, usage / 100.0 * max_v); ctx.lineTo(x, y); } let bottomRight = coord(CPU_USAGE[CPU_USAGE.length - 1][0], 0); ctx.lineTo(bottomRight.x, bottomRight.y); ctx.fill(); } function draw_line(style, key) { let first = CONCURRENCY_DATA[0]; let last = coord(first.t, key(first)); ctx.strokeStyle = style; ctx.beginPath(); ctx.moveTo(last.x, last.y); for (let i=1; i 100) { throw Error("tick loop too long"); } count += 1; if (max_value <= max_ticks * step) { break; } step += 10; } } const tick_dist = px_per_v * step; const num_ticks = Math.floor(max_value / step); return {step, tick_dist, num_ticks}; } function codegen_time(unit) { if (unit.rmeta_time == null) { return null; } let ctime = unit.duration - unit.rmeta_time; return [unit.rmeta_time, ctime]; } function roundedRect(ctx, x, y, width, height, r) { r = Math.min(r, width, height); ctx.beginPath(); ctx.moveTo(x+r, y); ctx.lineTo(x+width-r, y); ctx.arc(x+width-r, y+r, r, 3*Math.PI/2, 0); ctx.lineTo(x+width, y+height-r); ctx.arc(x+width-r, y+height-r, r, 0, Math.PI/2); ctx.lineTo(x+r, y+height); ctx.arc(x+r, y+height-r, r, Math.PI/2, Math.PI); ctx.lineTo(x, y-r); ctx.arc(x+r, y+r, r, Math.PI, 3*Math.PI/2); ctx.closePath(); } function pipeline_mouse_hit(event) { // This brute-force method can be optimized if needed. for (let box of HIT_BOXES) { if (event.offsetX >= box.x && event.offsetX <= box.x2 && event.offsetY >= box.y && event.offsetY <= box.y2) { return box; } } } function pipeline_mousemove(event) { // Highlight dependency lines on mouse hover. let box = pipeline_mouse_hit(event); if (box) { if (box.i != LAST_HOVER) { LAST_HOVER = box.i; let g = document.getElementById('pipeline-graph-lines'); let ctx = g.getContext('2d'); ctx.clearRect(0, 0, g.width, g.height); ctx.save(); ctx.translate(X_LINE, MARGIN); ctx.lineWidth = 2; draw_dep_lines(ctx, box.i, true); if (box.i in REVERSE_UNIT_DEPS) { const dep_unit = REVERSE_UNIT_DEPS[box.i]; if (dep_unit in UNIT_COORDS) { const {x, y, rmeta_x} = UNIT_COORDS[dep_unit]; draw_one_dep_line(ctx, x, y, box.i, true); } } if (box.i in REVERSE_UNIT_RMETA_DEPS) { const dep_unit = REVERSE_UNIT_RMETA_DEPS[box.i]; if (dep_unit in UNIT_COORDS) { const {x, y, rmeta_x} = UNIT_COORDS[dep_unit]; draw_one_dep_line(ctx, rmeta_x, y, box.i, true); } } ctx.restore(); } } } render_pipeline_graph(); render_timing_graph(); // Set up and handle controls. { const range = document.getElementById('min-unit-time'); const time_output = document.getElementById('min-unit-time-output'); time_output.innerHTML = `${range.value}s`; range.oninput = event => { time_output.innerHTML = `${range.value}s`; render_pipeline_graph(); }; const scale = document.getElementById('scale'); const scale_output = document.getElementById('scale-output'); scale_output.innerHTML = `${scale.value}`; scale.oninput = event => { scale_output.innerHTML = `${scale.value}`; render_pipeline_graph(); render_timing_graph(); }; } cargo-0.91.0/src/cargo/core/compiler/timings.rs000064400000000000000000000604611046102023000174610ustar 00000000000000//! Timing tracking. //! //! This module implements some simple tracking information for timing of how //! long it takes for different units to compile. use super::{CompileMode, Unit}; use crate::core::PackageId; use crate::core::compiler::job_queue::JobId; use crate::core::compiler::{BuildContext, BuildRunner, TimingOutput}; use crate::util::cpu::State; use crate::util::machine_message::{self, Message}; use crate::util::style; use crate::util::{CargoResult, GlobalContext}; use anyhow::Context as _; use cargo_util::paths; use std::collections::HashMap; use std::io::{BufWriter, Write}; use std::thread::available_parallelism; use std::time::{Duration, Instant}; /// Tracking information for the entire build. /// /// Methods on this structure are generally called from the main thread of a /// running [`JobQueue`] instance (`DrainState` in specific) when the queue /// receives messages from spawned off threads. /// /// [`JobQueue`]: super::JobQueue pub struct Timings<'gctx> { gctx: &'gctx GlobalContext, /// Whether or not timings should be captured. enabled: bool, /// If true, saves an HTML report to disk. report_html: bool, /// If true, emits JSON information with timing information. report_json: bool, /// When Cargo started. start: Instant, /// A rendered string of when compilation started. start_str: String, /// A summary of the root units. /// /// Tuples of `(package_description, target_descriptions)`. root_targets: Vec<(String, Vec)>, /// The build profile. profile: String, /// Total number of fresh units. total_fresh: u32, /// Total number of dirty units. total_dirty: u32, /// Time tracking for each individual unit. unit_times: Vec, /// Units that are in the process of being built. /// When they finished, they are moved to `unit_times`. active: HashMap, /// Concurrency-tracking information. This is periodically updated while /// compilation progresses. concurrency: Vec, /// Last recorded state of the system's CPUs and when it happened last_cpu_state: Option, last_cpu_recording: Instant, /// Recorded CPU states, stored as tuples. First element is when the /// recording was taken and second element is percentage usage of the /// system. cpu_usage: Vec<(f64, f64)>, } /// Tracking information for an individual unit. struct UnitTime { unit: Unit, /// A string describing the cargo target. target: String, /// The time when this unit started as an offset in seconds from `Timings::start`. start: f64, /// Total time to build this unit in seconds. duration: f64, /// The time when the `.rmeta` file was generated, an offset in seconds /// from `start`. rmeta_time: Option, /// Reverse deps that are freed to run after this unit finished. unlocked_units: Vec, /// Same as `unlocked_units`, but unlocked by rmeta. unlocked_rmeta_units: Vec, } /// Periodic concurrency tracking information. #[derive(serde::Serialize)] struct Concurrency { /// Time as an offset in seconds from `Timings::start`. t: f64, /// Number of units currently running. active: usize, /// Number of units that could run, but are waiting for a jobserver token. waiting: usize, /// Number of units that are not yet ready, because they are waiting for /// dependencies to finish. inactive: usize, } impl<'gctx> Timings<'gctx> { pub fn new(bcx: &BuildContext<'_, 'gctx>, root_units: &[Unit]) -> Timings<'gctx> { let has_report = |what| bcx.build_config.timing_outputs.contains(&what); let report_html = has_report(TimingOutput::Html); let report_json = has_report(TimingOutput::Json); let enabled = report_html | report_json; let mut root_map: HashMap> = HashMap::new(); for unit in root_units { let target_desc = unit.target.description_named(); root_map .entry(unit.pkg.package_id()) .or_default() .push(target_desc); } let root_targets = root_map .into_iter() .map(|(pkg_id, targets)| { let pkg_desc = format!("{} {}", pkg_id.name(), pkg_id.version()); (pkg_desc, targets) }) .collect(); let start_str = jiff::Timestamp::now().to_string(); let profile = bcx.build_config.requested_profile.to_string(); let last_cpu_state = if enabled { match State::current() { Ok(state) => Some(state), Err(e) => { tracing::info!("failed to get CPU state, CPU tracking disabled: {:?}", e); None } } } else { None }; Timings { gctx: bcx.gctx, enabled, report_html, report_json, start: bcx.gctx.creation_time(), start_str, root_targets, profile, total_fresh: 0, total_dirty: 0, unit_times: Vec::new(), active: HashMap::new(), concurrency: Vec::new(), last_cpu_state, last_cpu_recording: Instant::now(), cpu_usage: Vec::new(), } } /// Mark that a unit has started running. pub fn unit_start(&mut self, id: JobId, unit: Unit) { if !self.enabled { return; } let mut target = if unit.target.is_lib() && unit.mode == CompileMode::Build { // Special case for brevity, since most dependencies hit // this path. "".to_string() } else { format!(" {}", unit.target.description_named()) }; match unit.mode { CompileMode::Test => target.push_str(" (test)"), CompileMode::Build => {} CompileMode::Check { test: true } => target.push_str(" (check-test)"), CompileMode::Check { test: false } => target.push_str(" (check)"), CompileMode::Doc { .. } => target.push_str(" (doc)"), CompileMode::Doctest => target.push_str(" (doc test)"), CompileMode::Docscrape => target.push_str(" (doc scrape)"), CompileMode::RunCustomBuild => target.push_str(" (run)"), } let unit_time = UnitTime { unit, target, start: self.start.elapsed().as_secs_f64(), duration: 0.0, rmeta_time: None, unlocked_units: Vec::new(), unlocked_rmeta_units: Vec::new(), }; assert!(self.active.insert(id, unit_time).is_none()); } /// Mark that the `.rmeta` file as generated. pub fn unit_rmeta_finished(&mut self, id: JobId, unlocked: Vec<&Unit>) { if !self.enabled { return; } // `id` may not always be active. "fresh" units unconditionally // generate `Message::Finish`, but this active map only tracks dirty // units. let Some(unit_time) = self.active.get_mut(&id) else { return; }; let t = self.start.elapsed().as_secs_f64(); unit_time.rmeta_time = Some(t - unit_time.start); assert!(unit_time.unlocked_rmeta_units.is_empty()); unit_time .unlocked_rmeta_units .extend(unlocked.iter().cloned().cloned()); } /// Mark that a unit has finished running. pub fn unit_finished(&mut self, id: JobId, unlocked: Vec<&Unit>) { if !self.enabled { return; } // See note above in `unit_rmeta_finished`, this may not always be active. let Some(mut unit_time) = self.active.remove(&id) else { return; }; let t = self.start.elapsed().as_secs_f64(); unit_time.duration = t - unit_time.start; assert!(unit_time.unlocked_units.is_empty()); unit_time .unlocked_units .extend(unlocked.iter().cloned().cloned()); if self.report_json { let msg = machine_message::TimingInfo { package_id: unit_time.unit.pkg.package_id().to_spec(), target: &unit_time.unit.target, mode: unit_time.unit.mode, duration: unit_time.duration, rmeta_time: unit_time.rmeta_time, } .to_json_string(); crate::drop_println!(self.gctx, "{}", msg); } self.unit_times.push(unit_time); } /// This is called periodically to mark the concurrency of internal structures. pub fn mark_concurrency(&mut self, active: usize, waiting: usize, inactive: usize) { if !self.enabled { return; } let c = Concurrency { t: self.start.elapsed().as_secs_f64(), active, waiting, inactive, }; self.concurrency.push(c); } /// Mark that a fresh unit was encountered. (No re-compile needed) pub fn add_fresh(&mut self) { self.total_fresh += 1; } /// Mark that a dirty unit was encountered. (Re-compile needed) pub fn add_dirty(&mut self) { self.total_dirty += 1; } /// Take a sample of CPU usage pub fn record_cpu(&mut self) { if !self.enabled { return; } let Some(prev) = &mut self.last_cpu_state else { return; }; // Don't take samples too frequently, even if requested. let now = Instant::now(); if self.last_cpu_recording.elapsed() < Duration::from_millis(100) { return; } let current = match State::current() { Ok(s) => s, Err(e) => { tracing::info!("failed to get CPU state: {:?}", e); return; } }; let pct_idle = current.idle_since(prev); *prev = current; self.last_cpu_recording = now; let dur = now.duration_since(self.start).as_secs_f64(); self.cpu_usage.push((dur, 100.0 - pct_idle)); } /// Call this when all units are finished. pub fn finished( &mut self, build_runner: &BuildRunner<'_, '_>, error: &Option, ) -> CargoResult<()> { if !self.enabled { return Ok(()); } self.mark_concurrency(0, 0, 0); self.unit_times .sort_unstable_by(|a, b| a.start.partial_cmp(&b.start).unwrap()); if self.report_html { self.report_html(build_runner, error) .context("failed to save timing report")?; } Ok(()) } /// Save HTML report to disk. fn report_html( &self, build_runner: &BuildRunner<'_, '_>, error: &Option, ) -> CargoResult<()> { let duration = self.start.elapsed().as_secs_f64(); let timestamp = self.start_str.replace(&['-', ':'][..], ""); let timings_path = build_runner.files().host_root().join("cargo-timings"); paths::create_dir_all(&timings_path)?; let filename = timings_path.join(format!("cargo-timing-{}.html", timestamp)); let mut f = BufWriter::new(paths::create(&filename)?); let roots: Vec<&str> = self .root_targets .iter() .map(|(name, _targets)| name.as_str()) .collect(); f.write_all(HTML_TMPL.replace("{ROOTS}", &roots.join(", ")).as_bytes())?; self.write_summary_table(&mut f, duration, build_runner.bcx, error)?; f.write_all(HTML_CANVAS.as_bytes())?; self.write_unit_table(&mut f)?; // It helps with pixel alignment to use whole numbers. writeln!( f, "\n\ \n\ \n\ ", include_str!("timings.js") )?; drop(f); let unstamped_filename = timings_path.join("cargo-timing.html"); paths::link_or_copy(&filename, &unstamped_filename)?; let mut shell = self.gctx.shell(); let timing_path = std::env::current_dir().unwrap_or_default().join(&filename); let link = shell.err_file_hyperlink(&timing_path); let msg = format!("report saved to {link}{}{link:#}", timing_path.display(),); shell.status_with_color("Timing", msg, &style::NOTE)?; Ok(()) } /// Render the summary table. fn write_summary_table( &self, f: &mut impl Write, duration: f64, bcx: &BuildContext<'_, '_>, error: &Option, ) -> CargoResult<()> { let targets: Vec = self .root_targets .iter() .map(|(name, targets)| format!("{} ({})", name, targets.join(", "))) .collect(); let targets = targets.join("
"); let time_human = if duration > 60.0 { format!(" ({}m {:.1}s)", duration as u32 / 60, duration % 60.0) } else { "".to_string() }; let total_time = format!("{:.1}s{}", duration, time_human); let max_concurrency = self.concurrency.iter().map(|c| c.active).max().unwrap(); let num_cpus = available_parallelism() .map(|x| x.get().to_string()) .unwrap_or_else(|_| "n/a".into()); let rustc_info = render_rustc_info(bcx); let error_msg = match error { Some(e) => format!(r#"Error:{e}"#), None => "".to_string(), }; write!( f, r#" {}
Targets:{}
Profile:{}
Fresh units:{}
Dirty units:{}
Total units:{}
Max concurrency:{} (jobs={} ncpu={})
Build start:{}
Total time:{}
rustc:{}
"#, targets, self.profile, self.total_fresh, self.total_dirty, self.total_fresh + self.total_dirty, max_concurrency, bcx.jobs(), num_cpus, self.start_str, total_time, rustc_info, error_msg, )?; Ok(()) } /// Write timing data in JavaScript. Primarily for `timings.js` to put data /// in a ` cargo-0.91.0/src/doc/src/reference/overriding-dependencies.md000064400000000000000000000340751046102023000222160ustar 00000000000000# Overriding Dependencies The desire to override a dependency can arise through a number of scenarios. Most of them, however, boil down to the ability to work with a crate before it's been published to [crates.io]. For example: * A crate you're working on is also used in a much larger application you're working on, and you'd like to test a bug fix to the library inside of the larger application. * An upstream crate you don't work on has a new feature or a bug fix on the master branch of its git repository which you'd like to test out. * You're about to publish a new major version of your crate, but you'd like to do integration testing across an entire package to ensure the new major version works. * You've submitted a fix to an upstream crate for a bug you found, but you'd like to immediately have your application start depending on the fixed version of the crate to avoid blocking on the bug fix getting merged. These scenarios can be solved with the [`[patch]` manifest section](#the-patch-section). This chapter walks through a few different use cases, and includes details on the different ways to override a dependency. * Example use cases * [Testing a bugfix](#testing-a-bugfix) * [Working with an unpublished minor version](#working-with-an-unpublished-minor-version) * [Overriding repository URL](#overriding-repository-url) * [Prepublishing a breaking change](#prepublishing-a-breaking-change) * [Using `[patch]` with multiple versions](#using-patch-with-multiple-versions) * Reference * [The `[patch]` section](#the-patch-section) * [The `[replace]` section](#the-replace-section) * [`paths` overrides](#paths-overrides) > **Note**: See also specifying a dependency with [multiple locations], which > can be used to override the source for a single dependency declaration in a > local package. ## Testing a bugfix Let's say you're working with the [`uuid` crate] but while you're working on it you discover a bug. You are, however, quite enterprising so you decide to also try to fix the bug! Originally your manifest will look like: [`uuid` crate]: https://crates.io/crates/uuid ```toml [package] name = "my-library" version = "0.1.0" [dependencies] uuid = "1.0" ``` First thing we'll do is to clone the [`uuid` repository][uuid-repository] locally via: ```console $ git clone https://github.com/uuid-rs/uuid.git ``` Next we'll edit the manifest of `my-library` to contain: ```toml [patch.crates-io] uuid = { path = "../path/to/uuid" } ``` Here we declare that we're *patching* the source `crates-io` with a new dependency. This will effectively add the local checked out version of `uuid` to the crates.io registry for our local package. Next up we need to ensure that our lock file is updated to use this new version of `uuid` so our package uses the locally checked out copy instead of one from crates.io. The way `[patch]` works is that it'll load the dependency at `../path/to/uuid` and then whenever crates.io is queried for versions of `uuid` it'll *also* return the local version. This means that the version number of the local checkout is significant and will affect whether the patch is used. Our manifest declared `uuid = "1.0"` which means we'll only resolve to `>= 1.0.0, < 2.0.0`, and Cargo's greedy resolution algorithm also means that we'll resolve to the maximum version within that range. Typically this doesn't matter as the version of the git repository will already be greater or match the maximum version published on crates.io, but it's important to keep this in mind! In any case, typically all you need to do now is: ```console $ cargo build Compiling uuid v1.0.0 (.../uuid) Compiling my-library v0.1.0 (.../my-library) Finished dev [unoptimized + debuginfo] target(s) in 0.32 secs ``` And that's it! You're now building with the local version of `uuid` (note the path in parentheses in the build output). If you don't see the local path version getting built then you may need to run `cargo update uuid --precise $version` where `$version` is the version of the locally checked out copy of `uuid`. Once you've fixed the bug you originally found the next thing you'll want to do is to likely submit that as a pull request to the `uuid` crate itself. Once you've done this then you can also update the `[patch]` section. The listing inside of `[patch]` is just like the `[dependencies]` section, so once your pull request is merged you could change your `path` dependency to: ```toml [patch.crates-io] uuid = { git = 'https://github.com/uuid-rs/uuid.git' } ``` [uuid-repository]: https://github.com/uuid-rs/uuid ## Working with an unpublished minor version Let's now shift gears a bit from bug fixes to adding features. While working on `my-library` you discover that a whole new feature is needed in the `uuid` crate. You've implemented this feature, tested it locally above with `[patch]`, and submitted a pull request. Let's go over how you continue to use and test it before it's actually published. Let's also say that the current version of `uuid` on crates.io is `1.0.0`, but since then the master branch of the git repository has updated to `1.0.1`. This branch includes your new feature you submitted previously. To use this repository we'll edit our `Cargo.toml` to look like ```toml [package] name = "my-library" version = "0.1.0" [dependencies] uuid = "1.0.1" [patch.crates-io] uuid = { git = 'https://github.com/uuid-rs/uuid.git' } ``` Note that our local dependency on `uuid` has been updated to `1.0.1` as it's what we'll actually require once the crate is published. This version doesn't exist on crates.io, though, so we provide it with the `[patch]` section of the manifest. Now when our library is built it'll fetch `uuid` from the git repository and resolve to 1.0.1 inside the repository instead of trying to download a version from crates.io. Once 1.0.1 is published on crates.io the `[patch]` section can be deleted. It's also worth noting that `[patch]` applies *transitively*. Let's say you use `my-library` in a larger package, such as: ```toml [package] name = "my-binary" version = "0.1.0" [dependencies] my-library = { git = 'https://example.com/git/my-library' } uuid = "1.0" [patch.crates-io] uuid = { git = 'https://github.com/uuid-rs/uuid.git' } ``` Remember that `[patch]` is applicable *transitively* but can only be defined at the *top level* so we consumers of `my-library` have to repeat the `[patch]` section if necessary. Here, though, the new `uuid` crate applies to *both* our dependency on `uuid` and the `my-library -> uuid` dependency. The `uuid` crate will be resolved to one version for this entire crate graph, 1.0.1, and it'll be pulled from the git repository. ### Overriding repository URL In case the dependency you want to override isn't loaded from `crates.io`, you'll have to change a bit how you use `[patch]`. For example, if the dependency is a git dependency, you can override it to a local path with: ```toml [patch."https://github.com/your/repository"] my-library = { path = "../my-library/path" } ``` And that's it! ## Prepublishing a breaking change Let's take a look at working with a new major version of a crate, typically accompanied with breaking changes. Sticking with our previous crates, this means that we're going to be creating version 2.0.0 of the `uuid` crate. After we've submitted all changes upstream we can update our manifest for `my-library` to look like: ```toml [dependencies] uuid = "2.0" [patch.crates-io] uuid = { git = "https://github.com/uuid-rs/uuid.git", branch = "2.0.0" } ``` And that's it! Like with the previous example the 2.0.0 version doesn't actually exist on crates.io but we can still put it in through a git dependency through the usage of the `[patch]` section. As a thought exercise let's take another look at the `my-binary` manifest from above again as well: ```toml [package] name = "my-binary" version = "0.1.0" [dependencies] my-library = { git = 'https://example.com/git/my-library' } uuid = "1.0" [patch.crates-io] uuid = { git = 'https://github.com/uuid-rs/uuid.git', branch = '2.0.0' } ``` Note that this will actually resolve to two versions of the `uuid` crate. The `my-binary` crate will continue to use the 1.x.y series of the `uuid` crate but the `my-library` crate will use the `2.0.0` version of `uuid`. This will allow you to gradually roll out breaking changes to a crate through a dependency graph without being forced to update everything all at once. ## Using `[patch]` with multiple versions You can patch in multiple versions of the same crate with the `package` key used to rename dependencies. For example let's say that the `serde` crate has a bugfix that we'd like to use to its `1.*` series but we'd also like to prototype using a `2.0.0` version of serde we have in our git repository. To configure this we'd do: ```toml [patch.crates-io] serde = { git = 'https://github.com/serde-rs/serde.git' } serde2 = { git = 'https://github.com/example/serde.git', package = 'serde', branch = 'v2' } ``` The first `serde = ...` directive indicates that serde `1.*` should be used from the git repository (pulling in the bugfix we need) and the second `serde2 = ...` directive indicates that the `serde` package should also be pulled from the `v2` branch of `https://github.com/example/serde`. We're assuming here that `Cargo.toml` on that branch mentions version `2.0.0`. Note that when using the `package` key the `serde2` identifier here is actually ignored. We simply need a unique name which doesn't conflict with other patched crates. ## The `[patch]` section The `[patch]` section of `Cargo.toml` can be used to override dependencies with other copies. The syntax is similar to the [`[dependencies]`][dependencies] section: ```toml [patch.crates-io] foo = { git = 'https://github.com/example/foo.git' } bar = { path = 'my/local/bar' } [dependencies.baz] git = 'https://github.com/example/baz.git' [patch.'https://github.com/example/baz'] baz = { git = 'https://github.com/example/patched-baz.git', branch = 'my-branch' } ``` > **Note**: The `[patch]` table can also be specified as a [configuration > option](config.md), such as in a `.cargo/config.toml` file or a CLI option > like `--config 'patch.crates-io.rand.path="rand"'`. This can be useful for > local-only changes that you don't want to commit, or temporarily testing a > patch. The `[patch]` table is made of dependency-like sub-tables. Each key after `[patch]` is a URL of the source that is being patched, or the name of a registry. The name `crates-io` may be used to override the default registry [crates.io]. The first `[patch]` in the example above demonstrates overriding [crates.io], and the second `[patch]` demonstrates overriding a git source. Each entry in these tables is a normal dependency specification, the same as found in the `[dependencies]` section of the manifest. The dependencies listed in the `[patch]` section are resolved and used to patch the source at the URL specified. The above manifest snippet patches the `crates-io` source (e.g. crates.io itself) with the `foo` crate and `bar` crate. It also patches the `https://github.com/example/baz` source with a `my-branch` that comes from elsewhere. Sources can be patched with versions of crates that do not exist, and they can also be patched with versions of crates that already exist. If a source is patched with a crate version that already exists in the source, then the source's original crate is replaced. Cargo only looks at the patch settings in the `Cargo.toml` manifest at the root of the workspace. Patch settings defined in dependencies will be ignored. ## The `[replace]` section > **Note**: `[replace]` is deprecated. You should use the > [`[patch]`](#the-patch-section) table instead. This section of Cargo.toml can be used to override dependencies with other copies. The syntax is similar to the `[dependencies]` section: ```toml [replace] "foo:0.1.0" = { git = 'https://github.com/example/foo.git' } "bar:1.0.2" = { path = 'my/local/bar' } ``` Each key in the `[replace]` table is a [package ID specification](pkgid-spec.md), which allows arbitrarily choosing a node in the dependency graph to override (the 3-part version number is required). The value of each key is the same as the `[dependencies]` syntax for specifying dependencies, except that you can't specify features. Note that when a crate is overridden the copy it's overridden with must have both the same name and version, but it can come from a different source (e.g., git or a local path). Cargo only looks at the replace settings in the `Cargo.toml` manifest at the root of the workspace. Replace settings defined in dependencies will be ignored. ## `paths` overrides Sometimes you're only temporarily working on a crate and you don't want to have to modify `Cargo.toml` like with the `[patch]` section above. For this use case Cargo offers a much more limited version of overrides called **path overrides**. Path overrides are specified through [`.cargo/config.toml`](config.md) instead of `Cargo.toml`. Inside of `.cargo/config.toml` you'll specify a key called `paths`: ```toml paths = ["/path/to/uuid"] ``` This array should be filled with directories that contain a `Cargo.toml`. In this instance, we’re just adding `uuid`, so it will be the only one that’s overridden. This path can be either absolute or relative to the directory that contains the `.cargo` folder. Path overrides are more restricted than the `[patch]` section, however, in that they cannot change the structure of the dependency graph. When a path replacement is used then the previous set of dependencies must all match exactly to the new `Cargo.toml` specification. For example this means that path overrides cannot be used to test out adding a dependency to a crate. Instead, `[patch]` must be used in that situation. As a result, usage of a path override is typically isolated to quick bug fixes rather than larger changes. > **Note**: using a local configuration to override paths will only work for > crates that have been published to [crates.io]. You cannot use this feature > to tell Cargo how to find local unpublished crates. [crates.io]: https://crates.io/ [multiple locations]: specifying-dependencies.md#multiple-locations [dependencies]: specifying-dependencies.md cargo-0.91.0/src/doc/src/reference/pkgid-spec.md000064400000000000000000000076031046102023000174450ustar 00000000000000# Package ID Specifications ## Package ID specifications Subcommands of Cargo frequently need to refer to a particular package within a dependency graph for various operations like updating, cleaning, building, etc. To solve this problem, Cargo supports *Package ID Specifications*. A specification is a string which is used to uniquely refer to one package within a graph of packages. The specification may be fully qualified, such as `registry+https://github.com/rust-lang/crates.io-index#regex@1.4.3` or it may be abbreviated, such as `regex`. The abbreviated form may be used as long as it uniquely identifies a single package in the dependency graph. If there is ambiguity, additional qualifiers can be added to make it unique. For example, if there are two versions of the `regex` package in the graph, then it can be qualified with a version to make it unique, such as `regex@1.4.3`. Package ID specifications output by cargo, for example in [cargo metadata](../commands/cargo-metadata.md) output, are fully qualified. ### Specification grammar The formal grammar for a Package Id Specification is: ```notrust spec := pkgname | [ kind "+" ] proto "://" hostname-and-path [ "?" query] [ "#" ( pkgname | semver ) ] query = ( "branch" | "tag" | "rev" ) "=" ref pkgname := name [ ("@" | ":" ) semver ] semver := digits [ "." digits [ "." digits [ "-" prerelease ] [ "+" build ]]] kind = "registry" | "git" | "path" proto := "http" | "git" | "file" | ... ``` Here, brackets indicate that the contents are optional. The URL form can be used for git dependencies, or to differentiate packages that come from different sources such as different registries. ### Example specifications The following are references to the `regex` package on `crates.io`: | Spec | Name | Version | |:------------------------------------------------------------------|:-------:|:-------:| | `regex` | `regex` | `*` | | `regex@1.4` | `regex` | `1.4.*` | | `regex@1.4.3` | `regex` | `1.4.3` | | `https://github.com/rust-lang/crates.io-index#regex` | `regex` | `*` | | `https://github.com/rust-lang/crates.io-index#regex@1.4.3` | `regex` | `1.4.3` | | `registry+https://github.com/rust-lang/crates.io-index#regex@1.4.3` | `regex` | `1.4.3` | The following are some examples of specs for several different git dependencies: | Spec | Name | Version | |:-----------------------------------------------------------|:----------------:|:--------:| | `https://github.com/rust-lang/cargo#0.52.0` | `cargo` | `0.52.0` | | `https://github.com/rust-lang/cargo#cargo-platform@0.1.2` | `cargo-platform` | `0.1.2` | | `ssh://git@github.com/rust-lang/regex.git#regex@1.4.3` | `regex` | `1.4.3` | | `git+ssh://git@github.com/rust-lang/regex.git#regex@1.4.3` | `regex` | `1.4.3` | | `git+ssh://git@github.com/rust-lang/regex.git?branch=dev#regex@1.4.3` | `regex` | `1.4.3` | Local packages on the filesystem can use `file://` URLs to reference them: | Spec | Name | Version | |:--------------------------------------------|:-----:|:-------:| | `file:///path/to/my/project/foo` | `foo` | `*` | | `file:///path/to/my/project/foo#1.1.8` | `foo` | `1.1.8` | | `path+file:///path/to/my/project/foo#1.1.8` | `foo` | `1.1.8` | ### Brevity of specifications The goal of this is to enable both succinct and exhaustive syntaxes for referring to packages in a dependency graph. Ambiguous references may refer to one or more packages. Most commands generate an error if more than one package could be referred to with the same specification. cargo-0.91.0/src/doc/src/reference/profiles.md000064400000000000000000000433761046102023000172510ustar 00000000000000# Profiles Profiles provide a way to alter the compiler settings, influencing things like optimizations and debugging symbols. Cargo has 4 built-in profiles: `dev`, `release`, `test`, and `bench`. The profile is automatically chosen based on which command is being run if a profile is not specified on the command-line. In addition to the built-in profiles, custom user-defined profiles can also be specified. Profile settings can be changed in [`Cargo.toml`](manifest.md) with the `[profile]` table. Within each named profile, individual settings can be changed with key/value pairs like this: ```toml [profile.dev] opt-level = 1 # Use slightly better optimizations. overflow-checks = false # Disable integer overflow checks. ``` Cargo only looks at the profile settings in the `Cargo.toml` manifest at the root of the workspace. Profile settings defined in dependencies will be ignored. Additionally, profiles can be overridden from a [config] definition. Specifying a profile in a config file or environment variable will override the settings from `Cargo.toml`. [config]: config.md ## Profile settings The following is a list of settings that can be controlled in a profile. ### opt-level The `opt-level` setting controls the [`-C opt-level` flag] which controls the level of optimization. Higher optimization levels may produce faster runtime code at the expense of longer compiler times. Higher levels may also change and rearrange the compiled code which may make it harder to use with a debugger. The valid options are: * `0`: no optimizations * `1`: basic optimizations * `2`: some optimizations * `3`: all optimizations * `"s"`: optimize for binary size * `"z"`: optimize for binary size, but also turn off loop vectorization. It is recommended to experiment with different levels to find the right balance for your project. There may be surprising results, such as level `3` being slower than `2`, or the `"s"` and `"z"` levels not being necessarily smaller. You may also want to reevaluate your settings over time as newer versions of `rustc` change optimization behavior. See also [Profile Guided Optimization] for more advanced optimization techniques. [`-C opt-level` flag]: ../../rustc/codegen-options/index.html#opt-level [Profile Guided Optimization]: ../../rustc/profile-guided-optimization.html ### debug The `debug` setting controls the [`-C debuginfo` flag] which controls the amount of debug information included in the compiled binary. The valid options are: * `0`, `false`, or `"none"`: no debug info at all, default for [`release`](#release) * `"line-directives-only"`: line info directives only. For the nvptx* targets this enables [profiling]. For other use cases, `line-tables-only` is the better, more compatible choice. * `"line-tables-only"`: line tables only. Generates the minimal amount of debug info for backtraces with filename/line number info, but not anything else, i.e. no variable or function parameter info. * `1` or `"limited"`: debug info without type or variable-level information. Generates more detailed module-level info than `line-tables-only`. * `2`, `true`, or `"full"`: full debug info, default for [`dev`](#dev) For more information on what each option does see `rustc`'s docs on [debuginfo]. You may wish to also configure the [`split-debuginfo`](#split-debuginfo) option depending on your needs as well. > **MSRV:** 1.71 is required for `none`, `limited`, `full`, `line-directives-only`, and `line-tables-only` [`-C debuginfo` flag]: ../../rustc/codegen-options/index.html#debuginfo [debuginfo]: ../../rustc/codegen-options/index.html#debuginfo [profiling]: https://reviews.llvm.org/D46061 ### split-debuginfo The `split-debuginfo` setting controls the [`-C split-debuginfo` flag] which controls whether debug information, if generated, is either placed in the executable itself or adjacent to it. This option is a string and acceptable values are the same as those the [compiler accepts][`-C split-debuginfo` flag]. The default value for this option is `unpacked` on macOS for profiles that have debug information otherwise enabled. Otherwise the default for this option is [documented with rustc][`-C split-debuginfo` flag] and is platform-specific. Some options are only available on the [nightly channel]. The Cargo default may change in the future once more testing has been performed, and support for DWARF is stabilized. Be aware that Cargo and rustc have different defaults for this option. This option exists to allow Cargo to experiment on different combinations of flags thus providing better debugging and developer experience. [nightly channel]: ../../book/appendix-07-nightly-rust.html [`-C split-debuginfo` flag]: ../../rustc/codegen-options/index.html#split-debuginfo ### strip The `strip` option controls the [`-C strip` flag], which directs rustc to strip either symbols or debuginfo from a binary. This can be enabled like so: ```toml [package] # ... [profile.release] strip = "debuginfo" ``` Possible string values of `strip` are `"none"`, `"debuginfo"`, and `"symbols"`. The default is `"none"`. You can also configure this option with the boolean values `true` or `false`. `strip = true` is equivalent to `strip = "symbols"`. `strip = false` is equivalent to `strip = "none"` and disables `strip` completely. [`-C strip` flag]: ../../rustc/codegen-options/index.html#strip ### debug-assertions The `debug-assertions` setting controls the [`-C debug-assertions` flag] which turns `cfg(debug_assertions)` [conditional compilation] on or off. Debug assertions are intended to include runtime validation which is only available in debug/development builds. These may be things that are too expensive or otherwise undesirable in a release build. Debug assertions enables the [`debug_assert!` macro] in the standard library. The valid options are: * `true`: enabled * `false`: disabled [`-C debug-assertions` flag]: ../../rustc/codegen-options/index.html#debug-assertions [conditional compilation]: ../../reference/conditional-compilation.md#debug_assertions [`debug_assert!` macro]: ../../std/macro.debug_assert.html ### overflow-checks The `overflow-checks` setting controls the [`-C overflow-checks` flag] which controls the behavior of [runtime integer overflow]. When overflow-checks are enabled, a panic will occur on overflow. The valid options are: * `true`: enabled * `false`: disabled [`-C overflow-checks` flag]: ../../rustc/codegen-options/index.html#overflow-checks [runtime integer overflow]: ../../reference/expressions/operator-expr.md#overflow ### lto The `lto` setting controls `rustc`'s [`-C lto`], [`-C linker-plugin-lto`], and [`-C embed-bitcode`] options, which control LLVM's [link time optimizations]. LTO can produce better optimized code, using whole-program analysis, at the cost of longer linking time. The valid options are: * `false`: Performs "thin local LTO" which performs "thin" LTO on the local crate only across its [codegen units](#codegen-units). No LTO is performed if codegen units is 1 or [opt-level](#opt-level) is 0. * `true` or `"fat"`: Performs "fat" LTO which attempts to perform optimizations across all crates within the dependency graph. * `"thin"`: Performs ["thin" LTO]. This is similar to "fat", but takes substantially less time to run while still achieving performance gains similar to "fat". * `"off"`: Disables LTO. See the [linker-plugin-lto chapter] if you are interested in cross-language LTO. This is not yet supported natively in Cargo, but can be performed via `RUSTFLAGS`. [`-C lto`]: ../../rustc/codegen-options/index.html#lto [link time optimizations]: https://llvm.org/docs/LinkTimeOptimization.html [`-C linker-plugin-lto`]: ../../rustc/codegen-options/index.html#linker-plugin-lto [`-C embed-bitcode`]: ../../rustc/codegen-options/index.html#embed-bitcode [linker-plugin-lto chapter]: ../../rustc/linker-plugin-lto.html ["thin" LTO]: http://blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto.html ### panic The `panic` setting controls the [`-C panic` flag] which controls which panic strategy to use. The valid options are: * `"unwind"`: Unwind the stack upon panic. * `"abort"`: Terminate the process upon panic. When set to `"unwind"`, the actual value depends on the default of the target platform. For example, the NVPTX platform does not support unwinding, so it always uses `"abort"`. Tests, benchmarks, build scripts, and proc macros ignore the `panic` setting. The `rustc` test harness currently requires `unwind` behavior. See the [`panic-abort-tests`] unstable flag which enables `abort` behavior. Additionally, when using the `abort` strategy and building a test, all of the dependencies will also be forced to build with the `unwind` strategy. [`-C panic` flag]: ../../rustc/codegen-options/index.html#panic [`panic-abort-tests`]: unstable.md#panic-abort-tests ### incremental The `incremental` setting controls the [`-C incremental` flag] which controls whether or not incremental compilation is enabled. Incremental compilation causes `rustc` to save additional information to disk which will be reused when recompiling the crate, improving re-compile times. The additional information is stored in the `target` directory. The valid options are: * `true`: enabled * `false`: disabled Incremental compilation is only used for workspace members and "path" dependencies. The incremental value can be overridden globally with the `CARGO_INCREMENTAL` [environment variable] or the [`build.incremental`] config variable. [`-C incremental` flag]: ../../rustc/codegen-options/index.html#incremental [environment variable]: environment-variables.md [`build.incremental`]: config.md#buildincremental ### codegen-units The `codegen-units` setting controls the [`-C codegen-units` flag] which controls how many "code generation units" a crate will be split into. More code generation units allows more of a crate to be processed in parallel possibly reducing compile time, but may produce slower code. This option takes an integer greater than 0. The default is 256 for [incremental](#incremental) builds, and 16 for non-incremental builds. [`-C codegen-units` flag]: ../../rustc/codegen-options/index.html#codegen-units ### rpath The `rpath` setting controls the [`-C rpath` flag] which controls whether or not [`rpath`] is enabled. [`-C rpath` flag]: ../../rustc/codegen-options/index.html#rpath [`rpath`]: https://en.wikipedia.org/wiki/Rpath ## Default profiles ### dev The `dev` profile is used for normal development and debugging. It is the default for build commands like [`cargo build`], and is used for `cargo install --debug`. The default settings for the `dev` profile are: ```toml [profile.dev] opt-level = 0 debug = true split-debuginfo = '...' # Platform-specific. strip = "none" debug-assertions = true overflow-checks = true lto = false panic = 'unwind' incremental = true codegen-units = 256 rpath = false ``` ### release The `release` profile is intended for optimized artifacts used for releases and in production. This profile is used when the `--release` flag is used, and is the default for [`cargo install`]. The default settings for the `release` profile are: ```toml [profile.release] opt-level = 3 debug = false split-debuginfo = '...' # Platform-specific. strip = "none" debug-assertions = false overflow-checks = false lto = false panic = 'unwind' incremental = false codegen-units = 16 rpath = false ``` ### test The `test` profile is the default profile used by [`cargo test`]. The `test` profile inherits the settings from the [`dev`](#dev) profile. ### bench The `bench` profile is the default profile used by [`cargo bench`]. The `bench` profile inherits the settings from the [`release`](#release) profile. ### Build Dependencies To compile quickly, all profiles, by default, do not optimize build dependencies (build scripts, proc macros, and their dependencies), and avoid computing debug info when a build dependency is not used as a runtime dependency. The default settings for build overrides are: ```toml [profile.dev.build-override] opt-level = 0 codegen-units = 256 debug = false # when possible [profile.release.build-override] opt-level = 0 codegen-units = 256 ``` However, if errors occur while running build dependencies, turning full debug info on will improve backtraces and debuggability when needed: ```toml debug = true ``` Build dependencies otherwise inherit settings from the active profile in use, as described in [Profile selection](#profile-selection). ## Custom profiles In addition to the built-in profiles, additional custom profiles can be defined. These may be useful for setting up multiple workflows and build modes. When defining a custom profile, you must specify the `inherits` key to specify which profile the custom profile inherits settings from when the setting is not specified. For example, let's say you want to compare a normal release build with a release build with [LTO](#lto) optimizations, you can specify something like the following in `Cargo.toml`: ```toml [profile.release-lto] inherits = "release" lto = true ``` The `--profile` flag can then be used to choose this custom profile: ```console cargo build --profile release-lto ``` The output for each profile will be placed in a directory of the same name as the profile in the [`target` directory]. As in the example above, the output would go into the `target/release-lto` directory. [`target` directory]: build-cache.md ## Profile selection The profile used depends on the command, the command-line flags like `--release` or `--profile`, and the package (in the case of [overrides](#overrides)). The default profile if none is specified is: | Command | Default Profile | |---------|-----------------| | [`cargo run`], [`cargo build`],
[`cargo check`], [`cargo rustc`] | [`dev` profile](#dev) | | [`cargo test`] | [`test` profile](#test) | [`cargo bench`] | [`bench` profile](#bench) | [`cargo install`] | [`release` profile](#release) You can switch to a different profile using the `--profile=NAME` option which will used the given profile. The `--release` flag is equivalent to `--profile=release`. The selected profile applies to all Cargo targets, including [library](./cargo-targets.md#library), [binary](./cargo-targets.md#binaries), [example](./cargo-targets.md#examples), [test](./cargo-targets.md#tests), and [benchmark](./cargo-targets.md#benchmarks). The profile for specific packages can be specified with [overrides](#overrides), described below. [`cargo bench`]: ../commands/cargo-bench.md [`cargo build`]: ../commands/cargo-build.md [`cargo check`]: ../commands/cargo-check.md [`cargo install`]: ../commands/cargo-install.md [`cargo run`]: ../commands/cargo-run.md [`cargo rustc`]: ../commands/cargo-rustc.md [`cargo test`]: ../commands/cargo-test.md ## Overrides Profile settings can be overridden for specific packages and build-time crates. To override the settings for a specific package, use the `package` table to change the settings for the named package: ```toml # The `foo` package will use the -Copt-level=3 flag. [profile.dev.package.foo] opt-level = 3 ``` The package name is actually a [Package ID Spec](pkgid-spec.md), so you can target individual versions of a package with syntax such as `[profile.dev.package."foo:2.1.0"]`. To override the settings for all dependencies (but not any workspace member), use the `"*"` package name: ```toml # Set the default for dependencies. [profile.dev.package."*"] opt-level = 2 ``` To override the settings for build scripts, proc macros, and their dependencies, use the `build-override` table: ```toml # Set the settings for build scripts and proc-macros. [profile.dev.build-override] opt-level = 3 ``` > Note: When a dependency is both a normal dependency and a build dependency, > Cargo will try to only build it once when `--target` is not specified. When > using `build-override`, the dependency may need to be built twice, once as a > normal dependency and once with the overridden build settings. This may > increase initial build times. The precedence for which value is used is done in the following order (first match wins): 1. `[profile.dev.package.name]` --- A named package. 2. `[profile.dev.package."*"]` --- For any non-workspace member. 3. `[profile.dev.build-override]` --- Only for build scripts, proc macros, and their dependencies. 4. `[profile.dev]` --- Settings in `Cargo.toml`. 5. Default values built-in to Cargo. Overrides cannot specify the `panic`, `lto`, or `rpath` settings. ### Overrides and generics The location where generic code is instantiated will influence the optimization settings used for that generic code. This can cause subtle interactions when using profile overrides to change the optimization level of a specific crate. If you attempt to raise the optimization level of a dependency which defines generic functions, those generic functions may not be optimized when used in your local crate. This is because the code may be generated in the crate where it is instantiated, and thus may use the optimization settings of that crate. For example, [nalgebra] is a library which defines vectors and matrices making heavy use of generic parameters. If your local code defines concrete nalgebra types like `Vector4` and uses their methods, the corresponding nalgebra code will be instantiated and built within your crate. Thus, if you attempt to increase the optimization level of `nalgebra` using a profile override, it may not result in faster performance. Further complicating the issue, `rustc` has some optimizations where it will attempt to share monomorphized generics between crates. If the opt-level is 2 or 3, then a crate will not use monomorphized generics from other crates, nor will it export locally defined monomorphized items to be shared with other crates. When experimenting with optimizing dependencies for development, consider trying opt-level 1, which will apply some optimizations while still allowing monomorphized items to be shared. [nalgebra]: https://crates.io/crates/nalgebra cargo-0.91.0/src/doc/src/reference/publishing.md000064400000000000000000000301411046102023000175540ustar 00000000000000# Publishing on crates.io Once you've got a library that you'd like to share with the world, it's time to publish it on [crates.io]! Publishing a crate is when a specific version is uploaded to be hosted on [crates.io]. Take care when publishing a crate, because a publish is **permanent**. The version can never be overwritten, and the code cannot be deleted. There is no limit to the number of versions which can be published, however. ## Before your first publish First things first, you’ll need an account on [crates.io] to acquire an API token. To do so, [visit the home page][crates.io] and log in via a GitHub account (required for now). You will also need to provide and verify your email address on the [Account Settings](https://crates.io/settings/profile) page. Once that is done [create an API token](https://crates.io/settings/tokens), make sure you copy it. Once you leave the page you will not be able to see it again. Then run the [`cargo login`] command. ```console $ cargo login ``` Then at the prompt put in the token specified. ```console please paste the API Token found on https://crates.io/me below abcdefghijklmnopqrstuvwxyz012345 ``` This command will inform Cargo of your API token and store it locally in your `~/.cargo/credentials.toml`. Note that this token is a **secret** and should not be shared with anyone else. If it leaks for any reason, you should revoke it immediately. > **Note**: The [`cargo logout`] command can be used to remove the token from > `credentials.toml`. This can be useful if you no longer need it stored on > the local machine. ## Before publishing a new crate Keep in mind that crate names on [crates.io] are allocated on a first-come-first-serve basis. Once a crate name is taken, it cannot be used for another crate. Check out the [metadata you can specify](manifest.md) in `Cargo.toml` to ensure your crate can be discovered more easily! Before publishing, make sure you have filled out the following fields: - [`license` or `license-file`] - [`description`] - [`homepage`] - [`repository`] - [`readme`] It would also be a good idea to include some [`keywords`] and [`categories`], though they are not required. If you are publishing a library, you may also want to consult the [Rust API Guidelines]. ### Packaging a crate The next step is to package up your crate and upload it to [crates.io]. For this we’ll use the [`cargo publish`] subcommand. This command performs the following steps: 1. Perform some verification checks on your package. 2. Compress your source code into a `.crate` file. 3. Extract the `.crate` file into a temporary directory and verify that it compiles. 4. Upload the `.crate` file to [crates.io]. 5. The registry will perform some additional checks on the uploaded package before adding it. It is recommended that you first run `cargo publish --dry-run` (or [`cargo package`] which is equivalent) to ensure there aren't any warnings or errors before publishing. This will perform the first three steps listed above. ```console $ cargo publish --dry-run ``` You can inspect the generated `.crate` file in the `target/package` directory. [crates.io] currently has a 10MB size limit on the `.crate` file. You may want to check the size of the `.crate` file to ensure you didn't accidentally package up large assets that are not required to build your package, such as test data, website documentation, or code generation. You can check which files are included with the following command: ```console $ cargo package --list ``` Cargo will automatically ignore files ignored by your version control system when packaging, but if you want to specify an extra set of files to ignore you can use the [`exclude` key](manifest.md#the-exclude-and-include-fields) in the manifest: ```toml [package] # ... exclude = [ "public/assets/*", "videos/*", ] ``` If you’d rather explicitly list the files to include, Cargo also supports an [`include` key](manifest.md#the-exclude-and-include-fields), which if set, overrides the `exclude` key: ```toml [package] # ... include = [ "**/*.rs", ] ``` ## Uploading the crate When you are ready to publish, use the [`cargo publish`] command to upload to [crates.io]: ```console $ cargo publish ``` And that’s it, you’ve now published your first crate! ## Publishing a new version of an existing crate In order to release a new version, change [the `version` value](manifest.md#the-version-field) specified in your `Cargo.toml` manifest. Keep in mind [the SemVer rules](semver.md) which provide guidelines on what is a compatible change. Then run [`cargo publish`] as described above to upload the new version. > **Recommendation:** Consider the full release process and automate what you can. > > Each version should include: > - A changelog entry, preferably [manually curated](https://keepachangelog.com/en/1.0.0/) though a generated one is better than nothing > - A [git tag](https://git-scm.com/book/en/v2/Git-Basics-Tagging) pointing to the published commit > > Examples of third-party tools that are representative of different workflows include (in alphabetical order): > - [cargo-release](https://crates.io/crates/cargo-release) > - [cargo-smart-release](https://crates.io/crates/cargo-smart-release) > - [release-plz](https://crates.io/crates/release-plz) > > For more, see [crates.io](https://crates.io/search?q=cargo%20release). ## Managing a crates.io-based crate Management of crates is primarily done through the command line `cargo` tool rather than the [crates.io] web interface. For this, there are a few subcommands to manage a crate. ### `cargo yank` Occasions may arise where you publish a version of a crate that actually ends up being broken for one reason or another (syntax error, forgot to include a file, etc.). For situations such as this, Cargo supports a “yank” of a version of a crate. ```console $ cargo yank --version 1.0.1 $ cargo yank --version 1.0.1 --undo ``` A yank **does not** delete any code. This feature is not intended for deleting accidentally uploaded secrets, for example. If that happens, you must reset those secrets immediately. The semantics of a yanked version are that no new dependencies can be created against that version, but all existing dependencies continue to work. One of the major goals of [crates.io] is to act as a permanent archive of crates that does not change over time, and allowing deletion of a version would go against this goal. Essentially a yank means that all packages with a `Cargo.lock` will not break, while any future `Cargo.lock` files generated will not list the yanked version. ### `cargo owner` A crate is often developed by more than one person, or the primary maintainer may change over time! The owner of a crate is the only person allowed to publish new versions of the crate, but an owner may designate additional owners. ```console $ cargo owner --add github-handle $ cargo owner --remove github-handle $ cargo owner --add github:rust-lang:owners $ cargo owner --remove github:rust-lang:owners ``` The owner IDs given to these commands must be GitHub user names or GitHub teams. If a user name is given to `--add`, that user is invited as a “named” owner, with full rights to the crate. In addition to being able to publish or yank versions of the crate, they have the ability to add or remove owners, *including* the owner that made *them* an owner. Needless to say, you shouldn’t make people you don’t fully trust into a named owner. In order to become a named owner, a user must have logged into [crates.io] previously. If a team name is given to `--add`, that team is invited as a “team” owner, with restricted right to the crate. While they have permission to publish or yank versions of the crate, they *do not* have the ability to add or remove owners. In addition to being more convenient for managing groups of owners, teams are just a bit more secure against owners becoming malicious. The syntax for teams is currently `github:org:team` (see examples above). In order to invite a team as an owner one must be a member of that team. No such restriction applies to removing a team as an owner. ## GitHub permissions Team membership is not something GitHub provides simple public access to, and it is likely for you to encounter the following message when working with them: > It looks like you don’t have permission to query a necessary property from GitHub to complete this request. You may need to re-authenticate on [crates.io] to grant permission to read GitHub org memberships. This is basically a catch-all for “you tried to query a team, and one of the five levels of membership access control denied this”. That is not an exaggeration. GitHub’s support for team access control is Enterprise Grade. The most likely cause of this is simply that you last logged in before this feature was added. We originally requested *no* permissions from GitHub when authenticating users, because we didn’t actually ever use the user’s token for anything other than logging them in. However to query team membership on your behalf, we now require [the `read:org` scope][oauth-scopes]. You are free to deny us this scope, and everything that worked before teams were introduced will keep working. However you will never be able to add a team as an owner, or publish a crate as a team owner. If you ever attempt to do this, you will get the error above. You may also see this error if you ever try to publish a crate that you don’t own at all, but otherwise happens to have a team. If you ever change your mind, or just aren’t sure if [crates.io] has sufficient permission, you can always go to and re-authenticate, which will prompt you for permission if [crates.io] doesn’t have all the scopes it would like to. An additional barrier to querying GitHub is that the organization may be actively denying third party access. To check this, you can go to: ```text https://github.com/organizations/:org/settings/oauth_application_policy ``` where `:org` is the name of the organization (e.g., `rust-lang`). You may see something like: ![Organization Access Control](../images/org-level-acl.png) Where you may choose to explicitly remove [crates.io] from your organization’s blacklist, or simply press the “Remove Restrictions” button to allow all third party applications to access this data. Alternatively, when [crates.io] requested the `read:org` scope, you could have explicitly whitelisted [crates.io] querying the org in question by pressing the “Grant Access” button next to its name: ![Authentication Access Control](../images/auth-level-acl.png) ### Troubleshooting GitHub team access errors When trying to add a GitHub team as crate owner, you may see an error like: ```text error: failed to invite owners to crate : api errors (status 200 OK): could not find the github team org/repo ``` In that case, you should go to [the GitHub Application settings page] and check if crates.io is listed in the `Authorized OAuth Apps` tab. If it isn't, you should go to and authorize it. Then go back to the Application Settings page on GitHub, click on the crates.io application in the list, and make sure you or your organization is listed in the "Organization access" list with a green check mark. If there's a button labeled `Grant` or `Request`, you should grant the access or request the org owner to do so. [Rust API Guidelines]: https://rust-lang.github.io/api-guidelines/ [`cargo login`]: ../commands/cargo-login.md [`cargo logout`]: ../commands/cargo-logout.md [`cargo package`]: ../commands/cargo-package.md [`cargo publish`]: ../commands/cargo-publish.md [`categories`]: manifest.md#the-categories-field [`description`]: manifest.md#the-description-field [`documentation`]: manifest.md#the-documentation-field [`homepage`]: manifest.md#the-homepage-field [`keywords`]: manifest.md#the-keywords-field [`license` or `license-file`]: manifest.md#the-license-and-license-file-fields [`readme`]: manifest.md#the-readme-field [`repository`]: manifest.md#the-repository-field [crates.io]: https://crates.io/ [oauth-scopes]: https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/ [the GitHub Application settings page]: https://github.com/settings/applications cargo-0.91.0/src/doc/src/reference/registries.md000064400000000000000000000131561046102023000175770ustar 00000000000000# Registries Cargo installs crates and fetches dependencies from a "registry". The default registry is [crates.io]. A registry contains an "index" which contains a searchable list of available crates. A registry may also provide a web API to support publishing new crates directly from Cargo. > Note: If you are interested in mirroring or vendoring an existing registry, > take a look at [Source Replacement]. If you are implementing a registry server, see [Running a Registry] for more details about the protocol between Cargo and a registry. If you're using a registry that requires authentication, see [Registry Authentication]. If you are implementing a credential provider, see [Credential Provider Protocol] for details. ## Using an Alternate Registry To use a registry other than [crates.io], the name and index URL of the registry must be added to a [`.cargo/config.toml` file][config]. The `registries` table has a key for each registry, for example: ```toml [registries] my-registry = { index = "https://my-intranet:8080/git/index" } ``` The `index` key should be a URL to a git repository with the registry's index or a Cargo sparse registry URL with the `sparse+` prefix. A crate can then depend on a crate from another registry by specifying the `registry` key and a value of the registry's name in that dependency's entry in `Cargo.toml`: ```toml # Sample Cargo.toml [package] name = "my-project" version = "0.1.0" edition = "2024" [dependencies] other-crate = { version = "1.0", registry = "my-registry" } ``` As with most config values, the index may be specified with an environment variable instead of a config file. For example, setting the following environment variable will accomplish the same thing as defining a config file: ```ignore CARGO_REGISTRIES_MY_REGISTRY_INDEX=https://my-intranet:8080/git/index ``` > Note: [crates.io] does not accept packages that depend on crates from other > registries. ## Publishing to an Alternate Registry If the registry supports web API access, then packages can be published directly to the registry from Cargo. Several of Cargo's commands such as [`cargo publish`] take a `--registry` command-line flag to indicate which registry to use. For example, to publish the package in the current directory: 1. `cargo login --registry=my-registry` This only needs to be done once. You must enter the secret API token retrieved from the registry's website. Alternatively the token may be passed directly to the `publish` command with the `--token` command-line flag or an environment variable with the name of the registry such as `CARGO_REGISTRIES_MY_REGISTRY_TOKEN`. 2. `cargo publish --registry=my-registry` Instead of always passing the `--registry` command-line option, the default registry may be set in [`.cargo/config.toml`][config] with the `registry.default` key. For example: ```toml [registry] default = "my-registry" ``` Setting the `package.publish` key in the `Cargo.toml` manifest restricts which registries the package is allowed to be published to. This is useful to prevent accidentally publishing a closed-source package to [crates.io]. The value may be a list of registry names, for example: ```toml [package] # ... publish = ["my-registry"] ``` The `publish` value may also be `false` to restrict all publishing, which is the same as an empty list. The authentication information saved by [`cargo login`] is stored in the `credentials.toml` file in the Cargo home directory (default `$HOME/.cargo`). It has a separate table for each registry, for example: ```toml [registries.my-registry] token = "854DvwSlUwEHtIo3kWy6x7UCPKHfzCmy" ``` ## Registry Protocols Cargo supports two remote registry protocols: `git` and `sparse`. If the registry index URL starts with `sparse+`, Cargo uses the sparse protocol. Otherwise Cargo uses the `git` protocol. The `git` protocol stores index metadata in a git repository and requires Cargo to clone the entire repo. The `sparse` protocol fetches individual metadata files using plain HTTP requests. Since Cargo only downloads the metadata for relevant crates, the `sparse` protocol can save significant time and bandwidth. The [crates.io] registry supports both protocols. The protocol for crates.io is controlled via the [`registries.crates-io.protocol`] config key. [Source Replacement]: source-replacement.md [Running a Registry]: running-a-registry.md [Credential Provider Protocol]: credential-provider-protocol.md [Registry Authentication]: registry-authentication.md [`cargo publish`]: ../commands/cargo-publish.md [`cargo package`]: ../commands/cargo-package.md [`cargo login`]: ../commands/cargo-login.md [config]: config.md [crates.io]: https://crates.io/ [`registries.crates-io.protocol`]: config.md#registriescrates-ioprotocol cargo-0.91.0/src/doc/src/reference/registry-authentication.md000064400000000000000000000110101046102023000222670ustar 00000000000000# Registry Authentication Cargo authenticates to registries with credential providers. These credential providers are external executables or built-in providers that Cargo uses to store and retrieve credentials. Using alternative registries with authentication *requires* a credential provider to be configured to avoid unknowingly storing unencrypted credentials on disk. For historical reasons, public (non-authenticated) registries do not require credential provider configuration, and the `cargo:token` provider is used if no providers are configured. Cargo also includes platform-specific providers that use the operating system to securely store tokens. The `cargo:token` provider is also included which stores credentials in unencrypted plain text in the [credentials](config.md#credentials) file. ## Recommended configuration It's recommended to configure a global credential provider list in `$CARGO_HOME/config.toml` which defaults to: * Windows: `%USERPROFILE%\.cargo\config.toml` * Unix: `~/.cargo/config.toml` This recommended configuration uses the operating system provider, with a fallback to `cargo:token` to look in Cargo's [credentials](config.md#credentials) file or environment variables: ```toml # ~/.cargo/config.toml [registry] global-credential-providers = ["cargo:token", "cargo:libsecret", "cargo:macos-keychain", "cargo:wincred"] ``` *Note that later entries have higher precedence. See [`registry.global-credential-providers`](config.md#registryglobal-credential-providers) for more details.* Some private registries may also recommend a registry-specific credential-provider. Check your registry's documentation to see if this is the case. ## Built-in providers Cargo includes several built-in credential providers. The available built-in providers may change in future Cargo releases (though there are currently no plans to do so). ### `cargo:token` Uses Cargo's [credentials](config.md#credentials) file to store tokens unencrypted in plain text. When retrieving tokens, checks the `CARGO_REGISTRIES__TOKEN` environment variable. If this credential provider is not listed, then the `*_TOKEN` environment variables will not work. ### `cargo:wincred` Uses the Windows Credential Manager to store tokens. The credentials are stored as `cargo-registry:` in the Credential Manager under "Windows Credentials". ### `cargo:macos-keychain` Uses the macOS Keychain to store tokens. The Keychain Access app can be used to view stored tokens. ### `cargo:libsecret` Uses [libsecret](https://wiki.gnome.org/Projects/Libsecret) to store tokens. Any password manager with libsecret support can be used to view stored tokens. The following are a few examples (non-exhaustive): - [GNOME Keyring](https://wiki.gnome.org/Projects/GnomeKeyring) - [KDE Wallet Manager](https://apps.kde.org/kwalletmanager5/) (since KDE Frameworks 5.97.0) - [KeePassXC](https://keepassxc.org/) (since 2.5.0) ### `cargo:token-from-stdout ` Launch a subprocess that returns a token on stdout. Newlines will be trimmed. * The process inherits the user's stdin and stderr. * It should exit 0 on success, and nonzero on error. * [`cargo login`] and [`cargo logout`] are not supported and return an error if used. The following environment variables will be provided to the executed command: * `CARGO` --- Path to the `cargo` binary executing the command. * `CARGO_REGISTRY_INDEX_URL` --- The URL of the registry index. * `CARGO_REGISTRY_NAME_OPT` --- Optional name of the registry. Should not be used as a lookup key. Arguments will be passed on to the subcommand. [`cargo login`]: ../commands/cargo-login.md [`cargo logout`]: ../commands/cargo-logout.md ## Credential plugins For credential provider plugins that follow Cargo's [credential provider protocol](credential-provider-protocol.md), the configuration value should be a string with the path to the executable (or the executable name if on the `PATH`). For example, to install [cargo-credential-1password](https://crates.io/crates/cargo-credential-1password) from crates.io do the following: Install the provider with `cargo install cargo-credential-1password` In the config, add to (or create) `registry.global-credential-providers`: ```toml [registry] global-credential-providers = ["cargo:token", "cargo-credential-1password --account my.1password.com"] ``` The values in `global-credential-providers` are split on spaces into path and command-line arguments. To define a global credential provider where the path or arguments contain spaces, use the [`[credential-alias]` table](config.md#credential-alias). cargo-0.91.0/src/doc/src/reference/registry-index.md000064400000000000000000000405401046102023000203710ustar 00000000000000# Index Format The following defines the format of the index. New features are occasionally added, which are only understood starting with the version of Cargo that introduced them. Older versions of Cargo may not be able to use packages that make use of new features. However, the format for older packages should not change, so older versions of Cargo should be able to use them. ## Index Configuration The root of the index contains a file named `config.json` which contains JSON information used by Cargo for accessing the registry. This is an example of what the [crates.io] config file looks like: ```javascript { "dl": "https://crates.io/api/v1/crates", "api": "https://crates.io" } ``` The keys are: - `dl`: This is the URL for downloading crates listed in the index. The value may have the following markers which will be replaced with their corresponding value: - `{crate}`: The name of crate. - `{version}`: The crate version. - `{prefix}`: A directory prefix computed from the crate name. For example, a crate named `cargo` has a prefix of `ca/rg`. See below for details. - `{lowerprefix}`: Lowercase variant of `{prefix}`. - `{sha256-checksum}`: The crate's sha256 checksum. If none of the markers are present, then the value `/{crate}/{version}/download` is appended to the end. - `api`: This is the base URL for the web API. This key is optional, but if it is not specified, commands such as [`cargo publish`] will not work. The web API is described below. - `auth-required`: indicates whether this is a private registry that requires all operations to be authenticated including API requests, crate downloads and sparse index updates. ## Download Endpoint The download endpoint should send the `.crate` file for the requested package. Cargo supports https, http, and file URLs, HTTP redirects, HTTP1 and HTTP2. The exact specifics of TLS support depend on the platform that Cargo is running on, the version of Cargo, and how it was compiled. If `auth-required: true` is set in `config.json`, the `Authorization` header will be included with http(s) download requests. ## Index files The rest of the index repository contains one file for each package, where the filename is the name of the package in lowercase. Each version of the package has a separate line in the file. The files are organized in a tier of directories: - Packages with 1 character names are placed in a directory named `1`. - Packages with 2 character names are placed in a directory named `2`. - Packages with 3 character names are placed in the directory `3/{first-character}` where `{first-character}` is the first character of the package name. - All other packages are stored in directories named `{first-two}/{second-two}` where the top directory is the first two characters of the package name, and the next subdirectory is the third and fourth characters of the package name. For example, `cargo` would be stored in a file named `ca/rg/cargo`. > Note: Although the index filenames are in lowercase, the fields that contain > package names in `Cargo.toml` and the index JSON data are case-sensitive and > may contain upper and lower case characters. The directory name above is calculated based on the package name converted to lowercase; it is represented by the marker `{lowerprefix}`. When the original package name is used without case conversion, the resulting directory name is represented by the marker `{prefix}`. For example, the package `MyCrate` would have a `{prefix}` of `My/Cr` and a `{lowerprefix}` of `my/cr`. In general, using `{prefix}` is recommended over `{lowerprefix}`, but there are pros and cons to each choice. Using `{prefix}` on case-insensitive filesystems results in (harmless-but-inelegant) directory aliasing. For example, `crate` and `CrateTwo` have `{prefix}` values of `cr/at` and `Cr/at`; these are distinct on Unix machines but alias to the same directory on Windows. Using directories with normalized case avoids aliasing, but on case-sensitive filesystems it's harder to support older versions of Cargo that lack `{prefix}`/`{lowerprefix}`. For example, nginx rewrite rules can easily construct `{prefix}` but can't perform case-conversion to construct `{lowerprefix}`. ## Name restrictions Registries should consider enforcing limitations on package names added to their index. Cargo itself allows names with any [alphanumeric], `-`, or `_` characters. [crates.io] imposes its own limitations, including the following: - Only allows ASCII characters. - Only alphanumeric, `-`, and `_` characters. - First character must be alphabetic. - Case-insensitive collision detection. - Prevent differences of `-` vs `_`. - Under a specific length (max 64). - Rejects reserved names, such as Windows special filenames like "nul". Registries should consider incorporating similar restrictions, and consider the security implications, such as [IDN homograph attacks](https://en.wikipedia.org/wiki/IDN_homograph_attack) and other concerns in [UTR36](https://www.unicode.org/reports/tr36/) and [UTS39](https://www.unicode.org/reports/tr39/). ## Version uniqueness Indexes *must* ensure that each version only appears once for each package. This includes ignoring SemVer build metadata. For example, the index must *not* contain two entries with a version `1.0.7` and `1.0.7+extra`. ## JSON schema Each line in a package file contains a JSON object that describes a published version of the package. The following is a pretty-printed example with comments explaining the format of the entry. ```javascript { // The name of the package. // This must only contain alphanumeric, `-`, or `_` characters. "name": "foo", // The version of the package this row is describing. // This must be a valid version number according to the Semantic // Versioning 2.0.0 spec at https://semver.org/. "vers": "0.1.0", // Array of direct dependencies of the package. "deps": [ { // Name of the dependency. // If the dependency is renamed from the original package name, // this is the new name. The original package name is stored in // the `package` field. "name": "rand", // The SemVer requirement for this dependency. // This must be a valid version requirement defined at // https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html. "req": "^0.6", // Array of features (as strings) enabled for this dependency. // May be omitted since Cargo 1.84. "features": ["i128_support"], // Boolean of whether or not this is an optional dependency. // Since Cargo 1.84, defaults to `false` if not specified. "optional": false, // Boolean of whether or not default features are enabled. // Since Cargo 1.84, defaults to `true` if not specified. "default_features": true, // The target platform for the dependency. // If not specified or `null`, it is not a target dependency. // Otherwise, a string such as "cfg(windows)". "target": null, // The dependency kind. // "dev", "build", or "normal". // If not specified or `null`, it defaults to "normal". "kind": "normal", // The URL of the index of the registry where this dependency is // from as a string. If not specified or `null`, it is assumed the // dependency is in the current registry. "registry": null, // If the dependency is renamed, this is a string of the actual // package name. If not specified or `null`, this dependency is not // renamed. "package": null, } ], // A SHA256 checksum of the `.crate` file. "cksum": "d867001db0e2b6e0496f9fac96930e2d42233ecd3ca0413e0753d4c7695d289c", // Set of features defined for the package. // Each feature maps to an array of features or dependencies it enables. // May be omitted since Cargo 1.84. "features": { "extras": ["rand/simd_support"] }, // Boolean of whether or not this version has been yanked. "yanked": false, // The `links` string value from the package's manifest, or null if not // specified. This field is optional and defaults to null. "links": null, // An unsigned 32-bit integer value indicating the schema version of this // entry. // // If this is not specified, it should be interpreted as the default of 1. // // Cargo (starting with version 1.51) will ignore versions it does not // recognize. This provides a method to safely introduce changes to index // entries and allow older versions of cargo to ignore newer entries it // doesn't understand. Versions older than 1.51 ignore this field, and // thus may misinterpret the meaning of the index entry. // // The current values are: // // * 1: The schema as documented here, not including newer additions. // This is honored in Rust version 1.51 and newer. // * 2: The addition of the `features2` field. // This is honored in Rust version 1.60 and newer. "v": 2, // This optional field contains features with new, extended syntax. // Specifically, namespaced features (`dep:`) and weak dependencies // (`pkg?/feat`). // // This is separated from `features` because versions older than 1.19 // will fail to load due to not being able to parse the new syntax, even // with a `Cargo.lock` file. // // Cargo will merge any values listed here with the "features" field. // // If this field is included, the "v" field should be set to at least 2. // // Registries are not required to use this field for extended feature // syntax, they are allowed to include those in the "features" field. // Using this is only necessary if the registry wants to support cargo // versions older than 1.19, which in practice is only crates.io since // those older versions do not support other registries. "features2": { "serde": ["dep:serde", "chrono?/serde"] } // The minimal supported Rust version (optional) // This must be a valid version requirement without an operator (e.g. no `=`) "rust_version": "1.60" } ``` The JSON objects should not be modified after they are added except for the `yanked` field whose value may change at any time. > **Note**: The index JSON format has subtle differences from the JSON format of the [Publish API] and [`cargo metadata`]. > If you are using one of those as a source to generate index entries, you are encouraged to carefully inspect the documentation differences between them. > > For the [Publish API], the differences are: > > * `deps` > * `name` --- When the dependency is [renamed] in `Cargo.toml`, the publish API puts the original package name in the `name` field and the aliased name in the `explicit_name_in_toml` field. > The index places the aliased name in the `name` field, and the original package name in the `package` field. > * `req` --- The Publish API field is called `version_req`. > * `cksum` --- The publish API does not specify the checksum, it must be computed by the registry before adding to the index. > * `features` --- Some features may be placed in the `features2` field. > Note: This is only a legacy requirement for [crates.io]; other registries should not need to bother with modifying the features map. > The `v` field indicates the presence of the `features2` field. > * The publish API includes several other fields, such as `description` and `readme`, which don't appear in the index. > These are intended to make it easier for a registry to obtain the metadata about the crate to display on a website without needing to extract and parse the `.crate` file. > This additional information is typically added to a database on the registry server. > * Although `rust_version` is included here, [crates.io] will ignore this field > and instead read it from the `Cargo.toml` contained in the `.crate` file. > > For [`cargo metadata`], the differences are: > > * `vers` --- The `cargo metadata` field is called `version`. > * `deps` > * `name` --- When the dependency is [renamed] in `Cargo.toml`, `cargo metadata` puts the original package name in the `name` field and the aliased name in the `rename` field. > The index places the aliased name in the `name` field, and the original package name in the `package` field. > * `default_features` --- The `cargo metadata` field is called `uses_default_features`. > * `registry` --- `cargo metadata` uses a value of `null` to indicate that the dependency comes from [crates.io]. > The index uses a value of `null` to indicate that the dependency comes from the same registry as the index. > When creating an index entry, a registry other than [crates.io] should translate a value of `null` to be `https://github.com/rust-lang/crates.io-index` and translate a URL that matches the current index to be `null`. > * `cargo metadata` includes some extra fields, such as `source` and `path`. > * The index includes additional fields such as `yanked`, `cksum`, and `v`. [renamed]: specifying-dependencies.md#renaming-dependencies-in-cargotoml [Publish API]: registry-web-api.md#publish [`cargo metadata`]: ../commands/cargo-metadata.md ## Index Protocols Cargo supports two remote registry protocols: `git` and `sparse`. The `git` protocol stores index files in a git repository and the `sparse` protocol fetches individual files over HTTP. ### Git Protocol The git protocol has no protocol prefix in the index url. For example the git index URL for [crates.io] is `https://github.com/rust-lang/crates.io-index`. Cargo caches the git repository on disk so that it can efficiently incrementally fetch updates. ### Sparse Protocol The sparse protocol uses the `sparse+` protocol prefix in the registry URL. For example, the sparse index URL for [crates.io] is `sparse+https://index.crates.io/`. The sparse protocol downloads each index file using an individual HTTP request. Since this results in a large number of small HTTP requests, performance is significantly improved with a server that supports pipelining and HTTP/2. #### Sparse authentication Cargo will attempt to fetch the `config.json` file before fetching any other files. If the server responds with an HTTP 401, then Cargo will assume that the registry requires authentication and re-attempt the request for `config.json` with the authentication token included. On authentication failure (or a missing authentication token) the server may include a `www-authenticate` header with a `Cargo login_url=""` challenge to indicate where the user can go to get a token. Registries that require authentication must set `auth-required: true` in `config.json`. #### Caching Cargo caches the crate metadata files, and captures the `ETag` or `Last-Modified` HTTP header from the server for each entry. When refreshing crate metadata, Cargo sends the `If-None-Match` or `If-Modified-Since` header to allow the server to respond with HTTP 304 "Not Modified" if the local cache is valid, saving time and bandwidth. If both `ETag` and `Last-Modified` headers are present, Cargo uses the `ETag` only. #### Cache Invalidation If a registry is using some kind of CDN or proxy which caches access to the index files, then it is recommended that registries implement some form of cache invalidation when the files are updated. If these caches are not updated, then users may not be able to access new crates until the cache is cleared. #### Nonexistent Crates For crates that do not exist, the registry should respond with a 404 "Not Found", 410 "Gone" or 451 "Unavailable For Legal Reasons" code. #### Sparse Limitations Since the URL of the registry is stored in the lockfile, it's not recommended to offer a registry with both protocols. Discussion about a transition plan is ongoing in issue [#10964]. The [crates.io] registry is an exception, since Cargo internally substitutes the equivalent git URL when the sparse protocol is used. If a registry does offer both protocols, it's currently recommended to choose one protocol as the canonical protocol and use [source replacement] for the other protocol. [`cargo publish`]: ../commands/cargo-publish.md [alphanumeric]: ../../std/primitive.char.html#method.is_alphanumeric [crates.io]: https://crates.io/ [source replacement]: ../reference/source-replacement.md [#10964]: https://github.com/rust-lang/cargo/issues/10964 cargo-0.91.0/src/doc/src/reference/registry-web-api.md000064400000000000000000000267641046102023000206220ustar 00000000000000# Web API A registry may host a web API at the location defined in `config.json` to support any of the actions listed below. Cargo includes the `Authorization` header for requests that require authentication. The header value is the API token. The server should respond with a 403 response code if the token is not valid. Users are expected to visit the registry's website to obtain a token, and Cargo can store the token using the [`cargo login`] command, or by passing the token on the command-line. Responses use a 2xx response code for success. Errors should use an appropriate response code, such as 404. Failure responses should have a JSON object with the following structure: ```javascript { // Array of errors to display to the user. "errors": [ { // The error message as a string. "detail": "error message text" } ] } ``` If the response has this structure Cargo will display the detailed message to the user, even if the response code is 200. If the response code indicates an error and the content does not have this structure, Cargo will display to the user a message intended to help debugging the server error. A server returning an `errors` object allows a registry to provide a more detailed or user-centric error message. For backwards compatibility, servers should ignore any unexpected query parameters or JSON fields. If a JSON field is missing, it should be assumed to be null. The endpoints are versioned with the `v1` component of the path, and Cargo is responsible for handling backwards compatibility fallbacks should any be required in the future. Cargo sets the following headers for all requests: - `Content-Type`: `application/json` (for requests with a body payload) - `Accept`: `application/json` - `User-Agent`: The Cargo version such as `cargo/1.32.0 (8610973aa 2019-01-02)`. This may be modified by the user in a configuration value. Added in 1.29. ## Publish - Endpoint: `/api/v1/crates/new` - Method: PUT - Authorization: Included The publish endpoint is used to publish a new version of a crate. The server should validate the crate, make it available for download, and add it to the index. It is not required for the index to be updated before the successful response is sent. After a successful response, Cargo will poll the index for a short period of time to identify that the new crate has been added. If the crate does not appear in the index after a short period of time, then Cargo will display a warning letting the user know that the new crate is not yet available. The body of the data sent by Cargo is: - 32-bit unsigned little-endian integer of the length of JSON data. - Metadata of the package as a JSON object. - 32-bit unsigned little-endian integer of the length of the `.crate` file. - The `.crate` file. The following is a commented example of the JSON object. Some notes of some restrictions imposed by [crates.io] are included only to illustrate some suggestions on types of validation that may be done, and should not be considered as an exhaustive list of restrictions [crates.io] imposes. ```javascript { // The name of the package. "name": "foo", // The version of the package being published. "vers": "0.1.0", // Array of direct dependencies of the package. "deps": [ { // Name of the dependency. // If the dependency is renamed from the original package name, // this is the original name. The new package name is stored in // the `explicit_name_in_toml` field. "name": "rand", // The semver requirement for this dependency. "version_req": "^0.6", // Array of features (as strings) enabled for this dependency. "features": ["i128_support"], // Boolean of whether or not this is an optional dependency. "optional": false, // Boolean of whether or not default features are enabled. "default_features": true, // The target platform for the dependency. // null if not a target dependency. // Otherwise, a string such as "cfg(windows)". "target": null, // The dependency kind. // "dev", "build", or "normal". "kind": "normal", // The URL of the index of the registry where this dependency is // from as a string. If not specified or null, it is assumed the // dependency is in the current registry. "registry": null, // If the dependency is renamed, this is a string of the new // package name. If not specified or null, this dependency is not // renamed. "explicit_name_in_toml": null, } ], // Set of features defined for the package. // Each feature maps to an array of features or dependencies it enables. // Cargo does not impose limitations on feature names, but crates.io // requires alphanumeric ASCII, `_` or `-` characters. "features": { "extras": ["rand/simd_support"] }, // List of strings of the authors. // May be empty. "authors": ["Alice "], // Description field from the manifest. // May be null. crates.io requires at least some content. "description": null, // String of the URL to the website for this package's documentation. // May be null. "documentation": null, // String of the URL to the website for this package's home page. // May be null. "homepage": null, // String of the content of the README file. // May be null. "readme": null, // String of a relative path to a README file in the crate. // May be null. "readme_file": null, // Array of strings of keywords for the package. "keywords": [], // Array of strings of categories for the package. "categories": [], // String of the license for the package. // May be null. crates.io requires either `license` or `license_file` to be set. "license": null, // String of a relative path to a license file in the crate. // May be null. "license_file": null, // String of the URL to the website for the source repository of this package. // May be null. "repository": null, // Optional object of "status" badges. Each value is an object of // arbitrary string to string mappings. // crates.io has special interpretation of the format of the badges. "badges": { "travis-ci": { "branch": "master", "repository": "rust-lang/cargo" } }, // The `links` string value from the package's manifest, or null if not // specified. This field is optional and defaults to null. "links": null, // The minimal supported Rust version (optional) // This must be a valid version requirement without an operator (e.g. no `=`) "rust_version": null } ``` A successful response includes the JSON object: ```javascript { // Optional object of warnings to display to the user. "warnings": { // Array of strings of categories that are invalid and ignored. "invalid_categories": [], // Array of strings of badge names that are invalid and ignored. "invalid_badges": [], // Array of strings of arbitrary warnings to display to the user. "other": [] } } ``` ## Yank - Endpoint: `/api/v1/crates/{crate_name}/{version}/yank` - Method: DELETE - Authorization: Included The yank endpoint will set the `yank` field of the given version of a crate to `true` in the index. A successful response includes the JSON object: ```javascript { // Indicates the yank succeeded, always true. "ok": true, } ``` ## Unyank - Endpoint: `/api/v1/crates/{crate_name}/{version}/unyank` - Method: PUT - Authorization: Included The unyank endpoint will set the `yank` field of the given version of a crate to `false` in the index. A successful response includes the JSON object: ```javascript { // Indicates the unyank succeeded, always true. "ok": true, } ``` ## Owners Cargo does not have an inherent notion of users and owners, but it does provide the `owner` command to assist managing who has authorization to control a crate. It is up to the registry to decide exactly how users and owners are handled. See the [publishing documentation] for a description of how [crates.io] handles owners via GitHub users and teams. ### Owners: List - Endpoint: `/api/v1/crates/{crate_name}/owners` - Method: GET - Authorization: Included The owners endpoint returns a list of owners of the crate. A successful response includes the JSON object: ```javascript { // Array of owners of the crate. "users": [ { // Unique unsigned 32-bit integer of the owner. "id": 70, // The unique username of the owner. "login": "github:rust-lang:core", // Name of the owner. // This is optional and may be null. "name": "Core", } ] } ``` ### Owners: Add - Endpoint: `/api/v1/crates/{crate_name}/owners` - Method: PUT - Authorization: Included A PUT request will send a request to the registry to add a new owner to a crate. It is up to the registry how to handle the request. For example, [crates.io] sends an invite to the user that they must accept before being added. The request should include the following JSON object: ```javascript { // Array of `login` strings of owners to add. "users": ["login_name"] } ``` A successful response includes the JSON object: ```javascript { // Indicates the add succeeded, always true. "ok": true, // A string to be displayed to the user. "msg": "user ehuss has been invited to be an owner of crate cargo" } ``` ### Owners: Remove - Endpoint: `/api/v1/crates/{crate_name}/owners` - Method: DELETE - Authorization: Included A DELETE request will remove an owner from a crate. The request should include the following JSON object: ```javascript { // Array of `login` strings of owners to remove. "users": ["login_name"] } ``` A successful response includes the JSON object: ```javascript { // Indicates the remove succeeded, always true. "ok": true // A string to be displayed to the user. Currently ignored by cargo. "msg": "owners successfully removed", } ``` ## Search - Endpoint: `/api/v1/crates` - Method: GET - Query Parameters: - `q`: The search query string. - `per_page`: Number of results, default 10, max 100. The search request will perform a search for crates, using criteria defined on the server. A successful response includes the JSON object: ```javascript { // Array of results. "crates": [ { // Name of the crate. "name": "rand", // The highest version available. "max_version": "0.6.1", // Textual description of the crate. "description": "Random number generators and other randomness functionality.\n", } ], "meta": { // Total number of results available on the server. "total": 119 } } ``` ## Login - Endpoint: `/me` The "login" endpoint is not an actual API request. It exists solely for the [`cargo login`] command to display a URL to instruct a user to visit in a web browser to log in and retrieve an API token. [`cargo login`]: ../commands/cargo-login.md [`cargo package`]: ../commands/cargo-package.md [`cargo publish`]: ../commands/cargo-publish.md [alphanumeric]: ../../std/primitive.char.html#method.is_alphanumeric [config]: config.md [crates.io]: https://crates.io/ [publishing documentation]: publishing.md#cargo-owner cargo-0.91.0/src/doc/src/reference/resolver.md000064400000000000000000000733101046102023000172560ustar 00000000000000# Dependency Resolution One of Cargo's primary tasks is to determine the versions of dependencies to use based on the version requirements specified in each package. This process is called "dependency resolution" and is performed by the "resolver". The result of the resolution is stored in the `Cargo.lock` file which "locks" the dependencies to specific versions, and keeps them fixed over time. The [`cargo tree`] command can be used to visualize the result of the resolver. [dependency specifications]: specifying-dependencies.md [dependency specification]: specifying-dependencies.md [`cargo tree`]: ../commands/cargo-tree.md ## Constraints and Heuristics In many cases there is no single "best" dependency resolution. The resolver operates under various constraints and heuristics to find a generally applicable resolution. To understand how these interact, it is helpful to have a coarse understanding of how dependency resolution works. This pseudo-code approximates what Cargo's resolver does: ```rust pub fn resolve(workspace: &[Package], policy: Policy) -> Option { let dep_queue = Queue::new(workspace); let resolved = ResolveGraph::new(); resolve_next(pkq_queue, resolved, policy) } fn resolve_next(dep_queue: Queue, resolved: ResolveGraph, policy: Policy) -> Option { let Some(dep_spec) = policy.pick_next_dep(dep_queue) else { // Done return Some(resolved); }; if let Some(resolved) = policy.try_unify_version(dep_spec, resolved.clone()) { return Some(resolved); } let dep_versions = dep_spec.lookup_versions()?; let mut dep_versions = policy.filter_versions(dep_spec, dep_versions); while let Some(dep_version) = policy.pick_next_version(&mut dep_versions) { if policy.needs_version_unification(dep_version, &resolved) { continue; } let mut dep_queue = dep_queue.clone(); dep_queue.enqueue(dep_version.dependencies); let mut resolved = resolved.clone(); resolved.register(dep_version); if let Some(resolved) = resolve_next(dep_queue, resolved) { return Some(resolved); } } // No valid solution found, backtrack and `pick_next_version` None } ``` Key steps: - Walking dependencies (`pick_next_dep`): The order dependencies are walked can affect how related version requirements for the same dependency get resolved, see unifying versions, and how much the resolver backtracks, affecting resolver performance, - Unifying versions (`try_unify_version`, `needs_version_unification`): Cargo reuses versions where possible to reduce build times and allow types from common dependencies to be passed between APIs. If multiple versions would have been unified if it wasn't for conflicts in their [dependency specifications], Cargo will backtrack, erroring if no solution is found, rather than selecting multiple versions. A [dependency specification] or Cargo may decide that a version is undesirable, preferring to backtrack or error rather than use it. - Preferring versions (`pick_next_version`): Cargo may decide that it should prefer a specific version, falling back to the next version when backtracking. ### Version numbers Generally, Cargo prefers the highest version currently available. For example, if you had a package in the resolve graph with: ```toml [dependencies] bitflags = "*" ``` If at the time the `Cargo.lock` file is generated, the greatest version of `bitflags` is `1.2.1`, then the package will use `1.2.1`. For an example of a possible exception, see [Rust version](#rust-version). ### Version requirements Package specify what versions they support, rejecting all others, through [version requirements]. For example, if you had a package in the resolve graph with: ```toml [dependencies] bitflags = "1.0" # meaning `>=1.0.0,<2.0.0` ``` If at the time the `Cargo.lock` file is generated, the greatest version of `bitflags` is `1.2.1`, then the package will use `1.2.1` because it is the greatest within the compatibility range. If `2.0.0` is published, it will still use `1.2.1` because `2.0.0` is considered incompatible. [version requirements]: specifying-dependencies.md#version-requirement-syntax ### SemVer compatibility Cargo assumes packages follow [SemVer] and will unify dependency versions if they are [SemVer] compatible according to the [Caret version requirements]. If two compatible versions cannot be unified because of conflicting version requirements, Cargo will error. See the [SemVer Compatibility] chapter for guidance on what is considered a "compatible" change. Examples: The following two packages will have their dependencies on `bitflags` unified because any version picked will be compatible with each other. ```toml # Package A [dependencies] bitflags = "1.0" # meaning `>=1.0.0,<2.0.0` # Package B [dependencies] bitflags = "1.1" # meaning `>=1.1.0,<2.0.0` ``` The following packages will error because the version requirements conflict, selecting two distinct compatible versions. ```toml # Package A [dependencies] log = "=0.4.11" # Package B [dependencies] log = "=0.4.8" ``` The following two packages will not have their dependencies on `rand` unified because only incompatible versions are available for each. Instead, two different versions (e.g. 0.6.5 and 0.7.3) will be resolved and built. This can lead to potential problems, see the [Version-incompatibility hazards] section for more details. ```toml # Package A [dependencies] rand = "0.7" # meaning `>=0.7.0,<0.8.0` # Package B [dependencies] rand = "0.6" # meaning `>=0.6.0,<0.7.0` ``` Generally, the following two packages will not have their dependencies unified because incompatible versions are available that satisfy the version requirements: Instead, two different versions (e.g. 0.6.5 and 0.7.3) will be resolved and built. The application of other constraints or heuristics may cause these to be unified, picking one version (e.g. 0.6.5). ```toml # Package A [dependencies] rand = ">=0.6,<0.8.0" # Package B [dependencies] rand = "0.6" # meaning `>=0.6.0,<0.7.0` ``` [SemVer]: https://semver.org/ [SemVer Compatibility]: semver.md [Caret version requirements]: specifying-dependencies.md#default-requirements [Version-incompatibility hazards]: #version-incompatibility-hazards #### Version-incompatibility hazards When multiple versions of a crate appear in the resolve graph, this can cause problems when types from those crates are exposed by the crates using them. This is because the types and items are considered different by the Rust compiler, even if they have the same name. Libraries should take care when publishing a SemVer-incompatible version (for example, publishing `2.0.0` after `1.0.0` has been in use), particularly for libraries that are widely used. The "[semver trick]" is a workaround for this problem of publishing a breaking change while retaining compatibility with older versions. The linked page goes into detail about what the problem is and how to address it. In short, when a library wants to publish a SemVer-breaking release, publish the new release, and also publish a point release of the previous version that reexports the types from the newer version. These incompatibilities usually manifest as a compile-time error, but sometimes they will only appear as a runtime misbehavior. For example, let's say there is a common library named `foo` that ends up appearing with both version `1.0.0` and `2.0.0` in the resolve graph. If [`downcast_ref`] is used on an object created by a library using version `1.0.0`, and the code calling `downcast_ref` is downcasting to a type from version `2.0.0`, the downcast will fail at runtime. It is important to make sure that if you have multiple versions of a library that you are properly using them, especially if it is ever possible for the types from different versions to be used together. The [`cargo tree -d`][`cargo tree`] command can be used to identify duplicate versions and where they come from. Similarly, it is important to consider the impact on the ecosystem if you publish a SemVer-incompatible version of a popular library. [semver trick]: https://github.com/dtolnay/semver-trick [`downcast_ref`]: ../../std/any/trait.Any.html#method.downcast_ref ### Rust version To support developing software with a minimum supported [Rust version], the resolver can take into account a dependency version's compatibility with your Rust version. This is controlled by the config field [`resolver.incompatible-rust-versions`]. With the `fallback` setting, the resolver will prefer packages with a Rust version that is less than or equal to your own Rust version. For example, you are using Rust 1.85 to develop the following package: ```toml [package] name = "my-cli" rust-version = "1.62" [dependencies] clap = "4.0" # resolves to 4.0.32 ``` The resolver would pick version 4.0.32 because it has a Rust version of 1.60.0. - 4.0.0 is not picked because it is a [lower version number](#version-numbers) despite it also having a Rust version of 1.60.0. - 4.5.20 is not picked because it is incompatible with `my-cli`'s Rust version of 1.62 despite having a much [higher version](#version-numbers) and it has a Rust version of 1.74.0 which is compatible with your 1.85 toolchain. If a version requirement does not include a Rust version compatible dependency version, the resolver won't error but will instead pick a version, even if its potentially suboptimal. For example, you change the dependency on `clap`: ```toml [package] name = "my-cli" rust-version = "1.62" [dependencies] clap = "4.2" # resolves to 4.5.20 ``` No version of `clap` matches that [version requirement](#version-requirements) that is compatible with Rust version 1.62. The resolver will then pick an incompatible version, like 4.5.20 despite it having a Rust version of 1.74. When the resolver selects a dependency version of a package, it does not know all the workspace members that will eventually have a transitive dependency on that version and so it cannot take into account only the Rust versions relevant for that dependency. The resolver has heuristics to find a "good enough" solution when workspace members have different Rust versions. This applies even for packages in a workspace without a Rust version. When a workspace has members with different Rust versions, the resolver may pick a lower dependency version than necessary. For example, you have the following workspace members: ```toml [package] name = "a" rust-version = "1.62" [package] name = "b" [dependencies] clap = "4.2" # resolves to 4.5.20 ``` Though package `b` does not have a Rust version and could use a higher version like 4.5.20, 4.0.32 will be selected because of package `a`'s Rust version of 1.62. Or the resolver may pick too high of a version. For example, you have the following workspace members: ```toml [package] name = "a" rust-version = "1.62" [dependencies] clap = "4.2" # resolves to 4.5.20 [package] name = "b" [dependencies] clap = "4.5" # resolves to 4.5.20 ``` Though each package has a version requirement for `clap` that would meet its own Rust version, because of [version unification](#version-numbers), the resolver will need to pick one version that works in both cases and that would be a version like 4.5.20. [Rust version]: rust-version.md [`resolver.incompatible-rust-versions`]: config.md#resolverincompatible-rust-versions ### Features For the purpose of generating `Cargo.lock`, the resolver builds the dependency graph as-if all [features] of all [workspace] members are enabled. This ensures that any optional dependencies are available and properly resolved with the rest of the graph when features are added or removed with the [`--features` command-line flag](features.md#command-line-feature-options). The resolver runs a second time to determine the actual features used when *compiling* a crate, based on the features selected on the command-line. Dependencies are resolved with the union of all features enabled on them. For example, if one package depends on the [`im`] package with the [`serde` dependency] enabled and another package depends on it with the [`rayon` dependency] enabled, then `im` will be built with both features enabled, and the `serde` and `rayon` crates will be included in the resolve graph. If no packages depend on `im` with those features, then those optional dependencies will be ignored, and they will not affect resolution. When building multiple packages in a workspace (such as with `--workspace` or multiple `-p` flags), the features of the dependencies of all of those packages are unified. If you have a circumstance where you want to avoid that unification for different workspace members, you will need to build them via separate `cargo` invocations. The resolver will skip over versions of packages that are missing required features. For example, if a package depends on version `^1` of [`regex`] with the [`perf` feature], then the oldest version it can select is `1.3.0`, because versions prior to that did not contain the `perf` feature. Similarly, if a feature is removed from a new release, then packages that require that feature will be stuck on the older releases that contain that feature. It is discouraged to remove features in a SemVer-compatible release. Beware that optional dependencies also define an implicit feature, so removing an optional dependency or making it non-optional can cause problems, see [removing an optional dependency]. [`im`]: https://crates.io/crates/im [`perf` feature]: https://github.com/rust-lang/regex/blob/1.3.0/Cargo.toml#L56 [`rayon` dependency]: https://github.com/bodil/im-rs/blob/v15.0.0/Cargo.toml#L47 [`regex`]: https://crates.io/crates/regex [`serde` dependency]: https://github.com/bodil/im-rs/blob/v15.0.0/Cargo.toml#L46 [features]: features.md [removing an optional dependency]: semver.md#cargo-remove-opt-dep [workspace]: workspaces.md #### Feature resolver version 2 When `resolver = "2"` is specified in `Cargo.toml` (see [resolver versions](#resolver-versions) below), a different feature resolver is used which uses a different algorithm for unifying features. The version `"1"` resolver will unify features for a package no matter where it is specified. The version `"2"` resolver will avoid unifying features in the following situations: * Features for target-specific dependencies are not enabled if the target is not currently being built. For example: ```toml [dependencies.common] version = "1.0" features = ["f1"] [target.'cfg(windows)'.dependencies.common] version = "1.0" features = ["f2"] ``` When building this example for a non-Windows platform, the `f2` feature will *not* be enabled. * Features enabled on [build-dependencies] or proc-macros will not be unified when those same dependencies are used as a normal dependency. For example: ```toml [dependencies] log = "0.4" [build-dependencies] log = {version = "0.4", features=['std']} ``` When building the build script, the `log` crate will be built with the `std` feature. When building the library of your package, it will not enable the feature. * Features enabled on [dev-dependencies] will not be unified when those same dependencies are used as a normal dependency, unless those dev-dependencies are currently being built. For example: ```toml [dependencies] serde = {version = "1.0", default-features = false} [dev-dependencies] serde = {version = "1.0", features = ["std"]} ``` In this example, the library will normally link against `serde` without the `std` feature. However, when built as a test or example, it will include the `std` feature. For example, `cargo test` or `cargo build --all-targets` will unify these features. Note that dev-dependencies in dependencies are always ignored, this is only relevant for the top-level package or workspace members. [build-dependencies]: specifying-dependencies.md#build-dependencies [dev-dependencies]: specifying-dependencies.md#development-dependencies [resolver-field]: features.md#resolver-versions ### `links` The [`links` field] is used to ensure only one copy of a native library is linked into a binary. The resolver will attempt to find a graph where there is only one instance of each `links` name. If it is unable to find a graph that satisfies that constraint, it will return an error. For example, it is an error if one package depends on [`libgit2-sys`] version `0.11` and another depends on `0.12`, because Cargo is unable to unify those, but they both link to the `git2` native library. Due to this requirement, it is encouraged to be very careful when making SemVer-incompatible releases with the `links` field if your library is in common use. [`links` field]: manifest.md#the-links-field [`libgit2-sys`]: https://crates.io/crates/libgit2-sys ### Yanked versions [Yanked releases][yank] are those that are marked that they should not be used. When the resolver is building the graph, it will ignore all yanked releases unless they already exist in the `Cargo.lock` file or are explicitly requested by the [`--precise`] flag of `cargo update` (nightly only). [yank]: publishing.md#cargo-yank [`--precise`]: ../commands/cargo-update.md#option-cargo-update---precise ## Dependency updates Dependency resolution is automatically performed by all Cargo commands that need to know about the dependency graph. For example, [`cargo build`] will run the resolver to discover all the dependencies to build. After the first time it runs, the result is stored in the `Cargo.lock` file. Subsequent commands will run the resolver, keeping dependencies locked to the versions in `Cargo.lock` *if it can*. If the dependency list in `Cargo.toml` has been modified, for example changing the version of a dependency from `1.0` to `2.0`, then the resolver will select a new version for that dependency that matches the new requirements. If that new dependency introduces new requirements, those new requirements may also trigger additional updates. The `Cargo.lock` file will be updated with the new result. The `--locked` or `--frozen` flags can be used to change this behavior to prevent automatic updates when requirements change, and return an error instead. [`cargo update`] can be used to update the entries in `Cargo.lock` when new versions are published. Without any options, it will attempt to update all packages in the lock file. The `-p` flag can be used to target the update for a specific package, and other flags such as `--recursive` or `--precise` can be used to control how versions are selected. [`cargo build`]: ../commands/cargo-build.md [`cargo update`]: ../commands/cargo-update.md ## Overrides Cargo has several mechanisms to override dependencies within the graph. The [Overriding Dependencies] chapter goes into detail on how to use overrides. The overrides appear as an overlay to a registry, replacing the patched version with the new entry. Otherwise, resolution is performed like normal. [Overriding Dependencies]: overriding-dependencies.md ## Dependency kinds There are three kinds of dependencies in a package: normal, [build], and [dev][dev-dependencies]. For the most part these are all treated the same from the perspective of the resolver. One difference is that dev-dependencies for non-workspace members are always ignored, and do not influence resolution. [Platform-specific dependencies] with the `[target]` table are resolved as-if all platforms are enabled. In other words, the resolver ignores the platform or `cfg` expression. [build]: specifying-dependencies.md#build-dependencies [dev-dependencies]: specifying-dependencies.md#development-dependencies [Platform-specific dependencies]: specifying-dependencies.md#platform-specific-dependencies ### dev-dependency cycles Usually the resolver does not allow cycles in the graph, but it does allow them for [dev-dependencies]. For example, project "foo" has a dev-dependency on "bar", which has a normal dependency on "foo" (usually as a "path" dependency). This is allowed because there isn't really a cycle from the perspective of the build artifacts. In this example, the "foo" library is built (which does not need "bar" because "bar" is only used for tests), and then "bar" can be built depending on "foo", then the "foo" tests can be built linking to "bar". Beware that this can lead to confusing errors. In the case of building library unit tests, there are actually two copies of the library linked into the final test binary: the one that was linked with "bar", and the one built that contains the unit tests. Similar to the issues highlighted in the [Version-incompatibility hazards] section, the types between the two are not compatible. Be careful when exposing types of "foo" from "bar" in this situation, since the "foo" unit tests won't treat them the same as the local types. If possible, try to split your package into multiple packages and restructure it so that it remains strictly acyclic. ## Resolver versions Different resolver behavior can be specified through the resolver version in `Cargo.toml` like this: ```toml [package] name = "my-package" version = "1.0.0" resolver = "2" ``` - `"1"` (default) - `"2"` ([`edition = "2021"`](manifest.md#the-edition-field) default): Introduces changes in [feature unification](#features). See the [features chapter][features-2] for more details. - `"3"` ([`edition = "2024"`](manifest.md#the-edition-field) default, requires Rust 1.84+): Change the default for [`resolver.incompatible-rust-versions`] from `allow` to `fallback` The resolver is a global option that affects the entire workspace. The `resolver` version in dependencies is ignored, only the value in the top-level package will be used. If using a [virtual workspace], the version should be specified in the `[workspace]` table, for example: ```toml [workspace] members = ["member1", "member2"] resolver = "2" ``` > **MSRV:** Requires 1.51+ [virtual workspace]: workspaces.md#virtual-workspace [features-2]: features.md#feature-resolver-version-2 ## Recommendations The following are some recommendations for setting the version within your package, and for specifying dependency requirements. These are general guidelines that should apply to common situations, but of course some situations may require specifying unusual requirements. * Follow the [SemVer guidelines] when deciding how to update your version number, and whether or not you will need to make a SemVer-incompatible version change. * Use caret requirements for dependencies, such as `"1.2.3"`, for most situations. This ensures that the resolver can be maximally flexible in choosing a version while maintaining build compatibility. * Specify all three components with the version you are currently using. This helps set the minimum version that will be used, and ensures that other users won't end up with an older version of the dependency that might be missing something that your package requires. * Avoid `*` requirements, as they are not allowed on [crates.io], and they can pull in SemVer-breaking changes during a normal `cargo update`. * Avoid overly broad version requirements. For example, `>=2.0.0` can pull in any SemVer-incompatible version, like version `5.0.0`, which can result in broken builds in the future. * Avoid overly narrow version requirements if possible. For example, if you specify a tilde requirement like `bar="~1.3"`, and another package specifies a requirement of `bar="1.4"`, this will fail to resolve, even though minor releases should be compatible. * Try to keep the dependency versions up-to-date with the actual minimum versions that your library requires. For example, if you have a requirement of `bar="1.0.12"`, and then in a future release you start using new features added in the `1.1.0` release of "bar", update your dependency requirement to `bar="1.1.0"`. If you fail to do this, it may not be immediately obvious because Cargo can opportunistically choose the newest version when you run a blanket `cargo update`. However, if another user depends on your library, and runs `cargo update your-library`, it will *not* automatically update "bar" if it is locked in their `Cargo.lock`. It will only update "bar" in that situation if the dependency declaration is also updated. Failure to do so can cause confusing build errors for the user using `cargo update your-library`. * If two packages are tightly coupled, then an `=` dependency requirement may help ensure that they stay in sync. For example, a library with a companion proc-macro library will sometimes make assumptions between the two libraries that won't work well if the two are out of sync (and it is never expected to use the two libraries independently). The parent library can use an `=` requirement on the proc-macro, and re-export the macros for easy access. * `0.0.x` versions can be used for packages that are permanently unstable. In general, the stricter you make the dependency requirements, the more likely it will be for the resolver to fail. Conversely, if you use requirements that are too loose, it may be possible for new versions to be published that will break the build. [SemVer guidelines]: semver.md [crates.io]: https://crates.io/ ## Troubleshooting The following illustrates some problems you may experience, and some possible solutions. ### Why was a dependency included? Say you see dependency `rand` in the `cargo check` output but don't think it's needed and want to understand why it's being pulled in. You can run ```console $ cargo tree --workspace --target all --all-features --invert rand rand v0.8.5 └── ... rand v0.8.5 └── ... ``` ### Why was that feature on this dependency enabled? You might identify that it was an activated feature that caused `rand` to show up. **To figure out which package activated the feature, you can add the `--edges features`** ```console $ cargo tree --workspace --target all --all-features --edges features --invert rand rand v0.8.5 └── ... rand v0.8.5 └── ... ``` ### Unexpected dependency duplication You see multiple instances of `rand` when you run ```console $ cargo tree --workspace --target all --all-features --duplicates rand v0.7.3 └── ... rand v0.8.5 └── ... ``` The resolver algorithm has converged on a solution that includes two copies of a dependency when one would suffice. For example: ```toml # Package A [dependencies] rand = "0.7" # Package B [dependencies] rand = ">=0.6" # note: open requirements such as this are discouraged ``` In this example, Cargo may build two copies of the `rand` crate, even though a single copy at version `0.7.3` would meet all requirements. This is because the resolver's algorithm favors building the latest available version of `rand` for Package B, which is `0.8.5` at the time of this writing, and that is incompatible with Package A's specification. The resolver's algorithm does not currently attempt to "deduplicate" in this situation. The use of open-ended version requirements like `>=0.6` is discouraged in Cargo. But, if you run into this situation, the [`cargo update`] command with the `--precise` flag can be used to manually remove such duplications. [`cargo update`]: ../commands/cargo-update.md ### Why wasn't a newer version selected? Say you noticed that the latest version of a dependency wasn't selected when you ran: ```console $ cargo update ``` You can enable some extra logging to see why this happened: ```console $ env CARGO_LOG=cargo::core::resolver=trace cargo update ``` **Note:** Cargo log targets and levels may change over time. ### SemVer-breaking patch release breaks the build Sometimes a project may inadvertently publish a point release with a SemVer-breaking change. When users update with `cargo update`, they will pick up this new release, and then their build may break. In this situation, it is recommended that the project should [yank] the release, and either remove the SemVer-breaking change, or publish it as a new SemVer-major version increase. If the change happened in a third-party project, if possible try to (politely!) work with the project to resolve the issue. While waiting for the release to be yanked, some workarounds depend on the circumstances: * If your project is the end product (such as a binary executable), just avoid updating the offending package in `Cargo.lock`. This can be done with the `--precise` flag in [`cargo update`]. * If you publish a binary on [crates.io], then you can temporarily add an `=` requirement to force the dependency to a specific good version. * Binary projects can alternatively recommend users to use the `--locked` flag with [`cargo install`] to use the original `Cargo.lock` that contains the known good version. * Libraries may also consider publishing a temporary new release with stricter requirements that avoid the troublesome dependency. You may want to consider using range requirements (instead of `=`) to avoid overly-strict requirements that may conflict with other packages using the same dependency. Once the problem has been resolved, you can publish another point release that relaxes the dependency back to a caret requirement. * If it looks like the third-party project is unable or unwilling to yank the release, then one option is to update your code to be compatible with the changes, and update the dependency requirement to set the minimum version to the new release. You will also need to consider if this is a SemVer-breaking change of your own library, for example if it exposes types from the dependency. [`cargo install`]: ../commands/cargo-install.md cargo-0.91.0/src/doc/src/reference/running-a-registry.md000064400000000000000000000016051046102023000211570ustar 00000000000000# Running a Registry A minimal registry can be implemented by having a git repository that contains an index, and a server that contains the compressed `.crate` files created by [`cargo package`]. Users won't be able to use Cargo to publish to it, but this may be sufficient for closed environments. The index format is described in [Registry Index]. A full-featured registry that supports publishing will additionally need to have a web API service that conforms to the API used by Cargo. The web API is described in [Registry Web API]. Commercial and community projects are available for building and running a registry. See for a list of what is available. [Registry Web API]: registry-web-api.md [Registry Index]: registry-index.md [`cargo publish`]: ../commands/cargo-publish.md [`cargo package`]: ../commands/cargo-package.md cargo-0.91.0/src/doc/src/reference/rust-version.md000064400000000000000000000212631046102023000200750ustar 00000000000000# Rust Version The `rust-version` field is an optional key that tells cargo what version of the Rust toolchain you support for your package. ```toml [package] # ... rust-version = "1.56" ``` The Rust version must be a bare version number with at least one component; it cannot include semver operators or pre-release identifiers. Compiler pre-release identifiers such as -nightly will be ignored while checking the Rust version. > **MSRV:** Respected as of 1.56 ## Uses **Diagnostics:** When your package is compiled on an unsupported toolchain, Cargo will report that as an error to the user. This makes the support expectations clear and avoids reporting a less direct diagnostic like invalid syntax or missing functionality in the standard library. This affects all [Cargo targets](cargo-targets.md) in the package, including binaries, examples, test suites, benchmarks, etc. A user can opt-in to an unsupported build of a package with the `--ignore-rust-version` flag. **Development aid:** `cargo add` will auto-select the dependency's version requirement to be the latest version compatible with your `rust-version`. If that isn't the latest version, `cargo add` will inform users so they can make the choice on whether to keep it or update your `rust-version`. The [resolver](resolver.md#rust-version) may take Rust version into account when picking dependencies. Other tools may also take advantage of it, like `cargo clippy`'s [`incompatible_msrv` lint](https://rust-lang.github.io/rust-clippy/stable/index.html#/incompatible_msrv). > **Note:** The `rust-version` may be ignored using the `--ignore-rust-version` option. ## Support Expectations These are general expectations; some packages may document when they do not follow these. **Complete:** All functionality, including binaries and API, are available on the supported Rust versions under every [feature](features.md). **Verified:** A package's functionality is verified on its supported Rust versions, including automated testing. See also our [Rust version CI guide](../guide/continuous-integration.md#verifying-rust-version). **Patchable:** When licenses allow it, users can [override their local dependency](overriding-dependencies.md) with a fork of your package. In this situation, Cargo may load the entire workspace for the patched dependency which should work on the supported Rust versions, even if other packages in the workspace have different supported Rust versions. **Dependency Support:** In support of the above, it is expected that each dependency's version-requirement supports at least one version compatible with your `rust-version`. However, it is **not** expected that the dependency specification excludes versions incompatible with your `rust-version`. In fact, supporting both allows you to balance the needs of users that support older Rust versions with those that don't. ## Setting and Updating Rust Version What Rust versions to support is a trade off between - Costs for the maintainer in not using newer features of the Rust toolchain or their dependencies - Costs to users who would benefit from a package using newer features of a toolchain, e.g. reducing build times by migrating to a feature in the standard library from a polyfill - Availability of a package to users supporting older Rust versions > **Note:** [Changing `rust-version`](semver.md#env-new-rust) is assumed to be a minor incompatibility > **Recommendation:** Choose a policy for what Rust versions to support and when that is changed so users can compare it with their own policy and, > if it isn't compatible, > decide whether the loss of general improvements or the risk of a blocking bug that won't be fixed is acceptable or not. > > The simplest policy to support is to always use the latest Rust version. > > Depending on your risk profile, the next simplest approach is to continue to support old major or minor versions of your package that support older Rust versions. ### Selecting supported Rust versions Users of your package are most likely to track their supported Rust versions to: - Their Rust toolchain vendor's support policy, e.g. The Rust Project or a Linux distribution - Note: the Rust Project only offers bug fixes and security updates for the latest version. - A fixed schedule for users to re-verify their packages with the new toolchain, e.g. the first release of the year, every 5 releases. In addition, users are unlikely to be using the new Rust version immediately but need time to notice and re-verify or might not be aligned on the exact same schedule.. Example version policies: - "N-2", meaning "latest version with a 2 release grace window for updating" - Every even release with a 2 release grace window for updating - Every version from this calendar year with a one year grace window for updating > **Note:** To find the minimum `rust-version` compatible with your project as-is, you can use third-party tools like [`cargo-msrv`](https://crates.io/crates/cargo-msrv). ### Update timeline When your policy specifies you no longer need to support a Rust version, you can update `rust-version` immediately or when needed. By allowing `rust-version` to drift from your policy, you offer users more of a grace window for upgrading. However, this is too unpredictable to be relied on for aligning with the Rust version users track. The further `rust-version` drifts from your specified policy, the more likely users are to infer a policy you did not intend, leading to frustration at the unmet expectations. When drift is allowed, there is the question of what is "justifiable enough" to drop supported Versions. Each person can come to a reasonably different justification; working through that discussion can be frustrating for the involved parties. This will disempower those who would want to avoid that type of conflict, which is particularly the case for new or casual contributors who either feel that they are not in a position to raise the question or that the conflict may hurt the chance of their change being merged. ### Multiple Policies in a Workspace Cargo allows supporting multiple policies within one workspace. Verifying specific packages under specific Rust versions can get complicated. Tools like [`cargo-hack`](https://crates.io/crates/cargo-hack) can help. For any dependency shared across policies, the lowest common versions must be used as Cargo [unifies SemVer-compatible versions](resolver.md#semver-compatibility), potentially limiting access to features of the shared dependency for the workspace member with the higher `rust-version`. To allow users to patch a dependency on one of your workspace members, every package in the workspace would need to be loadable in the oldest Rust version supported by the workspace. When using [`incompatible-rust-versions = "fallback"`](config.md#resolverincompatible-rust-versions), the Rust version of one package can affect dependency versions selected for another package with a different Rust version. See the [resolver](resolver.md#rust-version) chapter for more details. ### One or More Policies One way to mitigate the downsides of supporting older Rust versions is to apply your policy to older major or minor versions of your package that you continue to support. You likely still need a policy for what Rust versions the development branch support compared to the release branches for those major or minor versions. Only updating the development branch when "needed"' can help reduce the number of supported release branches. There is the question of what can be backported into these release branches. By backporting new functionality between minor versions, the next available version would be missing it which could be considered a breaking change, violating SemVer. Backporting changes also comes with the risk of introducing bugs. Supporting older versions comes at a cost. This cost is dependent on the risk and impact of bugs within the package and what is acceptable for backporting. Creating the release branches on-demand and putting the backport burden on the community are ways to balance this cost. There is not yet a way for dependency management tools to report that a non-latest version is still supported, shifting the responsibility to users to notice this in documentation. For example, a Rust version support policy could look like: - The development branch tracks to the latest stable release from the Rust Project, updated when needed - The minor version will be raised when changing `rust-version` - The project supports every version for this calendar year, with another year grace window - The last minor version that supports a supported Rust version will receive community provided bug fixes - Fixes must be backported to all supported minor releases between the development branch and the needed supported Rust version cargo-0.91.0/src/doc/src/reference/semver.md000064400000000000000000002244721046102023000167250ustar 00000000000000# SemVer Compatibility This chapter provides details on what is conventionally considered a compatible or breaking SemVer change for new releases of a package. See the [SemVer compatibility] section for details on what SemVer is, and how Cargo uses it to ensure compatibility of libraries. These are only *guidelines*, and not necessarily hard-and-fast rules that all projects will obey. The [Change categories] section details how this guide classifies the level and severity of a change. Most of this guide focuses on changes that will cause `cargo` and `rustc` to fail to build something that previously worked. Almost every change carries some risk that it will negatively affect the runtime behavior, and for those cases it is usually a judgment call by the project maintainers whether or not it is a SemVer-incompatible change. [Change categories]: #change-categories [SemVer compatibility]: resolver.md#semver-compatibility ## Change categories All of the policies listed below are categorized by the level of change: * **Major change**: a change that requires a major SemVer bump. * **Minor change**: a change that requires only a minor SemVer bump. * **Possibly-breaking change**: a change that some projects may consider major and others consider minor. The "Possibly-breaking" category covers changes that have the *potential* to break during an update, but may not necessarily cause a breakage. The impact of these changes should be considered carefully. The exact nature will depend on the change and the principles of the project maintainers. Some projects may choose to only bump the patch number on a minor change. It is encouraged to follow the SemVer spec, and only apply bug fixes in patch releases. However, a bug fix may require an API change that is marked as a "minor change", and shouldn't affect compatibility. This guide does not take a stance on how each individual "minor change" should be treated, as the difference between minor and patch changes are conventions that depend on the nature of the change. Some changes are marked as "minor", even though they carry the potential risk of breaking a build. This is for situations where the potential is extremely low, and the potentially breaking code is unlikely to be written in idiomatic Rust, or is specifically discouraged from use. This guide uses the terms "major" and "minor" assuming this relates to a "1.0.0" release or later. Initial development releases starting with "0.y.z" can treat changes in "y" as a major release, and "z" as a minor release. "0.0.z" releases are always major changes. This is because Cargo uses the convention that only changes in the left-most non-zero component are considered incompatible. * API compatibility * Items * [Major: renaming/moving/removing any public items](#item-remove) * [Minor: adding new public items](#item-new) * Types * [Major: Changing the alignment, layout, or size of a well-defined type](#type-layout) * Structs * [Major: adding a private struct field when all current fields are public](#struct-add-private-field-when-public) * [Major: adding a public field when no private field exists](#struct-add-public-field-when-no-private) * [Minor: adding or removing private fields when at least one already exists](#struct-private-fields-with-private) * [Minor: going from a tuple struct with all private fields (with at least one field) to a normal struct, or vice versa](#struct-tuple-normal-with-private) * Enums * [Major: adding new enum variants (without `non_exhaustive`)](#enum-variant-new) * [Major: adding new fields to an enum variant](#enum-fields-new) * Traits * [Major: adding a non-defaulted trait item](#trait-new-item-no-default) * [Major: any change to trait item signatures](#trait-item-signature) * [Possibly-breaking: adding a defaulted trait item](#trait-new-default-item) * [Major: adding a trait item that makes the trait non-object safe](#trait-object-safety) * [Major: adding a type parameter without a default](#trait-new-parameter-no-default) * [Minor: adding a defaulted trait type parameter](#trait-new-parameter-default) * Implementations * [Possibly-breaking change: adding any inherent items](#impl-item-new) * Generics * [Major: tightening generic bounds](#generic-bounds-tighten) * [Minor: loosening generic bounds](#generic-bounds-loosen) * [Minor: adding defaulted type parameters](#generic-new-default) * [Minor: generalizing a type to use generics (with identical types)](#generic-generalize-identical) * [Major: generalizing a type to use generics (with possibly different types)](#generic-generalize-different) * [Minor: changing a generic type to a more generic type](#generic-more-generic) * [Major: capturing more generic parameters in RPIT](#generic-rpit-capture) * Functions * [Major: adding/removing function parameters](#fn-change-arity) * [Possibly-breaking: introducing a new function type parameter](#fn-generic-new) * [Minor: generalizing a function to use generics (supporting original type)](#fn-generalize-compatible) * [Major: generalizing a function to use generics with type mismatch](#fn-generalize-mismatch) * [Minor: making an `unsafe` function safe](#fn-unsafe-safe) * Attributes * [Major: switching from `no_std` support to requiring `std`](#attr-no-std-to-std) * [Major: adding `non_exhaustive` to an existing enum, variant, or struct with no private fields](#attr-adding-non-exhaustive) * Tooling and environment compatibility * [Possibly-breaking: changing the minimum version of Rust required](#env-new-rust) * [Possibly-breaking: changing the platform and environment requirements](#env-change-requirements) * [Minor: introducing new lints](#new-lints) * Cargo * [Minor: adding a new Cargo feature](#cargo-feature-add) * [Major: removing a Cargo feature](#cargo-feature-remove) * [Major: removing a feature from a feature list if that changes functionality or public items](#cargo-feature-remove-another) * [Possibly-breaking: removing an optional dependency](#cargo-remove-opt-dep) * [Minor: changing dependency features](#cargo-change-dep-feature) * [Minor: adding dependencies](#cargo-dep-add) * [Application compatibility](#application-compatibility) ## API compatibility All of the examples below contain three parts: the original code, the code after it has been modified, and an example usage of the code that could appear in another project. In a minor change, the example usage should successfully build with both the before and after versions. ### Major: renaming/moving/removing any public items {#item-remove} The absence of a publicly exposed [item][items] will cause any uses of that item to fail to compile. ```rust,ignore // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before pub fn foo() {} /////////////////////////////////////////////////////////// // After // ... item has been removed /////////////////////////////////////////////////////////// // Example usage that will break. fn main() { updated_crate::foo(); // Error: cannot find function `foo` } ``` This includes adding any sort of [`cfg` attribute] which can change which items or behavior is available based on [conditional compilation]. Mitigating strategies: * Mark items to be removed as [deprecated], and then remove them at a later date in a SemVer-breaking release. * Mark renamed items as [deprecated], and use a [`pub use`] item to re-export to the old name. ### Minor: adding new public items {#item-new} Adding new, public [items] is a minor change. ```rust,ignore // MINOR CHANGE /////////////////////////////////////////////////////////// // Before // ... absence of item /////////////////////////////////////////////////////////// // After pub fn foo() {} /////////////////////////////////////////////////////////// // Example use of the library that will safely work. // `foo` is not used since it didn't previously exist. ``` Note that in some rare cases this can be a **breaking change** due to glob imports. For example, if you add a new trait, and a project has used a glob import that brings that trait into scope, and the new trait introduces an associated item that conflicts with any types it is implemented on, this can cause a compile-time error due to the ambiguity. Example: ```rust,ignore // Breaking change example /////////////////////////////////////////////////////////// // Before // ... absence of trait /////////////////////////////////////////////////////////// // After pub trait NewTrait { fn foo(&self) {} } impl NewTrait for i32 {} /////////////////////////////////////////////////////////// // Example usage that will break. use updated_crate::*; pub trait LocalTrait { fn foo(&self) {} } impl LocalTrait for i32 {} fn main() { 123i32.foo(); // Error: multiple applicable items in scope } ``` This is not considered a major change because conventionally glob imports are a known forwards-compatibility hazard. Glob imports of items from external crates should be avoided. ### Major: Changing the alignment, layout, or size of a well-defined type {#type-layout} It is a breaking change to change the alignment, layout, or size of a type that was previously well-defined. In general, types that use the [the default representation] do not have a well-defined alignment, layout, or size. The compiler is free to alter the alignment, layout, or size, so code should not make any assumptions about it. > **Note**: It may be possible for external crates to break if they make assumptions about the alignment, layout, or size of a type even if it is not well-defined. > This is not considered a SemVer breaking change since those assumptions should not be made. Some examples of changes that are not a breaking change are (assuming no other rules in this guide are violated): * Adding, removing, reordering, or changing fields of a default representation struct, union, or enum in such a way that the change follows the other rules in this guide (for example, using `non_exhaustive` to allow those changes, or changes to private fields that are already private). See [struct-add-private-field-when-public](#struct-add-private-field-when-public), [struct-add-public-field-when-no-private](#struct-add-public-field-when-no-private), [struct-private-fields-with-private](#struct-private-fields-with-private), [enum-fields-new](#enum-fields-new). * Adding variants to a default representation enum, if the enum uses `non_exhaustive`. This may change the alignment or size of the enumeration, but those are not well-defined. See [enum-variant-new](#enum-variant-new). * Adding, removing, reordering, or changing private fields of a `repr(C)` struct, union, or enum, following the other rules in this guide (for example, using `non_exhaustive`, or adding private fields when other private fields already exist). See [repr-c-private-change](#repr-c-private-change). * Adding variants to a `repr(C)` enum, if the enum uses `non_exhaustive`. See [repr-c-enum-variant-new](#repr-c-enum-variant-new). * Adding `repr(C)` to a default representation struct, union, or enum. See [repr-c-add](#repr-c-add). * Adding `repr()` [primitive representation] to an enum. See [repr-int-enum-add](#repr-int-enum-add). * Adding `repr(transparent)` to a default representation struct or enum. See [repr-transparent-add](#repr-transparent-add). Types that use the [`repr` attribute] can be said to have an alignment and layout that is defined in some way that code may make some assumptions about that may break as a result of changing that type. In some cases, types with a `repr` attribute may not have an alignment, layout, or size that is well-defined. In these cases, it may be safe to make changes to the types, though care should be exercised. For example, types with private fields that do not otherwise document their alignment, layout, or size guarantees cannot be relied upon by external crates since the public API does not fully define the alignment, layout, or size of the type. A common example where a type with *private* fields is well-defined is a type with a single private field with a generic type, using `repr(transparent)`, and the prose of the documentation discusses that it is transparent to the generic type. For example, see [`UnsafeCell`]. Some examples of breaking changes are: * Adding `repr(packed)` to a struct or union. See [repr-packed-add](#repr-packed-add). * Adding `repr(align)` to a struct, union, or enum. See [repr-align-add](#repr-align-add). * Removing `repr(packed)` from a struct or union. See [repr-packed-remove](#repr-packed-remove). * Changing the value N of `repr(packed(N))` if that changes the alignment or layout. See [repr-packed-n-change](#repr-packed-n-change). * Changing the value N of `repr(align(N))` if that changes the alignment. See [repr-align-n-change](#repr-align-n-change). * Removing `repr(align)` from a struct, union, or enum. See [repr-align-remove](#repr-align-remove). * Changing the order of public fields of a `repr(C)` type. See [repr-c-shuffle](#repr-c-shuffle). * Removing `repr(C)` from a struct, union, or enum. See [repr-c-remove](#repr-c-remove). * Removing `repr()` from an enum. See [repr-int-enum-remove](#repr-int-enum-remove). * Changing the primitive representation of a `repr()` enum. See [repr-int-enum-change](#repr-int-enum-change). * Removing `repr(transparent)` from a struct or enum. See [repr-transparent-remove](#repr-transparent-remove). [the default representation]: ../../reference/type-layout.html#the-default-representation [primitive representation]: ../../reference/type-layout.html#primitive-representations [`repr` attribute]: ../../reference/type-layout.html#representations [`std::mem::transmute`]: ../../std/mem/fn.transmute.html [`UnsafeCell`]: ../../std/cell/struct.UnsafeCell.html#memory-layout #### Minor: `repr(C)` add, remove, or change a private field {#repr-c-private-change} It is usually safe to add, remove, or change a private field of a `repr(C)` struct, union, or enum, assuming it follows the other guidelines in this guide (see [struct-add-private-field-when-public](#struct-add-private-field-when-public), [struct-add-public-field-when-no-private](#struct-add-public-field-when-no-private), [struct-private-fields-with-private](#struct-private-fields-with-private), [enum-fields-new](#enum-fields-new)). For example, adding private fields can only be done if there are already other private fields, or it is `non_exhaustive`. Public fields may be added if there are private fields, or it is `non_exhaustive`, and the addition does not alter the layout of the other fields. However, this may change the size and alignment of the type. Care should be taken if the size or alignment changes. Code should not make assumptions about the size or alignment of types with private fields or `non_exhaustive` unless it has a documented size or alignment. ```rust,ignore // MINOR CHANGE /////////////////////////////////////////////////////////// // Before #[derive(Default)] #[repr(C)] pub struct Example { pub f1: i32, f2: i32, // a private field } /////////////////////////////////////////////////////////// // After #[derive(Default)] #[repr(C)] pub struct Example { pub f1: i32, f2: i32, f3: i32, // a new field } /////////////////////////////////////////////////////////// // Example use of the library that will safely work. fn main() { // NOTE: Users should not make assumptions about the size or alignment // since they are not documented. let f = updated_crate::Example::default(); } ``` #### Minor: `repr(C)` add enum variant {#repr-c-enum-variant-new} It is usually safe to add variants to a `repr(C)` enum, if the enum uses `non_exhaustive`. See [enum-variant-new](#enum-variant-new) for more discussion. Note that this may be a breaking change since it changes the size and alignment of the type. See [repr-c-private-change](#repr-c-private-change) for similar concerns. ```rust,ignore // MINOR CHANGE /////////////////////////////////////////////////////////// // Before #[repr(C)] #[non_exhaustive] pub enum Example { Variant1 { f1: i16 }, Variant2 { f1: i32 }, } /////////////////////////////////////////////////////////// // After #[repr(C)] #[non_exhaustive] pub enum Example { Variant1 { f1: i16 }, Variant2 { f1: i32 }, Variant3 { f1: i64 }, // added } /////////////////////////////////////////////////////////// // Example use of the library that will safely work. fn main() { // NOTE: Users should not make assumptions about the size or alignment // since they are not specified. For example, this raised the size from 8 // to 16 bytes. let f = updated_crate::Example::Variant2 { f1: 123 }; } ``` #### Minor: Adding `repr(C)` to a default representation {#repr-c-add} It is safe to add `repr(C)` to a struct, union, or enum with [the default representation]. This is safe because users should not make assumptions about the alignment, layout, or size of types with the default representation. ```rust,ignore // MINOR CHANGE /////////////////////////////////////////////////////////// // Before pub struct Example { pub f1: i32, pub f2: i16, } /////////////////////////////////////////////////////////// // After #[repr(C)] // added pub struct Example { pub f1: i32, pub f2: i16, } /////////////////////////////////////////////////////////// // Example use of the library that will safely work. fn main() { let f = updated_crate::Example { f1: 123, f2: 456 }; } ``` #### Minor: Adding `repr()` to an enum {#repr-int-enum-add} It is safe to add `repr()` [primitive representation] to an enum with [the default representation]. This is safe because users should not make assumptions about the alignment, layout, or size of an enum with the default representation. ```rust,ignore // MINOR CHANGE /////////////////////////////////////////////////////////// // Before pub enum E { Variant1, Variant2(i32), Variant3 { f1: f64 }, } /////////////////////////////////////////////////////////// // After #[repr(i32)] // added pub enum E { Variant1, Variant2(i32), Variant3 { f1: f64 }, } /////////////////////////////////////////////////////////// // Example use of the library that will safely work. fn main() { let x = updated_crate::E::Variant3 { f1: 1.23 }; } ``` #### Minor: Adding `repr(transparent)` to a default representation struct or enum {#repr-transparent-add} It is safe to add `repr(transparent)` to a struct or enum with [the default representation]. This is safe because users should not make assumptions about the alignment, layout, or size of a struct or enum with the default representation. ```rust,ignore // MINOR CHANGE /////////////////////////////////////////////////////////// // Before #[derive(Default)] pub struct Example(T); /////////////////////////////////////////////////////////// // After #[derive(Default)] #[repr(transparent)] // added pub struct Example(T); /////////////////////////////////////////////////////////// // Example use of the library that will safely work. fn main() { let x = updated_crate::Example::::default(); } ``` #### Major: Adding `repr(packed)` to a struct or union {#repr-packed-add} It is a breaking change to add `repr(packed)` to a struct or union. Making a type `repr(packed)` makes changes that can break code, such as being invalid to take a reference to a field, or causing truncation of disjoint closure captures. ```rust,ignore // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before pub struct Example { pub f1: u8, pub f2: u16, } /////////////////////////////////////////////////////////// // After #[repr(packed)] // added pub struct Example { pub f1: u8, pub f2: u16, } /////////////////////////////////////////////////////////// // Example usage that will break. fn main() { let f = updated_crate::Example { f1: 1, f2: 2 }; let x = &f.f2; // Error: reference to packed field is unaligned } ``` ```rust,ignore // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before pub struct Example(pub i32, pub i32); /////////////////////////////////////////////////////////// // After #[repr(packed)] pub struct Example(pub i32, pub i32); /////////////////////////////////////////////////////////// // Example usage that will break. fn main() { let mut f = updated_crate::Example(123, 456); let c = || { // Without repr(packed), the closure precisely captures `&f.0`. // With repr(packed), the closure captures `&f` to avoid undefined behavior. let a = f.0; }; f.1 = 789; // Error: cannot assign to `f.1` because it is borrowed c(); } ``` #### Major: Adding `repr(align)` to a struct, union, or enum {#repr-align-add} It is a breaking change to add `repr(align)` to a struct, union, or enum. Making a type `repr(align)` would break any use of that type in a `repr(packed)` type because that combination is not allowed. ```rust,ignore // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before pub struct Aligned { pub a: i32, } /////////////////////////////////////////////////////////// // After #[repr(align(8))] // added pub struct Aligned { pub a: i32, } /////////////////////////////////////////////////////////// // Example usage that will break. use updated_crate::Aligned; #[repr(packed)] pub struct Packed { // Error: packed type cannot transitively contain a `#[repr(align)]` type f1: Aligned, } fn main() { let p = Packed { f1: Aligned { a: 123 }, }; } ``` #### Major: Removing `repr(packed)` from a struct or union {#repr-packed-remove} It is a breaking change to remove `repr(packed)` from a struct or union. This may change the alignment or layout that extern crates are relying on. If any fields are public, then removing `repr(packed)` may change the way disjoint closure captures work. In some cases, this can cause code to break, similar to those outlined in the [edition guide][edition-closures]. [edition-closures]: ../../edition-guide/rust-2021/disjoint-capture-in-closures.html ```rust,ignore // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before #[repr(C, packed)] pub struct Packed { pub a: u8, pub b: u16, } /////////////////////////////////////////////////////////// // After #[repr(C)] // removed packed pub struct Packed { pub a: u8, pub b: u16, } /////////////////////////////////////////////////////////// // Example usage that will break. use updated_crate::Packed; fn main() { let p = Packed { a: 1, b: 2 }; // Some assumption about the size of the type. // Without `packed`, this fails since the size is 4. const _: () = assert!(std::mem::size_of::() == 3); // Error: evaluation of constant value failed } ``` ```rust,ignore // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before #[repr(C, packed)] pub struct Packed { pub a: *mut i32, pub b: i32, } unsafe impl Send for Packed {} /////////////////////////////////////////////////////////// // After #[repr(C)] // removed packed pub struct Packed { pub a: *mut i32, pub b: i32, } unsafe impl Send for Packed {} /////////////////////////////////////////////////////////// // Example usage that will break. use updated_crate::Packed; fn main() { let mut x = 123; let p = Packed { a: &mut x as *mut i32, b: 456, }; // When the structure was packed, the closure captures `p` which is Send. // When `packed` is removed, this ends up capturing `p.a` which is not Send. std::thread::spawn(move || unsafe { *(p.a) += 1; // Error: cannot be sent between threads safely }); } ``` #### Major: Changing the value N of `repr(packed(N))` if that changes the alignment or layout {#repr-packed-n-change} It is a breaking change to change the value of N of `repr(packed(N))` if that changes the alignment or layout. This may change the alignment or layout that external crates are relying on. If the value `N` is lowered below the alignment of a public field, then that would break any code that attempts to take a reference of that field. Note that some changes to `N` may not change the alignment or layout, for example increasing it when the current value is already equal to the natural alignment of the type. ```rust,ignore // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before #[repr(packed(4))] pub struct Packed { pub a: u8, pub b: u32, } /////////////////////////////////////////////////////////// // After #[repr(packed(2))] // changed to 2 pub struct Packed { pub a: u8, pub b: u32, } /////////////////////////////////////////////////////////// // Example usage that will break. use updated_crate::Packed; fn main() { let p = Packed { a: 1, b: 2 }; let x = &p.b; // Error: reference to packed field is unaligned } ``` #### Major: Changing the value N of `repr(align(N))` if that changes the alignment {#repr-align-n-change} It is a breaking change to change the value `N` of `repr(align(N))` if that changes the alignment. This may change the alignment that external crates are relying on. This change should be safe to make if the type is not well-defined as discussed in [type layout](#type-layout) (such as having any private fields and having an undocumented alignment or layout). Note that some changes to `N` may not change the alignment or layout, for example decreasing it when the current value is already equal to or less than the natural alignment of the type. ```rust,ignore // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before #[repr(align(8))] pub struct Packed { pub a: u8, pub b: u32, } /////////////////////////////////////////////////////////// // After #[repr(align(4))] // changed to 4 pub struct Packed { pub a: u8, pub b: u32, } /////////////////////////////////////////////////////////// // Example usage that will break. use updated_crate::Packed; fn main() { let p = Packed { a: 1, b: 2 }; // Some assumption about the size of the type. // The alignment has changed from 8 to 4. const _: () = assert!(std::mem::align_of::() == 8); // Error: evaluation of constant value failed } ``` #### Major: Removing `repr(align)` from a struct, union, or enum {#repr-align-remove} It is a breaking change to remove `repr(align)` from a struct, union, or enum, if their layout was well-defined. This may change the alignment or layout that external crates are relying on. This change should be safe to make if the type is not well-defined as discussed in [type layout](#type-layout) (such as having any private fields and having an undocumented alignment). ```rust,ignore // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before #[repr(C, align(8))] pub struct Packed { pub a: u8, pub b: u32, } /////////////////////////////////////////////////////////// // After #[repr(C)] // removed align pub struct Packed { pub a: u8, pub b: u32, } /////////////////////////////////////////////////////////// // Example usage that will break. use updated_crate::Packed; fn main() { let p = Packed { a: 1, b: 2 }; // Some assumption about the size of the type. // The alignment has changed from 8 to 4. const _: () = assert!(std::mem::align_of::() == 8); // Error: evaluation of constant value failed } ``` #### Major: Changing the order of public fields of a `repr(C)` type {#repr-c-shuffle} It is a breaking change to change the order of public fields of a `repr(C)` type. External crates may be relying on the specific ordering of the fields. ```rust,ignore,run-fail // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before #[repr(C)] pub struct SpecificLayout { pub a: u8, pub b: u32, } /////////////////////////////////////////////////////////// // After #[repr(C)] pub struct SpecificLayout { pub b: u32, // changed order pub a: u8, } /////////////////////////////////////////////////////////// // Example usage that will break. use updated_crate::SpecificLayout; unsafe extern "C" { // This C function is assuming a specific layout defined in a C header. fn c_fn_get_b(x: &SpecificLayout) -> u32; } fn main() { let p = SpecificLayout { a: 1, b: 2 }; unsafe { assert_eq!(c_fn_get_b(&p), 2) } // Error: value not equal to 2 } # mod cdep { # // This simulates what would normally be something included from a build script. # // This definition would be in a C header. # #[repr(C)] # pub struct SpecificLayout { # pub a: u8, # pub b: u32, # } # # #[no_mangle] # pub fn c_fn_get_b(x: &SpecificLayout) -> u32 { # x.b # } # } ``` #### Major: Removing `repr(C)` from a struct, union, or enum {#repr-c-remove} It is a breaking change to remove `repr(C)` from a struct, union, or enum. External crates may be relying on the specific layout of the type. ```rust,ignore // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before #[repr(C)] pub struct SpecificLayout { pub a: u8, pub b: u32, } /////////////////////////////////////////////////////////// // After // removed repr(C) pub struct SpecificLayout { pub a: u8, pub b: u32, } /////////////////////////////////////////////////////////// // Example usage that will break. use updated_crate::SpecificLayout; unsafe extern "C" { // This C function is assuming a specific layout defined in a C header. fn c_fn_get_b(x: &SpecificLayout) -> u32; // Error: is not FFI-safe } fn main() { let p = SpecificLayout { a: 1, b: 2 }; unsafe { assert_eq!(c_fn_get_b(&p), 2) } } # mod cdep { # // This simulates what would normally be something included from a build script. # // This definition would be in a C header. # #[repr(C)] # pub struct SpecificLayout { # pub a: u8, # pub b: u32, # } # # #[no_mangle] # pub fn c_fn_get_b(x: &SpecificLayout) -> u32 { # x.b # } # } ``` #### Major: Removing `repr()` from an enum {#repr-int-enum-remove} It is a breaking change to remove `repr()` from an enum. External crates may be assuming that the discriminant is a specific size. For example, [`std::mem::transmute`] of an enum may fail. ```rust,ignore // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before #[repr(u16)] pub enum Example { Variant1, Variant2, Variant3, } /////////////////////////////////////////////////////////// // After // removed repr(u16) pub enum Example { Variant1, Variant2, Variant3, } /////////////////////////////////////////////////////////// // Example usage that will break. fn main() { let e = updated_crate::Example::Variant2; let i: u16 = unsafe { std::mem::transmute(e) }; // Error: cannot transmute between types of different sizes } ``` #### Major: Changing the primitive representation of a `repr()` enum {#repr-int-enum-change} It is a breaking change to change the primitive representation of a `repr()` enum. External crates may be assuming that the discriminant is a specific size. For example, [`std::mem::transmute`] of an enum may fail. ```rust,ignore // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before #[repr(u16)] pub enum Example { Variant1, Variant2, Variant3, } /////////////////////////////////////////////////////////// // After #[repr(u8)] // changed repr size pub enum Example { Variant1, Variant2, Variant3, } /////////////////////////////////////////////////////////// // Example usage that will break. fn main() { let e = updated_crate::Example::Variant2; let i: u16 = unsafe { std::mem::transmute(e) }; // Error: cannot transmute between types of different sizes } ``` #### Major: Removing `repr(transparent)` from a struct or enum {#repr-transparent-remove} It is a breaking change to remove `repr(transparent)` from a struct or enum. External crates may be relying on the type having the alignment, layout, or size of the transparent field. ```rust,ignore // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before #[repr(transparent)] pub struct Transparent(T); /////////////////////////////////////////////////////////// // After // removed repr pub struct Transparent(T); /////////////////////////////////////////////////////////// // Example usage that will break. #![deny(improper_ctypes)] use updated_crate::Transparent; unsafe extern "C" { fn c_fn() -> Transparent; // Error: is not FFI-safe } fn main() {} ``` ### Major: adding a private struct field when all current fields are public {#struct-add-private-field-when-public} When a private field is added to a struct that previously had all public fields, this will break any code that attempts to construct it with a [struct literal]. ```rust,ignore // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before pub struct Foo { pub f1: i32, } /////////////////////////////////////////////////////////// // After pub struct Foo { pub f1: i32, f2: i32, } /////////////////////////////////////////////////////////// // Example usage that will break. fn main() { let x = updated_crate::Foo { f1: 123 }; // Error: cannot construct `Foo` } ``` Mitigation strategies: * Do not add new fields to all-public field structs. * Mark structs as [`#[non_exhaustive]`][non_exhaustive] when first introducing a struct to prevent users from using struct literal syntax, and instead provide a constructor method and/or [Default] implementation. ### Major: adding a public field when no private field exists {#struct-add-public-field-when-no-private} When a public field is added to a struct that has all public fields, this will break any code that attempts to construct it with a [struct literal]. ```rust,ignore // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before pub struct Foo { pub f1: i32, } /////////////////////////////////////////////////////////// // After pub struct Foo { pub f1: i32, pub f2: i32, } /////////////////////////////////////////////////////////// // Example usage that will break. fn main() { let x = updated_crate::Foo { f1: 123 }; // Error: missing field `f2` } ``` Mitigation strategies: * Do not add new fields to all-public field structs. * Mark structs as [`#[non_exhaustive]`][non_exhaustive] when first introducing a struct to prevent users from using struct literal syntax, and instead provide a constructor method and/or [Default] implementation. ### Minor: adding or removing private fields when at least one already exists {#struct-private-fields-with-private} It is safe to add or remove private fields from a struct when the struct already has at least one private field. ```rust,ignore // MINOR CHANGE /////////////////////////////////////////////////////////// // Before #[derive(Default)] pub struct Foo { f1: i32, } /////////////////////////////////////////////////////////// // After #[derive(Default)] pub struct Foo { f2: f64, } /////////////////////////////////////////////////////////// // Example use of the library that will safely work. fn main() { // Cannot access private fields. let x = updated_crate::Foo::default(); } ``` This is safe because existing code cannot use a [struct literal] to construct it, nor exhaustively match its contents. Note that for tuple structs, this is a **major change** if the tuple contains public fields, and the addition or removal of a private field changes the index of any public field. ```rust,ignore // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before #[derive(Default)] pub struct Foo(pub i32, i32); /////////////////////////////////////////////////////////// // After #[derive(Default)] pub struct Foo(f64, pub i32, i32); /////////////////////////////////////////////////////////// // Example usage that will break. fn main() { let x = updated_crate::Foo::default(); let y = x.0; // Error: is private } ``` ### Minor: going from a tuple struct with all private fields (with at least one field) to a normal struct, or vice versa {#struct-tuple-normal-with-private} Changing a tuple struct to a normal struct (or vice-versa) is safe if all fields are private. ```rust,ignore // MINOR CHANGE /////////////////////////////////////////////////////////// // Before #[derive(Default)] pub struct Foo(i32); /////////////////////////////////////////////////////////// // After #[derive(Default)] pub struct Foo { f1: i32, } /////////////////////////////////////////////////////////// // Example use of the library that will safely work. fn main() { // Cannot access private fields. let x = updated_crate::Foo::default(); } ``` This is safe because existing code cannot use a [struct literal] to construct it, nor match its contents. ### Major: adding new enum variants (without `non_exhaustive`) {#enum-variant-new} It is a breaking change to add a new enum variant if the enum does not use the [`#[non_exhaustive]`][non_exhaustive] attribute. ```rust,ignore // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before pub enum E { Variant1, } /////////////////////////////////////////////////////////// // After pub enum E { Variant1, Variant2, } /////////////////////////////////////////////////////////// // Example usage that will break. fn main() { use updated_crate::E; let x = E::Variant1; match x { // Error: `E::Variant2` not covered E::Variant1 => {} } } ``` Mitigation strategies: * When introducing the enum, mark it as [`#[non_exhaustive]`][non_exhaustive] to force users to use [wildcard patterns] to catch new variants. ### Major: adding new fields to an enum variant {#enum-fields-new} It is a breaking change to add new fields to an enum variant because all fields are public, and constructors and matching will fail to compile. ```rust,ignore // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before pub enum E { Variant1 { f1: i32 }, } /////////////////////////////////////////////////////////// // After pub enum E { Variant1 { f1: i32, f2: i32 }, } /////////////////////////////////////////////////////////// // Example usage that will break. fn main() { use updated_crate::E; let x = E::Variant1 { f1: 1 }; // Error: missing f2 match x { E::Variant1 { f1 } => {} // Error: missing f2 } } ``` Mitigation strategies: * When introducing the enum, mark the variant as [`non_exhaustive`][non_exhaustive] so that it cannot be constructed or matched without wildcards. ```rust,ignore,skip pub enum E { #[non_exhaustive] Variant1{f1: i32} } ``` * When introducing the enum, use an explicit struct as a value, where you can have control over the field visibility. ```rust,ignore,skip pub struct Foo { f1: i32, f2: i32, } pub enum E { Variant1(Foo) } ``` ### Major: adding a non-defaulted trait item {#trait-new-item-no-default} It is a breaking change to add a non-defaulted item to a trait. This will break any implementors of the trait. ```rust,ignore // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before pub trait Trait {} /////////////////////////////////////////////////////////// // After pub trait Trait { fn foo(&self); } /////////////////////////////////////////////////////////// // Example usage that will break. use updated_crate::Trait; struct Foo; impl Trait for Foo {} // Error: not all trait items implemented ``` Mitigation strategies: * Always provide a default implementation or value for new associated trait items. * When introducing the trait, use the [sealed trait] technique to prevent users outside of the crate from implementing the trait. ### Major: any change to trait item signatures {#trait-item-signature} It is a breaking change to make any change to a trait item signature. This can break external implementors of the trait. ```rust,ignore // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before pub trait Trait { fn f(&self, x: i32) {} } /////////////////////////////////////////////////////////// // After pub trait Trait { // For sealed traits or normal functions, this would be a minor change // because generalizing with generics strictly expands the possible uses. // But in this case, trait implementations must use the same signature. fn f(&self, x: V) {} } /////////////////////////////////////////////////////////// // Example usage that will break. use updated_crate::Trait; struct Foo; impl Trait for Foo { fn f(&self, x: i32) {} // Error: trait declaration has 1 type parameter } ``` Mitigation strategies: * Introduce new items with default implementations to cover the new functionality instead of modifying existing items. * When introducing the trait, use the [sealed trait] technique to prevent users outside of the crate from implementing the trait. ### Possibly-breaking: adding a defaulted trait item {#trait-new-default-item} It is usually safe to add a defaulted trait item. However, this can sometimes cause a compile error. For example, this can introduce an ambiguity if a method of the same name exists in another trait. ```rust,ignore // Breaking change example /////////////////////////////////////////////////////////// // Before pub trait Trait {} /////////////////////////////////////////////////////////// // After pub trait Trait { fn foo(&self) {} } /////////////////////////////////////////////////////////// // Example usage that will break. use updated_crate::Trait; struct Foo; trait LocalTrait { fn foo(&self) {} } impl Trait for Foo {} impl LocalTrait for Foo {} fn main() { let x = Foo; x.foo(); // Error: multiple applicable items in scope } ``` Note that this ambiguity does *not* exist for name collisions on [inherent implementations], as they take priority over trait items. See [trait-object-safety](#trait-object-safety) for a special case to consider when adding trait items. Mitigation strategies: * Some projects may deem this acceptable breakage, particularly if the new item name is unlikely to collide with any existing code. Choose names carefully to help avoid these collisions. Additionally, it may be acceptable to require downstream users to add [disambiguation syntax] to select the correct function when updating the dependency. ### Major: adding a trait item that makes the trait non-object safe {#trait-object-safety} It is a breaking change to add a trait item that changes the trait to not be [object safe]. ```rust,ignore // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before pub trait Trait {} /////////////////////////////////////////////////////////// // After pub trait Trait { // An associated const makes the trait not object-safe. const CONST: i32 = 123; } /////////////////////////////////////////////////////////// // Example usage that will break. use updated_crate::Trait; struct Foo; impl Trait for Foo {} fn main() { let obj: Box = Box::new(Foo); // Error: the trait `Trait` is not dyn compatible } ``` It is safe to do the converse (making a non-object safe trait into a safe one). ### Major: adding a type parameter without a default {#trait-new-parameter-no-default} It is a breaking change to add a type parameter without a default to a trait. ```rust,ignore // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before pub trait Trait {} /////////////////////////////////////////////////////////// // After pub trait Trait {} /////////////////////////////////////////////////////////// // Example usage that will break. use updated_crate::Trait; struct Foo; impl Trait for Foo {} // Error: missing generics ``` Mitigating strategies: * See [adding a defaulted trait type parameter](#trait-new-parameter-default). ### Minor: adding a defaulted trait type parameter {#trait-new-parameter-default} It is safe to add a type parameter to a trait as long as it has a default. External implementors will use the default without needing to specify the parameter. ```rust,ignore // MINOR CHANGE /////////////////////////////////////////////////////////// // Before pub trait Trait {} /////////////////////////////////////////////////////////// // After pub trait Trait {} /////////////////////////////////////////////////////////// // Example use of the library that will safely work. use updated_crate::Trait; struct Foo; impl Trait for Foo {} ``` ### Possibly-breaking change: adding any inherent items {#impl-item-new} Usually adding inherent items to an implementation should be safe because inherent items take priority over trait items. However, in some cases the collision can cause problems if the name is the same as an implemented trait item with a different signature. ```rust,ignore // Breaking change example /////////////////////////////////////////////////////////// // Before pub struct Foo; /////////////////////////////////////////////////////////// // After pub struct Foo; impl Foo { pub fn foo(&self) {} } /////////////////////////////////////////////////////////// // Example usage that will break. use updated_crate::Foo; trait Trait { fn foo(&self, x: i32) {} } impl Trait for Foo {} fn main() { let x = Foo; x.foo(1); // Error: this method takes 0 arguments but 1 argument was supplied } ``` Note that if the signatures match, there would not be a compile-time error, but possibly a silent change in runtime behavior (because it is now executing a different function). Mitigation strategies: * Some projects may deem this acceptable breakage, particularly if the new item name is unlikely to collide with any existing code. Choose names carefully to help avoid these collisions. Additionally, it may be acceptable to require downstream users to add [disambiguation syntax] to select the correct function when updating the dependency. ### Major: tightening generic bounds {#generic-bounds-tighten} It is a breaking change to tighten generic bounds on a type since this can break users expecting the looser bounds. ```rust,ignore // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before pub struct Foo { pub f1: A, } /////////////////////////////////////////////////////////// // After pub struct Foo { pub f1: A, } /////////////////////////////////////////////////////////// // Example usage that will break. use updated_crate::Foo; fn main() { let s = Foo { f1: 1.23 }; // Error: the trait bound `{float}: Eq` is not satisfied } ``` ### Minor: loosening generic bounds {#generic-bounds-loosen} It is safe to loosen the generic bounds on a type, as it only expands what is allowed. ```rust,ignore // MINOR CHANGE /////////////////////////////////////////////////////////// // Before pub struct Foo { pub f1: A, } /////////////////////////////////////////////////////////// // After pub struct Foo { pub f1: A, } /////////////////////////////////////////////////////////// // Example use of the library that will safely work. use updated_crate::Foo; fn main() { let s = Foo { f1: 123 }; } ``` ### Minor: adding defaulted type parameters {#generic-new-default} It is safe to add a type parameter to a type as long as it has a default. All existing references will use the default without needing to specify the parameter. ```rust,ignore // MINOR CHANGE /////////////////////////////////////////////////////////// // Before #[derive(Default)] pub struct Foo {} /////////////////////////////////////////////////////////// // After #[derive(Default)] pub struct Foo { f1: A, } /////////////////////////////////////////////////////////// // Example use of the library that will safely work. use updated_crate::Foo; fn main() { let s: Foo = Default::default(); } ``` ### Minor: generalizing a type to use generics (with identical types) {#generic-generalize-identical} A struct or enum field can change from a concrete type to a generic type parameter, provided that the change results in an identical type for all existing use cases. For example, the following change is permitted: ```rust,ignore // MINOR CHANGE /////////////////////////////////////////////////////////// // Before pub struct Foo(pub u8); /////////////////////////////////////////////////////////// // After pub struct Foo(pub T); /////////////////////////////////////////////////////////// // Example use of the library that will safely work. use updated_crate::Foo; fn main() { let s: Foo = Foo(123); } ``` because existing uses of `Foo` are shorthand for `Foo` which yields the identical field type. ### Major: generalizing a type to use generics (with possibly different types) {#generic-generalize-different} Changing a struct or enum field from a concrete type to a generic type parameter can break if the type can change. ```rust,ignore // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before pub struct Foo(pub T, pub u8); /////////////////////////////////////////////////////////// // After pub struct Foo(pub T, pub T); /////////////////////////////////////////////////////////// // Example usage that will break. use updated_crate::Foo; fn main() { let s: Foo = Foo(3.14, 123); // Error: mismatched types } ``` ### Minor: changing a generic type to a more generic type {#generic-more-generic} It is safe to change a generic type to a more generic one. For example, the following adds a generic parameter that defaults to the original type, which is safe because all existing users will be using the same type for both fields, the defaulted parameter does not need to be specified. ```rust,ignore // MINOR CHANGE /////////////////////////////////////////////////////////// // Before pub struct Foo(pub T, pub T); /////////////////////////////////////////////////////////// // After pub struct Foo(pub T, pub U); /////////////////////////////////////////////////////////// // Example use of the library that will safely work. use updated_crate::Foo; fn main() { let s: Foo = Foo(1.0, 2.0); } ``` ### Major: capturing more generic parameters in RPIT {#generic-rpit-capture} It is a breaking change to capture additional generic parameters in an [RPIT] (return-position impl trait). ```rust,ignore // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before pub fn f<'a, 'b>(x: &'a str, y: &'b str) -> impl Iterator + use<'a> { x.chars() } /////////////////////////////////////////////////////////// // After pub fn f<'a, 'b>(x: &'a str, y: &'b str) -> impl Iterator + use<'a, 'b> { x.chars().chain(y.chars()) } /////////////////////////////////////////////////////////// // Example usage that will break. fn main() { let a = String::new(); let b = String::new(); let iter = updated_crate::f(&a, &b); drop(b); // Error: cannot move out of `b` because it is borrowed } ``` Adding generic parameters to an RPIT places additional constraints on how the resulting type may be used. Note that there are implicit captures when the `use<>` syntax is not specified. In Rust 2021 and earlier editions, the lifetime parameters are only captured if they appear syntactically within a bound in the RPIT type signature. Starting in Rust 2024, all lifetime parameters are unconditionally captured. This means that starting in Rust 2024, the default is maximally compatible, requiring you to be explicit when you want to capture less, which is a SemVer commitment. See the [edition guide][rpit-capture-guide] and the [reference][rpit-reference] for more information on RPIT capturing. It is a minor change to capture fewer generic parameters in an RPIT. > Note: All in-scope type and const generic parameters must be either implicitly captured (no `+ use<…>` specified) or explicitly captured (must be listed in `+ use<…>`), and thus currently it is not allowed to change what is captured of those kinds of generics. [RPIT]: ../../reference/types/impl-trait.md#abstract-return-types [rpit-capture-guide]: ../../edition-guide/rust-2024/rpit-lifetime-capture.html [rpit-reference]: ../../reference/types/impl-trait.md#capturing ### Major: adding/removing function parameters {#fn-change-arity} Changing the arity of a function is a breaking change. ```rust,ignore // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before pub fn foo() {} /////////////////////////////////////////////////////////// // After pub fn foo(x: i32) {} /////////////////////////////////////////////////////////// // Example usage that will break. fn main() { updated_crate::foo(); // Error: this function takes 1 argument } ``` Mitigating strategies: * Introduce a new function with the new signature and possibly [deprecate][deprecated] the old one. * Introduce functions that take a struct argument, where the struct is built with the builder pattern. This allows new fields to be added to the struct in the future. ### Possibly-breaking: introducing a new function type parameter {#fn-generic-new} Usually, adding a non-defaulted type parameter is safe, but in some cases it can be a breaking change: ```rust,ignore // Breaking change example /////////////////////////////////////////////////////////// // Before pub fn foo() {} /////////////////////////////////////////////////////////// // After pub fn foo() {} /////////////////////////////////////////////////////////// // Example usage that will break. use updated_crate::foo; fn main() { foo::(); // Error: function takes 2 generic arguments but 1 generic argument was supplied } ``` However, such explicit calls are rare enough (and can usually be written in other ways) that this breakage is usually acceptable. One should take into account how likely it is that the function in question is being called with explicit type arguments. ### Minor: generalizing a function to use generics (supporting original type) {#fn-generalize-compatible} The type of a parameter to a function, or its return value, can be *generalized* to use generics, including by introducing a new type parameter, as long as it can be instantiated to the original type. For example, the following changes are allowed: ```rust,ignore // MINOR CHANGE /////////////////////////////////////////////////////////// // Before pub fn foo(x: u8) -> u8 { x } pub fn bar>(t: T) {} /////////////////////////////////////////////////////////// // After use std::ops::Add; pub fn foo(x: T) -> T { x } pub fn bar>(t: T) {} /////////////////////////////////////////////////////////// // Example use of the library that will safely work. use updated_crate::{bar, foo}; fn main() { foo(1); bar(vec![1, 2, 3].into_iter()); } ``` because all existing uses are instantiations of the new signature. Perhaps somewhat surprisingly, generalization applies to trait objects as well, given that every trait implements itself: ```rust,ignore // MINOR CHANGE /////////////////////////////////////////////////////////// // Before pub trait Trait {} pub fn foo(t: &dyn Trait) {} /////////////////////////////////////////////////////////// // After pub trait Trait {} pub fn foo(t: &T) {} /////////////////////////////////////////////////////////// // Example use of the library that will safely work. use updated_crate::{foo, Trait}; struct Foo; impl Trait for Foo {} fn main() { let obj = Foo; foo(&obj); } ``` (The use of `?Sized` is essential; otherwise you couldn't recover the original signature.) Introducing generics in this way can potentially create type inference failures. These are usually rare, and may be acceptable breakage for some projects, as this can be fixed with additional type annotations. ```rust,ignore // Breaking change example /////////////////////////////////////////////////////////// // Before pub fn foo() -> i32 { 0 } /////////////////////////////////////////////////////////// // After pub fn foo() -> T { Default::default() } /////////////////////////////////////////////////////////// // Example usage that will break. use updated_crate::foo; fn main() { let x = foo(); // Error: type annotations needed } ``` ### Major: generalizing a function to use generics with type mismatch {#fn-generalize-mismatch} It is a breaking change to change a function parameter or return type if the generic type constrains or changes the types previously allowed. For example, the following adds a generic constraint that may not be satisfied by existing code: ```rust,ignore // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before pub fn foo(x: Vec) {} /////////////////////////////////////////////////////////// // After pub fn foo>(x: T) {} /////////////////////////////////////////////////////////// // Example usage that will break. use updated_crate::foo; fn main() { foo(vec![1, 2, 3]); // Error: `Copy` is not implemented for `Vec` } ``` ### Minor: making an `unsafe` function safe {#fn-unsafe-safe} A previously `unsafe` function can be made safe without breaking code. Note however that it may cause the [`unused_unsafe`][unused_unsafe] lint to trigger as in the example below, which will cause local crates that have specified `#![deny(warnings)]` to stop compiling. Per [introducing new lints](#new-lints), it is allowed for updates to introduce new warnings. Going the other way (making a safe function `unsafe`) is a breaking change. ```rust,ignore // MINOR CHANGE /////////////////////////////////////////////////////////// // Before pub unsafe fn foo() {} /////////////////////////////////////////////////////////// // After pub fn foo() {} /////////////////////////////////////////////////////////// // Example use of the library that will trigger a lint. use updated_crate::foo; unsafe fn bar(f: unsafe fn()) { f() } fn main() { unsafe { foo() }; // The `unused_unsafe` lint will trigger here unsafe { bar(foo) }; } ``` Making a previously `unsafe` associated function or method on structs / enums safe is also a minor change, while the same is not true for associated function on traits (see [any change to trait item signatures](#trait-item-signature)). ### Major: switching from `no_std` support to requiring `std` {#attr-no-std-to-std} If your library specifically supports a [`no_std`] environment, it is a breaking change to make a new release that requires `std`. ```rust,ignore,skip // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before #![no_std] pub fn foo() {} /////////////////////////////////////////////////////////// // After pub fn foo() { std::time::SystemTime::now(); } /////////////////////////////////////////////////////////// // Example usage that will break. // This will fail to link for no_std targets because they don't have a `std` crate. #![no_std] use updated_crate::foo; fn example() { foo(); } ``` Mitigation strategies: * A common idiom to avoid this is to include a `std` [Cargo feature] that optionally enables `std` support, and when the feature is off, the library can be used in a `no_std` environment. ### Major: adding `non_exhaustive` to an existing enum, variant, or struct with no private fields {#attr-adding-non-exhaustive} Making items [`#[non_exhaustive]`][non_exhaustive] changes how they may be used outside the crate where they are defined: - Non-exhaustive structs and enum variants cannot be constructed using [struct literal] syntax, including [functional update syntax]. - Pattern matching on non-exhaustive structs requires `..` and matching on enums does not count towards exhaustiveness. - Casting enum variants to their discriminant with `as` is not allowed. Structs with private fields cannot be constructed using [struct literal] syntax regardless of whether [`#[non_exhaustive]`][non_exhaustive] is used. Adding [`#[non_exhaustive]`][non_exhaustive] to such a struct is not a breaking change. ```rust,ignore // MAJOR CHANGE /////////////////////////////////////////////////////////// // Before pub struct Foo { pub bar: usize, } pub enum Bar { X, Y(usize), Z { a: usize }, } pub enum Quux { Var, } /////////////////////////////////////////////////////////// // After #[non_exhaustive] pub struct Foo { pub bar: usize, } pub enum Bar { #[non_exhaustive] X, #[non_exhaustive] Y(usize), #[non_exhaustive] Z { a: usize }, } #[non_exhaustive] pub enum Quux { Var, } /////////////////////////////////////////////////////////// // Example usage that will break. use updated_crate::{Bar, Foo, Quux}; fn main() { let foo = Foo { bar: 0 }; // Error: cannot create non-exhaustive struct using struct expression let bar_x = Bar::X; // Error: unit variant `X` is private let bar_y = Bar::Y(0); // Error: tuple variant `Y` is private let bar_z = Bar::Z { a: 0 }; // Error: cannot create non-exhaustive variant using struct expression let q = Quux::Var; match q { Quux::Var => 0, // Error: non-exhaustive patterns: `_` not covered }; } ``` Mitigation strategies: * Mark structs, enums, and enum variants as [`#[non_exhaustive]`][non_exhaustive] when first introducing them, rather than adding [`#[non_exhaustive]`][non_exhaustive] later on. ## Tooling and environment compatibility ### Possibly-breaking: changing the minimum version of Rust required {#env-new-rust} Introducing the use of new features in a new release of Rust can break projects that are using older versions of Rust. This also includes using new features in a new release of Cargo, and requiring the use of a nightly-only feature in a crate that previously worked on stable. It is generally recommended to treat this as a minor change, rather than as a major change, for [various reasons][msrv-is-minor]. It is usually relatively easy to update to a newer version of Rust. Rust also has a rapid 6-week release cycle, and some projects will provide compatibility within a window of releases (such as the current stable release plus N previous releases). Just keep in mind that some large projects may not be able to update their Rust toolchain rapidly. Mitigation strategies: * Use [Cargo features] to make the new features opt-in. * Provide a large window of support for older releases. * Copy the source of new standard library items if possible so that you can continue to use an older version but take advantage of the new feature. * Provide a separate branch of older minor releases that can receive backports of important bugfixes. * Keep an eye out for the [`[cfg(version(..))]`][cfg-version] and [`#[cfg(accessible(..))]`][cfg-accessible] features which provide an opt-in mechanism for new features. These are currently unstable and only available in the nightly channel. ### Possibly-breaking: changing the platform and environment requirements {#env-change-requirements} There is a very wide range of assumptions a library makes about the environment that it runs in, such as the host platform, operating system version, available services, filesystem support, etc. It can be a breaking change if you make a new release that restricts what was previously supported, for example requiring a newer version of an operating system. These changes can be difficult to track, since you may not always know if a change breaks in an environment that is not automatically tested. Some projects may deem this acceptable breakage, particularly if the breakage is unlikely for most users, or the project doesn't have the resources to support all environments. Another notable situation is when a vendor discontinues support for some hardware or OS, the project may deem it reasonable to also discontinue support. Mitigation strategies: * Document the platforms and environments you specifically support. * Test your code on a wide range of environments in CI. ### Minor: introducing new lints {#new-lints} Some changes to a library may cause new lints to be triggered in users of that library. This should generally be considered a compatible change. ```rust,ignore,dont-deny // MINOR CHANGE /////////////////////////////////////////////////////////// // Before pub fn foo() {} /////////////////////////////////////////////////////////// // After #[deprecated] pub fn foo() {} /////////////////////////////////////////////////////////// // Example use of the library that will safely work. fn main() { updated_crate::foo(); // Warning: use of deprecated function } ``` Beware that it may be possible for this to technically cause a project to fail if they have explicitly denied the warning, and the updated crate is a direct dependency. Denying warnings should be done with care and the understanding that new lints may be introduced over time. However, library authors should be cautious about introducing new warnings and may want to consider the potential impact on their users. The following lints are examples of those that may be introduced when updating a dependency: * [`deprecated`][deprecated-lint] --- Introduced when a dependency adds the [`#[deprecated]` attribute][deprecated] to an item you are using. * [`unused_must_use`] --- Introduced when a dependency adds the [`#[must_use]` attribute][must-use-attr] to an item where you are not consuming the result. * [`unused_unsafe`] --- Introduced when a dependency *removes* the `unsafe` qualifier from a function, and that is the only unsafe function called in an unsafe block. Additionally, updating `rustc` to a new version may introduce new lints. Transitive dependencies which introduce new lints should not usually cause a failure because Cargo uses [`--cap-lints`](../../rustc/lints/levels.html#capping-lints) to suppress all lints in dependencies. Mitigating strategies: * If you build with warnings denied, understand you may need to deal with resolving new warnings whenever you update your dependencies. If using RUSTFLAGS to pass `-Dwarnings`, also add the `-A` flag to allow lints that are likely to cause issues, such as `-Adeprecated`. * Introduce deprecations behind a [feature][Cargo features]. For example `#[cfg_attr(feature = "deprecated", deprecated="use bar instead")]`. Then, when you plan to remove an item in a future SemVer breaking change, you can communicate with your users that they should enable the `deprecated` feature *before* updating to remove the use of the deprecated items. This allows users to choose when to respond to deprecations without needing to immediately respond to them. A downside is that it can be difficult to communicate to users that they need to take these manual steps to prepare for a major update. [`unused_must_use`]: ../../rustc/lints/listing/warn-by-default.html#unused-must-use [deprecated-lint]: ../../rustc/lints/listing/warn-by-default.html#deprecated [must-use-attr]: ../../reference/attributes/diagnostics.html#the-must_use-attribute [`unused_unsafe`]: ../../rustc/lints/listing/warn-by-default.html#unused-unsafe ### Cargo #### Minor: adding a new Cargo feature {#cargo-feature-add} It is usually safe to add new [Cargo features]. If the feature introduces new changes that cause a breaking change, this can cause difficulties for projects that have stricter backwards-compatibility needs. In that scenario, avoid adding the feature to the "default" list, and possibly document the consequences of enabling the feature. ```toml # MINOR CHANGE ########################################################### # Before [features] # ..empty ########################################################### # After [features] std = [] ``` #### Major: removing a Cargo feature {#cargo-feature-remove} It is usually a breaking change to remove [Cargo features]. This will cause an error for any project that enabled the feature. ```toml # MAJOR CHANGE ########################################################### # Before [features] logging = [] ########################################################### # After [dependencies] # ..logging removed ``` Mitigation strategies: * Clearly document your features. If there is an internal or experimental feature, mark it as such, so that users know the status of the feature. * Leave the old feature in `Cargo.toml`, but otherwise remove its functionality. Document that the feature is deprecated, and remove it in a future major SemVer release. #### Major: removing a feature from a feature list if that changes functionality or public items {#cargo-feature-remove-another} If removing a feature from another feature, this can break existing users if they are expecting that functionality to be available through that feature. ```toml # Breaking change example ########################################################### # Before [features] default = ["std"] std = [] ########################################################### # After [features] default = [] # This may cause packages to fail if they are expecting std to be enabled. std = [] ``` #### Possibly-breaking: removing an optional dependency {#cargo-remove-opt-dep} Removing an [optional dependency][opt-dep] can break a project using your library because another project may be enabling that dependency via [Cargo features]. When there is an optional dependency, cargo implicitly defines a feature of the same name to provide a mechanism to enable the dependency and to check when it is enabled. This problem can be avoided by using the `dep:` syntax in the `[features]` table, which disables this implicit feature. Using `dep:` makes it possible to hide the existence of optional dependencies under more semantically-relevant names which can be more safely modified. ```toml # Breaking change example ########################################################### # Before [dependencies] curl = { version = "0.4.31", optional = true } ########################################################### # After [dependencies] # ..curl removed ``` ```toml # MINOR CHANGE # # This example shows how to avoid breaking changes with optional dependencies. ########################################################### # Before [dependencies] curl = { version = "0.4.31", optional = true } [features] networking = ["dep:curl"] ########################################################### # After [dependencies] # Here, one optional dependency was replaced with another. hyper = { version = "0.14.27", optional = true } [features] networking = ["dep:hyper"] ``` Mitigation strategies: * Use the `dep:` syntax in the `[features]` table to avoid exposing optional dependencies in the first place. See [optional dependencies][opt-dep] for more information. * Clearly document your features. If the optional dependency is not included in the documented list of features, then you may decide to consider it safe to change undocumented entries. * Leave the optional dependency, and just don't use it within your library. * Replace the optional dependency with a [Cargo feature] that does nothing, and document that it is deprecated. * Use high-level features which enable optional dependencies, and document those as the preferred way to enable the extended functionality. For example, if your library has optional support for something like "networking", create a generic feature name "networking" that enables the optional dependencies necessary to implement "networking". Then document the "networking" feature. [opt-dep]: features.md#optional-dependencies #### Minor: changing dependency features {#cargo-change-dep-feature} It is usually safe to change the features on a dependency, as long as the feature does not introduce a breaking change. ```toml # MINOR CHANGE ########################################################### # Before [dependencies] rand = { version = "0.7.3", features = ["small_rng"] } ########################################################### # After [dependencies] rand = "0.7.3" ``` #### Minor: adding dependencies {#cargo-dep-add} It is usually safe to add new dependencies, as long as the new dependency does not introduce new requirements that result in a breaking change. For example, adding a new dependency that requires nightly in a project that previously worked on stable is a major change. ```toml # MINOR CHANGE ########################################################### # Before [dependencies] # ..empty ########################################################### # After [dependencies] log = "0.4.11" ``` ## Application compatibility Cargo projects may also include executable binaries which have their own interfaces (such as a CLI interface, OS-level interaction, etc.). Since these are part of the Cargo package, they often use and share the same version as the package. You will need to decide if and how you want to employ a SemVer contract with your users in the changes you make to your application. The potential breaking and compatible changes to an application are too numerous to list, so you are encouraged to use the spirit of the [SemVer] spec to guide your decisions on how to apply versioning to your application, or at least document what your commitments are. [`cfg` attribute]: ../../reference/conditional-compilation.md#the-cfg-attribute [`no_std`]: ../../reference/names/preludes.html#the-no_std-attribute [`pub use`]: ../../reference/items/use-declarations.html [Cargo feature]: features.md [Cargo features]: features.md [cfg-accessible]: https://github.com/rust-lang/rust/issues/64797 [cfg-version]: https://github.com/rust-lang/rust/issues/64796 [conditional compilation]: ../../reference/conditional-compilation.md [Default]: ../../std/default/trait.Default.html [deprecated]: ../../reference/attributes/diagnostics.html#the-deprecated-attribute [disambiguation syntax]: ../../reference/expressions/call-expr.html#disambiguating-function-calls [functional update syntax]: ../../reference/expressions/struct-expr.html#functional-update-syntax [inherent implementations]: ../../reference/items/implementations.html#inherent-implementations [items]: ../../reference/items.html [non_exhaustive]: ../../reference/attributes/type_system.html#the-non_exhaustive-attribute [object safe]: ../../reference/items/traits.html#object-safety [rust-feature]: https://doc.rust-lang.org/nightly/unstable-book/ [sealed trait]: https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed [SemVer]: https://semver.org/ [struct literal]: ../../reference/expressions/struct-expr.html [wildcard patterns]: ../../reference/patterns.html#wildcard-pattern [unused_unsafe]: ../../rustc/lints/listing/warn-by-default.html#unused-unsafe [msrv-is-minor]: https://github.com/rust-lang/api-guidelines/discussions/231 cargo-0.91.0/src/doc/src/reference/source-replacement.md000064400000000000000000000125501046102023000212110ustar 00000000000000# Source Replacement This document is about replacing the crate index. You can read about overriding dependencies in the [overriding dependencies] section of this documentation. A *source* is a provider that contains crates that may be included as dependencies for a package. Cargo supports the ability to **replace one source with another** to express strategies such as: * Vendoring --- custom sources can be defined which represent crates on the local filesystem. These sources are subsets of the source that they're replacing and can be checked into packages if necessary. * Mirroring --- sources can be replaced with an equivalent version which acts as a cache for crates.io itself. Cargo has a core assumption about source replacement that the source code is exactly the same from both sources. Note that this also means that a replacement source is not allowed to have crates which are not present in the original source. As a consequence, source replacement is not appropriate for situations such as patching a dependency or a private registry. Cargo supports patching dependencies through the usage of [the `[patch]` key][overriding dependencies], and private registry support is described in [the Registries chapter][registries]. When using source replacement, running commands that need to contact the registry directly[^1] requires passing the `--registry` option. This helps avoid any ambiguity about which registry to contact, and will use the authentication token for the specified registry. [^1]: Examples of such commands are in [Publishing Commands]. [Publishing Commands]: ../commands/publishing-commands.md [overriding dependencies]: overriding-dependencies.md [registries]: registries.md ## Configuration Configuration of replacement sources is done through [`.cargo/config.toml`][config] and the full set of available keys are: ```toml # The `source` table is where all keys related to source-replacement # are stored. [source] # Under the `source` table are a number of other tables whose keys are a # name for the relevant source. For example this section defines a new # source, called `my-vendor-source`, which comes from a directory # located at `vendor` relative to the directory containing this `.cargo/config.toml` # file [source.my-vendor-source] directory = "vendor" # The crates.io default source for crates is available under the name # "crates-io", and here we use the `replace-with` key to indicate that it's # replaced with our source above. # # The `replace-with` key can also reference an alternative registry name # defined in the `[registries]` table. [source.crates-io] replace-with = "my-vendor-source" # Each source has its own table where the key is the name of the source [source.the-source-name] # Indicate that `the-source-name` will be replaced with `another-source`, # defined elsewhere replace-with = "another-source" # Several kinds of sources can be specified (described in more detail below): registry = "https://example.com/path/to/index" local-registry = "path/to/registry" directory = "path/to/vendor" # Git sources can optionally specify a branch/tag/rev as well git = "https://example.com/path/to/repo" # branch = "master" # tag = "v1.0.1" # rev = "313f44e8" ``` [config]: config.md ## Registry Sources A "registry source" is one that is the same as crates.io itself. That is, it has an index served in a git repository which matches the format of the [crates.io index](https://github.com/rust-lang/crates.io-index). That repository then has configuration indicating where to download crates from. Currently there is not an already-available project for setting up a mirror of crates.io. Stay tuned though! ## Local Registry Sources A "local registry source" is intended to be a subset of another registry source, but available on the local filesystem (aka vendoring). Local registries are downloaded ahead of time, typically sync'd with a `Cargo.lock`, and are made up of a set of `*.crate` files and an index like the normal registry is. The primary way to manage and create local registry sources is through the [`cargo-local-registry`][cargo-local-registry] subcommand, [available on crates.io][cargo-local-registry] and can be installed with `cargo install cargo-local-registry`. [cargo-local-registry]: https://crates.io/crates/cargo-local-registry Local registries are contained within one directory and contain a number of `*.crate` files downloaded from crates.io as well as an `index` directory with the same format as the crates.io-index project (populated with just entries for the crates that are present). ## Directory Sources A "directory source" is similar to a local registry source where it contains a number of crates available on the local filesystem, suitable for vendoring dependencies. Directory sources are primarily managed by the `cargo vendor` subcommand. Directory sources are distinct from local registries though in that they contain the unpacked version of `*.crate` files, making it more suitable in some situations to check everything into source control. A directory source is just a directory containing a number of other directories which contain the source code for crates (the unpacked version of `*.crate` files). Currently no restriction is placed on the name of each directory. Each crate in a directory source also has an associated metadata file indicating the checksum of each file in the crate to protect against accidental modifications. cargo-0.91.0/src/doc/src/reference/specifying-dependencies.md000064400000000000000000000606071046102023000222060ustar 00000000000000# Specifying Dependencies Your crates can depend on other libraries from [crates.io] or other registries, `git` repositories, or subdirectories on your local file system. You can also temporarily override the location of a dependency --- for example, to be able to test out a bug fix in the dependency that you are working on locally. You can have different dependencies for different platforms, and dependencies that are only used during development. Let's take a look at how to do each of these. ## Specifying dependencies from crates.io Cargo is configured to look for dependencies on [crates.io] by default. Only the name and a version string are required in this case. In [the cargo guide](../guide/index.md), we specified a dependency on the `time` crate: ```toml [dependencies] time = "0.1.12" ``` The version string `"0.1.12"` is called a [version requirement](#version-requirement-syntax). It specifies a range of versions that can be selected from when [resolving dependencies](resolver.md). In this case, `"0.1.12"` represents the version range `>=0.1.12, <0.2.0`. An update is allowed if it is within that range. In this case, if we ran `cargo update time`, cargo should update us to version `0.1.13` if it is the latest `0.1.z` release, but would not update us to `0.2.0`. ## Version requirement syntax ### Default requirements **Default requirements** specify a minimum version with the ability to update to [SemVer] compatible versions. Versions are considered compatible if their left-most non-zero major/minor/patch component is the same. This is different from [SemVer] which considers all pre-1.0.0 packages to be incompatible. `1.2.3` is an example of a default requirement. ```notrust 1.2.3 := >=1.2.3, <2.0.0 1.2 := >=1.2.0, <2.0.0 1 := >=1.0.0, <2.0.0 0.2.3 := >=0.2.3, <0.3.0 0.2 := >=0.2.0, <0.3.0 0.0.3 := >=0.0.3, <0.0.4 0.0 := >=0.0.0, <0.1.0 0 := >=0.0.0, <1.0.0 ``` ### Caret requirements **Caret requirements** are the default version requirement strategy. This version strategy allows [SemVer] compatible updates. They are specified as version requirements with a leading caret (`^`). `^1.2.3` is an example of a caret requirement. Leaving off the caret is a simplified equivalent syntax to using caret requirements. While caret requirements are the default, it is recommended to use the simplified syntax when possible. `log = "^1.2.3"` is exactly equivalent to `log = "1.2.3"`. ### Tilde requirements **Tilde requirements** specify a minimal version with some ability to update. If you specify a major, minor, and patch version or only a major and minor version, only patch-level changes are allowed. If you only specify a major version, then minor- and patch-level changes are allowed. `~1.2.3` is an example of a tilde requirement. ```notrust ~1.2.3 := >=1.2.3, <1.3.0 ~1.2 := >=1.2.0, <1.3.0 ~1 := >=1.0.0, <2.0.0 ``` ### Wildcard requirements **Wildcard requirements** allow for any version where the wildcard is positioned. `*`, `1.*` and `1.2.*` are examples of wildcard requirements. ```notrust * := >=0.0.0 1.* := >=1.0.0, <2.0.0 1.2.* := >=1.2.0, <1.3.0 ``` > **Note**: [crates.io] does not allow bare `*` versions. ### Comparison requirements **Comparison requirements** allow manually specifying a version range or an exact version to depend on. Here are some examples of comparison requirements: ```notrust >= 1.2.0 > 1 < 2 = 1.2.3 ``` ### Multiple version requirements As shown in the examples above, multiple version requirements can be separated with a comma, e.g., `>= 1.2, < 1.5`. ### Pre-releases Version requirements exclude [pre-release versions](manifest.md#the-version-field), such as `1.0.0-alpha`, unless specifically asked for. For example, if `1.0.0-alpha` of package `foo` is published, then a requirement of `foo = "1.0"` will *not* match, and will return an error. The pre-release must be specified, such as `foo = "1.0.0-alpha"`. Similarly [`cargo install`] will avoid pre-releases unless explicitly asked to install one. Cargo allows "newer" pre-releases to be used automatically. For example, if `1.0.0-beta` is published, then a requirement `foo = "1.0.0-alpha"` will allow updating to the `beta` version. Note that this only works on the same release version, `foo = "1.0.0-alpha"` will not allow updating to `foo = "1.0.1-alpha"` or `foo = "1.0.1-beta"`. Cargo will also upgrade automatically to semver-compatible released versions from prereleases. The requirement `foo = "1.0.0-alpha"` will allow updating to `foo = "1.0.0"` as well as `foo = "1.2.0"`. Beware that pre-release versions can be unstable, and as such care should be taken when using them. Some projects may choose to publish breaking changes between pre-release versions. It is recommended to not use pre-release dependencies in a library if your library is not also a pre-release. Care should also be taken when updating your `Cargo.lock`, and be prepared if a pre-release update causes issues. [`cargo install`]: ../commands/cargo-install.md ### Version metadata [Version metadata](manifest.md#the-version-field), such as `1.0.0+21AF26D3`, is ignored and should not be used in version requirements. > **Recommendation:** When in doubt, use the default version requirement operator. > > In rare circumstances, a package with a "public dependency" > (re-exports the dependency or interoperates with it in its public API) > that is compatible with multiple semver-incompatible versions > (e.g. only uses a simple type that hasn't changed between releases, like an `Id`) > may support users choosing which version of the "public dependency" to use. > In this case, a version requirement like `">=0.4, <2"` may be of interest. > *However* users of the package will likely run into errors and need to > manually select a version of the "public dependency" via `cargo update` if > they also depend on it as Cargo might pick different versions of the "public > dependency" when [resolving dependency versions](resolver.md) (see > [#10599]). > > Avoid constraining the upper bound of a version to be anything less than the > next semver incompatible version > (e.g. avoid `">=2.0, <2.4"`, `"2.0.*"`, or `~2.0`), > as other packages in the dependency tree may > require a newer version, leading to an unresolvable error (see [#9029]). > Consider whether controlling the version in your [`Cargo.lock`] would be more > appropriate. > > In some instances this won't matter or the benefits might outweigh the cost, including: > - When no one else depends on your package; e.g. it only has a `[[bin]]` > - When depending on a pre-release package and wishing to avoid breaking > changes, then a fully specified `"=1.2.3-alpha.3"` might be warranted (see > [#2222]) > - When a library re-exports a proc-macro but the proc-macro generates code that > calls into the re-exporting library, then a fully specified `=1.2.3` might be > warranted to ensure the proc-macro isn't newer than the re-exporting library > and generating code that uses parts of the API that don't exist within the > current version [`Cargo.lock`]: ../guide/cargo-toml-vs-cargo-lock.md [#2222]: https://github.com/rust-lang/cargo/issues/2222 [#9029]: https://github.com/rust-lang/cargo/issues/9029 [#10599]: https://github.com/rust-lang/cargo/issues/10599 ## Specifying dependencies from other registries To specify a dependency from a registry other than [crates.io] set the `registry` key to the name of the registry to use: ```toml [dependencies] some-crate = { version = "1.0", registry = "my-registry" } ``` where `my-registry` is the registry name configured in `.cargo/config.toml` file. See the [registries documentation] for more information. > **Note**: [crates.io] does not allow packages to be published with > dependencies on code published outside of [crates.io]. [registries documentation]: registries.md ## Specifying dependencies from `git` repositories To depend on a library located in a `git` repository, the minimum information you need to specify is the location of the repository with the `git` key: ```toml [dependencies] regex = { git = "https://github.com/rust-lang/regex.git" } ``` Cargo fetches the `git` repository at that location and traverses the file tree to find `Cargo.toml` file for the requested crate anywhere inside the `git` repository. For example, `regex-lite` and `regex-syntax` are members of `rust-lang/regex` repo and can be referred to by the repo's root URL (`https://github.com/rust-lang/regex.git`) regardless of where in the file tree they reside. ```toml regex-lite = { git = "https://github.com/rust-lang/regex.git" } regex-syntax = { git = "https://github.com/rust-lang/regex.git" } ``` The above rule does not apply to [`path` dependencies](#specifying-path-dependencies). ### Choice of commit Cargo assumes that we intend to use the latest commit on the default branch to build our package if we only specify the repo URL, as in the examples above. You can combine the `git` key with the `rev`, `tag`, or `branch` keys to be more specific about which commit to use. Here's an example of using the latest commit on a branch named `next`: ```toml [dependencies] regex = { git = "https://github.com/rust-lang/regex.git", branch = "next" } ``` Anything that is not a branch or a tag falls under `rev` key. This can be a commit hash like `rev = "4c59b707"`, or a named reference exposed by the remote repository such as `rev = "refs/pull/493/head"`. What references are available for the `rev` key varies by where the repo is hosted. GitHub exposes a reference to the most recent commit of every pull request as in the example above. Other git hosts may provide something equivalent under a different naming scheme. **More `git` dependency examples:** ```toml # .git suffix can be omitted if the host accepts such URLs - both examples work the same regex = { git = "https://github.com/rust-lang/regex" } regex = { git = "https://github.com/rust-lang/regex.git" } # a commit with a particular tag regex = { git = "https://github.com/rust-lang/regex.git", tag = "1.10.3" } # a commit by its SHA1 hash regex = { git = "https://github.com/rust-lang/regex.git", rev = "0c0990399270277832fbb5b91a1fa118e6f63dba" } # HEAD commit of PR 493 regex = { git = "https://github.com/rust-lang/regex.git", rev = "refs/pull/493/head" } # INVALID EXAMPLES # specifying the commit after # ignores the commit ID and generates a warning regex = { git = "https://github.com/rust-lang/regex.git#4c59b70" } # git and path cannot be used at the same time regex = { git = "https://github.com/rust-lang/regex.git#4c59b70", path = "../regex" } ``` Cargo locks the commits of `git` dependencies in `Cargo.lock` file at the time of their addition and checks for updates only when you run `cargo update` command. ### The role of the `version` key The `version` key always implies that the package is available in a registry, regardless of the presence of `git` or `path` keys. The `version` key does _not_ affect which commit is used when Cargo retrieves the `git` dependency, but Cargo checks the version information in the dependency's `Cargo.toml` file against the `version` key and raises an error if the check fails. In this example, Cargo retrieves the HEAD commit of the branch called `next` from Git and checks if the crate's version is compatible with `version = "1.10.3"`: ```toml [dependencies] regex = { version = "1.10.3", git = "https://github.com/rust-lang/regex.git", branch = "next" } ``` `version`, `git`, and `path` keys are considered separate locations for resolving the dependency. See [Multiple locations](#multiple-locations) section below for detailed explanations. > **Note**: [crates.io] does not allow packages to be published with > dependencies on code published outside of [crates.io] itself > ([dev-dependencies] are ignored). See the [Multiple > locations](#multiple-locations) section for a fallback alternative for `git` > and `path` dependencies. ### Accessing private Git repositories See [Git Authentication](../appendix/git-authentication.md) for help with Git authentication for private repos. ## Specifying path dependencies Over time, our `hello_world` package from [the guide](../guide/index.md) has grown significantly in size! It’s gotten to the point that we probably want to split out a separate crate for others to use. To do this Cargo supports **path dependencies** which are typically sub-crates that live within one repository. Let’s start by making a new crate inside of our `hello_world` package: ```console # inside of hello_world/ $ cargo new hello_utils ``` This will create a new folder `hello_utils` inside of which a `Cargo.toml` and `src` folder are ready to be configured. To tell Cargo about this, open up `hello_world/Cargo.toml` and add `hello_utils` to your dependencies: ```toml [dependencies] hello_utils = { path = "hello_utils" } ``` This tells Cargo that we depend on a crate called `hello_utils` which is found in the `hello_utils` folder, relative to the `Cargo.toml` file it’s written in. The next `cargo build` will automatically build `hello_utils` and all of its dependencies. ### No local path traversal The local paths must point to the exact folder with the dependency's `Cargo.toml`. Unlike with `git` dependencies, Cargo does not traverse local paths. For example, if `regex-lite` and `regex-syntax` are members of a locally cloned `rust-lang/regex` repo, they have to be referred to by the full path: ```toml # git key accepts the repo root URL and Cargo traverses the tree to find the crate [dependencies] regex-lite = { git = "https://github.com/rust-lang/regex.git" } regex-syntax = { git = "https://github.com/rust-lang/regex.git" } # path key requires the member name to be included in the local path [dependencies] regex-lite = { path = "../regex/regex-lite" } regex-syntax = { path = "../regex/regex-syntax" } ``` ### Local paths in published crates Crates that use dependencies specified with only a path are not permitted on [crates.io]. If we wanted to publish our `hello_world` crate, we would need to publish a version of `hello_utils` to [crates.io] as a separate crate and specify its version in the dependencies line of `hello_world`: ```toml [dependencies] hello_utils = { path = "hello_utils", version = "0.1.0" } ``` The use of `path` and `version` keys together is explained in the [Multiple locations](#multiple-locations) section. > **Note**: [crates.io] does not allow packages to be published with > dependencies on code outside of [crates.io], except for [dev-dependencies]. > See the [Multiple locations](#multiple-locations) section > for a fallback alternative for `git` and `path` dependencies. ## Multiple locations It is possible to specify both a registry version and a `git` or `path` location. The `git` or `path` dependency will be used locally (in which case the `version` is checked against the local copy), and when published to a registry like [crates.io], it will use the registry version. Other combinations are not allowed. Examples: ```toml [dependencies] # Uses `my-bitflags` when used locally, and uses # version 1.0 from crates.io when published. bitflags = { path = "my-bitflags", version = "1.0" } # Uses the given git repo when used locally, and uses # version 1.0 from crates.io when published. smallvec = { git = "https://github.com/servo/rust-smallvec.git", version = "1.0" } # Note: if a version doesn't match, Cargo will fail to compile! ``` One example where this can be useful is when you have split up a library into multiple packages within the same workspace. You can then use `path` dependencies to point to the local packages within the workspace to use the local version during development, and then use the [crates.io] version once it is published. This is similar to specifying an [override](overriding-dependencies.md), but only applies to this one dependency declaration. ## Platform specific dependencies Platform-specific dependencies take the same format, but are listed under a `target` section. Normally Rust-like [`#[cfg]` syntax](../../reference/conditional-compilation.html) will be used to define these sections: ```toml [target.'cfg(windows)'.dependencies] winhttp = "0.4.0" [target.'cfg(unix)'.dependencies] openssl = "1.0.1" [target.'cfg(target_arch = "x86")'.dependencies] native-i686 = { path = "native/i686" } [target.'cfg(target_arch = "x86_64")'.dependencies] native-x86_64 = { path = "native/x86_64" } ``` Like with Rust, the syntax here supports the `not`, `any`, and `all` operators to combine various cfg name/value pairs. If you want to know which cfg targets are available on your platform, run `rustc --print=cfg` from the command line. If you want to know which `cfg` targets are available for another platform, such as 64-bit Windows, run `rustc --print=cfg --target=x86_64-pc-windows-msvc`. Unlike in your Rust source code, you cannot use `[target.'cfg(feature = "fancy-feature")'.dependencies]` to add dependencies based on optional features. Use [the `[features]` section](features.md) instead: ```toml [dependencies] foo = { version = "1.0", optional = true } bar = { version = "1.0", optional = true } [features] fancy-feature = ["foo", "bar"] ``` The same applies to `cfg(debug_assertions)`, `cfg(test)` and `cfg(proc_macro)`. These values will not work as expected and will always have the default value returned by `rustc --print=cfg`. There is currently no way to add dependencies based on these configuration values. In addition to `#[cfg]` syntax, Cargo also supports listing out the full target the dependencies would apply to: ```toml [target.x86_64-pc-windows-gnu.dependencies] winhttp = "0.4.0" [target.i686-unknown-linux-gnu.dependencies] openssl = "1.0.1" ``` ### Custom target specifications If you’re using a custom target specification (such as `--target foo/bar.json`), use the base filename without the `.json` extension: ```toml [target.bar.dependencies] winhttp = "0.4.0" [target.my-special-i686-platform.dependencies] openssl = "1.0.1" native = { path = "native/i686" } ``` > **Note**: Custom target specifications are not usable on the stable channel. ## Development dependencies You can add a `[dev-dependencies]` section to your `Cargo.toml` whose format is equivalent to `[dependencies]`: ```toml [dev-dependencies] tempdir = "0.3" ``` Dev-dependencies are not used when compiling a package for building, but are used for compiling tests, examples, and benchmarks. These dependencies are *not* propagated to other packages which depend on this package. You can also have target-specific development dependencies by using `dev-dependencies` in the target section header instead of `dependencies`. For example: ```toml [target.'cfg(unix)'.dev-dependencies] mio = "0.0.1" ``` > **Note**: When a package is published, only dev-dependencies that specify a > `version` will be included in the published crate. For most use cases, > dev-dependencies are not needed when published, though some users (like OS > packagers) may want to run tests within a crate, so providing a `version` if > possible can still be beneficial. ## Build dependencies You can depend on other Cargo-based crates for use in your build scripts. Dependencies are declared through the `build-dependencies` section of the manifest: ```toml [build-dependencies] cc = "1.0.3" ``` You can also have target-specific build dependencies by using `build-dependencies` in the target section header instead of `dependencies`. For example: ```toml [target.'cfg(unix)'.build-dependencies] cc = "1.0.3" ``` In this case, the dependency will only be built when the host platform matches the specified target. The build script **does not** have access to the dependencies listed in the `dependencies` or `dev-dependencies` section. Build dependencies will likewise not be available to the package itself unless listed under the `dependencies` section as well. A package itself and its build script are built separately, so their dependencies need not coincide. Cargo is kept simpler and cleaner by using independent dependencies for independent purposes. ## Choosing features If a package you depend on offers conditional features, you can specify which to use: ```toml [dependencies.awesome] version = "1.3.5" default-features = false # do not include the default features, and optionally # cherry-pick individual features features = ["secure-password", "civet"] ``` More information about features can be found in the [features chapter](features.md#dependency-features). ## Renaming dependencies in `Cargo.toml` When writing a `[dependencies]` section in `Cargo.toml` the key you write for a dependency typically matches up to the name of the crate you import from in the code. For some projects, though, you may wish to reference the crate with a different name in the code regardless of how it's published on crates.io. For example you may wish to: * Avoid the need to `use foo as bar` in Rust source. * Depend on multiple versions of a crate. * Depend on crates with the same name from different registries. To support this Cargo supports a `package` key in the `[dependencies]` section of which package should be depended on: ```toml [package] name = "mypackage" version = "0.0.1" [dependencies] foo = "0.1" bar = { git = "https://github.com/example/project.git", package = "foo" } baz = { version = "0.1", registry = "custom", package = "foo" } ``` In this example, three crates are now available in your Rust code: ```rust,ignore extern crate foo; // crates.io extern crate bar; // git repository extern crate baz; // registry `custom` ``` All three of these crates have the package name of `foo` in their own `Cargo.toml`, so we're explicitly using the `package` key to inform Cargo that we want the `foo` package even though we're calling it something else locally. The `package` key, if not specified, defaults to the name of the dependency being requested. Note that if you have an optional dependency like: ```toml [dependencies] bar = { version = "0.1", package = 'foo', optional = true } ``` you're depending on the crate `foo` from crates.io, but your crate has a `bar` feature instead of a `foo` feature. That is, names of features take after the name of the dependency, not the package name, when renamed. Enabling transitive dependencies works similarly, for example we could add the following to the above manifest: ```toml [features] log-debug = ['bar/log-debug'] # using 'foo/log-debug' would be an error! ``` ## Inheriting a dependency from a workspace Dependencies can be inherited from a workspace by specifying the dependency in the workspace's [`[workspace.dependencies]`][workspace.dependencies] table. After that, add it to the `[dependencies]` table with `workspace = true`. Along with the `workspace` key, dependencies can also include these keys: - [`optional`][optional]: Note that the`[workspace.dependencies]` table is not allowed to specify `optional`. - [`features`][features]: These are additive with the features declared in the `[workspace.dependencies]` Other than `optional` and `features`, inherited dependencies cannot use any other dependency key (such as `version` or `default-features`). Dependencies in the `[dependencies]`, `[dev-dependencies]`, `[build-dependencies]`, and `[target."...".dependencies]` sections support the ability to reference the `[workspace.dependencies]` definition of dependencies. ```toml [package] name = "bar" version = "0.2.0" [dependencies] regex = { workspace = true, features = ["unicode"] } [build-dependencies] cc.workspace = true [dev-dependencies] rand = { workspace = true, optional = true } ``` [SemVer]: https://semver.org [crates.io]: https://crates.io/ [dev-dependencies]: #development-dependencies [workspace.dependencies]: workspaces.md#the-dependencies-table [optional]: features.md#optional-dependencies [features]: features.md cargo-0.91.0/src/doc/src/reference/timings.md000064400000000000000000000056221046102023000170700ustar 00000000000000# Reporting build timings The `--timings` option gives some information about how long each compilation takes, and tracks concurrency information over time. ```sh cargo build --timings ``` This writes an HTML report in `target/cargo-timings/cargo-timing.html`. This also writes a copy of the report to the same directory with a timestamp in the filename, if you want to look at older runs. ## Reading the graphs There are two tables and two graphs in the output. The first table displays the build information of the project, including the number of units built, the maximum number of concurrency, build time, and the version information of the currently used compiler. ![build-info](../images/build-info.png) The "unit" graph shows the duration of each unit over time. A "unit" is a single compiler invocation. There are lines that show which additional units are "unlocked" when a unit finishes. That is, it shows the new units that are now allowed to run because their dependencies are all finished. Hover the mouse over a unit to highlight the lines. This can help visualize the critical path of dependencies. This may change between runs because the units may finish in different orders. The "codegen" times are highlighted in a lavender color. In some cases, build pipelining allows units to start when their dependencies are performing code generation. This information is not always displayed (for example, binary units do not show when code generation starts). The "custom build" units are `build.rs` scripts, which when run are highlighted in orange. ![build-unit-time](../images/build-unit-time.png) The second graph shows Cargo's concurrency over time. The background indicates CPU usage. The three lines are: - "Waiting" (red) --- This is the number of units waiting for a CPU slot to open. - "Inactive" (blue) --- This is the number of units that are waiting for their dependencies to finish. - "Active" (green) --- This is the number of units currently running. ![cargo-concurrency-over-time](../images/cargo-concurrency-over-time.png) Note: This does not show the concurrency in the compiler itself. `rustc` coordinates with Cargo via the "job server" to stay within the concurrency limit. This currently mostly applies to the code generation phase. Tips for addressing compile times: - Look for slow dependencies. - Check if they have features that you may wish to consider disabling. - Consider trying to remove the dependency completely. - Look for a crate being built multiple times with different versions. Try to remove the older versions from the dependency graph. - Split large crates into smaller pieces. - If there are a large number of crates bottlenecked on a single crate, focus your attention on improving that one crate to improve parallelism. The last table lists the total time and "codegen" time spent on each unit, as well as the features that were enabled during each unit's compilation. cargo-0.91.0/src/doc/src/reference/unstable.md000064400000000000000000002706421046102023000172410ustar 00000000000000# Unstable Features Experimental Cargo features are only available on the [nightly channel]. You are encouraged to experiment with these features to see if they meet your needs, and if there are any issues or problems. Check the linked tracking issues listed below for more information on the feature, and click the GitHub subscribe button if you want future updates. After some period of time, if the feature does not have any major concerns, it can be [stabilized], which will make it available on stable once the current nightly release reaches the stable channel (anywhere from 6 to 12 weeks). There are three different ways that unstable features can be enabled based on how the feature works: * New syntax in `Cargo.toml` requires a `cargo-features` key at the top of `Cargo.toml`, before any tables. For example: ```toml # This specifies which new Cargo.toml features are enabled. cargo-features = ["test-dummy-unstable"] [package] name = "my-package" version = "0.1.0" im-a-teapot = true # This is a new option enabled by test-dummy-unstable. ``` * New command-line flags, options, and subcommands require the `-Z unstable-options` CLI option to also be included. For example, the new `--artifact-dir` option is only available on nightly: ```cargo +nightly build --artifact-dir=out -Z unstable-options``` * `-Z` command-line flags are used to enable new functionality that may not have an interface, or the interface has not yet been designed, or for more complex features that affect multiple parts of Cargo. For example, the [mtime-on-use](#mtime-on-use) feature can be enabled with: ```cargo +nightly build -Z mtime-on-use``` Run `cargo -Z help` to see a list of flags available. Anything which can be configured with a `-Z` flag can also be set in the cargo [config file] (`.cargo/config.toml`) in the `unstable` table. For example: ```toml [unstable] mtime-on-use = true build-std = ["core", "alloc"] ``` Each new feature described below should explain how to use it. *For the latest nightly, see the [nightly version] of this page.* [config file]: config.md [nightly channel]: ../../book/appendix-07-nightly-rust.html [stabilized]: https://doc.crates.io/contrib/process/unstable.html#stabilization [nightly version]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html ## List of unstable features * Unstable-specific features * [-Z allow-features](#allow-features) --- Provides a way to restrict which unstable features are used. * Build scripts and linking * [Metabuild](#metabuild) --- Provides declarative build scripts. * [Multiple Build Scripts](#multiple-build-scripts) --- Allows use of multiple build scripts. * Resolver and features * [no-index-update](#no-index-update) --- Prevents cargo from updating the index cache. * [avoid-dev-deps](#avoid-dev-deps) --- Prevents the resolver from including dev-dependencies during resolution. * [minimal-versions](#minimal-versions) --- Forces the resolver to use the lowest compatible version instead of the highest. * [direct-minimal-versions](#direct-minimal-versions) — Forces the resolver to use the lowest compatible version instead of the highest. * [public-dependency](#public-dependency) --- Allows dependencies to be classified as either public or private. * [msrv-policy](#msrv-policy) --- MSRV-aware resolver and version selection * [precise-pre-release](#precise-pre-release) --- Allows pre-release versions to be selected with `update --precise` * [sbom](#sbom) --- Generates SBOM pre-cursor files for compiled artifacts * [update-breaking](#update-breaking) --- Allows upgrading to breaking versions with `update --breaking` * [feature-unification](#feature-unification) --- Enable new feature unification modes in workspaces * Output behavior * [artifact-dir](#artifact-dir) --- Adds a directory where artifacts are copied to. * [build-dir](#build-dir) --- Adds a directory where intermediate build artifacts are stored. * [Different binary name](#different-binary-name) --- Assign a name to the built binary that is separate from the crate name. * [root-dir](#root-dir) --- Controls the root directory relative to which paths are printed * Compile behavior * [mtime-on-use](#mtime-on-use) --- Updates the last-modified timestamp on every dependency every time it is used, to provide a mechanism to delete unused artifacts. * [build-std](#build-std) --- Builds the standard library instead of using pre-built binaries. * [build-std-features](#build-std-features) --- Sets features to use with the standard library. * [binary-dep-depinfo](#binary-dep-depinfo) --- Causes the dep-info file to track binary dependencies. * [checksum-freshness](#checksum-freshness) --- When passed, the decision as to whether a crate needs to be rebuilt is made using file checksums instead of the file mtime. * [panic-abort-tests](#panic-abort-tests) --- Allows running tests with the "abort" panic strategy. * [host-config](#host-config) --- Allows setting `[target]`-like configuration settings for host build targets. * [no-embed-metadata](#no-embed-metadata) --- Passes `-Zembed-metadata=no` to the compiler, which avoid embedding metadata into rlib and dylib artifacts, to save disk space. * [target-applies-to-host](#target-applies-to-host) --- Alters whether certain flags will be passed to host build targets. * [gc](#gc) --- Global cache garbage collection. * [open-namespaces](#open-namespaces) --- Allow multiple packages to participate in the same API namespace * rustdoc * [rustdoc-map](#rustdoc-map) --- Provides mappings for documentation to link to external sites like [docs.rs](https://docs.rs/). * [scrape-examples](#scrape-examples) --- Shows examples within documentation. * [output-format](#output-format-for-rustdoc) --- Allows documentation to also be emitted in the experimental [JSON format](https://doc.rust-lang.org/nightly/nightly-rustc/rustdoc_json_types/). * [rustdoc-depinfo](#rustdoc-depinfo) --- Use dep-info files in rustdoc rebuild detection. * `Cargo.toml` extensions * [Profile `rustflags` option](#profile-rustflags-option) --- Passed directly to rustc. * [Profile `hint-mostly-unused` option](#profile-hint-mostly-unused-option) --- Hint that a dependency is mostly unused, to optimize compilation time. * [codegen-backend](#codegen-backend) --- Select the codegen backend used by rustc. * [per-package-target](#per-package-target) --- Sets the `--target` to use for each individual package. * [artifact dependencies](#artifact-dependencies) --- Allow build artifacts to be included into other build artifacts and build them for different targets. * [Profile `trim-paths` option](#profile-trim-paths-option) --- Control the sanitization of file paths in build outputs. * [`[lints.cargo]`](#lintscargo) --- Allows configuring lints for Cargo. * [path bases](#path-bases) --- Named base directories for path dependencies. * [`unstable-editions`](#unstable-editions) --- Allows use of editions that are not yet stable. * Information and metadata * [Build-plan](#build-plan) --- Emits JSON information on which commands will be run. * [unit-graph](#unit-graph) --- Emits JSON for Cargo's internal graph structure. * [`cargo rustc --print`](#rustc---print) --- Calls rustc with `--print` to display information from rustc. * Configuration * [config-include](#config-include) --- Adds the ability for config files to include other files. * [`cargo config`](#cargo-config) --- Adds a new subcommand for viewing config files. * Registries * [publish-timeout](#publish-timeout) --- Controls the timeout between uploading the crate and being available in the index * [asymmetric-token](#asymmetric-token) --- Adds support for authentication tokens using asymmetric cryptography (`cargo:paseto` provider). * Other * [gitoxide](#gitoxide) --- Use `gitoxide` instead of `git2` for a set of operations. * [script](#script) --- Enable support for single-file `.rs` packages. * [lockfile-path](#lockfile-path) --- Allows to specify a path to lockfile other than the default path `/Cargo.lock`. * [package-workspace](#package-workspace) --- Allows for packaging and publishing multiple crates in a workspace. * [native-completions](#native-completions) --- Move cargo shell completions to native completions. * [warnings](#warnings) --- controls warning behavior; options for allowing or denying warnings. * [Package message format](#package-message-format) --- Message format for `cargo package`. * [`fix-edition`](#fix-edition) --- A permanently unstable edition migration helper. ## allow-features This permanently-unstable flag makes it so that only a listed set of unstable features can be used. Specifically, if you pass `-Zallow-features=foo,bar`, you'll continue to be able to pass `-Zfoo` and `-Zbar` to `cargo`, but you will be unable to pass `-Zbaz`. You can pass an empty string (`-Zallow-features=`) to disallow all unstable features. `-Zallow-features` also restricts which unstable features can be passed to the `cargo-features` entry in `Cargo.toml`. If, for example, you want to allow ```toml cargo-features = ["test-dummy-unstable"] ``` where `test-dummy-unstable` is unstable, that features would also be disallowed by `-Zallow-features=`, and allowed with `-Zallow-features=test-dummy-unstable`. The list of features passed to cargo's `-Zallow-features` is also passed to any Rust tools that cargo ends up calling (like `rustc` or `rustdoc`). Thus, if you run `cargo -Zallow-features=`, no unstable Cargo _or_ Rust features can be used. ## no-index-update * Original Issue: [#3479](https://github.com/rust-lang/cargo/issues/3479) * Tracking Issue: [#7404](https://github.com/rust-lang/cargo/issues/7404) The `-Z no-index-update` flag ensures that Cargo does not attempt to update the registry index. This is intended for tools such as Crater that issue many Cargo commands, and you want to avoid the network latency for updating the index each time. ## mtime-on-use * Original Issue: [#6477](https://github.com/rust-lang/cargo/pull/6477) * Cache usage meta tracking issue: [#7150](https://github.com/rust-lang/cargo/issues/7150) The `-Z mtime-on-use` flag is an experiment to have Cargo update the mtime of used files to make it easier for tools like cargo-sweep to detect which files are stale. For many workflows this needs to be set on *all* invocations of cargo. To make this more practical setting the `unstable.mtime_on_use` flag in `.cargo/config.toml` or the corresponding ENV variable will apply the `-Z mtime-on-use` to all invocations of nightly cargo. (the config flag is ignored by stable) ## avoid-dev-deps * Original Issue: [#4988](https://github.com/rust-lang/cargo/issues/4988) * Tracking Issue: [#5133](https://github.com/rust-lang/cargo/issues/5133) When running commands such as `cargo install` or `cargo build`, Cargo currently requires dev-dependencies to be downloaded, even if they are not used. The `-Z avoid-dev-deps` flag allows Cargo to avoid downloading dev-dependencies if they are not needed. The `Cargo.lock` file will not be generated if dev-dependencies are skipped. ## minimal-versions * Original Issue: [#4100](https://github.com/rust-lang/cargo/issues/4100) * Tracking Issue: [#5657](https://github.com/rust-lang/cargo/issues/5657) > Note: It is not recommended to use this feature. Because it enforces minimal > versions for all transitive dependencies, its usefulness is limited since > not all external dependencies declare proper lower version bounds. It is > intended that it will be changed in the future to only enforce minimal > versions for direct dependencies. When a `Cargo.lock` file is generated, the `-Z minimal-versions` flag will resolve the dependencies to the minimum SemVer version that will satisfy the requirements (instead of the greatest version). The intended use-case of this flag is to check, during continuous integration, that the versions specified in Cargo.toml are a correct reflection of the minimum versions that you are actually using. That is, if Cargo.toml says `foo = "1.0.0"` that you don't accidentally depend on features added only in `foo 1.5.0`. ## direct-minimal-versions * Original Issue: [#4100](https://github.com/rust-lang/cargo/issues/4100) * Tracking Issue: [#5657](https://github.com/rust-lang/cargo/issues/5657) When a `Cargo.lock` file is generated, the `-Z direct-minimal-versions` flag will resolve the dependencies to the minimum SemVer version that will satisfy the requirements (instead of the greatest version) for direct dependencies only. The intended use-case of this flag is to check, during continuous integration, that the versions specified in Cargo.toml are a correct reflection of the minimum versions that you are actually using. That is, if Cargo.toml says `foo = "1.0.0"` that you don't accidentally depend on features added only in `foo 1.5.0`. Indirect dependencies are resolved as normal so as not to be blocked on their minimal version validation. ## artifact-dir * Original Issue: [#4875](https://github.com/rust-lang/cargo/issues/4875) * Tracking Issue: [#6790](https://github.com/rust-lang/cargo/issues/6790) This feature allows you to specify the directory where artifacts will be copied to after they are built. Typically artifacts are only written to the `target/release` or `target/debug` directories. However, determining the exact filename can be tricky since you need to parse JSON output. The `--artifact-dir` flag makes it easier to predictably access the artifacts. Note that the artifacts are copied, so the originals are still in the `target` directory. Example: ```sh cargo +nightly build --artifact-dir=out -Z unstable-options ``` This can also be specified in `.cargo/config.toml` files. ```toml [build] artifact-dir = "out" ``` ## build-dir * Original Issue: [#14125](https://github.com/rust-lang/cargo/issues/14125) * Tracking Issue: [#14125](https://github.com/rust-lang/cargo/issues/14125) The directory where intermediate build artifacts will be stored. Intermediate artifacts are produced by Rustc/Cargo during the build process. ```toml [build] build-dir = "out" ``` ### `build.build-dir` * Type: string (path) * Default: Defaults to the value of `build.target-dir` * Environment: `CARGO_BUILD_BUILD_DIR` The path to where internal files used as part of the build are placed. This option supports path templating. Available template variables: * `{workspace-root}` resolves to root of the current workspace. * `{cargo-cache-home}` resolves to `CARGO_HOME` * `{workspace-path-hash}` resolves to a hash of the manifest path ## root-dir * Original Issue: [#9887](https://github.com/rust-lang/cargo/issues/9887) * Tracking Issue: None (not currently slated for stabilization) The `-Zroot-dir` flag sets the root directory relative to which paths are printed. This affects both diagnostics and paths emitted by the `file!()` macro. ## Build-plan * Tracking Issue: [#5579](https://github.com/rust-lang/cargo/issues/5579)

The `--build-plan` argument for the `build` command will output JSON with information about which commands would be run without actually executing anything. This can be useful when integrating with another build tool. Example: ```sh cargo +nightly build --build-plan -Z unstable-options ``` ## Metabuild * Tracking Issue: [rust-lang/rust#49803](https://github.com/rust-lang/rust/issues/49803) * RFC: [#2196](https://github.com/rust-lang/rfcs/blob/master/text/2196-metabuild.md) Metabuild is a feature to have declarative build scripts. Instead of writing a `build.rs` script, you specify a list of build dependencies in the `metabuild` key in `Cargo.toml`. A build script is automatically generated that runs each build dependency in order. Metabuild packages can then read metadata from `Cargo.toml` to specify their behavior. Include `cargo-features` at the top of `Cargo.toml`, a `metabuild` key in the `package`, list the dependencies in `build-dependencies`, and add any metadata that the metabuild packages require under `package.metadata`. Example: ```toml cargo-features = ["metabuild"] [package] name = "mypackage" version = "0.0.1" metabuild = ["foo", "bar"] [build-dependencies] foo = "1.0" bar = "1.0" [package.metadata.foo] extra-info = "qwerty" ``` Metabuild packages should have a public function called `metabuild` that performs the same actions as a regular `build.rs` script would perform. ## Multiple Build Scripts * Tracking Issue: [#14903](https://github.com/rust-lang/cargo/issues/14903) * Original Pull Request: [#15630](https://github.com/rust-lang/cargo/pull/15630) Multiple Build Scripts feature allows you to have multiple build scripts in your package. Include `cargo-features` at the top of `Cargo.toml` and add `multiple-build-scripts` to enable feature. Add the paths of the build scripts as an array in `package.build`. For example: ```toml cargo-features = ["multiple-build-scripts"] [package] name = "mypackage" version = "0.0.1" build = ["foo.rs", "bar.rs"] ``` ## public-dependency * Tracking Issue: [#44663](https://github.com/rust-lang/rust/issues/44663) The 'public-dependency' feature allows marking dependencies as 'public' or 'private'. When this feature is enabled, additional information is passed to rustc to allow the [exported_private_dependencies](../../rustc/lints/listing/warn-by-default.html#exported-private-dependencies) lint to function properly. To enable this feature, you can either use `-Zpublic-dependency` ```sh cargo +nightly run -Zpublic-dependency ``` or `[unstable]` table, for example, ```toml # .cargo/config.toml [unstable] public-dependency = true ``` `public-dependency` could also be enabled in `cargo-features`, **though this is deprecated and will be removed soon**. ```toml cargo-features = ["public-dependency"] [dependencies] my_dep = { version = "1.2.3", public = true } private_dep = "2.0.0" # Will be 'private' by default ``` Documentation updates: - For workspace's "The `dependencies` table" section, include `public` as an unsupported field for `workspace.dependencies` ## msrv-policy - [RFC: MSRV-aware Resolver](https://rust-lang.github.io/rfcs/3537-msrv-resolver.html) - [#9930](https://github.com/rust-lang/cargo/issues/9930) (MSRV-aware resolver) Catch-all unstable feature for MSRV-aware cargo features under [RFC 2495](https://github.com/rust-lang/rfcs/pull/2495). ### MSRV-aware cargo add This was stabilized in 1.79 in [#13608](https://github.com/rust-lang/cargo/pull/13608). ### MSRV-aware resolver This was stabilized in 1.84 in [#14639](https://github.com/rust-lang/cargo/pull/14639). ### Convert `incompatible_toolchain` error into a lint Unimplemented ### `--update-rust-version` flag for `cargo add`, `cargo update` Unimplemented ### `package.rust-version = "toolchain"` Unimplemented ### Update `cargo new` template to set `package.rust-version = "toolchain"` Unimplemented ## precise-pre-release * Tracking Issue: [#13290](https://github.com/rust-lang/cargo/issues/13290) * RFC: [#3493](https://github.com/rust-lang/rfcs/pull/3493) The `precise-pre-release` feature allows pre-release versions to be selected with `update --precise` even when a pre-release is not specified by a projects `Cargo.toml`. Take for example this `Cargo.toml`. ```toml [dependencies] my-dependency = "0.1.1" ``` It's possible to update `my-dependency` to a pre-release with `update -Zunstable-options my-dependency --precise 0.1.2-pre.0`. This is because `0.1.2-pre.0` is considered compatible with `0.1.1`. It would not be possible to upgrade to `0.2.0-pre.0` from `0.1.1` in the same way. ## sbom * Tracking Issue: [#13709](https://github.com/rust-lang/cargo/pull/13709) * RFC: [#3553](https://github.com/rust-lang/rfcs/pull/3553) The `sbom` build config allows to generate so-called SBOM pre-cursor files alongside each compiled artifact. A Software Bill Of Material (SBOM) tool can incorporate these generated files to collect important information from the cargo build process that are difficult or impossible to obtain in another way. To enable this feature either set the `sbom` field in the `.cargo/config.toml` ```toml [unstable] sbom = true [build] sbom = true ``` or set the `CARGO_BUILD_SBOM` environment variable to `true`. The functionality is available behind the flag `-Z sbom`. The generated output files are in JSON format and follow the naming scheme `.cargo-sbom.json`. The JSON file contains information about dependencies, target, features and the used `rustc` compiler. SBOM pre-cursor files are generated for all executable and linkable outputs that are uplifted into the target or artifact directories. ### Environment variables Cargo sets for crates * `CARGO_SBOM_PATH` -- a list of generated SBOM precursor files, separated by the platform PATH separator. The list can be split with `std::env::split_paths`. ### SBOM pre-cursor schema ```json5 { // Schema version. "version": 1, // Index into the crates array for the root crate. "root": 0, // Array of all crates. There may be duplicates of the same crate if that // crate is compiled differently (different opt-level, features, etc). "crates": [ { // Fully qualified package ID specification "id": "path+file:///sample-package#0.1.0", // List of target kinds: bin, lib, rlib, dylib, cdylib, staticlib, proc-macro, example, test, bench, custom-build "kind": ["bin"], // Enabled feature flags. "features": [], // Dependencies for this crate. "dependencies": [ { // Index in to the crates array. "index": 1, // Dependency kind: // Normal: A dependency linked to the artifact produced by this crate. // Build: A compile-time dependency used to build this crate (build-script or proc-macro). "kind": "normal" }, { // A crate can depend on another crate with both normal and build edges. "index": 1, "kind": "build" } ] }, { "id": "registry+https://github.com/rust-lang/crates.io-index#zerocopy@0.8.16", "kind": ["bin"], "features": [], "dependencies": [] } ], // Information about rustc used to perform the compilation. "rustc": { // Compiler version "version": "1.86.0-nightly", // Compiler wrapper "wrapper": null, // Compiler workspace wrapper "workspace_wrapper": null, // Commit hash for rustc "commit_hash": "bef3c3b01f690de16738b1c9f36470fbfc6ac623", // Host target triple "host": "x86_64-pc-windows-msvc", // Verbose version string: `rustc -vV` "verbose_version": "rustc 1.86.0-nightly (bef3c3b01 2025-02-04)\nbinary: rustc\ncommit-hash: bef3c3b01f690de16738b1c9f36470fbfc6ac623\ncommit-date: 2025-02-04\nhost: x86_64-pc-windows-msvc\nrelease: 1.86.0-nightly\nLLVM version: 19.1.7\n" } } ``` ## update-breaking * Tracking Issue: [#12425](https://github.com/rust-lang/cargo/issues/12425) Allow upgrading dependencies version requirements in `Cargo.toml` across SemVer incompatible versions using with the `--breaking` flag. This only applies to dependencies when - The package is a dependency of a workspace member - The dependency is not renamed - A SemVer-incompatible version is available - The "SemVer operator" is used (`^` which is the default) Users may further restrict which packages get upgraded by specifying them on the command line. Example: ```console $ cargo +nightly -Zunstable-options update --breaking $ cargo +nightly -Zunstable-options update --breaking clap ``` *This is meant to fill a similar role as [cargo-upgrade](https://github.com/killercup/cargo-edit/)* ## build-std * Tracking Repository: The `build-std` feature enables Cargo to compile the standard library itself as part of a crate graph compilation. This feature has also historically been known as "std-aware Cargo". This feature is still in very early stages of development, and is also a possible massive feature addition to Cargo. This is a very large feature to document, even in the minimal form that it exists in today, so if you're curious to stay up to date you'll want to follow the [tracking repository](https://github.com/rust-lang/wg-cargo-std-aware) and its set of issues. The functionality implemented today is behind a flag called `-Z build-std`. This flag indicates that Cargo should compile the standard library from source code using the same profile as the main build itself. Note that for this to work you need to have the source code for the standard library available, and at this time the only supported method of doing so is to add the `rust-src` rust rustup component: ```console $ rustup component add rust-src --toolchain nightly ``` Usage looks like: ```console $ cargo new foo $ cd foo $ cargo +nightly run -Z build-std --target x86_64-unknown-linux-gnu Compiling core v0.0.0 (...) ... Compiling foo v0.1.0 (...) Finished dev [unoptimized + debuginfo] target(s) in 21.00s Running `target/x86_64-unknown-linux-gnu/debug/foo` Hello, world! ``` Here we recompiled the standard library in debug mode with debug assertions (like `src/main.rs` is compiled) and everything was linked together at the end. Using `-Z build-std` will implicitly compile the stable crates `core`, `std`, `alloc`, and `proc_macro`. If you're using `cargo test` it will also compile the `test` crate. If you're working with an environment which does not support some of these crates, then you can pass an argument to `-Zbuild-std` as well: ```console $ cargo +nightly build -Z build-std=core,alloc ``` The value here is a comma-separated list of standard library crates to build. ### Requirements As a summary, a list of requirements today to use `-Z build-std` are: * You must install libstd's source code through `rustup component add rust-src` * You must use both a nightly Cargo and a nightly rustc * The `-Z build-std` flag must be passed to all `cargo` invocations. ### Reporting bugs and helping out The `-Z build-std` feature is in the very early stages of development! This feature for Cargo has an extremely long history and is very large in scope, and this is just the beginning. If you'd like to report bugs please either report them to: * Cargo --- --- for implementation bugs * The tracking repository --- --- for larger design questions. Also if you'd like to see a feature that's not yet implemented and/or if something doesn't quite work the way you'd like it to, feel free to check out the [issue tracker](https://github.com/rust-lang/wg-cargo-std-aware/issues) of the tracking repository, and if it's not there please file a new issue! ## build-std-features * Tracking Repository: This flag is a sibling to the `-Zbuild-std` feature flag. This will configure the features enabled for the standard library itself when building the standard library. The default enabled features, at this time, are `backtrace` and `panic-unwind`. This flag expects a comma-separated list and, if provided, will override the default list of features enabled. ## binary-dep-depinfo * Tracking rustc issue: [#63012](https://github.com/rust-lang/rust/issues/63012) The `-Z binary-dep-depinfo` flag causes Cargo to forward the same flag to `rustc` which will then cause `rustc` to include the paths of all binary dependencies in the "dep info" file (with the `.d` extension). Cargo then uses that information for change-detection (if any binary dependency changes, then the crate will be rebuilt). The primary use case is for building the compiler itself, which has implicit dependencies on the standard library that would otherwise be untracked for change-detection. ## checksum-freshness * Tracking issue: [#14136](https://github.com/rust-lang/cargo/issues/14136) The `-Z checksum-freshness` flag will replace the use of file mtimes in cargo's fingerprints with a file checksum value. This is most useful on systems with a poor mtime implementation, or in CI/CD. The checksum algorithm can change without notice between cargo versions. Fingerprints are used by cargo to determine when a crate needs to be rebuilt. For the time being files ingested by build script will continue to use mtimes, even when `checksum-freshness` is enabled. This is not intended as a long term solution. ## panic-abort-tests * Tracking Issue: [#67650](https://github.com/rust-lang/rust/issues/67650) * Original Pull Request: [#7460](https://github.com/rust-lang/cargo/pull/7460) The `-Z panic-abort-tests` flag will enable nightly support to compile test harness crates with `-Cpanic=abort`. Without this flag Cargo will compile tests, and everything they depend on, with `-Cpanic=unwind` because it's the only way `test`-the-crate knows how to operate. As of [rust-lang/rust#64158], however, the `test` crate supports `-C panic=abort` with a test-per-process, and can help avoid compiling crate graphs multiple times. It's currently unclear how this feature will be stabilized in Cargo, but we'd like to stabilize it somehow! [rust-lang/rust#64158]: https://github.com/rust-lang/rust/pull/64158 ## config-include * Tracking Issue: [#7723](https://github.com/rust-lang/cargo/issues/7723) This feature requires the `-Zconfig-include` command-line option. The `include` key in a config file can be used to load another config file. It takes a string for a path to another file relative to the config file, or an array of config file paths. Only path ending with `.toml` is accepted. ```toml # a path ending with `.toml` include = "path/to/mordor.toml" # or an array of paths include = ["frodo.toml", "samwise.toml"] ``` Unlike other config values, the merge behavior of the `include` key is different. When a config file contains an `include` key: 1. The config values are first loaded from the `include` path. * If the value of the `include` key is an array of paths, the config values are loaded and merged from left to right for each path. * Recurse this step if the config values from the `include` path also contain an `include` key. 2. Then, the config file's own values are merged on top of the config from the `include` path. ## target-applies-to-host * Original Pull Request: [#9322](https://github.com/rust-lang/cargo/pull/9322) * Tracking Issue: [#9453](https://github.com/rust-lang/cargo/issues/9453) Historically, Cargo's behavior for whether the `linker` and `rustflags` configuration options from environment variables and [`[target]`](config.md#target) are respected for build scripts, plugins, and other artifacts that are _always_ built for the host platform has been somewhat inconsistent. When `--target` is _not_ passed, Cargo respects the same `linker` and `rustflags` for build scripts as for all other compile artifacts. When `--target` _is_ passed, however, Cargo respects `linker` from [`[target.]`](config.md#targettriplelinker), and does not pick up any `rustflags` configuration. This dual behavior is confusing, but also makes it difficult to correctly configure builds where the host triple and the [target triple] happen to be the same, but artifacts intended to run on the build host should still be configured differently. `-Ztarget-applies-to-host` enables the top-level `target-applies-to-host` setting in Cargo configuration files which allows users to opt into different (and more consistent) behavior for these properties. When `target-applies-to-host` is unset, or set to `true`, in the configuration file, the existing Cargo behavior is preserved (though see `-Zhost-config`, which changes that default). When it is set to `false`, no options from `[target.]`, `RUSTFLAGS`, or `[build]` are respected for host artifacts regardless of whether `--target` is passed to Cargo. To customize artifacts intended to be run on the host, use `[host]` ([`host-config`](#host-config)). In the future, `target-applies-to-host` may end up defaulting to `false` to provide more sane and consistent default behavior. ```toml # config.toml target-applies-to-host = false ``` ```console cargo +nightly -Ztarget-applies-to-host build --target x86_64-unknown-linux-gnu ``` ## host-config * Original Pull Request: [#9322](https://github.com/rust-lang/cargo/pull/9322) * Tracking Issue: [#9452](https://github.com/rust-lang/cargo/issues/9452) The `host` key in a config file can be used to pass flags to host build targets such as build scripts that must run on the host system instead of the target system when cross compiling. It supports both generic and host arch specific tables. Matching host arch tables take precedence over generic host tables. It requires the `-Zhost-config` and `-Ztarget-applies-to-host` command-line options to be set, and that `target-applies-to-host = false` is set in the Cargo configuration file. ```toml # config.toml [host] linker = "/path/to/host/linker" [host.x86_64-unknown-linux-gnu] linker = "/path/to/host/arch/linker" rustflags = ["-Clink-arg=--verbose"] [target.x86_64-unknown-linux-gnu] linker = "/path/to/target/linker" ``` The generic `host` table above will be entirely ignored when building on an `x86_64-unknown-linux-gnu` host as the `host.x86_64-unknown-linux-gnu` table takes precedence. Setting `-Zhost-config` changes the default for `target-applies-to-host` to `false` from `true`. ```console cargo +nightly -Ztarget-applies-to-host -Zhost-config build --target x86_64-unknown-linux-gnu ``` ## unit-graph * Tracking Issue: [#8002](https://github.com/rust-lang/cargo/issues/8002) The `--unit-graph` flag can be passed to any build command (`build`, `check`, `run`, `test`, `bench`, `doc`, etc.) to emit a JSON object to stdout which represents Cargo's internal unit graph. Nothing is actually built, and the command returns immediately after printing. Each "unit" corresponds to an execution of the compiler. These objects also include which unit each unit depends on. ``` cargo +nightly build --unit-graph -Z unstable-options ``` This structure provides a more complete view of the dependency relationship as Cargo sees it. In particular, the "features" field supports the new feature resolver where a dependency can be built multiple times with different features. `cargo metadata` fundamentally cannot represent the relationship of features between different dependency kinds, and features now depend on which command is run and which packages and targets are selected. Additionally it can provide details about intra-package dependencies like build scripts or tests. The following is a description of the JSON structure: ```javascript { /* Version of the JSON output structure. If any backwards incompatible changes are made, this value will be increased. */ "version": 1, /* Array of all build units. */ "units": [ { /* An opaque string which indicates the package. Information about the package can be obtained from `cargo metadata`. */ "pkg_id": "my-package 0.1.0 (path+file:///path/to/my-package)", /* The Cargo target. See the `cargo metadata` documentation for more information about these fields. https://doc.rust-lang.org/cargo/commands/cargo-metadata.html */ "target": { "kind": ["lib"], "crate_types": ["lib"], "name": "my_package", "src_path": "/path/to/my-package/src/lib.rs", "edition": "2018", "test": true, "doctest": true }, /* The profile settings for this unit. These values may not match the profile defined in the manifest. Units can use modified profile settings. For example, the "panic" setting can be overridden for tests to force it to "unwind". */ "profile": { /* The profile name these settings are derived from. */ "name": "dev", /* The optimization level as a string. */ "opt_level": "0", /* The LTO setting as a string. */ "lto": "false", /* The codegen units as an integer. `null` if it should use the compiler's default. */ "codegen_units": null, /* The debug information level as an integer. `null` if it should use the compiler's default (0). */ "debuginfo": 2, /* Whether or not debug-assertions are enabled. */ "debug_assertions": true, /* Whether or not overflow-checks are enabled. */ "overflow_checks": true, /* Whether or not rpath is enabled. */ "rpath": false, /* Whether or not incremental is enabled. */ "incremental": true, /* The panic strategy, "unwind" or "abort". */ "panic": "unwind" }, /* Which platform this target is being built for. A value of `null` indicates it is for the host. Otherwise it is a string of the target triple (such as "x86_64-unknown-linux-gnu"). */ "platform": null, /* The "mode" for this unit. Valid values: * "test" --- Build using `rustc` as a test. * "build" --- Build using `rustc`. * "check" --- Build using `rustc` in "check" mode. * "doc" --- Build using `rustdoc`. * "doctest" --- Test using `rustdoc`. * "run-custom-build" --- Represents the execution of a build script. */ "mode": "build", /* Array of features enabled on this unit as strings. */ "features": ["somefeat"], /* Whether or not this is a standard-library unit, part of the unstable build-std feature. If not set, treat as `false`. */ "is_std": false, /* Array of dependencies of this unit. */ "dependencies": [ { /* Index in the "units" array for the dependency. */ "index": 1, /* The name that this dependency will be referred as. */ "extern_crate_name": "unicode_xid", /* Whether or not this dependency is "public", part of the unstable public-dependency feature. If not set, the public-dependency feature is not enabled. */ "public": false, /* Whether or not this dependency is injected into the prelude, currently used by the build-std feature. If not set, treat as `false`. */ "noprelude": false } ] }, // ... ], /* Array of indices in the "units" array that are the "roots" of the dependency graph. */ "roots": [0], } ``` ## Profile `rustflags` option * Original Issue: [rust-lang/cargo#7878](https://github.com/rust-lang/cargo/issues/7878) * Tracking Issue: [rust-lang/cargo#10271](https://github.com/rust-lang/cargo/issues/10271) This feature provides a new option in the `[profile]` section to specify flags that are passed directly to rustc. This can be enabled like so: ```toml cargo-features = ["profile-rustflags"] [package] # ... [profile.release] rustflags = [ "-C", "..." ] ``` To set this in a profile in Cargo configuration, you need to use either `-Z profile-rustflags` or `[unstable]` table to enable it. For example, ```toml # .cargo/config.toml [unstable] profile-rustflags = true [profile.release] rustflags = [ "-C", "..." ] ``` ## Profile `hint-mostly-unused` option * Tracking Issue: [#15644](https://github.com/rust-lang/cargo/issues/15644) This feature provides a new option in the `[profile]` section to enable the rustc `hint-mostly-unused` option. This is primarily useful to enable for specific dependencies: ```toml [profile.dev.package.huge-mostly-unused-dependency] hint-mostly-unused = true ``` To enable this feature, pass `-Zprofile-hint-mostly-unused`. However, since this option is a hint, using it without passing `-Zprofile-hint-mostly-unused` will only warn and ignore the profile option. Versions of Cargo prior to the introduction of this feature will give an "unused manifest key" warning, but will otherwise function without erroring. This allows using the hint in a crate's `Cargo.toml` without mandating the use of a newer Cargo to build it. A crate can also provide this hint automatically for crates that depend on it, using the `[hints]` table (which will likewise be ignored by older Cargo): ```toml [hints] mostly-unused = true ``` This will cause the crate to default to hint-mostly-unused, unless overridden via `profile`, which takes precedence, and which can only be specified in the top-level crate being built. ## rustdoc-map * Tracking Issue: [#8296](https://github.com/rust-lang/cargo/issues/8296) This feature adds configuration settings that are passed to `rustdoc` so that it can generate links to dependencies whose documentation is hosted elsewhere when the dependency is not documented. First, add this to `.cargo/config`: ```toml [doc.extern-map.registries] crates-io = "https://docs.rs/" ``` Then, when building documentation, use the following flags to cause links to dependencies to link to [docs.rs](https://docs.rs/): ``` cargo +nightly doc --no-deps -Zrustdoc-map ``` The `registries` table contains a mapping of registry name to the URL to link to. The URL may have the markers `{pkg_name}` and `{version}` which will get replaced with the corresponding values. If neither are specified, then Cargo defaults to appending `{pkg_name}/{version}/` to the end of the URL. Another config setting is available to redirect standard library links. By default, rustdoc creates links to . To change this behavior, use the `doc.extern-map.std` setting: ```toml [doc.extern-map] std = "local" ``` A value of `"local"` means to link to the documentation found in the `rustc` sysroot. If you are using rustup, this documentation can be installed with `rustup component add rust-docs`. The default value is `"remote"`. The value may also take a URL for a custom location. ## per-package-target * Tracking Issue: [#9406](https://github.com/rust-lang/cargo/pull/9406) * Original Pull Request: [#9030](https://github.com/rust-lang/cargo/pull/9030) * Original Issue: [#7004](https://github.com/rust-lang/cargo/pull/7004) The `per-package-target` feature adds two keys to the manifest: `package.default-target` and `package.forced-target`. The first makes the package be compiled by default (ie. when no `--target` argument is passed) for some target. The second one makes the package always be compiled for the target. Example: ```toml [package] forced-target = "wasm32-unknown-unknown" ``` In this example, the crate is always built for `wasm32-unknown-unknown`, for instance because it is going to be used as a plugin for a main program that runs on the host (or provided on the command line) target. ## artifact-dependencies * Tracking Issue: [#9096](https://github.com/rust-lang/cargo/pull/9096) * Original Pull Request: [#9992](https://github.com/rust-lang/cargo/pull/9992) Artifact dependencies allow Cargo packages to depend on `bin`, `cdylib`, and `staticlib` crates, and use the artifacts built by those crates at compile time. Run `cargo` with `-Z bindeps` to enable this functionality. ### artifact-dependencies: Dependency declarations Artifact-dependencies adds the following keys to a dependency declaration in `Cargo.toml`: - `artifact` --- This specifies the [Cargo Target](cargo-targets.md) to build. Normally without this field, Cargo will only build the `[lib]` target from a dependency. This field allows specifying which target will be built, and made available as a binary at build time: * `"bin"` --- Compiled executable binaries, corresponding to all of the `[[bin]]` sections in the dependency's manifest. * `"bin:"` --- Compiled executable binary, corresponding to a specific binary target specified by the given ``. * `"cdylib"` --- A C-compatible dynamic library, corresponding to a `[lib]` section with `crate-type = ["cdylib"]` in the dependency's manifest. * `"staticlib"` --- A C-compatible static library, corresponding to a `[lib]` section with `crate-type = ["staticlib"]` in the dependency's manifest. The `artifact` value can be a string, or it can be an array of strings to specify multiple targets. Example: ```toml [dependencies] bar = { version = "1.0", artifact = "staticlib" } zoo = { version = "1.0", artifact = ["bin:cat", "bin:dog"]} ``` - `lib` --- This is a Boolean value which indicates whether or not to also build the dependency's library as a normal Rust `lib` dependency. This field can only be specified when `artifact` is specified. The default for this field is `false` when `artifact` is specified. If this is set to `true`, then the dependency's `[lib]` target will also be built for the platform target the declaring package is being built for. This allows the package to use the dependency from Rust code like a normal dependency in addition to an artifact dependency. Example: ```toml [dependencies] bar = { version = "1.0", artifact = "bin", lib = true } ``` - `target` --- The platform target to build the dependency for. This field can only be specified when `artifact` is specified. The default if this is not specified depends on the dependency kind. For build dependencies, it will be built for the host target. For all other dependencies, it will be built for the same targets the declaring package is built for. For a build dependency, this can also take the special value of `"target"` which means to build the dependency for the same targets that the package is being built for. ```toml [build-dependencies] bar = { version = "1.0", artifact = "cdylib", target = "wasm32-unknown-unknown"} same-target = { version = "1.0", artifact = "bin", target = "target" } ``` ### artifact-dependencies: Environment variables After building an artifact dependency, Cargo provides the following environment variables that you can use to access the artifact: - `CARGO__DIR_` --- This is the directory containing all the artifacts from the dependency. `` is the `artifact` specified for the dependency (uppercased as in `CDYLIB`, `STATICLIB`, or `BIN`) and `` is the name of the dependency. As with other Cargo environment variables, dependency names are converted to uppercase, with dashes replaced by underscores. If your manifest renames the dependency, `` corresponds to the name you specify, not the original package name. - `CARGO__FILE__` --- This is the full path to the artifact. `` is the `artifact` specified for the dependency (uppercased as above), `` is the name of the dependency (transformed as above), and `` is the name of the artifact from the dependency. Note that `` is not modified in any way from the `name` specified in the crate supplying the artifact, or the crate name if not specified; for instance, it may be in lowercase, or contain dashes. For convenience, if the artifact name matches the original package name, cargo additionally supplies a copy of this variable with the `_` suffix omitted. For instance, if the `cmake` crate supplies a binary named `cmake`, Cargo supplies both `CARGO_BIN_FILE_CMAKE` and `CARGO_BIN_FILE_CMAKE_cmake`. For each kind of dependency, these variables are supplied to the same part of the build process that has access to that kind of dependency: - For build-dependencies, these variables are supplied to the `build.rs` script, and can be accessed using [`std::env::var_os`](https://doc.rust-lang.org/std/env/fn.var_os.html). (As with any OS file path, these may or may not be valid UTF-8.) - For normal dependencies, these variables are supplied during the compilation of the crate, and can be accessed using the [`env!`] macro. - For dev-dependencies, these variables are supplied during the compilation of examples, tests, and benchmarks, and can be accessed using the [`env!`] macro. [`env!`]: https://doc.rust-lang.org/std/macro.env.html ### artifact-dependencies: Examples #### Example: use a binary executable from a build script In the `Cargo.toml` file, you can specify a dependency on a binary to make available for a build script: ```toml [build-dependencies] some-build-tool = { version = "1.0", artifact = "bin" } ``` Then inside the build script, the binary can be executed at build time: ```rust fn main() { let build_tool = std::env::var_os("CARGO_BIN_FILE_SOME_BUILD_TOOL").unwrap(); let status = std::process::Command::new(build_tool) .arg("do-stuff") .status() .unwrap(); if !status.success() { eprintln!("failed!"); std::process::exit(1); } } ``` #### Example: use _cdylib_ artifact in build script The `Cargo.toml` in the consuming package, building the `bar` library as `cdylib` for a specific build target… ```toml [build-dependencies] bar = { artifact = "cdylib", version = "1.0", target = "wasm32-unknown-unknown" } ``` …along with the build script in `build.rs`. ```rust fn main() { wasm::run_file(std::env::var("CARGO_CDYLIB_FILE_BAR").unwrap()); } ``` #### Example: use _binary_ artifact and its library in a binary The `Cargo.toml` in the consuming package, building the `bar` binary for inclusion as artifact while making it available as library as well… ```toml [dependencies] bar = { artifact = "bin", version = "1.0", lib = true } ``` …along with the executable using `main.rs`. ```rust fn main() { bar::init(); command::run(env!("CARGO_BIN_FILE_BAR")); } ``` ## publish-timeout * Tracking Issue: [11222](https://github.com/rust-lang/cargo/issues/11222) The `publish.timeout` key in a config file can be used to control how long `cargo publish` waits between posting a package to the registry and it being available in the local index. A timeout of `0` prevents any checks from occurring. The current default is `60` seconds. It requires the `-Zpublish-timeout` command-line options to be set. ```toml # config.toml [publish] timeout = 300 # in seconds ``` ## asymmetric-token * Tracking Issue: [10519](https://github.com/rust-lang/cargo/issues/10519) * RFC: [#3231](https://github.com/rust-lang/rfcs/pull/3231) The `-Z asymmetric-token` flag enables the `cargo:paseto` credential provider which allows Cargo to authenticate to registries without sending secrets over the network. In [`config.toml`](config.md) and `credentials.toml` files there is a field called `private-key`, which is a private key formatted in the secret [subset of `PASERK`](https://github.com/paseto-standard/paserk/blob/master/types/secret.md) and is used to sign asymmetric tokens A keypair can be generated with `cargo login --generate-keypair` which will: - generate a public/private keypair in the currently recommended fashion. - save the private key in `credentials.toml`. - print the public key in [PASERK public](https://github.com/paseto-standard/paserk/blob/master/types/public.md) format. It is recommended that the `private-key` be saved in `credentials.toml`. It is also supported in `config.toml`, primarily so that it can be set using the associated environment variable, which is the recommended way to provide it in CI contexts. This setup is what we have for the `token` field for setting a secret token. There is also an optional field called `private-key-subject` which is a string chosen by the registry. This string will be included as part of an asymmetric token and should not be secret. It is intended for the rare use cases like "cryptographic proof that the central CA server authorized this action". Cargo requires it to be non-whitespace printable ASCII. Registries that need non-ASCII data should base64 encode it. Both fields can be set with `cargo login --registry=name --private-key --private-key-subject="subject"` which will prompt you to put in the key value. A registry can have at most one of `private-key` or `token` set. All PASETOs will include `iat`, the current time in ISO 8601 format. Cargo will include the following where appropriate: - `sub` an optional, non-secret string chosen by the registry that is expected to be claimed with every request. The value will be the `private-key-subject` from the `config.toml` file. - `mutation` if present, indicates that this request is a mutating operation (or a read-only operation if not present), must be one of the strings `publish`, `yank`, or `unyank`. - `name` name of the crate related to this request. - `vers` version string of the crate related to this request. - `cksum` the SHA256 hash of the crate contents, as a string of 64 lowercase hexadecimal digits, must be present only when `mutation` is equal to `publish` - `challenge` the challenge string received from a 401/403 from this server this session. Registries that issue challenges must track which challenges have been issued/used and never accept a given challenge more than once within the same validity period (avoiding the need to track every challenge ever issued). The "footer" (which is part of the signature) will be a JSON string in UTF-8 and include: - `url` the RFC 3986 compliant URL where cargo got the config.json file, - If this is a registry with an HTTP index, then this is the base URL that all index queries are relative to. - If this is a registry with a GIT index, it is the URL Cargo used to clone the index. - `kid` the identifier of the private key used to sign the request, using the [PASERK IDs](https://github.com/paseto-standard/paserk/blob/master/operations/ID.md) standard. PASETO includes the message that was signed, so the server does not have to reconstruct the exact string from the request in order to check the signature. The server does need to check that the signature is valid for the string in the PASETO and that the contents of that string matches the request. If a claim should be expected for the request but is missing in the PASETO then the request must be rejected. ## `cargo config` * Original Issue: [#2362](https://github.com/rust-lang/cargo/issues/2362) * Tracking Issue: [#9301](https://github.com/rust-lang/cargo/issues/9301) The `cargo config` subcommand provides a way to display the configuration files that cargo loads. It currently includes the `get` subcommand which can take an optional config value to display. ```console cargo +nightly -Zunstable-options config get build.rustflags ``` If no config value is included, it will display all config values. See the `--help` output for more options available. ## rustc `--print` * Tracking Issue: [#9357](https://github.com/rust-lang/cargo/issues/9357) `cargo rustc --print=VAL` forwards the `--print` flag to `rustc` in order to extract information from `rustc`. This runs `rustc` with the corresponding [`--print`](https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information) flag, and then immediately exits without compiling. Exposing this as a cargo flag allows cargo to inject the correct target and RUSTFLAGS based on the current configuration. The primary use case is to run `cargo rustc --print=cfg` to get config values for the appropriate target and influenced by any other RUSTFLAGS. ## Different binary name * Tracking Issue: [#9778](https://github.com/rust-lang/cargo/issues/9778) * PR: [#9627](https://github.com/rust-lang/cargo/pull/9627) The `different-binary-name` feature allows setting the filename of the binary without having to obey the restrictions placed on crate names. For example, the crate name must use only `alphanumeric` characters or `-` or `_`, and cannot be empty. The `filename` parameter should **not** include the binary extension, `cargo` will figure out the appropriate extension and use that for the binary on its own. The `filename` parameter is only available in the `[[bin]]` section of the manifest. ```toml cargo-features = ["different-binary-name"] [package] name = "foo" version = "0.0.1" [[bin]] name = "foo" filename = "007bar" path = "src/main.rs" ``` ## scrape-examples * RFC: [#3123](https://github.com/rust-lang/rfcs/pull/3123) * Tracking Issue: [#9910](https://github.com/rust-lang/cargo/issues/9910) The `-Z rustdoc-scrape-examples` flag tells Rustdoc to search crates in the current workspace for calls to functions. Those call-sites are then included as documentation. You can use the flag like this: ``` cargo doc -Z unstable-options -Z rustdoc-scrape-examples ``` By default, Cargo will scrape examples from the example targets of packages being documented. You can individually enable or disable targets from being scraped with the `doc-scrape-examples` flag, such as: ```toml # Enable scraping examples from a library [lib] doc-scrape-examples = true # Disable scraping examples from an example target [[example]] name = "my-example" doc-scrape-examples = false ``` **Note on tests:** enabling `doc-scrape-examples` on test targets will not currently have any effect. Scraping examples from tests is a work-in-progress. **Note on dev-dependencies:** documenting a library does not normally require the crate's dev-dependencies. However, example targets require dev-deps. For backwards compatibility, `-Z rustdoc-scrape-examples` will *not* introduce a dev-deps requirement for `cargo doc`. Therefore examples will *not* be scraped from example targets under the following conditions: 1. No target being documented requires dev-deps, AND 2. At least one crate with targets being documented has dev-deps, AND 3. The `doc-scrape-examples` parameter is unset or false for all `[[example]]` targets. If you want examples to be scraped from example targets, then you must not satisfy one of the above conditions. For example, you can set `doc-scrape-examples` to true for one example target, and that signals to Cargo that you are ok with dev-deps being build for `cargo doc`. ## output-format for rustdoc * Tracking Issue: [#13283](https://github.com/rust-lang/cargo/issues/13283) This flag determines the output format of `cargo rustdoc`, accepting `html` or `json`, providing tools with a way to lean on [rustdoc's experimental JSON format](https://doc.rust-lang.org/nightly/nightly-rustc/rustdoc_json_types/). You can use the flag like this: ``` cargo rustdoc -Z unstable-options --output-format json ``` ## codegen-backend The `codegen-backend` feature makes it possible to select the codegen backend used by rustc using a profile. Example: ```toml [package] name = "foo" [dependencies] serde = "1.0.117" [profile.dev.package.foo] codegen-backend = "cranelift" ``` To set this in a profile in Cargo configuration, you need to use either `-Z codegen-backend` or `[unstable]` table to enable it. For example, ```toml # .cargo/config.toml [unstable] codegen-backend = true [profile.dev.package.foo] codegen-backend = "cranelift" ``` ## gitoxide * Tracking Issue: [#11813](https://github.com/rust-lang/cargo/issues/11813) With the 'gitoxide' unstable feature, all or the specified git operations will be performed by the `gitoxide` crate instead of `git2`. While `-Zgitoxide` enables all currently implemented features, one can individually select git operations to run with `gitoxide` with the `-Zgitoxide=operation[,operationN]` syntax. Valid operations are the following: * `fetch` - All fetches are done with `gitoxide`, which includes git dependencies as well as the crates index. * `checkout` *(planned)* - checkout the worktree, with support for filters and submodules. ## git * Tracking Issue: [#13285](https://github.com/rust-lang/cargo/issues/13285) With the 'git' unstable feature, both `gitoxide` and `git2` will perform shallow fetches of the crate index and git dependencies. While `-Zgit` enables all currently implemented features, one can individually select when to perform shallow fetches with the `-Zgit=operation[,operationN]` syntax. Valid operations are the following: * `shallow-index` - perform a shallow clone of the index. * `shallow-deps` - perform a shallow clone of git dependencies. **Details on shallow clones** * To enable shallow clones, add `-Zgit=shallow-deps` for fetching git dependencies or `-Zgit=shallow-index` for fetching registry index. * Shallow-cloned and shallow-checked-out git repositories reside at their own `-shallow` suffixed directories, i.e, - `~/.cargo/registry/index/*-shallow` - `~/.cargo/git/db/*-shallow` - `~/.cargo/git/checkouts/*-shallow` * When the unstable feature is on, fetching/cloning a git repository is always a shallow fetch. This roughly equals to `git fetch --depth 1` everywhere. * Even with the presence of `Cargo.lock` or specifying a commit `{ rev = "…" }`, gitoxide and libgit2 are still smart enough to shallow fetch without unshallowing the existing repository. ## script * Tracking Issue: [#12207](https://github.com/rust-lang/cargo/issues/12207) Cargo can directly run `.rs` files as: ```console $ cargo +nightly -Zscript file.rs ``` where `file.rs` can be as simple as: ```rust fn main() {} ``` A user may optionally specify a manifest in a `cargo` code fence in a module-level comment, like: ````rust #!/usr/bin/env -S cargo +nightly -Zscript ---cargo [dependencies] clap = { version = "4.2", features = ["derive"] } --- use clap::Parser; #[derive(Parser, Debug)] #[clap(version)] struct Args { #[clap(short, long, help = "Path to config")] config: Option, } fn main() { let args = Args::parse(); println!("{:?}", args); } ```` ### Single-file packages In addition to today's multi-file packages (`Cargo.toml` file with other `.rs` files), we are adding the concept of single-file packages which may contain an embedded manifest. There is no required distinguishment for a single-file `.rs` package from any other `.rs` file. Single-file packages may be selected via `--manifest-path`, like `cargo test --manifest-path foo.rs`. Unlike `Cargo.toml`, these files cannot be auto-discovered. A single-file package may contain an embedded manifest. An embedded manifest is stored using `TOML` in rust "frontmatter", a markdown code-fence with `cargo` at the start of the infostring at the top of the file. Inferred / defaulted manifest fields: - `package.name = ` - `package.edition = ` to avoid always having to add an embedded manifest at the cost of potentially breaking scripts on rust upgrades - Warn when `edition` is unspecified to raise awareness of this Disallowed manifest fields: - `[workspace]`, `[lib]`, `[[bin]]`, `[[example]]`, `[[test]]`, `[[bench]]` - `package.workspace`, `package.build`, `package.links`, `package.autolib`, `package.autobins`, `package.autoexamples`, `package.autotests`, `package.autobenches` The default `CARGO_TARGET_DIR` for single-file packages is at `$CARGO_HOME/target/`: - Avoid conflicts from multiple single-file packages being in the same directory - Avoid problems with the single-file package's parent directory being read-only - Avoid cluttering the user's directory The lockfile for single-file packages will be placed in `CARGO_TARGET_DIR`. In the future, when workspaces are supported, that will allow a user to have a persistent lockfile. ### Manifest-commands You may pass a manifest directly to the `cargo` command, without a subcommand, like `foo/Cargo.toml` or a single-file package like `foo.rs`. This is mostly intended for being put in `#!` lines. The precedence for how to interpret `cargo ` is 1. Built-in xor single-file packages 2. Aliases 3. External subcommands A parameter is identified as a manifest-command if it has one of: - Path separators - A `.rs` extension - The file name is `Cargo.toml` Differences between `cargo run --manifest-path ` and `cargo ` - `cargo ` runs with the config for `` and not the current dir, more like `cargo install --path ` - `cargo ` is at a verbosity level below the normal default. Pass `-v` to get normal output. ### Documentation Updates ## Profile `trim-paths` option * Tracking Issue: [rust-lang/cargo#12137](https://github.com/rust-lang/cargo/issues/12137) * Tracking Rustc Issue: [rust-lang/rust#111540](https://github.com/rust-lang/rust/issues/111540) This adds a new profile setting to control how paths are sanitized in the resulting binary. This can be enabled like so: ```toml cargo-features = ["trim-paths"] [package] # ... [profile.release] trim-paths = ["diagnostics", "object"] ``` To set this in a profile in Cargo configuration, you need to use either `-Z trim-paths` or `[unstable]` table to enable it. For example, ```toml # .cargo/config.toml [unstable] trim-paths = true [profile.release] trim-paths = ["diagnostics", "object"] ``` ### Documentation updates #### trim-paths *as a new ["Profiles settings" entry](./profiles.html#profile-settings)* `trim-paths` is a profile setting which enables and controls the sanitization of file paths in build outputs. It takes the following values: - `"none"` and `false` --- disable path sanitization - `"macro"` --- sanitize paths in the expansion of `std::file!()` macro. This is where paths in embedded panic messages come from - `"diagnostics"` --- sanitize paths in printed compiler diagnostics - `"object"` --- sanitize paths in compiled executables or libraries - `"all"` and `true` --- sanitize paths in all possible locations It also takes an array with the combinations of `"macro"`, `"diagnostics"`, and `"object"`. It is defaulted to `none` for the `dev` profile, and `object` for the `release` profile. You can manually override it by specifying this option in `Cargo.toml`: ```toml [profile.dev] trim-paths = "all" [profile.release] trim-paths = ["object", "diagnostics"] ``` The default `release` profile setting (`object`) sanitizes only the paths in emitted executable or library files. It always affects paths from macros such as panic messages, and in debug information only if they will be embedded together with the binary (the default on platforms with ELF binaries, such as Linux and windows-gnu), but will not touch them if they are in separate files (the default on Windows MSVC and macOS). But the paths to these separate files are sanitized. If `trim-paths` is not `none` or `false`, then the following paths are sanitized if they appear in a selected scope: 1. Path to the source files of the standard and core library (sysroot) will begin with `/rustc/[rustc commit hash]`, e.g. `/home/username/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/result.rs` -> `/rustc/fe72845f7bb6a77b9e671e6a4f32fe714962cec4/library/core/src/result.rs` 2. Path to the current package will be stripped, relatively to the current workspace root, e.g. `/home/username/crate/src/lib.rs` -> `src/lib.rs`. 3. Path to dependency packages will be replaced with `[package name]-[version]`. E.g. `/home/username/deps/foo/src/lib.rs` -> `foo-0.1.0/src/lib.rs` When a path to the source files of the standard and core library is *not* in scope for sanitization, the emitted path will depend on if `rust-src` component is present. If it is, then some paths will point to the copy of the source files on your file system; if it isn't, then they will show up as `/rustc/[rustc commit hash]/library/...` (just like when it is selected for sanitization). Paths to all other source files will not be affected. This will not affect any hard-coded paths in the source code, such as in strings. #### Environment variable *as a new entry of ["Environment variables Cargo sets for build scripts"](./environment-variables.md#environment-variables-cargo-sets-for-crates)* * `CARGO_TRIM_PATHS` --- The value of `trim-paths` profile option. `false`, `"none"`, and empty arrays would be converted to `none`. `true` and `"all"` become `all`. Values in a non-empty array would be joined into a comma-separated list. If the build script introduces absolute paths to built artifacts (such as by invoking a compiler), the user may request them to be sanitized in different types of artifacts. Common paths requiring sanitization include `OUT_DIR`, `CARGO_MANIFEST_DIR` and `CARGO_MANIFEST_PATH`, plus any other introduced by the build script, such as include directories. ## gc * Tracking Issue: [#12633](https://github.com/rust-lang/cargo/issues/12633) The `-Zgc` flag is used to enable certain features related to garbage-collection of cargo's global cache within the cargo home directory. #### Automatic gc configuration The `-Zgc` flag will enable Cargo to read extra configuration options related to garbage collection. The settings available are: ```toml # Example config.toml file. # Sub-table for defining specific settings for cleaning the global cache. [cache.global-clean] # Anything older than this duration will be deleted in the source cache. max-src-age = "1 month" # Anything older than this duration will be deleted in the compressed crate cache. max-crate-age = "3 months" # Any index older than this duration will be deleted from the index cache. max-index-age = "3 months" # Any git checkout older than this duration will be deleted from the checkout cache. max-git-co-age = "1 month" # Any git clone older than this duration will be deleted from the git cache. max-git-db-age = "3 months" ``` Note that the [`cache.auto-clean-frequency`] option was stabilized in Rust 1.88. [`cache.auto-clean-frequency`]: config.md#cacheauto-clean-frequency ### Manual garbage collection with `cargo clean` Manual deletion can be done with the `cargo clean gc -Zgc` command. Deletion of cache contents can be performed by passing one of the cache options: - `--max-src-age=DURATION` --- Deletes source cache files that have not been used since the given age. - `--max-crate-age=DURATION` --- Deletes crate cache files that have not been used since the given age. - `--max-index-age=DURATION` --- Deletes registry indexes that have not been used since then given age (including their `.crate` and `src` files). - `--max-git-co-age=DURATION` --- Deletes git dependency checkouts that have not been used since then given age. - `--max-git-db-age=DURATION` --- Deletes git dependency clones that have not been used since then given age. - `--max-download-age=DURATION` --- Deletes any downloaded cache data that has not been used since then given age. - `--max-src-size=SIZE` --- Deletes the oldest source cache files until the cache is under the given size. - `--max-crate-size=SIZE` --- Deletes the oldest crate cache files until the cache is under the given size. - `--max-git-size=SIZE` --- Deletes the oldest git dependency caches until the cache is under the given size. - `--max-download-size=SIZE` --- Deletes the oldest downloaded cache data until the cache is under the given size. A DURATION is specified in the form "N seconds/minutes/days/weeks/months" where N is an integer. A SIZE is specified in the form "N *suffix*" where *suffix* is B, kB, MB, GB, kiB, MiB, or GiB, and N is an integer or floating point number. If no suffix is specified, the number is the number of bytes. ```sh cargo clean gc -Zgc cargo clean gc -Zgc --max-download-age=1week cargo clean gc -Zgc --max-git-size=0 --max-download-size=100MB ``` ## open-namespaces * Tracking Issue: [#13576](https://github.com/rust-lang/cargo/issues/13576) Allow multiple packages to participate in the same API namespace This can be enabled like so: ```toml cargo-features = ["open-namespaces"] [package] # ... ``` ## `[lints.cargo]` * Tracking Issue: [#12235](https://github.com/rust-lang/cargo/issues/12235) A new `lints` tool table for `cargo` that can be used to configure lints emitted by `cargo` itself when `-Zcargo-lints` is used ```toml [lints.cargo] implicit-features = "warn" ``` This will work with [RFC 2906 `workspace-deduplicate`](https://rust-lang.github.io/rfcs/2906-cargo-workspace-deduplicate.html): ```toml [workspace.lints.cargo] implicit-features = "warn" [lints] workspace = true ``` ## Path Bases * Tracking Issue: [#14355](https://github.com/rust-lang/cargo/issues/14355) A `path` dependency may optionally specify a base by setting the `base` key to the name of a path base from the `[path-bases]` table in either the [configuration](config.md) or one of the [built-in path bases](#built-in-path-bases). The value of that path base is prepended to the `path` value (along with a path separator if necessary) to produce the actual location where Cargo will look for the dependency. For example, if the `Cargo.toml` contains: ```toml cargo-features = ["path-bases"] [dependencies] foo = { base = "dev", path = "foo" } ``` Given a `[path-bases]` table in the configuration that contains: ```toml [path-bases] dev = "/home/user/dev/rust/libraries/" ``` This will produce a `path` dependency `foo` located at `/home/user/dev/rust/libraries/foo`. Path bases can be either absolute or relative. Relative path bases are relative to the parent directory of the configuration file that declared that path base. The name of a path base must use only [alphanumeric](https://doc.rust-lang.org/std/primitive.char.html#method.is_alphanumeric) characters or `-` or `_`, must start with an [alphabetic](https://doc.rust-lang.org/std/primitive.char.html#method.is_alphabetic) character, and must not be empty. If the name of path base used in a dependency is neither in the configuration nor one of the built-in path base, then Cargo will raise an error. #### Built-in path bases Cargo provides implicit path bases that can be used without the need to specify them in a `[path-bases]` table. * `workspace` - If a project is [a workspace or workspace member](workspaces.md) then this path base is defined as the parent directory of the root `Cargo.toml` of the workspace. If a built-in path base name is also declared in the configuration, then Cargo will prefer the value in the configuration. The allows Cargo to add new built-in path bases without compatibility issues (as existing uses will shadow the built-in name). ## lockfile-path * Original Issue: [#5707](https://github.com/rust-lang/cargo/issues/5707) * Tracking Issue: [#14421](https://github.com/rust-lang/cargo/issues/14421) This feature allows you to specify the path of lockfile Cargo.lock. By default, lockfile is written into `/Cargo.lock`. However, when sources are stored in read-only directory, most of the cargo commands would fail, trying to write a lockfile. The `--lockfile-path` flag makes it easier to work with readonly sources. Note, that currently path must end with `Cargo.lock`. Meaning, if you want to use this feature in multiple projects, lockfiles should be stored in different directories. Example: ```sh cargo +nightly metadata --lockfile-path=$LOCKFILES_ROOT/my-project/Cargo.lock -Z unstable-options ``` ## package-workspace * Tracking Issue: [#10948](https://github.com/rust-lang/cargo/issues/10948) This allows cargo to package (or publish) multiple crates in a workspace, even if they have inter-dependencies. For example, consider a workspace containing packages `foo` and `dep`, where `foo` depends on `dep`. Then ```sh cargo +nightly -Zpackage-workspace package -p foo -p dep ``` will package both `foo` and `dep`, while ```sh cargo +nightly -Zpackage-workspace publish -p foo -p dep ``` will publish both `foo` and `dep`. If `foo` and `dep` are the only crates in the workspace, you can use the `--workspace` flag instead of specifying the crates individually: ```sh cargo +nightly -Zpackage-workspace package --workspace cargo +nightly -Zpackage-workspace publish --workspace ``` #### Lock-file behavior When packaging a binary at the same time as one of its dependencies, the binary will be packaged with a lock-file pointing at the dependency's registry entry *as though the dependency were already published*, even though it has not yet been. In this case, `cargo` needs to know the registry that the dependency will eventually be published on. `cargo` will attempt to infer this registry by examining the [the `publish` field](manifest.md#the-publish-field), falling back to `crates.io` if no `publish` field is set. To explicitly set the registry, pass a `--registry` or `--index` flag. ```sh cargo +nightly -Zpackage-workspace --registry=my-registry package -p foo -p dep cargo +nightly -Zpackage-workspace --index=https://example.com package -p foo -p dep ``` ## native-completions * Original Issue: [#6645](https://github.com/rust-lang/cargo/issues/6645) * Tracking Issue: [#14520](https://github.com/rust-lang/cargo/issues/14520) This feature moves the handwritten completion scripts to Rust native, making it easier for us to add, extend and test new completions. This feature is enabled with the nightly channel, without requiring additional `-Z` options. Areas of particular interest for feedback - Arguments that need escaping or quoting that aren't handled correctly - Inaccuracies in the information - Bugs in parsing of the command-line - Arguments that don't report their completions - If a known issue is being problematic Feedback can be broken down into - What completion candidates are reported - Known issues: [#14520](https://github.com/rust-lang/cargo/issues/14520), [`A-completions`](https://github.com/rust-lang/cargo/labels/A-completions) - [Report an issue](https://github.com/rust-lang/cargo/issues/new) or [discuss the behavior](https://github.com/rust-lang/cargo/issues/14520) - Shell integration, command-line parsing, and completion filtering - Known issues: [clap#3166](https://github.com/clap-rs/clap/issues/3166), [clap's `A-completions`](https://github.com/clap-rs/clap/labels/A-completion) - [Report an issue](https://github.com/clap-rs/clap/issues/new/choose) or [discuss the behavior](https://github.com/clap-rs/clap/discussions/new/choose) When in doubt, you can discuss this in [#14520](https://github.com/rust-lang/cargo/issues/14520) or on [zulip](https://rust-lang.zulipchat.com/#narrow/stream/246057-t-cargo) ### How to use native-completions feature: - bash: Add `source <(CARGO_COMPLETE=bash cargo +nightly)` to `~/.local/share/bash-completion/completions/cargo`. - zsh: Add `source <(CARGO_COMPLETE=zsh cargo +nightly)` to your `.zshrc`. - fish: Add `source (CARGO_COMPLETE=fish cargo +nightly | psub)` to `$XDG_CONFIG_HOME/fish/completions/cargo.fish` - elvish: Add `eval (E:CARGO_COMPLETE=elvish cargo +nightly | slurp)` to `$XDG_CONFIG_HOME/elvish/rc.elv` - powershell: Add `CARGO_COMPLETE=powershell cargo +nightly | Invoke-Expression` to `$PROFILE`. ## warnings * Original Issue: [#8424](https://github.com/rust-lang/cargo/issues/8424) * Tracking Issue: [#14802](https://github.com/rust-lang/cargo/issues/14802) The `-Z warnings` feature enables the `build.warnings` configuration option to control how Cargo handles warnings. If the `-Z warnings` unstable flag is not enabled, then the `build.warnings` config will be ignored. This setting currently only applies to rustc warnings. It may apply to additional warnings (such as Cargo lints or Cargo warnings) in the future. ### `build.warnings` * Type: string * Default: `warn` * Environment: `CARGO_BUILD_WARNINGS` Controls how Cargo handles warnings. Allowed values are: * `warn`: warnings are emitted as warnings (default). * `allow`: warnings are hidden. * `deny`: if warnings are emitted, an error will be raised at the end of the operation and the process will exit with a failure exit code. ## feature unification * RFC: [#3692](https://github.com/rust-lang/rfcs/blob/master/text/3692-feature-unification.md) * Tracking Issue: [#14774](https://github.com/rust-lang/cargo/issues/14774) The `-Z feature-unification` enables the `resolver.feature-unification` configuration option to control how features are unified across a workspace. If the `-Z feature-unification` unstable flag is not enabled, then the `resolver.feature-unification` configuration will be ignored. ### `resolver.feature-unification` * Type: string * Default: `"selected"` * Environment: `CARGO_RESOLVER_FEATURE_UNIFICATION` Specify which packages participate in [feature unification](../reference/features.html#feature-unification). * `selected`: Merge dependency features from all packages specified for the current build. * `workspace`: Merge dependency features across all workspace members, regardless of which packages are specified for the current build. * `package`: Dependency features are considered on a package-by-package basis, preferring duplicate builds of dependencies when different sets of features are activated by the packages. ## Package message format * Original Issue: [#11666](https://github.com/rust-lang/cargo/issues/11666) * Tracking Issue: [#15353](https://github.com/rust-lang/cargo/issues/15353) The `--message-format` flag in `cargo package` controls the output message format. Currently, it only works with the `--list` flag and affects the file listing format, Requires `-Zunstable-options`. See [`cargo package --message-format`](../commands/cargo-package.md#option-cargo-package---message-format) for more information. ## rustdoc depinfo * Original Issue: [#12266](https://github.com/rust-lang/cargo/issues/12266) * Tracking Issue: [#15370](https://github.com/rust-lang/cargo/issues/15370) The `-Z rustdoc-depinfo` flag leverages rustdoc's dep-info files to determine whether documentations are required to re-generate. This can be combined with `-Z checksum-freshness` to detect checksum changes rather than file mtime. ## no-embed-metadata * Original Pull Request: [#15378](https://github.com/rust-lang/cargo/pull/15378) * Tracking Issue: [#15495](https://github.com/rust-lang/cargo/issues/15495) The default behavior of Rust is to embed crate metadata into `rlib` and `dylib` artifacts. Since Cargo also passes `--emit=metadata` to these intermediate artifacts to enable pipelined compilation, this means that a lot of metadata ends up being duplicated on disk, which wastes disk space in the target directory. This feature tells Cargo to pass the `-Zembed-metadata=no` flag to the compiler, which instructs it not to embed metadata within rlib and dylib artifacts. In this case, the metadata will only be stored in `.rmeta` files. ```console cargo +nightly -Zno-embed-metadata build ``` ## `unstable-editions` The `unstable-editions` value in the `cargo-features` list allows a `Cargo.toml` manifest to specify an edition that is not yet stable. ```toml cargo-features = ["unstable-editions"] [package] name = "my-package" edition = "future" ``` When new editions are introduced, the `unstable-editions` feature is required until the edition is stabilized. The special "future" edition is a home for new features that are under development, and is permanently unstable. The "future" edition also has no new behavior by itself. Each change in the future edition requires an opt-in such as a `#![feature(...)]` attribute. ## `fix-edition` `-Zfix-edition` is a permanently unstable flag to assist with testing edition migrations, particularly with the use of crater. It only works with the `cargo fix` subcommand. It takes two different forms: - `-Zfix-edition=start=$INITIAL` --- This form checks if the current edition is equal to the given number. If not, it exits with success (because we want to ignore older editions). If it is, then it runs the equivalent of `cargo check`. This is intended to be used with crater's "start" toolchain to set a baseline for the "before" toolchain. - `-Zfix-edition=end=$INITIAL,$NEXT` --- This form checks if the current edition is equal to the given `$INITIAL` value. If not, it exits with success. If it is, then it performs an edition migration to the edition specified in `$NEXT`. Afterwards, it will modify `Cargo.toml` to add the appropriate `cargo-features = ["unstable-edition"]`, update the `edition` field, and run the equivalent of `cargo check` to verify that the migration works on the new edition. For example: ```console cargo +nightly fix -Zfix-edition=end=2024,future ``` # Stabilized and removed features ## Compile progress The compile-progress feature has been stabilized in the 1.30 release. Progress bars are now enabled by default. See [`term.progress`](config.md#termprogresswhen) for more information about controlling this feature. ## Edition Specifying the `edition` in `Cargo.toml` has been stabilized in the 1.31 release. See [the edition field](manifest.md#the-edition-field) for more information about specifying this field. ## rename-dependency Specifying renamed dependencies in `Cargo.toml` has been stabilized in the 1.31 release. See [renaming dependencies](specifying-dependencies.md#renaming-dependencies-in-cargotoml) for more information about renaming dependencies. ## Alternate Registries Support for alternate registries has been stabilized in the 1.34 release. See the [Registries chapter](registries.md) for more information about alternate registries. ## Offline Mode The offline feature has been stabilized in the 1.36 release. See the [`--offline` flag](../commands/cargo.md#option-cargo---offline) for more information on using the offline mode. ## publish-lockfile The `publish-lockfile` feature has been removed in the 1.37 release. The `Cargo.lock` file is always included when a package is published if the package contains a binary target. `cargo install` requires the `--locked` flag to use the `Cargo.lock` file. See [`cargo package`](../commands/cargo-package.md) and [`cargo install`](../commands/cargo-install.md) for more information. ## default-run The `default-run` feature has been stabilized in the 1.37 release. See [the `default-run` field](manifest.md#the-default-run-field) for more information about specifying the default target to run. ## cache-messages Compiler message caching has been stabilized in the 1.40 release. Compiler warnings are now cached by default and will be replayed automatically when re-running Cargo. ## install-upgrade The `install-upgrade` feature has been stabilized in the 1.41 release. [`cargo install`] will now automatically upgrade packages if they appear to be out-of-date. See the [`cargo install`] documentation for more information. [`cargo install`]: ../commands/cargo-install.md ## Profile Overrides Profile overrides have been stabilized in the 1.41 release. See [Profile Overrides](profiles.md#overrides) for more information on using overrides. ## Config Profiles Specifying profiles in Cargo config files and environment variables has been stabilized in the 1.43 release. See the [config `[profile]` table](config.md#profile) for more information about specifying [profiles](profiles.md) in config files. ## crate-versions The `-Z crate-versions` flag has been stabilized in the 1.47 release. The crate version is now automatically included in the [`cargo doc`](../commands/cargo-doc.md) documentation sidebar. ## Features The `-Z features` flag has been stabilized in the 1.51 release. See [feature resolver version 2](features.md#feature-resolver-version-2) for more information on using the new feature resolver. ## package-features The `-Z package-features` flag has been stabilized in the 1.51 release. See the [resolver version 2 command-line flags](features.md#resolver-version-2-command-line-flags) for more information on using the features CLI options. ## Resolver The `resolver` feature in `Cargo.toml` has been stabilized in the 1.51 release. See the [resolver versions](resolver.md#resolver-versions) for more information about specifying resolvers. ## extra-link-arg The `extra-link-arg` feature to specify additional linker arguments in build scripts has been stabilized in the 1.56 release. See the [build script documentation](build-scripts.md#outputs-of-the-build-script) for more information on specifying extra linker arguments. ## configurable-env The `configurable-env` feature to specify environment variables in Cargo configuration has been stabilized in the 1.56 release. See the [config documentation](config.html#env) for more information about configuring environment variables. ## rust-version The `rust-version` field in `Cargo.toml` has been stabilized in the 1.56 release. See the [rust-version field](manifest.html#the-rust-version-field) for more information on using the `rust-version` field and the `--ignore-rust-version` option. ## patch-in-config The `-Z patch-in-config` flag, and the corresponding support for `[patch]` section in Cargo configuration files has been stabilized in the 1.56 release. See the [patch field](config.html#patch) for more information. ## edition 2021 The 2021 edition has been stabilized in the 1.56 release. See the [`edition` field](manifest.md#the-edition-field) for more information on setting the edition. See [`cargo fix --edition`](../commands/cargo-fix.md) and [The Edition Guide](../../edition-guide/index.html) for more information on migrating existing projects. ## Custom named profiles Custom named profiles have been stabilized in the 1.57 release. See the [profiles chapter](profiles.md#custom-profiles) for more information. ## Profile `strip` option The profile `strip` option has been stabilized in the 1.59 release. See the [profiles chapter](profiles.md#strip) for more information. ## Future incompat report Support for generating a future-incompat report has been stabilized in the 1.59 release. See the [future incompat report chapter](future-incompat-report.md) for more information. ## Namespaced features Namespaced features has been stabilized in the 1.60 release. See the [Features chapter](features.md#optional-dependencies) for more information. ## Weak dependency features Weak dependency features has been stabilized in the 1.60 release. See the [Features chapter](features.md#dependency-features) for more information. ## timings The `-Ztimings` option has been stabilized as `--timings` in the 1.60 release. (`--timings=html` and the machine-readable `--timings=json` output remain unstable and require `-Zunstable-options`.) ## config-cli The `--config` CLI option has been stabilized in the 1.63 release. See the [config documentation](config.html#command-line-overrides) for more information. ## multitarget The `-Z multitarget` option has been stabilized in the 1.64 release. See [`build.target`](config.md#buildtarget) for more information about setting the default [target platform triples][target triple]. ## crate-type The `--crate-type` flag for `cargo rustc` has been stabilized in the 1.64 release. See the [`cargo rustc` documentation](../commands/cargo-rustc.md) for more information. ## Workspace Inheritance Workspace Inheritance has been stabilized in the 1.64 release. See [workspace.package](workspaces.md#the-package-table), [workspace.dependencies](workspaces.md#the-dependencies-table), and [inheriting-a-dependency-from-a-workspace](specifying-dependencies.md#inheriting-a-dependency-from-a-workspace) for more information. ## terminal-width The `-Z terminal-width` option has been stabilized in the 1.68 release. The terminal width is always passed to the compiler when running from a terminal where Cargo can automatically detect the width. ## sparse-registry Sparse registry support has been stabilized in the 1.68 release. See [Registry Protocols](registries.md#registry-protocols) for more information. ### `cargo logout` The [`cargo logout`] command has been stabilized in the 1.70 release. [target triple]: ../appendix/glossary.md#target '"target" (glossary)' [`cargo logout`]: ../commands/cargo-logout.md ## `doctest-in-workspace` The `-Z doctest-in-workspace` option for `cargo test` has been stabilized and enabled by default in the 1.72 release. See the [`cargo test` documentation](../commands/cargo-test.md#working-directory-of-tests) for more information about the working directory for compiling and running tests. ## keep-going The `--keep-going` option has been stabilized in the 1.74 release. See the [`--keep-going` flag](../commands/cargo-build.html#option-cargo-build---keep-going) in `cargo build` as an example for more details. ## `[lints]` [`[lints]`](manifest.html#the-lints-section) (enabled via `-Zlints`) has been stabilized in the 1.74 release. ## credential-process The `-Z credential-process` feature has been stabilized in the 1.74 release. See [Registry Authentication](registry-authentication.md) documentation for details. ## registry-auth The `-Z registry-auth` feature has been stabilized in the 1.74 release with the additional requirement that a credential-provider is configured. See [Registry Authentication](registry-authentication.md) documentation for details. ## check-cfg The `-Z check-cfg` feature has been stabilized in the 1.80 release by making it the default behavior. See the [build script documentation](build-scripts.md#rustc-check-cfg) for information about specifying custom cfgs. ## Edition 2024 The 2024 edition has been stabilized in the 1.85 release. See the [`edition` field](manifest.md#the-edition-field) for more information on setting the edition. See [`cargo fix --edition`](../commands/cargo-fix.md) and [The Edition Guide](../../edition-guide/index.html) for more information on migrating existing projects. ## Automatic garbage collection Support for automatically deleting old files was stabilized in Rust 1.88. More information can be found in the [config chapter](config.md#cache). ## doctest-xcompile Doctest cross-compiling is now unconditionally enabled starting in Rust 1.89. Running doctests with `cargo test` will now honor the `--target` flag. ## compile-time-deps This permanently-unstable flag to only build proc-macros and build scripts (and their required dependencies), as well as run the build scripts. It is intended for use by tools like rust-analyzer and will never be stabilized. Example: ```console cargo +nightly build --compile-time-deps -Z unstable-options cargo +nightly check --compile-time-deps --all-targets -Z unstable-options ``` cargo-0.91.0/src/doc/src/reference/workspaces.md000064400000000000000000000246241046102023000176020ustar 00000000000000# Workspaces A *workspace* is a collection of one or more packages, called *workspace members*, that are managed together. The key points of workspaces are: * Common commands can run across all workspace members, like `cargo check --workspace`. * All packages share a common [`Cargo.lock`] file which resides in the *workspace root*. * All packages share a common [output directory], which defaults to a directory named `target` in the *workspace root*. * Sharing package metadata, like with [`workspace.package`](#the-package-table). * The [`[patch]`][patch], [`[replace]`][replace] and [`[profile.*]`][profiles] sections in `Cargo.toml` are only recognized in the *root* manifest, and ignored in member crates' manifests. The root `Cargo.toml` of a workspace supports the following sections: * [`[workspace]`](#the-workspace-section) --- Defines a workspace. * [`resolver`](resolver.md#resolver-versions) --- Sets the dependency resolver to use. * [`members`](#the-members-and-exclude-fields) --- Packages to include in the workspace. * [`exclude`](#the-members-and-exclude-fields) --- Packages to exclude from the workspace. * [`default-members`](#the-default-members-field) --- Packages to operate on when a specific package wasn't selected. * [`package`](#the-package-table) --- Keys for inheriting in packages. * [`dependencies`](#the-dependencies-table) --- Keys for inheriting in package dependencies. * [`lints`](#the-lints-table) --- Keys for inheriting in package lints. * [`metadata`](#the-metadata-table) --- Extra settings for external tools. * [`[patch]`](overriding-dependencies.md#the-patch-section) --- Override dependencies. * [`[replace]`](overriding-dependencies.md#the-replace-section) --- Override dependencies (deprecated). * [`[profile]`](profiles.md) --- Compiler settings and optimizations. ## The `[workspace]` section To create a workspace, you add the `[workspace]` table to a `Cargo.toml`: ```toml [workspace] # ... ``` At minimum, a workspace has to have a member, either with a root package or as a virtual manifest. ### Root package If the [`[workspace]` section](#the-workspace-section) is added to a `Cargo.toml` that already defines a `[package]`, the package is the *root package* of the workspace. The *workspace root* is the directory where the workspace's `Cargo.toml` is located. ```toml [workspace] [package] name = "hello_world" # the name of the package version = "0.1.0" # the current version, obeying semver ``` ### Virtual workspace Alternatively, a `Cargo.toml` file can be created with a `[workspace]` section but without a [`[package]` section][package]. This is called a *virtual manifest*. This is typically useful when there isn't a "primary" package, or you want to keep all the packages organized in separate directories. ```toml # [PROJECT_DIR]/Cargo.toml [workspace] members = ["hello_world"] resolver = "3" ``` ```toml # [PROJECT_DIR]/hello_world/Cargo.toml [package] name = "hello_world" # the name of the package version = "0.1.0" # the current version, obeying semver edition = "2024" # the edition, will have no effect on a resolver used in the workspace ``` By having a workspace without a root package, - [`resolver`](resolver.md#resolver-versions) must be set explicitly in virtual workspaces as they have no [`package.edition`][package-edition] to infer it from [resolver version](resolver.md#resolver-versions). - Commands run in the workspace root will run against all workspace members by default, see [`default-members`](#the-default-members-field). ## The `members` and `exclude` fields The `members` and `exclude` fields define which packages are members of the workspace: ```toml [workspace] members = ["member1", "path/to/member2", "crates/*"] exclude = ["crates/foo", "path/to/other"] ``` All [`path` dependencies] residing in the workspace directory automatically become members. Additional members can be listed with the `members` key, which should be an array of strings containing directories with `Cargo.toml` files. The `members` list also supports [globs] to match multiple paths, using typical filename glob patterns like `*` and `?`. The `exclude` key can be used to prevent paths from being included in a workspace. This can be useful if some path dependencies aren't desired to be in the workspace at all, or using a glob pattern and you want to remove a directory. When inside a subdirectory within the workspace, Cargo will automatically search the parent directories for a `Cargo.toml` file with a `[workspace]` definition to determine which workspace to use. The [`package.workspace`] manifest key can be used in member crates to point at a workspace's root to override this automatic search. The manual setting can be useful if the member is not inside a subdirectory of the workspace root. ### Package selection In a workspace, package-related Cargo commands like [`cargo build`] can use the `-p` / `--package` or `--workspace` command-line flags to determine which packages to operate on. If neither of those flags are specified, Cargo will use the package in the current working directory. However, if the current directory is a workspace root, the [`default-members`](#the-default-members-field) will be used. ## The `default-members` field The `default-members` field specifies paths of [members](#the-members-and-exclude-fields) to operate on when in the workspace root and the package selection flags are not used: ```toml [workspace] members = ["path/to/member1", "path/to/member2", "path/to/member3/*"] default-members = ["path/to/member2", "path/to/member3/foo"] ``` > Note: when a [root package](#root-package) is present, > you can only operate on it using `--package` and `--workspace` flags. When unspecified, the [root package](#root-package) will be used. In the case of a [virtual workspace](#virtual-workspace), all members will be used (as if `--workspace` were specified on the command-line). ## The `package` table The `workspace.package` table is where you define keys that can be inherited by members of a workspace. These keys can be inherited by defining them in the member package with `{key}.workspace = true`. Keys that are supported: | | | |----------------|-----------------| | `authors` | `categories` | | `description` | `documentation` | | `edition` | `exclude` | | `homepage` | `include` | | `keywords` | `license` | | `license-file` | `publish` | | `readme` | `repository` | | `rust-version` | `version` | - `license-file` and `readme` are relative to the workspace root - `include` and `exclude` are relative to your package root Example: ```toml # [PROJECT_DIR]/Cargo.toml [workspace] members = ["bar"] [workspace.package] version = "1.2.3" authors = ["Nice Folks"] description = "A short description of my package" documentation = "https://example.com/bar" ``` ```toml # [PROJECT_DIR]/bar/Cargo.toml [package] name = "bar" version.workspace = true authors.workspace = true description.workspace = true documentation.workspace = true ``` > **MSRV:** Requires 1.64+ ## The `dependencies` table The `workspace.dependencies` table is where you define dependencies to be inherited by members of a workspace. Specifying a workspace dependency is similar to [package dependencies][specifying-dependencies] except: - Dependencies from this table cannot be declared as `optional` - [`features`][features] declared in this table are additive with the `features` from `[dependencies]` You can then [inherit the workspace dependency as a package dependency][inheriting-a-dependency-from-a-workspace] Example: ```toml # [PROJECT_DIR]/Cargo.toml [workspace] members = ["bar"] [workspace.dependencies] cc = "1.0.73" rand = "0.8.5" regex = { version = "1.6.0", default-features = false, features = ["std"] } ``` ```toml # [PROJECT_DIR]/bar/Cargo.toml [package] name = "bar" version = "0.2.0" [dependencies] regex = { workspace = true, features = ["unicode"] } [build-dependencies] cc.workspace = true [dev-dependencies] rand.workspace = true ``` > **MSRV:** Requires 1.64+ ## The `lints` table The `workspace.lints` table is where you define lint configuration to be inherited by members of a workspace. Specifying a workspace lint configuration is similar to [package lints](manifest.md#the-lints-section). Example: ```toml # [PROJECT_DIR]/Cargo.toml [workspace] members = ["crates/*"] [workspace.lints.rust] unsafe_code = "forbid" ``` ```toml # [PROJECT_DIR]/crates/bar/Cargo.toml [package] name = "bar" version = "0.1.0" [lints] workspace = true ``` > **MSRV:** Respected as of 1.74 ## The `metadata` table The `workspace.metadata` table is ignored by Cargo and will not be warned about. This section can be used for tools that would like to store workspace configuration in `Cargo.toml`. For example: ```toml [workspace] members = ["member1", "member2"] [workspace.metadata.webcontents] root = "path/to/webproject" tool = ["npm", "run", "build"] # ... ``` There is a similar set of tables at the package level at [`package.metadata`][package-metadata]. While cargo does not specify a format for the content of either of these tables, it is suggested that external tools may wish to use them in a consistent fashion, such as referring to the data in `workspace.metadata` if data is missing from `package.metadata`, if that makes sense for the tool in question. [package]: manifest.md#the-package-section [`Cargo.lock`]: ../guide/cargo-toml-vs-cargo-lock.md [package-metadata]: manifest.md#the-metadata-table [package-edition]: manifest.md#the-edition-field [output directory]: build-cache.md [patch]: overriding-dependencies.md#the-patch-section [replace]: overriding-dependencies.md#the-replace-section [profiles]: profiles.md [`path` dependencies]: specifying-dependencies.md#specifying-path-dependencies [`package.workspace`]: manifest.md#the-workspace-field [globs]: https://docs.rs/glob/0.3.0/glob/struct.Pattern.html [`cargo build`]: ../commands/cargo-build.md [specifying-dependencies]: specifying-dependencies.md [features]: features.md [inheriting-a-dependency-from-a-workspace]: specifying-dependencies.md#inheriting-a-dependency-from-a-workspace cargo-0.91.0/src/doc/theme/favicon.png000064400000000000000000000353561046102023000156130ustar 0000000000000000 %6  % h6(0` $  '-:l3LbKn@^y[/=$= 3b(=O4OhDfi`Mq@_zT*>O /FXc!1A=]z?aFipxS}8^Ae@_zZ&6D ):JWcij&9KAc>]yCb~\kNx)Rw/W|8_?c@`{],>N !/9L#-x uegis':L=Ys>a}>yMS}Kp3Y|*Rw*Rw.V{8^@d@`|a(;K  #,6D$/u*@T:Xt>]z+@R"+65K`;_{-ks?AKO{Di4Z|+Sw)Rw-Uy<<@W_Hl6[|#Gg3XyndKpAai<<:Z}cOFjBdlvHr3[~>bBcn7Nc   1 @ENQV0H^>Zu>[t:d'ow|} ~   >;;:V{_cAbq5La" +)5b/G]7Sm!3C Z%t.AR;Ys4d0tz{ } } s m v>;;;@[W99>KOOv?d/Vz)Rw,Uz3[?dJlu6Qj".<,-CX7Tn@bIo>_}5Md;Zt1go v! ~ ~ q o o h~ >::::9:9:_|>_}=\xB`|IiN|rvy{# x q}qh{"=::::99::a;_pumj9Vp]>\w?[u:`}3oVN |z{ }$niqpv~$=99998999;F=<<<WwW558G:80+.-118AR5#2$ !&38(@=AF`}^524G984./-018@R8 !$$(5'+2Fd: !@<<<@V[H?9F877201.37@S<# "*'((-<8+9\6#@<<<<;L?DKME:656>P^1(/GnWMM@)?:6-3627:>L=<fvB9>M\ tYCSPNLKLRaY !"""##1?87-,./0588200//578710//7;J;:::999::@H a[UG޾*()*-0/.,:>67763267:I:99989999?H ;m-+,=aN07BSRA8667776:I:98889999?G >m/-.SsXGTWTUXTG;65669I988888888?P Am0.5d]PLMNNRWXOA859I847777777Bx Fm>HSgr^QMNNNPUYTG>I7278;6777B} VmXTOLLWj}{jXONOOORY]^M@8JNMKLR`ttaSOQUWUSUXRKpo=53A| 3m#$,;GMMMLNXj|lUROMMMOYpH70@| 7m''()-9EMNMMOVghVMMLLP]psZSGH| 9m**+8UD09LWXVPMKKKMVgzn]QNPTZf睔=m--.WtQBQWVY\WRNQ_sucTNNOQSW]Kn@m..3fuYQMMNOTZi{iWNMNORVYl]%sCm:EQp|jXNMNNOTck^TNNPSW_?q a_YTPLP^rvcTNNONOT[`YX^a~y]YROMLKMUfyo\QOQTXh?h[0T|QNMLLO\okXfa wc[LTQNMMOYz돠Gq^,W}STYo]!L( @   # 1?l8SlTyHi]3I\/J%08VqEgn\:_=^{V0DV "?S`g-;?]yCg\T{-Uy1Y}9^>_|Z2H\   (>)6w1Ja2Ka!* ,72Qj,g$~BIDn4[~,Tx0W{BgLn^3J^"0?!2B:XtAbEf9\x*iu {;Wo , =FP+>P?]x:`~*lw | ~ <;AqYEP~c?f.Vz7]=_~f5Oe  1?^3Ng9Xt"3C+?Q0\y)n| } { q q;;;MG9BJI|EO`fI1\x)qw-  {pqz%:88888?@:559@6/449?O% $ #0@7:K;6>?5/0.6>M*%'!&+'(CEHpN3>720/4=M3!&6*,922P =<;;>BMI;7336IfZ1S~PM/#&')(1;740/4A@::99:>G_QZ1(.;10=@76546@@99999=FB1+EjFKTTLA865??78888^OOQS_nxq_RNPSTUK<[uY~:^]6Sl #%8Hh ,8!9L5mK:e4XzEhUKk  8 -:v9]z+hr9RHRAf3WwY7Vr/EZ8Tm:^y dy z s8@?DA:iRxk"3_~/%l.vt889>:=L^/} (9>:=319E (*-"=QF=4/7E+$@@3$(99>F@:9BEB`iB%705ADVBH~Y)PvUB"%,802?;>YOSYed]WVbcJWRde_[]UP?V sR+O}TcryW&ocargo-0.91.0/src/doc/theme/head.hbs000064400000000000000000000000741046102023000150440ustar 00000000000000 cargo-0.91.0/src/etc/_cargo000064400000000000000000000577261046102023000135460ustar 00000000000000#compdef cargo autoload -U regexp-replace _cargo() { local curcontext="$curcontext" ret=1 local -a command_scope_spec common parallel features msgfmt triple target registry local -a state line state_descr # These are set by _arguments typeset -A opt_args common=( '(-q --quiet)*'{-v,--verbose}'[use verbose output]' '(-q --quiet -v --verbose)'{-q,--quiet}'[no output printed to stdout]' '-Z+[pass unstable (nightly-only) flags to cargo]: :_cargo_unstable_flags' '--offline[run without accessing the network]' '--frozen[require that Cargo.lock and cache are up-to-date]' '--locked[require that Cargo.lock is up-to-date]' '--color=[specify colorization option]:coloring:(auto always never)' '(- 1 *)'{-h,--help}'[show help message]' ) # leading items in parentheses are an exclusion list for the arguments following that arg # See: http://zsh.sourceforge.net/Doc/Release/Completion-System.html#Completion-Functions # - => exclude all other options # 1 => exclude positional arg 1 # * => exclude all other args # +blah => exclude +blah _arguments -s -S -C $common \ '(- 1 *)--list[list installed commands]' \ '(- 1 *)--explain=[provide a detailed explanation of an error message]:error code' \ '(- 1 *)'{-V,--version}'[show version information]' \ '(+beta +nightly)+stable[use the stable toolchain]' \ '(+stable +nightly)+beta[use the beta toolchain]' \ '(+stable +beta)+nightly[use the nightly toolchain]' \ '1: :_cargo_cmds' \ '*:: :->args' # These flags are mutually exclusive specifiers for the scope of a command; as # they are used in multiple places without change, they are expanded into the # appropriate command's `_arguments` where appropriate. command_scope_spec=( '(--bin --example --test --lib)--bench=[specify benchmark name]: :_cargo_benchmark_names' '(--bench --bin --test --lib)--example=[specify example name]:example name:_cargo_example_names' '(--bench --example --test --lib)--bin=[specify binary name]:binary name' '(--bench --bin --example --test)--lib=[specify library name]:library name' '(--bench --bin --example --lib)--test=[specify test name]:test name' ) jobs=( '(-j --jobs)'{-j+,--jobs=}'[specify number of parallel jobs]:jobs [# of CPUs]' ) parallel=( "${jobs[@]}" '--keep-going[do not abort build on first build error]' ) features=( '(--all-features)'{-F+,--features=}'[specify features to activate]:feature' '(--features -F)--all-features[activate all available features]' "--no-default-features[don't build the default features]" ) msgfmt='--message-format=[specify error format]:error format [human]:(human json short)' triple='--target=[specify target triple]:target triple:_cargo_target_triple' target='--target-dir=[specify directory for all generated artifacts]:directory:_directories' manifest='--manifest-path=[specify path to manifest]:path:_directories' registry='--registry=[specify registry to use]:registry' case $state in args) curcontext="${curcontext%:*}-${words[1]}:" case ${words[1]} in add) _arguments -s -A "^--" $common $manifest $registry \ {-F+,--features=}'[specify features to activate]:feature' \ "--default-features[enable the default features]" \ "--no-default-features[don't enable the default features]" \ "--optional[mark the dependency as optional]" \ "--no-optional[mark the dependency as required]" \ "--dev[add as a dev dependency]" \ "--build[add as a build dependency]" \ "--target=[add as a dependency to the given target platform]" \ "--rename=[rename the dependency]" \ "--dry-run[don't actually write the manifest]" \ '--branch=[branch to use when adding from git]:branch' \ '--git=[specify URL from which to add the crate]:url:_urls' \ '--path=[local filesystem path to crate to add]: :_directories' \ '--rev=[specific commit to use when adding from git]:commit' \ '--tag=[tag to use when adding from git]:tag' \ '--ignore-rust-version[Ignore rust-version specification in packages]' \ '1: :_guard "^-*" "crate name"' \ '*:args:_default' ;; bench) _arguments -s -A "^--" $common $jobs $features $msgfmt $triple $target $manifest \ "${command_scope_spec[@]}" \ '--all-targets[benchmark all targets]' \ "--no-run[compile but don't run]" \ '(-p --package)'{-p+,--package=}'[specify package to run benchmarks for]:package:_cargo_package_names' \ '--exclude=[exclude packages from the benchmark]:spec' \ '--no-fail-fast[run all benchmarks regardless of failure]' \ '--ignore-rust-version[Ignore rust-version specification in packages]' \ '1: :_guard "^-*" "bench name"' \ '*:args:_default' ;; build | b) _arguments -s -S $common $parallel $features $msgfmt $triple $target $manifest \ '--all-targets[equivalent to specifying --lib --bins --tests --benches --examples]' \ "${command_scope_spec[@]}" \ '(-p --package)'{-p+,--package=}'[specify package to build]:package:_cargo_package_names' \ '--release[build in release mode]' \ '--build-plan[output the build plan in JSON]' \ '--ignore-rust-version[Ignore rust-version specification in packages]' ;; check | c) _arguments -s -S $common $parallel $features $msgfmt $triple $target $manifest \ '--all-targets[equivalent to specifying --lib --bins --tests --benches --examples]' \ "${command_scope_spec[@]}" \ '(-p --package)'{-p+,--package=}'[specify package to check]:package:_cargo_package_names' \ '--release[check in release mode]' \ '--ignore-rust-version[Ignore rust-version specification in packages]' ;; clean) _arguments -s -S $common $triple $target $manifest \ '(-p --package)'{-p+,--package=}'[specify package to clean]:package:_cargo_package_names' \ '--release[clean release artifacts]' \ '--doc[clean just the documentation directory]' ;; doc | d) _arguments -s -S $common $parallel $features $msgfmt $triple $target $manifest \ '--no-deps[do not build docs for dependencies]' \ '--document-private-items[include non-public items in the documentation]' \ '--open[open docs in browser after the build]' \ '(-p --package)'{-p+,--package=}'[specify package to document]:package:_cargo_package_names' \ '--release[build artifacts in release mode, with optimizations]' \ '--ignore-rust-version[Ignore rust-version specification in packages]' ;; fetch) _arguments -s -S $common $triple $manifest ;; fix) _arguments -s -S $common $parallel $features $msgfmt $triple $target $manifest \ "${command_scope_spec[@]}" \ '--broken-code[fix code even if it already has compiler errors]' \ '--edition[fix in preparation for the next edition]' \ '--edition-idioms[fix warnings to migrate to the idioms of an edition]' \ '--allow-no-vcs[fix code even if a VCS was not detected]' \ '--allow-dirty[fix code even if the working directory is dirty]' \ '--allow-staged[fix code even if the working directory has staged changes]' \ '--ignore-rust-version[Ignore rust-version specification in packages]' ;; generate-lockfile) _arguments -s -S $common $manifest ;; help) _cargo_cmds ;; info) _arguments -s -A "^--" $common $registry \ '--index=[specify registry index]:index' \ '*: :_guard "^-*" "crate"' ;; init) _arguments -s -S $common $registry \ '--lib[use library template]' \ '--edition=[specify edition to set for the crate generated]:edition:(2015 2018 2021)' \ '--vcs=[initialize a new repo with a given VCS]:vcs:(git hg pijul fossil none)' \ '--name=[set the resulting package name]:name' \ '1:path:_directories' ;; install) _arguments -s -S $common $parallel $features $triple $registry \ '(-f --force)'{-f,--force}'[force overwriting of existing crates or binaries]' \ '--bin=[only install the specified binary]:binary' \ '--branch=[branch to use when installing from git]:branch' \ '--debug[Build in debug mode (with the "dev" profile) instead of release mode]' \ '--example=[install the specified example instead of binaries]:example:_cargo_example_names' \ '--git=[specify URL from which to install the crate]:url:_urls' \ '--path=[local filesystem path to crate to install]: :_directories' \ '--rev=[specific commit to use when installing from git]:commit' \ '--root=[directory to install packages into]: :_directories' \ '--tag=[tag to use when installing from git]:tag' \ '--version=[version to install from crates.io]:version' \ '--list[list all installed packages and their versions]' \ '--ignore-rust-version[Ignore rust-version specification in packages]' \ '*: :_guard "^-*" "crate"' ;; locate-project) _arguments -s -S $common $manifest \ '--message-format=[specify output representation]:output representation [json]:(json plain)' \ '--workspace[locate Cargo.toml of the workspace root]' ;; login) _arguments -s -S $common $registry \ '*: :_guard "^-*" "token"' ;; metadata) _arguments -s -S $common $features $manifest \ "--no-deps[output information only about the root package and don't fetch dependencies]" \ '--format-version=[specify format version]:version [1]:(1)' ;; new) _arguments -s -S $common $registry \ '--lib[use library template]' \ '--vcs:initialize a new repo with a given VCS:(git hg none)' \ '--name=[set the resulting package name]' ;; owner) _arguments -s -S $common $registry \ '(-a --add)'{-a,--add}'[specify name of a user or team to invite as an owner]:name' \ '--index=[specify registry index]:index' \ '(-l --list)'{-l,--list}'[list owners of a crate]' \ '(-r --remove)'{-r,--remove}'[specify name of a user or team to remove as an owner]:name' \ '--token=[specify API token to use when authenticating]:token' \ '*: :_guard "^-*" "crate"' ;; package) _arguments -s -S $common $parallel $features $triple $target $manifest $registry \ '--index=[specify registry index]:index' \ '(-l --list)'{-l,--list}'[print files included in a package without making one]' \ '--no-metadata[ignore warnings about a lack of human-usable metadata]' \ '--allow-dirty[allow dirty working directories to be packaged]' \ "--no-verify[don't build to verify contents]" ;; pkgid) _arguments -s -S $common $manifest \ '(-p --package)'{-p+,--package=}'[specify package to get ID specifier for]:package:_cargo_package_names' \ '*: :_guard "^-*" "spec"' ;; publish) _arguments -s -S $common $parallel $features $triple $target $manifest $registry \ '--index=[specify registry index]:index' \ '--allow-dirty[allow dirty working directories to be packaged]' \ "--no-verify[don't verify the contents by building them]" \ '--token=[specify token to use when uploading]:token' \ '--dry-run[perform all checks without uploading]' ;; remove | rm) _arguments -s -A "^--" $common $manifest \ "--dev[remove as a dev dependency]" \ "--build[remove as a build dependency]" \ "--target=[remove as a dependency from the given target platform]" \ "--dry-run[don't actually write the manifest]" \ '(-p --package)'{-p+,--package=}'[package to remove from]:package:_cargo_package_names' \ '1: :_guard "^-*" "crate name"' \ '*:args:_default' ;; run | r) _arguments -s -S $common $parallel $features $msgfmt $triple $target $manifest \ '--example=[name of the bin target]:name:_cargo_example_names' \ '--bin=[name of the bin target]:name' \ '(-p --package)'{-p+,--package=}'[specify package with the target to run]:package:_cargo_package_names' \ '--release[build in release mode]' \ '--ignore-rust-version[Ignore rust-version specification in packages]' \ '*: :_default' ;; rustc) _arguments -s -S $common $parallel $features $msgfmt $triple $target $manifest \ '(-p --package)'{-p+,--package=}'[specify package to build]:package:_cargo_package_names' \ '--profile=[specify profile to build the selected target for]:profile' \ '--release[build artifacts in release mode, with optimizations]' \ "${command_scope_spec[@]}" \ '--ignore-rust-version[Ignore rust-version specification in packages]' \ '*: : _dispatch rustc rustc -default-' ;; rustdoc) _arguments -s -S $common $parallel $features $msgfmt $triple $target $manifest \ '--document-private-items[include non-public items in the documentation]' \ '--open[open the docs in a browser after the operation]' \ '(-p --package)'{-p+,--package=}'[specify package to document]:package:_cargo_package_names' \ '--release[build artifacts in release mode, with optimizations]' \ "${command_scope_spec[@]}" \ '--ignore-rust-version[Ignore rust-version specification in packages]' \ '*: : _dispatch rustdoc rustdoc -default-' ;; search) _arguments -s -S $common $registry \ '--index=[specify registry index]:index' \ '--limit=[limit the number of results]:results [10]' \ '*: :_guard "^-*" "query"' ;; test | t) _arguments -s -S $common $jobs $features $msgfmt $triple $target $manifest \ '--test=[test name]: :_cargo_test_names' \ '--no-fail-fast[run all tests regardless of failure]' \ '--no-run[compile but do not run]' \ '(-p --package)'{-p+,--package=}'[package to run tests for]:package:_cargo_package_names' \ '--all[test all packages in the workspace]' \ '--release[build artifacts in release mode, with optimizations]' \ '1: :_cargo_test_names' \ '(--doc --bin --example --test --bench)--lib[only test library]' \ '(--lib --bin --example --test --bench)--doc[only test documentation]' \ '(--lib --doc --example --test --bench)--bin=[binary name]' \ '(--lib --doc --bin --test --bench)--example=[example name]:_cargo_example_names' \ '(--lib --doc --bin --example --bench)--test=[test name]' \ '(--lib --doc --bin --example --test)--bench=[benchmark name]' \ '--ignore-rust-version[Ignore rust-version specification in packages]' \ '*: :_default' ;; tree) _arguments -s -S $common $features $triple $manifest \ '(-p --package)'{-p+,--package=}'[package to use as the root]:package:_cargo_package_names' \ '(-i --invert)'{-i+,--invert=}'[invert the tree for the given package]:package:_cargo_package_names' \ '--prefix=[line prefix]:prefix:(depth indent none)' \ '--no-dedupe[repeat shared dependencies]' \ '(-d --duplicates)'{-d,--duplicates}'[packages with multiple versions]' \ '--charset=[utf8 or ascii]:charset:(utf8 ascii)' \ '(-f --format)'{-f,--format=}'[format string]:format' \ '(-e --edges)'{-e,--edges=}'[edge kinds]:kind:(features normal build dev all no-dev no-build no-normal)' \ ;; uninstall) _arguments -s -S $common \ '(-p --package)'{-p+,--package=}'[specify package to uninstall]:package:_cargo_package_names' \ '--bin=[only uninstall the specified binary]:name' \ '--root=[directory to uninstall packages from]: :_files -/' \ '*:crate:_cargo_installed_crates -F line' ;; update) _arguments -s -S $common $manifest \ '--aggressive=[force dependency update]' \ '--recursive=[force dependency update]' \ "--dry-run[don't actually write the lockfile]" \ '(-p --package)'{-p+,--package=}'[specify package to update]:package:_cargo_package_names' \ '--precise=[update single dependency to precise release]:release' \ '*:package:_cargo_package_names' ;; version) _arguments -s -S $common ;; yank) _arguments -s -S $common $registry \ '--version=[specify yank version]:version' \ '--undo[undo a yank, putting a version back into the index]' \ '--index=[specify registry index to yank from]:registry index' \ '--token=[specify API token to use when authenticating]:token' \ '*: :_guard "^-*" "crate"' ;; *) # allow plugins to define their own functions if ! _call_function ret _cargo-${words[1]}; then # fallback on default completion for unknown commands _default && ret=0 fi (( ! ret )) ;; esac ;; esac } _cargo_unstable_flags() { local flags flags=( help ${${${(M)${(f)"$(_call_program flags cargo -Z help)"}:#*--*}/ #-- #/:}##*-Z } ) _describe -t flags 'unstable flag' flags } _cargo_installed_crates() { local expl _description crates expl 'crate' compadd "$@" "$expl[@]" - ${${${(f)"$(cargo install --list)"}:# *}%% *} } _cargo_cmds() { local -a commands # This uses Parameter Expansion Flags, which are a built-in Zsh feature. # See more: http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion-Flags # and http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion # # # How this work? # # First it splits the result of `cargo --list` at newline, then it removes the first line. # Then it removes indentation (4 whitespaces) before each items. (Note the x## pattern [1]). # Then it replaces those spaces between item and description with a `:` # # [1]: https://github.com/zsh-users/zsh-completions/blob/master/zsh-completions-howto.org#patterns commands=( ${${${(M)"${(f)$(_call_program commands cargo --list)}":# *}/ ##/}/ ##/:} ) _describe -t commands 'command' commands } _cargo_target_triple() { local -a result if (( $+commands[rustup] )); then result=( ${(f)"$(rustup target list --installed)"} ) else result=( ${(f)"$(rustc --print target-list)"} ) fi _describe 'target triple' result } #FIXME: Disabled until fixed #gets package names from the manifest file _cargo_package_names() { _message -e packages package } # Extracts the values of "name" from the array given in $1 and shows them as # command line options for completion _cargo_names_from_array() { local manifest=$(cargo locate-project --message-format plain) if [[ -z $manifest ]]; then return 0 fi local last_line local -a names; local in_block=false local block_name=$1 names=() while read -r line; do if [[ $last_line == "[[$block_name]]" ]]; then in_block=true else if [[ $last_line =~ '\s*\[\[.*' ]]; then in_block=false fi fi if [[ $in_block == true ]]; then if [[ $line =~ '\s*name\s*=' ]]; then regexp-replace line '^\s*name\s*=\s*|"' '' names+=( "$line" ) fi fi last_line=$line done < "$manifest" _describe "$block_name" names } #Gets the test names from the manifest file _cargo_test_names() { _cargo_names_from_array "test" } #Gets the bench names from the manifest file _cargo_benchmark_names() { _cargo_names_from_array "bench" } _cargo_example_names() { if [[ -d examples ]]; then local -a files=(${(@f)$(echo examples/*.rs(:t:r))}) _values 'example' "${files[@]}" fi } _cargo cargo-0.91.0/src/etc/cargo.bashcomp.sh000064400000000000000000000255101046102023000155750ustar 00000000000000# Required for bash versions < 4.1 # Default bash version is 3.2 on latest macOS. See #6874 shopt -s extglob command -v cargo >/dev/null 2>&1 && _cargo() { local cur prev words cword _get_comp_words_by_ref cur prev words cword COMPREPLY=() # Skip past - and + options to find the command. local nwords=${#words[@]} local cmd_i cmd dd_i for (( cmd_i=1; cmd_i<$nwords; cmd_i++ )); do if [[ ! "${words[$cmd_i]}" =~ ^[+-] ]]; then cmd="${words[$cmd_i]}" break fi done # Find the location of the -- separator. for (( dd_i=1; dd_i<$nwords-1; dd_i++ )); do if [[ "${words[$dd_i]}" = "--" ]]; then break fi done local vcs='git hg none pijul fossil' local color='auto always never' local msg_format='human json short' local opt_help='-h --help' local opt_verbose='-v --verbose' local opt_quiet='-q --quiet' local opt_color='--color' local opt_common="$opt_help $opt_verbose $opt_quiet $opt_color" local opt_pkg_spec='-p --package --all --exclude --workspace' local opt_pkg='-p --package' local opt_feat='-F --features --all-features --no-default-features' local opt_mani='--manifest-path' local opt_jobs='-j --jobs' local opt_parallel="$opt_jobs --keep-going" local opt_force='-f --force' local opt_sync='-s --sync' local opt_lock='--frozen --locked --offline' local opt_targets="--lib --bin --bins --example --examples --test --tests --bench --benches --all-targets" local opt___nocmd="$opt_common -V --version --list --explain" local opt__add="$opt_common -p --package --features --default-features --no-default-features $opt_mani $opt_lock --optional --no-optional --rename --dry-run --path --git --branch --tag --rev --registry --dev --build --target --ignore-rust-version" local opt__bench="$opt_common $opt_pkg_spec $opt_feat $opt_mani $opt_lock $opt_jobs $opt_targets --message-format --target --no-run --no-fail-fast --target-dir --ignore-rust-version" local opt__build="$opt_common $opt_pkg_spec $opt_feat $opt_mani $opt_lock $opt_parallel $opt_targets --message-format --target --release --profile --target-dir --ignore-rust-version" local opt__b="$opt__build" local opt__check="$opt_common $opt_pkg_spec $opt_feat $opt_mani $opt_lock $opt_parallel $opt_targets --message-format --target --release --profile --target-dir --ignore-rust-version" local opt__c="$opt__check" local opt__clean="$opt_common $opt_pkg $opt_mani $opt_lock --target --release --doc --target-dir --profile" local opt__clippy="$opt_common $opt_pkg_spec $opt_feat $opt_mani $opt_lock $opt_parallel $opt_targets --message-format --target --release --profile --target-dir --no-deps --fix" local opt__doc="$opt_common $opt_pkg_spec $opt_feat $opt_mani $opt_lock $opt_parallel --message-format --bin --bins --lib --target --open --no-deps --release --document-private-items --target-dir --profile --ignore-rust-version" local opt__d="$opt__doc" local opt__fetch="$opt_common $opt_mani $opt_lock --target" local opt__fix="$opt_common $opt_pkg_spec $opt_feat $opt_mani $opt_parallel $opt_targets $opt_lock --release --target --message-format --broken-code --edition --edition-idioms --allow-no-vcs --allow-dirty --allow-staged --profile --target-dir --ignore-rust-version" local opt__generate_lockfile="$opt_common $opt_mani $opt_lock" local opt__help="$opt_help" local opt__info="$opt_common $opt_lock --registry --index" local opt__init="$opt_common $opt_lock --bin --lib --name --vcs --edition --registry" local opt__install="$opt_common $opt_feat $opt_parallel $opt_lock $opt_force --bin --bins --branch --debug --example --examples --git --list --path --rev --root --tag --version --registry --target --profile --no-track --ignore-rust-version" local opt__locate_project="$opt_common $opt_mani $opt_lock --message-format --workspace" local opt__login="$opt_common $opt_lock --registry" local opt__metadata="$opt_common $opt_feat $opt_mani $opt_lock --format-version=1 --no-deps --filter-platform" local opt__new="$opt_common $opt_lock --vcs --bin --lib --name --edition --registry" local opt__owner="$opt_common $opt_lock -a --add -r --remove -l --list --index --token --registry" local opt__package="$opt_common $opt_mani $opt_feat $opt_lock $opt_parallel --allow-dirty -l --list --no-verify --no-metadata --index --registry --target --target-dir" local opt__pkgid="$opt_common $opt_mani $opt_lock $opt_pkg" local opt__publish="$opt_common $opt_mani $opt_feat $opt_lock $opt_parallel --allow-dirty --dry-run --token --no-verify --index --registry --target --target-dir" local opt__remove="$opt_common $opt_pkg $opt_lock $opt_mani --dry-run --dev --build --target" local opt__rm="$opt__remove" local opt__report="$opt_help $opt_verbose $opt_color future-incompat future-incompatibilities" local opt__report__future_incompat="$opt_help $opt_verbose $opt_color $opt_pkg --id" local opt__run="$opt_common $opt_pkg $opt_feat $opt_mani $opt_lock $opt_parallel --message-format --target --bin --example --release --target-dir --profile --ignore-rust-version" local opt__r="$opt__run" local opt__rustc="$opt_common $opt_pkg $opt_feat $opt_mani $opt_lock $opt_parallel $opt_targets -L --crate-type --extern --message-format --profile --target --release --target-dir --ignore-rust-version" local opt__rustdoc="$opt_common $opt_pkg $opt_feat $opt_mani $opt_lock $opt_parallel $opt_targets --message-format --target --release --open --target-dir --profile --ignore-rust-version" local opt__search="$opt_common $opt_lock --limit --index --registry" local opt__test="$opt_common $opt_pkg_spec $opt_feat $opt_mani $opt_lock $opt_jobs $opt_targets --message-format --doc --target --no-run --release --no-fail-fast --target-dir --profile --ignore-rust-version" local opt__t="$opt__test" local opt__tree="$opt_common $opt_pkg_spec $opt_feat $opt_mani $opt_lock --target -i --invert --prefix --no-dedupe --duplicates -d --charset -f --format -e --edges" local opt__uninstall="$opt_common $opt_lock $opt_pkg --bin --root" local opt__update="$opt_common $opt_mani $opt_lock $opt_pkg --aggressive --recursive --precise --dry-run" local opt__vendor="$opt_common $opt_mani $opt_lock $opt_sync --no-delete --respect-source-config --versioned-dirs" local opt__version="$opt_common $opt_lock" local opt__yank="$opt_common $opt_lock --version --undo --index --token --registry" local opt__libtest="--help --include-ignored --ignored --test --bench --list --logfile --nocapture --test-threads --skip -q --quiet --exact --color --format" if [[ $cword -gt $dd_i ]]; then # Completion after -- separator. if [[ "${cmd}" = @(test|bench) ]]; then COMPREPLY=( $( compgen -W "${opt__libtest}" -- "$cur" ) ) else # Fallback to filename completion, useful with `cargo run`. _filedir fi elif [[ $cword -le $cmd_i ]]; then # Completion before or at the command. if [[ "$cur" == -* ]]; then COMPREPLY=( $( compgen -W "${opt___nocmd}" -- "$cur" ) ) elif [[ "$cur" == +* ]]; then COMPREPLY=( $( compgen -W "$(_toolchains)" -- "$cur" ) ) else _ensure_cargo_commands_cache_filled COMPREPLY=( $( compgen -W "$__cargo_commands_cache" -- "$cur" ) ) fi else case "${prev}" in --vcs) COMPREPLY=( $( compgen -W "$vcs" -- "$cur" ) ) ;; --color) COMPREPLY=( $( compgen -W "$color" -- "$cur" ) ) ;; --message-format) COMPREPLY=( $( compgen -W "$msg_format" -- "$cur" ) ) ;; --manifest-path) _filedir toml ;; --bin) COMPREPLY=( $( compgen -W "$(_bin_names)" -- "$cur" ) ) ;; --test) COMPREPLY=( $( compgen -W "$(_test_names)" -- "$cur" ) ) ;; --bench) COMPREPLY=( $( compgen -W "$(_benchmark_names)" -- "$cur" ) ) ;; --example) COMPREPLY=( $( compgen -W "$(_get_examples)" -- "$cur" ) ) ;; --target) COMPREPLY=( $( compgen -W "$(_get_targets)" -- "$cur" ) ) ;; --target-dir|--path) _filedir -d ;; help) _ensure_cargo_commands_cache_filled COMPREPLY=( $( compgen -W "$__cargo_commands_cache" -- "$cur" ) ) ;; *) if [[ "$cmd" == "report" && "$prev" == future-incompat* ]]; then local opt_var=opt__${cmd//-/_}__${prev//-/_} else local opt_var=opt__${cmd//-/_} fi if [[ -z "${!opt_var-}" ]]; then # Forward to subcommands completion if bash-completion >= 2.12 is available if [[ $BASH_COMPLETION_VERSINFO && (${BASH_COMPLETION_VERSINFO[0]} -gt 2 || (${BASH_COMPLETION_VERSINFO[0]} -eq 2 && ${BASH_COMPLETION_VERSINFO[1]} -ge 12)) ]]; then COMP_WORDS[cmd_i]="cargo-$cmd" _comp_command_offset "$cmd_i" COMP_WORDS[cmd_i]="$cmd" else # Fallback to filename completion. _filedir fi else COMPREPLY=( $( compgen -W "${!opt_var}" -- "$cur" ) ) fi ;; esac fi # compopt does not work in bash version 3 return 0 } && complete -F _cargo cargo __cargo_commands_cache= _ensure_cargo_commands_cache_filled(){ if [[ -z $__cargo_commands_cache ]]; then __cargo_commands_cache="$(cargo --list 2>/dev/null | awk 'NR>1 {print $1}')" fi } _locate_manifest(){ cargo locate-project --message-format plain 2>/dev/null } # Extracts the values of "name" from the array given in $1 and shows them as # command line options for completion _get_names_from_array() { local manifest=$(_locate_manifest) if [[ -z $manifest ]]; then return 0 fi local last_line local -a names local in_block=false local block_name=$1 while read line do if [[ $last_line == "[[$block_name]]" ]]; then in_block=true else if [[ $last_line =~ .*\[\[.* ]]; then in_block=false fi fi if [[ $in_block == true ]]; then if [[ $line =~ .*name.*\= ]]; then line=${line##*=} line=${line%%\"} line=${line##*\"} names+=("$line") fi fi last_line=$line done < "$manifest" echo "${names[@]}" } #Gets the bin names from the manifest file _bin_names() { _get_names_from_array "bin" } #Gets the test names from the manifest file _test_names() { _get_names_from_array "test" } #Gets the bench names from the manifest file _benchmark_names() { _get_names_from_array "bench" } _get_examples(){ local manifest=$(_locate_manifest) [ -z "$manifest" ] && return 0 local files=("${manifest%/*}"/examples/*.rs) local names=("${files[@]##*/}") local names=("${names[@]%.*}") # "*" means no examples found if [[ "${names[@]}" != "*" ]]; then echo "${names[@]}" fi } _get_targets(){ if command -v rustup >/dev/null 2>/dev/null; then rustup target list --installed else rustc --print target-list fi } _toolchains(){ local result=() local toolchains=$(rustup toolchain list) local channels="nightly|beta|stable|[0-9]\.[0-9]{1,2}\.[0-9]" local date="[0-9]{4}-[0-9]{2}-[0-9]{2}" while read line do # Strip " (default)" line=${line%% *} if [[ "$line" =~ ^($channels)(-($date))?(-.*) ]]; then if [[ -z ${BASH_REMATCH[3]} ]]; then result+=("+${BASH_REMATCH[1]}") else # channel-date result+=("+${BASH_REMATCH[1]}-${BASH_REMATCH[3]}") fi result+=("+$line") else result+=("+$line") fi done <<< "$toolchains" echo "${result[@]}" } # vim:ft=sh cargo-0.91.0/src/etc/man/cargo-add.1000064400000000000000000000266251046102023000150410ustar 00000000000000'\" t .TH "CARGO\-ADD" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-add \[em] Add dependencies to a Cargo.toml manifest file .SH "SYNOPSIS" \fBcargo add\fR [\fIoptions\fR] \fIcrate\fR\[u2026] .br \fBcargo add\fR [\fIoptions\fR] \fB\-\-path\fR \fIpath\fR .br \fBcargo add\fR [\fIoptions\fR] \fB\-\-git\fR \fIurl\fR [\fIcrate\fR\[u2026]] .SH "DESCRIPTION" This command can add or modify dependencies. .sp The source for the dependency can be specified with: .sp .RS 4 \h'-04'\(bu\h'+03'\fIcrate\fR\fB@\fR\fIversion\fR: Fetch from a registry with a version constraint of \[lq]\fIversion\fR\[rq] .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB\-\-path\fR \fIpath\fR: Fetch from the specified \fIpath\fR .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB\-\-git\fR \fIurl\fR: Pull from a git repo at \fIurl\fR .RE .sp If no source is specified, then a best effort will be made to select one, including: .sp .RS 4 \h'-04'\(bu\h'+03'Existing dependencies in other tables (like \fBdev\-dependencies\fR) .RE .sp .RS 4 \h'-04'\(bu\h'+03'Workspace members .RE .sp .RS 4 \h'-04'\(bu\h'+03'Latest release in the registry .RE .sp When you add a package that is already present, the existing entry will be updated with the flags specified. .sp Upon successful invocation, the enabled (\fB+\fR) and disabled (\fB\-\fR) \fIfeatures\fR of the specified dependency will be listed in the command\[cq]s output. .SH "OPTIONS" .SS "Source options" .sp \fB\-\-git\fR \fIurl\fR .RS 4 \fIGit URL to add the specified crate from\fR \&. .RE .sp \fB\-\-branch\fR \fIbranch\fR .RS 4 Branch to use when adding from git. .RE .sp \fB\-\-tag\fR \fItag\fR .RS 4 Tag to use when adding from git. .RE .sp \fB\-\-rev\fR \fIsha\fR .RS 4 Specific commit to use when adding from git. .RE .sp \fB\-\-path\fR \fIpath\fR .RS 4 \fIFilesystem path\fR to local crate to add. .RE .sp \fB\-\-base\fR \fIbase\fR .RS 4 The \fIpath base\fR to use when adding a local crate. .sp \fIUnstable (nightly\-only)\fR .RE .sp \fB\-\-registry\fR \fIregistry\fR .RS 4 Name of the registry to use. Registry names are defined in \fICargo config files\fR \&. If not specified, the default registry is used, which is defined by the \fBregistry.default\fR config key which defaults to \fBcrates\-io\fR\&. .RE .SS "Section options" .sp \fB\-\-dev\fR .RS 4 Add as a \fIdevelopment dependency\fR \&. .RE .sp \fB\-\-build\fR .RS 4 Add as a \fIbuild dependency\fR \&. .RE .sp \fB\-\-target\fR \fItarget\fR .RS 4 Add as a dependency to the \fIgiven target platform\fR \&. .sp To avoid unexpected shell expansions, you may use quotes around each target, e.g., \fB\-\-target 'cfg(unix)'\fR\&. .RE .SS "Dependency options" .sp \fB\-\-dry\-run\fR .RS 4 Don\[cq]t actually write the manifest .RE .sp \fB\-\-rename\fR \fIname\fR .RS 4 \fIRename\fR the dependency. .RE .sp \fB\-\-optional\fR .RS 4 Mark the dependency as \fIoptional\fR \&. .RE .sp \fB\-\-no\-optional\fR .RS 4 Mark the dependency as \fIrequired\fR \&. .RE .sp \fB\-\-public\fR .RS 4 Mark the dependency as public. .sp The dependency can be referenced in your library\[cq]s public API. .sp \fIUnstable (nightly\-only)\fR .RE .sp \fB\-\-no\-public\fR .RS 4 Mark the dependency as private. .sp While you can use the crate in your implementation, it cannot be referenced in your public API. .sp \fIUnstable (nightly\-only)\fR .RE .sp \fB\-\-no\-default\-features\fR .RS 4 Disable the \fIdefault features\fR \&. .RE .sp \fB\-\-default\-features\fR .RS 4 Re\-enable the \fIdefault features\fR \&. .RE .sp \fB\-F\fR \fIfeatures\fR, \fB\-\-features\fR \fIfeatures\fR .RS 4 Space or comma separated list of \fIfeatures to activate\fR \&. When adding multiple crates, the features for a specific crate may be enabled with \fBpackage\-name/feature\-name\fR syntax. This flag may be specified multiple times, which enables all specified features. .RE .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .SS "Manifest Options" .sp \fB\-\-manifest\-path\fR \fIpath\fR .RS 4 Path to the \fBCargo.toml\fR file. By default, Cargo searches for the \fBCargo.toml\fR file in the current directory or any parent directory. .RE .sp \fB\-p\fR \fIspec\fR, \fB\-\-package\fR \fIspec\fR .RS 4 Add dependencies to only the specified package. .RE .sp \fB\-\-ignore\-rust\-version\fR .RS 4 Ignore \fBrust\-version\fR specification in packages. .RE .sp \fB\-\-locked\fR .RS 4 Asserts that the exact same dependencies and versions are used as when the existing \fBCargo.lock\fR file was originally generated. Cargo will exit with an error when either of the following scenarios arises: .sp .RS 4 \h'-04'\(bu\h'+03'The lock file is missing. .RE .sp .RS 4 \h'-04'\(bu\h'+03'Cargo attempted to change the lock file due to a different dependency resolution. .RE .sp It may be used in environments where deterministic builds are desired, such as in CI pipelines. .RE .sp \fB\-\-offline\fR .RS 4 Prevents Cargo from accessing the network for any reason. Without this flag, Cargo will stop with an error if it needs to access the network and the network is not available. With this flag, Cargo will attempt to proceed without the network if possible. .sp Beware that this may result in different dependency resolution than online mode. Cargo will restrict itself to crates that are downloaded locally, even if there might be a newer version as indicated in the local copy of the index. See the \fBcargo\-fetch\fR(1) command to download dependencies before going offline. .sp May also be specified with the \fBnet.offline\fR \fIconfig value\fR \&. .RE .sp \fB\-\-frozen\fR .RS 4 Equivalent to specifying both \fB\-\-locked\fR and \fB\-\-offline\fR\&. .RE .sp \fB\-\-lockfile\-path\fR \fIPATH\fR .RS 4 Changes the path of the lockfile from the default (\fB/Cargo.lock\fR) to \fIPATH\fR\&. \fIPATH\fR must end with \fBCargo.lock\fR (e.g. \fB\-\-lockfile\-path /tmp/temporary\-lockfile/Cargo.lock\fR). Note that providing \fB\-\-lockfile\-path\fR will ignore existing lockfile at the default path, and instead will either use the lockfile from \fIPATH\fR, or write a new lockfile into the provided \fIPATH\fR if it doesn\[cq]t exist. This flag can be used to run most commands in read\-only directories, writing lockfile into the provided \fIPATH\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#14421\fR ). .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Add \fBregex\fR as a dependency .sp .RS 4 .nf cargo add regex .fi .RE .RE .sp .RS 4 \h'-04' 2.\h'+01'Add \fBtrybuild\fR as a dev\-dependency .sp .RS 4 .nf cargo add \-\-dev trybuild .fi .RE .RE .sp .RS 4 \h'-04' 3.\h'+01'Add an older version of \fBnom\fR as a dependency .sp .RS 4 .nf cargo add nom@5 .fi .RE .RE .sp .RS 4 \h'-04' 4.\h'+01'Add support for serializing data structures to json with \fBderive\fRs .sp .RS 4 .nf cargo add serde serde_json \-F serde/derive .fi .RE .RE .sp .RS 4 \h'-04' 5.\h'+01'Add \fBwindows\fR as a platform specific dependency on \fBcfg(windows)\fR .sp .RS 4 .nf cargo add windows \-\-target 'cfg(windows)' .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-remove\fR(1) cargo-0.91.0/src/etc/man/cargo-bench.1000064400000000000000000000512651046102023000153660ustar 00000000000000'\" t .TH "CARGO\-BENCH" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-bench \[em] Execute benchmarks of a package .SH "SYNOPSIS" \fBcargo bench\fR [\fIoptions\fR] [\fIbenchname\fR] [\fB\-\-\fR \fIbench\-options\fR] .SH "DESCRIPTION" Compile and execute benchmarks. .sp The benchmark filtering argument \fIbenchname\fR and all the arguments following the two dashes (\fB\-\-\fR) are passed to the benchmark binaries and thus to \fIlibtest\fR (rustc\[cq]s built in unit\-test and micro\-benchmarking framework). If you are passing arguments to both Cargo and the binary, the ones after \fB\-\-\fR go to the binary, the ones before go to Cargo. For details about libtest\[cq]s arguments see the output of \fBcargo bench \-\- \-\-help\fR and check out the rustc book\[cq]s chapter on how tests work at \&. .sp As an example, this will run only the benchmark named \fBfoo\fR (and skip other similarly named benchmarks like \fBfoobar\fR): .sp .RS 4 .nf cargo bench \-\- foo \-\-exact .fi .RE .sp Benchmarks are built with the \fB\-\-test\fR option to \fBrustc\fR which creates a special executable by linking your code with libtest. The executable automatically runs all functions annotated with the \fB#[bench]\fR attribute. Cargo passes the \fB\-\-bench\fR flag to the test harness to tell it to run only benchmarks, regardless of whether the harness is libtest or a custom harness. .sp The libtest harness may be disabled by setting \fBharness = false\fR in the target manifest settings, in which case your code will need to provide its own \fBmain\fR function to handle running benchmarks. .RS 3 .ll -5 .sp \fBNote\fR: The \fI\f(BI#[bench]\fI attribute\fR is currently unstable and only available on the \fInightly channel\fR \&. There are some packages available on \fIcrates.io\fR that may help with running benchmarks on the stable channel, such as \fICriterion\fR \&. .br .RE .ll .sp By default, \fBcargo bench\fR uses the \fI\f(BIbench\fI profile\fR , which enables optimizations and disables debugging information. If you need to debug a benchmark, you can use the \fB\-\-profile=dev\fR command\-line option to switch to the dev profile. You can then run the debug\-enabled benchmark within a debugger. .SS "Working directory of benchmarks" The working directory of every benchmark is set to the root directory of the package the benchmark belongs to. Setting the working directory of benchmarks to the package\[cq]s root directory makes it possible for benchmarks to reliably access the package\[cq]s files using relative paths, regardless from where \fBcargo bench\fR was executed from. .SH "OPTIONS" .SS "Benchmark Options" .sp \fB\-\-no\-run\fR .RS 4 Compile, but don\[cq]t run benchmarks. .RE .sp \fB\-\-no\-fail\-fast\fR .RS 4 Run all benchmarks regardless of failure. Without this flag, Cargo will exit after the first executable fails. The Rust test harness will run all benchmarks within the executable to completion, this flag only applies to the executable as a whole. .RE .SS "Package Selection" By default, when no package selection options are given, the packages selected depend on the selected manifest file (based on the current working directory if \fB\-\-manifest\-path\fR is not given). If the manifest is the root of a workspace then the workspaces default members are selected, otherwise only the package defined by the manifest will be selected. .sp The default members of a workspace can be set explicitly with the \fBworkspace.default\-members\fR key in the root manifest. If this is not set, a virtual workspace will include all workspace members (equivalent to passing \fB\-\-workspace\fR), and a non\-virtual workspace will include only the root crate itself. .sp \fB\-p\fR \fIspec\fR\[u2026], \fB\-\-package\fR \fIspec\fR\[u2026] .RS 4 Benchmark only the specified packages. See \fBcargo\-pkgid\fR(1) for the SPEC format. This flag may be specified multiple times and supports common Unix glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell accidentally expanding glob patterns before Cargo handles them, you must use single quotes or double quotes around each pattern. .RE .sp \fB\-\-workspace\fR .RS 4 Benchmark all members in the workspace. .RE .sp \fB\-\-all\fR .RS 4 Deprecated alias for \fB\-\-workspace\fR\&. .RE .sp \fB\-\-exclude\fR \fISPEC\fR\[u2026] .RS 4 Exclude the specified packages. Must be used in conjunction with the \fB\-\-workspace\fR flag. This flag may be specified multiple times and supports common Unix glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell accidentally expanding glob patterns before Cargo handles them, you must use single quotes or double quotes around each pattern. .RE .SS "Target Selection" When no target selection options are given, \fBcargo bench\fR will build the following targets of the selected packages: .sp .RS 4 \h'-04'\(bu\h'+03'lib \[em] used to link with binaries and benchmarks .RE .sp .RS 4 \h'-04'\(bu\h'+03'bins (only if benchmark targets are built and required features are available) .RE .sp .RS 4 \h'-04'\(bu\h'+03'lib as a benchmark .RE .sp .RS 4 \h'-04'\(bu\h'+03'bins as benchmarks .RE .sp .RS 4 \h'-04'\(bu\h'+03'benchmark targets .RE .sp The default behavior can be changed by setting the \fBbench\fR flag for the target in the manifest settings. Setting examples to \fBbench = true\fR will build and run the example as a benchmark, replacing the example\[cq]s \fBmain\fR function with the libtest harness. .sp Setting targets to \fBbench = false\fR will stop them from being benchmarked by default. Target selection options that take a target by name (such as \fB\-\-example foo\fR) ignore the \fBbench\fR flag and will always benchmark the given target. .sp See \fIConfiguring a target\fR for more information on per\-target settings. .sp Binary targets are automatically built if there is an integration test or benchmark being selected to benchmark. This allows an integration test to execute the binary to exercise and test its behavior. The \fBCARGO_BIN_EXE_\fR \fIenvironment variable\fR is set when the integration test is built so that it can use the \fI\f(BIenv\fI macro\fR to locate the executable. .sp Passing target selection flags will benchmark only the specified targets. .sp Note that \fB\-\-bin\fR, \fB\-\-example\fR, \fB\-\-test\fR and \fB\-\-bench\fR flags also support common Unix glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell accidentally expanding glob patterns before Cargo handles them, you must use single quotes or double quotes around each glob pattern. .sp \fB\-\-lib\fR .RS 4 Benchmark the package\[cq]s library. .RE .sp \fB\-\-bin\fR \fIname\fR\[u2026] .RS 4 Benchmark the specified binary. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-bins\fR .RS 4 Benchmark all binary targets. .RE .sp \fB\-\-example\fR \fIname\fR\[u2026] .RS 4 Benchmark the specified example. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-examples\fR .RS 4 Benchmark all example targets. .RE .sp \fB\-\-test\fR \fIname\fR\[u2026] .RS 4 Benchmark the specified integration test. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-tests\fR .RS 4 Benchmark all targets that have the \fBtest = true\fR manifest flag set. By default this includes the library and binaries built as unittests, and integration tests. Be aware that this will also build any required dependencies, so the lib target may be built twice (once as a unittest, and once as a dependency for binaries, integration tests, etc.). Targets may be enabled or disabled by setting the \fBtest\fR flag in the manifest settings for the target. .RE .sp \fB\-\-bench\fR \fIname\fR\[u2026] .RS 4 Benchmark the specified benchmark. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-benches\fR .RS 4 Benchmark all targets that have the \fBbench = true\fR manifest flag set. By default this includes the library and binaries built as benchmarks, and bench targets. Be aware that this will also build any required dependencies, so the lib target may be built twice (once as a benchmark, and once as a dependency for binaries, benchmarks, etc.). Targets may be enabled or disabled by setting the \fBbench\fR flag in the manifest settings for the target. .RE .sp \fB\-\-all\-targets\fR .RS 4 Benchmark all targets. This is equivalent to specifying \fB\-\-lib \-\-bins \-\-tests \-\-benches \-\-examples\fR\&. .RE .SS "Feature Selection" The feature flags allow you to control which features are enabled. When no feature options are given, the \fBdefault\fR feature is activated for every selected package. .sp See \fIthe features documentation\fR for more details. .sp \fB\-F\fR \fIfeatures\fR, \fB\-\-features\fR \fIfeatures\fR .RS 4 Space or comma separated list of features to activate. Features of workspace members may be enabled with \fBpackage\-name/feature\-name\fR syntax. This flag may be specified multiple times, which enables all specified features. .RE .sp \fB\-\-all\-features\fR .RS 4 Activate all available features of all selected packages. .RE .sp \fB\-\-no\-default\-features\fR .RS 4 Do not activate the \fBdefault\fR feature of the selected packages. .RE .SS "Compilation Options" .sp \fB\-\-target\fR \fItriple\fR .RS 4 Benchmark for the given architecture. The default is the host architecture. The general format of the triple is \fB\-\-\-\fR\&. Run \fBrustc \-\-print target\-list\fR for a list of supported targets. This flag may be specified multiple times. .sp This may also be specified with the \fBbuild.target\fR \fIconfig value\fR \&. .sp Note that specifying this flag makes Cargo run in a different mode where the target artifacts are placed in a separate directory. See the \fIbuild cache\fR documentation for more details. .RE .sp \fB\-\-profile\fR \fIname\fR .RS 4 Benchmark with the given profile. See \fIthe reference\fR for more details on profiles. .RE .sp \fB\-\-timings=\fR\fIfmts\fR .RS 4 Output information how long each compilation takes, and track concurrency information over time. Accepts an optional comma\-separated list of output formats; \fB\-\-timings\fR without an argument will default to \fB\-\-timings=html\fR\&. Specifying an output format (rather than the default) is unstable and requires \fB\-Zunstable\-options\fR\&. Valid output formats: .sp .RS 4 \h'-04'\(bu\h'+03'\fBhtml\fR (unstable, requires \fB\-Zunstable\-options\fR): Write a human\-readable file \fBcargo\-timing.html\fR to the \fBtarget/cargo\-timings\fR directory with a report of the compilation. Also write a report to the same directory with a timestamp in the filename if you want to look at older runs. HTML output is suitable for human consumption only, and does not provide machine\-readable timing data. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\fR (unstable, requires \fB\-Zunstable\-options\fR): Emit machine\-readable JSON information about timing information. .RE .RE .SS "Output Options" .sp \fB\-\-target\-dir\fR \fIdirectory\fR .RS 4 Directory for all generated artifacts and intermediate files. May also be specified with the \fBCARGO_TARGET_DIR\fR environment variable, or the \fBbuild.target\-dir\fR \fIconfig value\fR \&. Defaults to \fBtarget\fR in the root of the workspace. .RE .SS "Display Options" By default the Rust test harness hides output from benchmark execution to keep results readable. Benchmark output can be recovered (e.g., for debugging) by passing \fB\-\-nocapture\fR to the benchmark binaries: .sp .RS 4 .nf cargo bench \-\- \-\-nocapture .fi .RE .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .sp \fB\-\-message\-format\fR \fIfmt\fR .RS 4 The output format for diagnostic messages. Can be specified multiple times and consists of comma\-separated values. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBhuman\fR (default): Display in a human\-readable text format. Conflicts with \fBshort\fR and \fBjson\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBshort\fR: Emit shorter, human\-readable text messages. Conflicts with \fBhuman\fR and \fBjson\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\fR: Emit JSON messages to stdout. See \fIthe reference\fR for more details. Conflicts with \fBhuman\fR and \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-diagnostic\-short\fR: Ensure the \fBrendered\fR field of JSON messages contains the \[lq]short\[rq] rendering from rustc. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-diagnostic\-rendered\-ansi\fR: Ensure the \fBrendered\fR field of JSON messages contains embedded ANSI color codes for respecting rustc\[cq]s default color scheme. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-render\-diagnostics\fR: Instruct Cargo to not include rustc diagnostics in JSON messages printed, but instead Cargo itself should render the JSON diagnostics coming from rustc. Cargo\[cq]s own JSON diagnostics and others coming from rustc are still emitted. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .RE .SS "Manifest Options" .sp \fB\-\-manifest\-path\fR \fIpath\fR .RS 4 Path to the \fBCargo.toml\fR file. By default, Cargo searches for the \fBCargo.toml\fR file in the current directory or any parent directory. .RE .sp \fB\-\-ignore\-rust\-version\fR .RS 4 Ignore \fBrust\-version\fR specification in packages. .RE .sp \fB\-\-locked\fR .RS 4 Asserts that the exact same dependencies and versions are used as when the existing \fBCargo.lock\fR file was originally generated. Cargo will exit with an error when either of the following scenarios arises: .sp .RS 4 \h'-04'\(bu\h'+03'The lock file is missing. .RE .sp .RS 4 \h'-04'\(bu\h'+03'Cargo attempted to change the lock file due to a different dependency resolution. .RE .sp It may be used in environments where deterministic builds are desired, such as in CI pipelines. .RE .sp \fB\-\-offline\fR .RS 4 Prevents Cargo from accessing the network for any reason. Without this flag, Cargo will stop with an error if it needs to access the network and the network is not available. With this flag, Cargo will attempt to proceed without the network if possible. .sp Beware that this may result in different dependency resolution than online mode. Cargo will restrict itself to crates that are downloaded locally, even if there might be a newer version as indicated in the local copy of the index. See the \fBcargo\-fetch\fR(1) command to download dependencies before going offline. .sp May also be specified with the \fBnet.offline\fR \fIconfig value\fR \&. .RE .sp \fB\-\-frozen\fR .RS 4 Equivalent to specifying both \fB\-\-locked\fR and \fB\-\-offline\fR\&. .RE .sp \fB\-\-lockfile\-path\fR \fIPATH\fR .RS 4 Changes the path of the lockfile from the default (\fB/Cargo.lock\fR) to \fIPATH\fR\&. \fIPATH\fR must end with \fBCargo.lock\fR (e.g. \fB\-\-lockfile\-path /tmp/temporary\-lockfile/Cargo.lock\fR). Note that providing \fB\-\-lockfile\-path\fR will ignore existing lockfile at the default path, and instead will either use the lockfile from \fIPATH\fR, or write a new lockfile into the provided \fIPATH\fR if it doesn\[cq]t exist. This flag can be used to run most commands in read\-only directories, writing lockfile into the provided \fIPATH\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#14421\fR ). .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SS "Miscellaneous Options" The \fB\-\-jobs\fR argument affects the building of the benchmark executable but does not affect how many threads are used when running the benchmarks. The Rust test harness runs benchmarks serially in a single thread. .sp \fB\-j\fR \fIN\fR, \fB\-\-jobs\fR \fIN\fR .RS 4 Number of parallel jobs to run. May also be specified with the \fBbuild.jobs\fR \fIconfig value\fR \&. Defaults to the number of logical CPUs. If negative, it sets the maximum number of parallel jobs to the number of logical CPUs plus provided value. If a string \fBdefault\fR is provided, it sets the value back to defaults. Should not be 0. .RE .sp While \fBcargo bench\fR involves compilation, it does not provide a \fB\-\-keep\-going\fR flag. Use \fB\-\-no\-fail\-fast\fR to run as many benchmarks as possible without stopping at the first failure. To \[lq]compile\[rq] as many benchmarks as possible, use \fB\-\-benches\fR to build benchmark binaries separately. For example: .sp .RS 4 .nf cargo build \-\-benches \-\-release \-\-keep\-going cargo bench \-\-no\-fail\-fast .fi .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Build and execute all the benchmarks of the current package: .sp .RS 4 .nf cargo bench .fi .RE .RE .sp .RS 4 \h'-04' 2.\h'+01'Run only a specific benchmark within a specific benchmark target: .sp .RS 4 .nf cargo bench \-\-bench bench_name \-\- modname::some_benchmark .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-test\fR(1) cargo-0.91.0/src/etc/man/cargo-build.1000064400000000000000000000425401046102023000154020ustar 00000000000000'\" t .TH "CARGO\-BUILD" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-build \[em] Compile the current package .SH "SYNOPSIS" \fBcargo build\fR [\fIoptions\fR] .SH "DESCRIPTION" Compile local packages and all of their dependencies. .SH "OPTIONS" .SS "Package Selection" By default, when no package selection options are given, the packages selected depend on the selected manifest file (based on the current working directory if \fB\-\-manifest\-path\fR is not given). If the manifest is the root of a workspace then the workspaces default members are selected, otherwise only the package defined by the manifest will be selected. .sp The default members of a workspace can be set explicitly with the \fBworkspace.default\-members\fR key in the root manifest. If this is not set, a virtual workspace will include all workspace members (equivalent to passing \fB\-\-workspace\fR), and a non\-virtual workspace will include only the root crate itself. .sp \fB\-p\fR \fIspec\fR\[u2026], \fB\-\-package\fR \fIspec\fR\[u2026] .RS 4 Build only the specified packages. See \fBcargo\-pkgid\fR(1) for the SPEC format. This flag may be specified multiple times and supports common Unix glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell accidentally expanding glob patterns before Cargo handles them, you must use single quotes or double quotes around each pattern. .RE .sp \fB\-\-workspace\fR .RS 4 Build all members in the workspace. .RE .sp \fB\-\-all\fR .RS 4 Deprecated alias for \fB\-\-workspace\fR\&. .RE .sp \fB\-\-exclude\fR \fISPEC\fR\[u2026] .RS 4 Exclude the specified packages. Must be used in conjunction with the \fB\-\-workspace\fR flag. This flag may be specified multiple times and supports common Unix glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell accidentally expanding glob patterns before Cargo handles them, you must use single quotes or double quotes around each pattern. .RE .SS "Target Selection" When no target selection options are given, \fBcargo build\fR will build all binary and library targets of the selected packages. Binaries are skipped if they have \fBrequired\-features\fR that are missing. .sp Binary targets are automatically built if there is an integration test or benchmark being selected to build. This allows an integration test to execute the binary to exercise and test its behavior. The \fBCARGO_BIN_EXE_\fR \fIenvironment variable\fR is set when the integration test is built so that it can use the \fI\f(BIenv\fI macro\fR to locate the executable. .sp Passing target selection flags will build only the specified targets. .sp Note that \fB\-\-bin\fR, \fB\-\-example\fR, \fB\-\-test\fR and \fB\-\-bench\fR flags also support common Unix glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell accidentally expanding glob patterns before Cargo handles them, you must use single quotes or double quotes around each glob pattern. .sp \fB\-\-lib\fR .RS 4 Build the package\[cq]s library. .RE .sp \fB\-\-bin\fR \fIname\fR\[u2026] .RS 4 Build the specified binary. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-bins\fR .RS 4 Build all binary targets. .RE .sp \fB\-\-example\fR \fIname\fR\[u2026] .RS 4 Build the specified example. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-examples\fR .RS 4 Build all example targets. .RE .sp \fB\-\-test\fR \fIname\fR\[u2026] .RS 4 Build the specified integration test. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-tests\fR .RS 4 Build all targets that have the \fBtest = true\fR manifest flag set. By default this includes the library and binaries built as unittests, and integration tests. Be aware that this will also build any required dependencies, so the lib target may be built twice (once as a unittest, and once as a dependency for binaries, integration tests, etc.). Targets may be enabled or disabled by setting the \fBtest\fR flag in the manifest settings for the target. .RE .sp \fB\-\-bench\fR \fIname\fR\[u2026] .RS 4 Build the specified benchmark. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-benches\fR .RS 4 Build all targets that have the \fBbench = true\fR manifest flag set. By default this includes the library and binaries built as benchmarks, and bench targets. Be aware that this will also build any required dependencies, so the lib target may be built twice (once as a benchmark, and once as a dependency for binaries, benchmarks, etc.). Targets may be enabled or disabled by setting the \fBbench\fR flag in the manifest settings for the target. .RE .sp \fB\-\-all\-targets\fR .RS 4 Build all targets. This is equivalent to specifying \fB\-\-lib \-\-bins \-\-tests \-\-benches \-\-examples\fR\&. .RE .SS "Feature Selection" The feature flags allow you to control which features are enabled. When no feature options are given, the \fBdefault\fR feature is activated for every selected package. .sp See \fIthe features documentation\fR for more details. .sp \fB\-F\fR \fIfeatures\fR, \fB\-\-features\fR \fIfeatures\fR .RS 4 Space or comma separated list of features to activate. Features of workspace members may be enabled with \fBpackage\-name/feature\-name\fR syntax. This flag may be specified multiple times, which enables all specified features. .RE .sp \fB\-\-all\-features\fR .RS 4 Activate all available features of all selected packages. .RE .sp \fB\-\-no\-default\-features\fR .RS 4 Do not activate the \fBdefault\fR feature of the selected packages. .RE .SS "Compilation Options" .sp \fB\-\-target\fR \fItriple\fR .RS 4 Build for the given architecture. The default is the host architecture. The general format of the triple is \fB\-\-\-\fR\&. Run \fBrustc \-\-print target\-list\fR for a list of supported targets. This flag may be specified multiple times. .sp This may also be specified with the \fBbuild.target\fR \fIconfig value\fR \&. .sp Note that specifying this flag makes Cargo run in a different mode where the target artifacts are placed in a separate directory. See the \fIbuild cache\fR documentation for more details. .RE .sp \fB\-r\fR, \fB\-\-release\fR .RS 4 Build optimized artifacts with the \fBrelease\fR profile. See also the \fB\-\-profile\fR option for choosing a specific profile by name. .RE .sp \fB\-\-profile\fR \fIname\fR .RS 4 Build with the given profile. See \fIthe reference\fR for more details on profiles. .RE .sp \fB\-\-timings=\fR\fIfmts\fR .RS 4 Output information how long each compilation takes, and track concurrency information over time. Accepts an optional comma\-separated list of output formats; \fB\-\-timings\fR without an argument will default to \fB\-\-timings=html\fR\&. Specifying an output format (rather than the default) is unstable and requires \fB\-Zunstable\-options\fR\&. Valid output formats: .sp .RS 4 \h'-04'\(bu\h'+03'\fBhtml\fR (unstable, requires \fB\-Zunstable\-options\fR): Write a human\-readable file \fBcargo\-timing.html\fR to the \fBtarget/cargo\-timings\fR directory with a report of the compilation. Also write a report to the same directory with a timestamp in the filename if you want to look at older runs. HTML output is suitable for human consumption only, and does not provide machine\-readable timing data. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\fR (unstable, requires \fB\-Zunstable\-options\fR): Emit machine\-readable JSON information about timing information. .RE .RE .SS "Output Options" .sp \fB\-\-target\-dir\fR \fIdirectory\fR .RS 4 Directory for all generated artifacts and intermediate files. May also be specified with the \fBCARGO_TARGET_DIR\fR environment variable, or the \fBbuild.target\-dir\fR \fIconfig value\fR \&. Defaults to \fBtarget\fR in the root of the workspace. .RE .sp \fB\-\-artifact\-dir\fR \fIdirectory\fR .RS 4 Copy final artifacts to this directory. .sp This option is unstable and available only on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable. See for more information. .RE .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .sp \fB\-\-message\-format\fR \fIfmt\fR .RS 4 The output format for diagnostic messages. Can be specified multiple times and consists of comma\-separated values. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBhuman\fR (default): Display in a human\-readable text format. Conflicts with \fBshort\fR and \fBjson\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBshort\fR: Emit shorter, human\-readable text messages. Conflicts with \fBhuman\fR and \fBjson\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\fR: Emit JSON messages to stdout. See \fIthe reference\fR for more details. Conflicts with \fBhuman\fR and \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-diagnostic\-short\fR: Ensure the \fBrendered\fR field of JSON messages contains the \[lq]short\[rq] rendering from rustc. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-diagnostic\-rendered\-ansi\fR: Ensure the \fBrendered\fR field of JSON messages contains embedded ANSI color codes for respecting rustc\[cq]s default color scheme. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-render\-diagnostics\fR: Instruct Cargo to not include rustc diagnostics in JSON messages printed, but instead Cargo itself should render the JSON diagnostics coming from rustc. Cargo\[cq]s own JSON diagnostics and others coming from rustc are still emitted. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .RE .sp \fB\-\-build\-plan\fR .RS 4 Outputs a series of JSON messages to stdout that indicate the commands to run the build. .sp This option is unstable and available only on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable. See for more information. .RE .SS "Manifest Options" .sp \fB\-\-manifest\-path\fR \fIpath\fR .RS 4 Path to the \fBCargo.toml\fR file. By default, Cargo searches for the \fBCargo.toml\fR file in the current directory or any parent directory. .RE .sp \fB\-\-ignore\-rust\-version\fR .RS 4 Ignore \fBrust\-version\fR specification in packages. .RE .sp \fB\-\-locked\fR .RS 4 Asserts that the exact same dependencies and versions are used as when the existing \fBCargo.lock\fR file was originally generated. Cargo will exit with an error when either of the following scenarios arises: .sp .RS 4 \h'-04'\(bu\h'+03'The lock file is missing. .RE .sp .RS 4 \h'-04'\(bu\h'+03'Cargo attempted to change the lock file due to a different dependency resolution. .RE .sp It may be used in environments where deterministic builds are desired, such as in CI pipelines. .RE .sp \fB\-\-offline\fR .RS 4 Prevents Cargo from accessing the network for any reason. Without this flag, Cargo will stop with an error if it needs to access the network and the network is not available. With this flag, Cargo will attempt to proceed without the network if possible. .sp Beware that this may result in different dependency resolution than online mode. Cargo will restrict itself to crates that are downloaded locally, even if there might be a newer version as indicated in the local copy of the index. See the \fBcargo\-fetch\fR(1) command to download dependencies before going offline. .sp May also be specified with the \fBnet.offline\fR \fIconfig value\fR \&. .RE .sp \fB\-\-frozen\fR .RS 4 Equivalent to specifying both \fB\-\-locked\fR and \fB\-\-offline\fR\&. .RE .sp \fB\-\-lockfile\-path\fR \fIPATH\fR .RS 4 Changes the path of the lockfile from the default (\fB/Cargo.lock\fR) to \fIPATH\fR\&. \fIPATH\fR must end with \fBCargo.lock\fR (e.g. \fB\-\-lockfile\-path /tmp/temporary\-lockfile/Cargo.lock\fR). Note that providing \fB\-\-lockfile\-path\fR will ignore existing lockfile at the default path, and instead will either use the lockfile from \fIPATH\fR, or write a new lockfile into the provided \fIPATH\fR if it doesn\[cq]t exist. This flag can be used to run most commands in read\-only directories, writing lockfile into the provided \fIPATH\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#14421\fR ). .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SS "Miscellaneous Options" .sp \fB\-j\fR \fIN\fR, \fB\-\-jobs\fR \fIN\fR .RS 4 Number of parallel jobs to run. May also be specified with the \fBbuild.jobs\fR \fIconfig value\fR \&. Defaults to the number of logical CPUs. If negative, it sets the maximum number of parallel jobs to the number of logical CPUs plus provided value. If a string \fBdefault\fR is provided, it sets the value back to defaults. Should not be 0. .RE .sp \fB\-\-keep\-going\fR .RS 4 Build as many crates in the dependency graph as possible, rather than aborting the build on the first one that fails to build. .sp For example if the current package depends on dependencies \fBfails\fR and \fBworks\fR, one of which fails to build, \fBcargo build \-j1\fR may or may not build the one that succeeds (depending on which one of the two builds Cargo picked to run first), whereas \fBcargo build \-j1 \-\-keep\-going\fR would definitely run both builds, even if the one run first fails. .RE .sp \fB\-\-future\-incompat\-report\fR .RS 4 Displays a future\-incompat report for any future\-incompatible warnings produced during execution of this command .sp See \fBcargo\-report\fR(1) .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Build the local package and all of its dependencies: .sp .RS 4 .nf cargo build .fi .RE .RE .sp .RS 4 \h'-04' 2.\h'+01'Build with optimizations: .sp .RS 4 .nf cargo build \-\-release .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-rustc\fR(1) cargo-0.91.0/src/etc/man/cargo-check.1000064400000000000000000000413461046102023000153630ustar 00000000000000'\" t .TH "CARGO\-CHECK" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-check \[em] Check the current package .SH "SYNOPSIS" \fBcargo check\fR [\fIoptions\fR] .SH "DESCRIPTION" Check a local package and all of its dependencies for errors. This will essentially compile the packages without performing the final step of code generation, which is faster than running \fBcargo build\fR\&. The compiler will save metadata files to disk so that future runs will reuse them if the source has not been modified. Some diagnostics and errors are only emitted during code generation, so they inherently won\[cq]t be reported with \fBcargo check\fR\&. .SH "OPTIONS" .SS "Package Selection" By default, when no package selection options are given, the packages selected depend on the selected manifest file (based on the current working directory if \fB\-\-manifest\-path\fR is not given). If the manifest is the root of a workspace then the workspaces default members are selected, otherwise only the package defined by the manifest will be selected. .sp The default members of a workspace can be set explicitly with the \fBworkspace.default\-members\fR key in the root manifest. If this is not set, a virtual workspace will include all workspace members (equivalent to passing \fB\-\-workspace\fR), and a non\-virtual workspace will include only the root crate itself. .sp \fB\-p\fR \fIspec\fR\[u2026], \fB\-\-package\fR \fIspec\fR\[u2026] .RS 4 Check only the specified packages. See \fBcargo\-pkgid\fR(1) for the SPEC format. This flag may be specified multiple times and supports common Unix glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell accidentally expanding glob patterns before Cargo handles them, you must use single quotes or double quotes around each pattern. .RE .sp \fB\-\-workspace\fR .RS 4 Check all members in the workspace. .RE .sp \fB\-\-all\fR .RS 4 Deprecated alias for \fB\-\-workspace\fR\&. .RE .sp \fB\-\-exclude\fR \fISPEC\fR\[u2026] .RS 4 Exclude the specified packages. Must be used in conjunction with the \fB\-\-workspace\fR flag. This flag may be specified multiple times and supports common Unix glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell accidentally expanding glob patterns before Cargo handles them, you must use single quotes or double quotes around each pattern. .RE .SS "Target Selection" When no target selection options are given, \fBcargo check\fR will check all binary and library targets of the selected packages. Binaries are skipped if they have \fBrequired\-features\fR that are missing. .sp Passing target selection flags will check only the specified targets. .sp Note that \fB\-\-bin\fR, \fB\-\-example\fR, \fB\-\-test\fR and \fB\-\-bench\fR flags also support common Unix glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell accidentally expanding glob patterns before Cargo handles them, you must use single quotes or double quotes around each glob pattern. .sp \fB\-\-lib\fR .RS 4 Check the package\[cq]s library. .RE .sp \fB\-\-bin\fR \fIname\fR\[u2026] .RS 4 Check the specified binary. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-bins\fR .RS 4 Check all binary targets. .RE .sp \fB\-\-example\fR \fIname\fR\[u2026] .RS 4 Check the specified example. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-examples\fR .RS 4 Check all example targets. .RE .sp \fB\-\-test\fR \fIname\fR\[u2026] .RS 4 Check the specified integration test. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-tests\fR .RS 4 Check all targets that have the \fBtest = true\fR manifest flag set. By default this includes the library and binaries built as unittests, and integration tests. Be aware that this will also build any required dependencies, so the lib target may be built twice (once as a unittest, and once as a dependency for binaries, integration tests, etc.). Targets may be enabled or disabled by setting the \fBtest\fR flag in the manifest settings for the target. .RE .sp \fB\-\-bench\fR \fIname\fR\[u2026] .RS 4 Check the specified benchmark. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-benches\fR .RS 4 Check all targets that have the \fBbench = true\fR manifest flag set. By default this includes the library and binaries built as benchmarks, and bench targets. Be aware that this will also build any required dependencies, so the lib target may be built twice (once as a benchmark, and once as a dependency for binaries, benchmarks, etc.). Targets may be enabled or disabled by setting the \fBbench\fR flag in the manifest settings for the target. .RE .sp \fB\-\-all\-targets\fR .RS 4 Check all targets. This is equivalent to specifying \fB\-\-lib \-\-bins \-\-tests \-\-benches \-\-examples\fR\&. .RE .SS "Feature Selection" The feature flags allow you to control which features are enabled. When no feature options are given, the \fBdefault\fR feature is activated for every selected package. .sp See \fIthe features documentation\fR for more details. .sp \fB\-F\fR \fIfeatures\fR, \fB\-\-features\fR \fIfeatures\fR .RS 4 Space or comma separated list of features to activate. Features of workspace members may be enabled with \fBpackage\-name/feature\-name\fR syntax. This flag may be specified multiple times, which enables all specified features. .RE .sp \fB\-\-all\-features\fR .RS 4 Activate all available features of all selected packages. .RE .sp \fB\-\-no\-default\-features\fR .RS 4 Do not activate the \fBdefault\fR feature of the selected packages. .RE .SS "Compilation Options" .sp \fB\-\-target\fR \fItriple\fR .RS 4 Check for the given architecture. The default is the host architecture. The general format of the triple is \fB\-\-\-\fR\&. Run \fBrustc \-\-print target\-list\fR for a list of supported targets. This flag may be specified multiple times. .sp This may also be specified with the \fBbuild.target\fR \fIconfig value\fR \&. .sp Note that specifying this flag makes Cargo run in a different mode where the target artifacts are placed in a separate directory. See the \fIbuild cache\fR documentation for more details. .RE .sp \fB\-r\fR, \fB\-\-release\fR .RS 4 Check optimized artifacts with the \fBrelease\fR profile. See also the \fB\-\-profile\fR option for choosing a specific profile by name. .RE .sp \fB\-\-profile\fR \fIname\fR .RS 4 Check with the given profile. .sp As a special case, specifying the \fBtest\fR profile will also enable checking in test mode which will enable checking tests and enable the \fBtest\fR cfg option. See \fIrustc tests\fR for more detail. .sp See \fIthe reference\fR for more details on profiles. .RE .sp \fB\-\-timings=\fR\fIfmts\fR .RS 4 Output information how long each compilation takes, and track concurrency information over time. Accepts an optional comma\-separated list of output formats; \fB\-\-timings\fR without an argument will default to \fB\-\-timings=html\fR\&. Specifying an output format (rather than the default) is unstable and requires \fB\-Zunstable\-options\fR\&. Valid output formats: .sp .RS 4 \h'-04'\(bu\h'+03'\fBhtml\fR (unstable, requires \fB\-Zunstable\-options\fR): Write a human\-readable file \fBcargo\-timing.html\fR to the \fBtarget/cargo\-timings\fR directory with a report of the compilation. Also write a report to the same directory with a timestamp in the filename if you want to look at older runs. HTML output is suitable for human consumption only, and does not provide machine\-readable timing data. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\fR (unstable, requires \fB\-Zunstable\-options\fR): Emit machine\-readable JSON information about timing information. .RE .RE .SS "Output Options" .sp \fB\-\-target\-dir\fR \fIdirectory\fR .RS 4 Directory for all generated artifacts and intermediate files. May also be specified with the \fBCARGO_TARGET_DIR\fR environment variable, or the \fBbuild.target\-dir\fR \fIconfig value\fR \&. Defaults to \fBtarget\fR in the root of the workspace. .RE .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .sp \fB\-\-message\-format\fR \fIfmt\fR .RS 4 The output format for diagnostic messages. Can be specified multiple times and consists of comma\-separated values. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBhuman\fR (default): Display in a human\-readable text format. Conflicts with \fBshort\fR and \fBjson\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBshort\fR: Emit shorter, human\-readable text messages. Conflicts with \fBhuman\fR and \fBjson\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\fR: Emit JSON messages to stdout. See \fIthe reference\fR for more details. Conflicts with \fBhuman\fR and \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-diagnostic\-short\fR: Ensure the \fBrendered\fR field of JSON messages contains the \[lq]short\[rq] rendering from rustc. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-diagnostic\-rendered\-ansi\fR: Ensure the \fBrendered\fR field of JSON messages contains embedded ANSI color codes for respecting rustc\[cq]s default color scheme. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-render\-diagnostics\fR: Instruct Cargo to not include rustc diagnostics in JSON messages printed, but instead Cargo itself should render the JSON diagnostics coming from rustc. Cargo\[cq]s own JSON diagnostics and others coming from rustc are still emitted. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .RE .SS "Manifest Options" .sp \fB\-\-manifest\-path\fR \fIpath\fR .RS 4 Path to the \fBCargo.toml\fR file. By default, Cargo searches for the \fBCargo.toml\fR file in the current directory or any parent directory. .RE .sp \fB\-\-ignore\-rust\-version\fR .RS 4 Ignore \fBrust\-version\fR specification in packages. .RE .sp \fB\-\-locked\fR .RS 4 Asserts that the exact same dependencies and versions are used as when the existing \fBCargo.lock\fR file was originally generated. Cargo will exit with an error when either of the following scenarios arises: .sp .RS 4 \h'-04'\(bu\h'+03'The lock file is missing. .RE .sp .RS 4 \h'-04'\(bu\h'+03'Cargo attempted to change the lock file due to a different dependency resolution. .RE .sp It may be used in environments where deterministic builds are desired, such as in CI pipelines. .RE .sp \fB\-\-offline\fR .RS 4 Prevents Cargo from accessing the network for any reason. Without this flag, Cargo will stop with an error if it needs to access the network and the network is not available. With this flag, Cargo will attempt to proceed without the network if possible. .sp Beware that this may result in different dependency resolution than online mode. Cargo will restrict itself to crates that are downloaded locally, even if there might be a newer version as indicated in the local copy of the index. See the \fBcargo\-fetch\fR(1) command to download dependencies before going offline. .sp May also be specified with the \fBnet.offline\fR \fIconfig value\fR \&. .RE .sp \fB\-\-frozen\fR .RS 4 Equivalent to specifying both \fB\-\-locked\fR and \fB\-\-offline\fR\&. .RE .sp \fB\-\-lockfile\-path\fR \fIPATH\fR .RS 4 Changes the path of the lockfile from the default (\fB/Cargo.lock\fR) to \fIPATH\fR\&. \fIPATH\fR must end with \fBCargo.lock\fR (e.g. \fB\-\-lockfile\-path /tmp/temporary\-lockfile/Cargo.lock\fR). Note that providing \fB\-\-lockfile\-path\fR will ignore existing lockfile at the default path, and instead will either use the lockfile from \fIPATH\fR, or write a new lockfile into the provided \fIPATH\fR if it doesn\[cq]t exist. This flag can be used to run most commands in read\-only directories, writing lockfile into the provided \fIPATH\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#14421\fR ). .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SS "Miscellaneous Options" .sp \fB\-j\fR \fIN\fR, \fB\-\-jobs\fR \fIN\fR .RS 4 Number of parallel jobs to run. May also be specified with the \fBbuild.jobs\fR \fIconfig value\fR \&. Defaults to the number of logical CPUs. If negative, it sets the maximum number of parallel jobs to the number of logical CPUs plus provided value. If a string \fBdefault\fR is provided, it sets the value back to defaults. Should not be 0. .RE .sp \fB\-\-keep\-going\fR .RS 4 Build as many crates in the dependency graph as possible, rather than aborting the build on the first one that fails to build. .sp For example if the current package depends on dependencies \fBfails\fR and \fBworks\fR, one of which fails to build, \fBcargo check \-j1\fR may or may not build the one that succeeds (depending on which one of the two builds Cargo picked to run first), whereas \fBcargo check \-j1 \-\-keep\-going\fR would definitely run both builds, even if the one run first fails. .RE .sp \fB\-\-future\-incompat\-report\fR .RS 4 Displays a future\-incompat report for any future\-incompatible warnings produced during execution of this command .sp See \fBcargo\-report\fR(1) .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Check the local package for errors: .sp .RS 4 .nf cargo check .fi .RE .RE .sp .RS 4 \h'-04' 2.\h'+01'Check all targets, including unit tests: .sp .RS 4 .nf cargo check \-\-all\-targets \-\-profile=test .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-build\fR(1) cargo-0.91.0/src/etc/man/cargo-clean.1000064400000000000000000000175001046102023000153630ustar 00000000000000'\" t .TH "CARGO\-CLEAN" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-clean \[em] Remove generated artifacts .SH "SYNOPSIS" \fBcargo clean\fR [\fIoptions\fR] .SH "DESCRIPTION" Remove artifacts from the target directory that Cargo has generated in the past. .sp With no options, \fBcargo clean\fR will delete the entire target directory. .SH "OPTIONS" .SS "Package Selection" When no packages are selected, all packages and all dependencies in the workspace are cleaned. .sp \fB\-p\fR \fIspec\fR\[u2026], \fB\-\-package\fR \fIspec\fR\[u2026] .RS 4 Clean only the specified packages. This flag may be specified multiple times. See \fBcargo\-pkgid\fR(1) for the SPEC format. .RE .SS "Clean Options" .sp \fB\-\-dry\-run\fR .RS 4 Displays a summary of what would be deleted without deleting anything. Use with \fB\-\-verbose\fR to display the actual files that would be deleted. .RE .sp \fB\-\-doc\fR .RS 4 This option will cause \fBcargo clean\fR to remove only the \fBdoc\fR directory in the target directory. .RE .sp \fB\-\-release\fR .RS 4 Remove all artifacts in the \fBrelease\fR directory. .RE .sp \fB\-\-profile\fR \fIname\fR .RS 4 Remove all artifacts in the directory with the given profile name. .RE .sp \fB\-\-target\-dir\fR \fIdirectory\fR .RS 4 Directory for all generated artifacts and intermediate files. May also be specified with the \fBCARGO_TARGET_DIR\fR environment variable, or the \fBbuild.target\-dir\fR \fIconfig value\fR \&. Defaults to \fBtarget\fR in the root of the workspace. .RE .sp \fB\-\-target\fR \fItriple\fR .RS 4 Clean for the given architecture. The default is the host architecture. The general format of the triple is \fB\-\-\-\fR\&. Run \fBrustc \-\-print target\-list\fR for a list of supported targets. This flag may be specified multiple times. .sp This may also be specified with the \fBbuild.target\fR \fIconfig value\fR \&. .sp Note that specifying this flag makes Cargo run in a different mode where the target artifacts are placed in a separate directory. See the \fIbuild cache\fR documentation for more details. .RE .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .SS "Manifest Options" .sp \fB\-\-manifest\-path\fR \fIpath\fR .RS 4 Path to the \fBCargo.toml\fR file. By default, Cargo searches for the \fBCargo.toml\fR file in the current directory or any parent directory. .RE .sp \fB\-\-locked\fR .RS 4 Asserts that the exact same dependencies and versions are used as when the existing \fBCargo.lock\fR file was originally generated. Cargo will exit with an error when either of the following scenarios arises: .sp .RS 4 \h'-04'\(bu\h'+03'The lock file is missing. .RE .sp .RS 4 \h'-04'\(bu\h'+03'Cargo attempted to change the lock file due to a different dependency resolution. .RE .sp It may be used in environments where deterministic builds are desired, such as in CI pipelines. .RE .sp \fB\-\-offline\fR .RS 4 Prevents Cargo from accessing the network for any reason. Without this flag, Cargo will stop with an error if it needs to access the network and the network is not available. With this flag, Cargo will attempt to proceed without the network if possible. .sp Beware that this may result in different dependency resolution than online mode. Cargo will restrict itself to crates that are downloaded locally, even if there might be a newer version as indicated in the local copy of the index. See the \fBcargo\-fetch\fR(1) command to download dependencies before going offline. .sp May also be specified with the \fBnet.offline\fR \fIconfig value\fR \&. .RE .sp \fB\-\-frozen\fR .RS 4 Equivalent to specifying both \fB\-\-locked\fR and \fB\-\-offline\fR\&. .RE .sp \fB\-\-lockfile\-path\fR \fIPATH\fR .RS 4 Changes the path of the lockfile from the default (\fB/Cargo.lock\fR) to \fIPATH\fR\&. \fIPATH\fR must end with \fBCargo.lock\fR (e.g. \fB\-\-lockfile\-path /tmp/temporary\-lockfile/Cargo.lock\fR). Note that providing \fB\-\-lockfile\-path\fR will ignore existing lockfile at the default path, and instead will either use the lockfile from \fIPATH\fR, or write a new lockfile into the provided \fIPATH\fR if it doesn\[cq]t exist. This flag can be used to run most commands in read\-only directories, writing lockfile into the provided \fIPATH\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#14421\fR ). .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Remove the entire target directory: .sp .RS 4 .nf cargo clean .fi .RE .RE .sp .RS 4 \h'-04' 2.\h'+01'Remove only the release artifacts: .sp .RS 4 .nf cargo clean \-\-release .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-build\fR(1) cargo-0.91.0/src/etc/man/cargo-doc.1000064400000000000000000000357371046102023000150620ustar 00000000000000'\" t .TH "CARGO\-DOC" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-doc \[em] Build a package\[cq]s documentation .SH "SYNOPSIS" \fBcargo doc\fR [\fIoptions\fR] .SH "DESCRIPTION" Build the documentation for the local package and all dependencies. The output is placed in \fBtarget/doc\fR in rustdoc\[cq]s usual format. .SH "OPTIONS" .SS "Documentation Options" .sp \fB\-\-open\fR .RS 4 Open the docs in a browser after building them. This will use your default browser unless you define another one in the \fBBROWSER\fR environment variable or use the \fI\f(BIdoc.browser\fI\fR configuration option. .RE .sp \fB\-\-no\-deps\fR .RS 4 Do not build documentation for dependencies. .RE .sp \fB\-\-document\-private\-items\fR .RS 4 Include non\-public items in the documentation. This will be enabled by default if documenting a binary target. .RE .SS "Package Selection" By default, when no package selection options are given, the packages selected depend on the selected manifest file (based on the current working directory if \fB\-\-manifest\-path\fR is not given). If the manifest is the root of a workspace then the workspaces default members are selected, otherwise only the package defined by the manifest will be selected. .sp The default members of a workspace can be set explicitly with the \fBworkspace.default\-members\fR key in the root manifest. If this is not set, a virtual workspace will include all workspace members (equivalent to passing \fB\-\-workspace\fR), and a non\-virtual workspace will include only the root crate itself. .sp \fB\-p\fR \fIspec\fR\[u2026], \fB\-\-package\fR \fIspec\fR\[u2026] .RS 4 Document only the specified packages. See \fBcargo\-pkgid\fR(1) for the SPEC format. This flag may be specified multiple times and supports common Unix glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell accidentally expanding glob patterns before Cargo handles them, you must use single quotes or double quotes around each pattern. .RE .sp \fB\-\-workspace\fR .RS 4 Document all members in the workspace. .RE .sp \fB\-\-all\fR .RS 4 Deprecated alias for \fB\-\-workspace\fR\&. .RE .sp \fB\-\-exclude\fR \fISPEC\fR\[u2026] .RS 4 Exclude the specified packages. Must be used in conjunction with the \fB\-\-workspace\fR flag. This flag may be specified multiple times and supports common Unix glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell accidentally expanding glob patterns before Cargo handles them, you must use single quotes or double quotes around each pattern. .RE .SS "Target Selection" When no target selection options are given, \fBcargo doc\fR will document all binary and library targets of the selected package. The binary will be skipped if its name is the same as the lib target. Binaries are skipped if they have \fBrequired\-features\fR that are missing. .sp The default behavior can be changed by setting \fBdoc = false\fR for the target in the manifest settings. Using target selection options will ignore the \fBdoc\fR flag and will always document the given target. .sp \fB\-\-lib\fR .RS 4 Document the package\[cq]s library. .RE .sp \fB\-\-bin\fR \fIname\fR\[u2026] .RS 4 Document the specified binary. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-bins\fR .RS 4 Document all binary targets. .RE .sp \fB\-\-example\fR \fIname\fR\[u2026] .RS 4 Document the specified example. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-examples\fR .RS 4 Document all example targets. .RE .SS "Feature Selection" The feature flags allow you to control which features are enabled. When no feature options are given, the \fBdefault\fR feature is activated for every selected package. .sp See \fIthe features documentation\fR for more details. .sp \fB\-F\fR \fIfeatures\fR, \fB\-\-features\fR \fIfeatures\fR .RS 4 Space or comma separated list of features to activate. Features of workspace members may be enabled with \fBpackage\-name/feature\-name\fR syntax. This flag may be specified multiple times, which enables all specified features. .RE .sp \fB\-\-all\-features\fR .RS 4 Activate all available features of all selected packages. .RE .sp \fB\-\-no\-default\-features\fR .RS 4 Do not activate the \fBdefault\fR feature of the selected packages. .RE .SS "Compilation Options" .sp \fB\-\-target\fR \fItriple\fR .RS 4 Document for the given architecture. The default is the host architecture. The general format of the triple is \fB\-\-\-\fR\&. Run \fBrustc \-\-print target\-list\fR for a list of supported targets. This flag may be specified multiple times. .sp This may also be specified with the \fBbuild.target\fR \fIconfig value\fR \&. .sp Note that specifying this flag makes Cargo run in a different mode where the target artifacts are placed in a separate directory. See the \fIbuild cache\fR documentation for more details. .RE .sp \fB\-r\fR, \fB\-\-release\fR .RS 4 Document optimized artifacts with the \fBrelease\fR profile. See also the \fB\-\-profile\fR option for choosing a specific profile by name. .RE .sp \fB\-\-profile\fR \fIname\fR .RS 4 Document with the given profile. See \fIthe reference\fR for more details on profiles. .RE .sp \fB\-\-timings=\fR\fIfmts\fR .RS 4 Output information how long each compilation takes, and track concurrency information over time. Accepts an optional comma\-separated list of output formats; \fB\-\-timings\fR without an argument will default to \fB\-\-timings=html\fR\&. Specifying an output format (rather than the default) is unstable and requires \fB\-Zunstable\-options\fR\&. Valid output formats: .sp .RS 4 \h'-04'\(bu\h'+03'\fBhtml\fR (unstable, requires \fB\-Zunstable\-options\fR): Write a human\-readable file \fBcargo\-timing.html\fR to the \fBtarget/cargo\-timings\fR directory with a report of the compilation. Also write a report to the same directory with a timestamp in the filename if you want to look at older runs. HTML output is suitable for human consumption only, and does not provide machine\-readable timing data. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\fR (unstable, requires \fB\-Zunstable\-options\fR): Emit machine\-readable JSON information about timing information. .RE .RE .SS "Output Options" .sp \fB\-\-target\-dir\fR \fIdirectory\fR .RS 4 Directory for all generated artifacts and intermediate files. May also be specified with the \fBCARGO_TARGET_DIR\fR environment variable, or the \fBbuild.target\-dir\fR \fIconfig value\fR \&. Defaults to \fBtarget\fR in the root of the workspace. .RE .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .sp \fB\-\-message\-format\fR \fIfmt\fR .RS 4 The output format for diagnostic messages. Can be specified multiple times and consists of comma\-separated values. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBhuman\fR (default): Display in a human\-readable text format. Conflicts with \fBshort\fR and \fBjson\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBshort\fR: Emit shorter, human\-readable text messages. Conflicts with \fBhuman\fR and \fBjson\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\fR: Emit JSON messages to stdout. See \fIthe reference\fR for more details. Conflicts with \fBhuman\fR and \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-diagnostic\-short\fR: Ensure the \fBrendered\fR field of JSON messages contains the \[lq]short\[rq] rendering from rustc. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-diagnostic\-rendered\-ansi\fR: Ensure the \fBrendered\fR field of JSON messages contains embedded ANSI color codes for respecting rustc\[cq]s default color scheme. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-render\-diagnostics\fR: Instruct Cargo to not include rustc diagnostics in JSON messages printed, but instead Cargo itself should render the JSON diagnostics coming from rustc. Cargo\[cq]s own JSON diagnostics and others coming from rustc are still emitted. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .RE .SS "Manifest Options" .sp \fB\-\-manifest\-path\fR \fIpath\fR .RS 4 Path to the \fBCargo.toml\fR file. By default, Cargo searches for the \fBCargo.toml\fR file in the current directory or any parent directory. .RE .sp \fB\-\-ignore\-rust\-version\fR .RS 4 Ignore \fBrust\-version\fR specification in packages. .RE .sp \fB\-\-locked\fR .RS 4 Asserts that the exact same dependencies and versions are used as when the existing \fBCargo.lock\fR file was originally generated. Cargo will exit with an error when either of the following scenarios arises: .sp .RS 4 \h'-04'\(bu\h'+03'The lock file is missing. .RE .sp .RS 4 \h'-04'\(bu\h'+03'Cargo attempted to change the lock file due to a different dependency resolution. .RE .sp It may be used in environments where deterministic builds are desired, such as in CI pipelines. .RE .sp \fB\-\-offline\fR .RS 4 Prevents Cargo from accessing the network for any reason. Without this flag, Cargo will stop with an error if it needs to access the network and the network is not available. With this flag, Cargo will attempt to proceed without the network if possible. .sp Beware that this may result in different dependency resolution than online mode. Cargo will restrict itself to crates that are downloaded locally, even if there might be a newer version as indicated in the local copy of the index. See the \fBcargo\-fetch\fR(1) command to download dependencies before going offline. .sp May also be specified with the \fBnet.offline\fR \fIconfig value\fR \&. .RE .sp \fB\-\-frozen\fR .RS 4 Equivalent to specifying both \fB\-\-locked\fR and \fB\-\-offline\fR\&. .RE .sp \fB\-\-lockfile\-path\fR \fIPATH\fR .RS 4 Changes the path of the lockfile from the default (\fB/Cargo.lock\fR) to \fIPATH\fR\&. \fIPATH\fR must end with \fBCargo.lock\fR (e.g. \fB\-\-lockfile\-path /tmp/temporary\-lockfile/Cargo.lock\fR). Note that providing \fB\-\-lockfile\-path\fR will ignore existing lockfile at the default path, and instead will either use the lockfile from \fIPATH\fR, or write a new lockfile into the provided \fIPATH\fR if it doesn\[cq]t exist. This flag can be used to run most commands in read\-only directories, writing lockfile into the provided \fIPATH\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#14421\fR ). .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SS "Miscellaneous Options" .sp \fB\-j\fR \fIN\fR, \fB\-\-jobs\fR \fIN\fR .RS 4 Number of parallel jobs to run. May also be specified with the \fBbuild.jobs\fR \fIconfig value\fR \&. Defaults to the number of logical CPUs. If negative, it sets the maximum number of parallel jobs to the number of logical CPUs plus provided value. If a string \fBdefault\fR is provided, it sets the value back to defaults. Should not be 0. .RE .sp \fB\-\-keep\-going\fR .RS 4 Build as many crates in the dependency graph as possible, rather than aborting the build on the first one that fails to build. .sp For example if the current package depends on dependencies \fBfails\fR and \fBworks\fR, one of which fails to build, \fBcargo doc \-j1\fR may or may not build the one that succeeds (depending on which one of the two builds Cargo picked to run first), whereas \fBcargo doc \-j1 \-\-keep\-going\fR would definitely run both builds, even if the one run first fails. .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Build the local package documentation and its dependencies and output to \fBtarget/doc\fR\&. .sp .RS 4 .nf cargo doc .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-rustdoc\fR(1), \fBrustdoc\fR(1) cargo-0.91.0/src/etc/man/cargo-fetch.1000064400000000000000000000160461046102023000153760ustar 00000000000000'\" t .TH "CARGO\-FETCH" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-fetch \[em] Fetch dependencies of a package from the network .SH "SYNOPSIS" \fBcargo fetch\fR [\fIoptions\fR] .SH "DESCRIPTION" If a \fBCargo.lock\fR file is available, this command will ensure that all of the git dependencies and/or registry dependencies are downloaded and locally available. Subsequent Cargo commands will be able to run offline after a \fBcargo fetch\fR unless the lock file changes. .sp If the lock file is not available, then this command will generate the lock file before fetching the dependencies. .sp If \fB\-\-target\fR is not specified, then all target dependencies are fetched. .sp See also the \fIcargo\-prefetch\fR plugin which adds a command to download popular crates. This may be useful if you plan to use Cargo without a network with the \fB\-\-offline\fR flag. .SH "OPTIONS" .SS "Fetch options" .sp \fB\-\-target\fR \fItriple\fR .RS 4 Fetch for the given architecture. The default is all architectures. The general format of the triple is \fB\-\-\-\fR\&. Run \fBrustc \-\-print target\-list\fR for a list of supported targets. This flag may be specified multiple times. .sp This may also be specified with the \fBbuild.target\fR \fIconfig value\fR \&. .sp Note that specifying this flag makes Cargo run in a different mode where the target artifacts are placed in a separate directory. See the \fIbuild cache\fR documentation for more details. .RE .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .SS "Manifest Options" .sp \fB\-\-manifest\-path\fR \fIpath\fR .RS 4 Path to the \fBCargo.toml\fR file. By default, Cargo searches for the \fBCargo.toml\fR file in the current directory or any parent directory. .RE .sp \fB\-\-locked\fR .RS 4 Asserts that the exact same dependencies and versions are used as when the existing \fBCargo.lock\fR file was originally generated. Cargo will exit with an error when either of the following scenarios arises: .sp .RS 4 \h'-04'\(bu\h'+03'The lock file is missing. .RE .sp .RS 4 \h'-04'\(bu\h'+03'Cargo attempted to change the lock file due to a different dependency resolution. .RE .sp It may be used in environments where deterministic builds are desired, such as in CI pipelines. .RE .sp \fB\-\-offline\fR .RS 4 Prevents Cargo from accessing the network for any reason. Without this flag, Cargo will stop with an error if it needs to access the network and the network is not available. With this flag, Cargo will attempt to proceed without the network if possible. .sp Beware that this may result in different dependency resolution than online mode. Cargo will restrict itself to crates that are downloaded locally, even if there might be a newer version as indicated in the local copy of the index. .sp May also be specified with the \fBnet.offline\fR \fIconfig value\fR \&. .RE .sp \fB\-\-frozen\fR .RS 4 Equivalent to specifying both \fB\-\-locked\fR and \fB\-\-offline\fR\&. .RE .sp \fB\-\-lockfile\-path\fR \fIPATH\fR .RS 4 Changes the path of the lockfile from the default (\fB/Cargo.lock\fR) to \fIPATH\fR\&. \fIPATH\fR must end with \fBCargo.lock\fR (e.g. \fB\-\-lockfile\-path /tmp/temporary\-lockfile/Cargo.lock\fR). Note that providing \fB\-\-lockfile\-path\fR will ignore existing lockfile at the default path, and instead will either use the lockfile from \fIPATH\fR, or write a new lockfile into the provided \fIPATH\fR if it doesn\[cq]t exist. This flag can be used to run most commands in read\-only directories, writing lockfile into the provided \fIPATH\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#14421\fR ). .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Fetch all dependencies: .sp .RS 4 .nf cargo fetch .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-update\fR(1), \fBcargo\-generate\-lockfile\fR(1) cargo-0.91.0/src/etc/man/cargo-fix.1000064400000000000000000000474271046102023000151020ustar 00000000000000'\" t .TH "CARGO\-FIX" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-fix \[em] Automatically fix lint warnings reported by rustc .SH "SYNOPSIS" \fBcargo fix\fR [\fIoptions\fR] .SH "DESCRIPTION" This Cargo subcommand will automatically take rustc\[cq]s suggestions from diagnostics like warnings and apply them to your source code. This is intended to help automate tasks that rustc itself already knows how to tell you to fix! .sp Executing \fBcargo fix\fR will under the hood execute \fBcargo\-check\fR(1). Any warnings applicable to your crate will be automatically fixed (if possible) and all remaining warnings will be displayed when the check process is finished. For example if you\[cq]d like to apply all fixes to the current package, you can run: .sp .RS 4 .nf cargo fix .fi .RE .sp which behaves the same as \fBcargo check \-\-all\-targets\fR\&. .sp \fBcargo fix\fR is only capable of fixing code that is normally compiled with \fBcargo check\fR\&. If code is conditionally enabled with optional features, you will need to enable those features for that code to be analyzed: .sp .RS 4 .nf cargo fix \-\-features foo .fi .RE .sp Similarly, other \fBcfg\fR expressions like platform\-specific code will need to pass \fB\-\-target\fR to fix code for the given target. .sp .RS 4 .nf cargo fix \-\-target x86_64\-pc\-windows\-gnu .fi .RE .sp If you encounter any problems with \fBcargo fix\fR or otherwise have any questions or feature requests please don\[cq]t hesitate to file an issue at \&. .SS "Edition migration" The \fBcargo fix\fR subcommand can also be used to migrate a package from one \fIedition\fR to the next. The general procedure is: .sp .RS 4 \h'-04' 1.\h'+01'Run \fBcargo fix \-\-edition\fR\&. Consider also using the \fB\-\-all\-features\fR flag if your project has multiple features. You may also want to run \fBcargo fix \-\-edition\fR multiple times with different \fB\-\-target\fR flags if your project has platform\-specific code gated by \fBcfg\fR attributes. .RE .sp .RS 4 \h'-04' 2.\h'+01'Modify \fBCargo.toml\fR to set the \fIedition field\fR to the new edition. .RE .sp .RS 4 \h'-04' 3.\h'+01'Run your project tests to verify that everything still works. If new warnings are issued, you may want to consider running \fBcargo fix\fR again (without the \fB\-\-edition\fR flag) to apply any suggestions given by the compiler. .RE .sp And hopefully that\[cq]s it! Just keep in mind of the caveats mentioned above that \fBcargo fix\fR cannot update code for inactive features or \fBcfg\fR expressions. Also, in some rare cases the compiler is unable to automatically migrate all code to the new edition, and this may require manual changes after building with the new edition. .SH "OPTIONS" .SS "Fix options" .sp \fB\-\-broken\-code\fR .RS 4 Fix code even if it already has compiler errors. This is useful if \fBcargo fix\fR fails to apply the changes. It will apply the changes and leave the broken code in the working directory for you to inspect and manually fix. .RE .sp \fB\-\-edition\fR .RS 4 Apply changes that will update the code to the next edition. This will not update the edition in the \fBCargo.toml\fR manifest, which must be updated manually after \fBcargo fix \-\-edition\fR has finished. .RE .sp \fB\-\-edition\-idioms\fR .RS 4 Apply suggestions that will update code to the preferred style for the current edition. .RE .sp \fB\-\-allow\-no\-vcs\fR .RS 4 Fix code even if a VCS was not detected. .RE .sp \fB\-\-allow\-dirty\fR .RS 4 Fix code even if the working directory has changes (including staged changes). .RE .sp \fB\-\-allow\-staged\fR .RS 4 Fix code even if the working directory has staged changes. .RE .SS "Package Selection" By default, when no package selection options are given, the packages selected depend on the selected manifest file (based on the current working directory if \fB\-\-manifest\-path\fR is not given). If the manifest is the root of a workspace then the workspaces default members are selected, otherwise only the package defined by the manifest will be selected. .sp The default members of a workspace can be set explicitly with the \fBworkspace.default\-members\fR key in the root manifest. If this is not set, a virtual workspace will include all workspace members (equivalent to passing \fB\-\-workspace\fR), and a non\-virtual workspace will include only the root crate itself. .sp \fB\-p\fR \fIspec\fR\[u2026], \fB\-\-package\fR \fIspec\fR\[u2026] .RS 4 Fix only the specified packages. See \fBcargo\-pkgid\fR(1) for the SPEC format. This flag may be specified multiple times and supports common Unix glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell accidentally expanding glob patterns before Cargo handles them, you must use single quotes or double quotes around each pattern. .RE .sp \fB\-\-workspace\fR .RS 4 Fix all members in the workspace. .RE .sp \fB\-\-all\fR .RS 4 Deprecated alias for \fB\-\-workspace\fR\&. .RE .sp \fB\-\-exclude\fR \fISPEC\fR\[u2026] .RS 4 Exclude the specified packages. Must be used in conjunction with the \fB\-\-workspace\fR flag. This flag may be specified multiple times and supports common Unix glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell accidentally expanding glob patterns before Cargo handles them, you must use single quotes or double quotes around each pattern. .RE .SS "Target Selection" When no target selection options are given, \fBcargo fix\fR will fix all targets (\fB\-\-all\-targets\fR implied). Binaries are skipped if they have \fBrequired\-features\fR that are missing. .sp Passing target selection flags will fix only the specified targets. .sp Note that \fB\-\-bin\fR, \fB\-\-example\fR, \fB\-\-test\fR and \fB\-\-bench\fR flags also support common Unix glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell accidentally expanding glob patterns before Cargo handles them, you must use single quotes or double quotes around each glob pattern. .sp \fB\-\-lib\fR .RS 4 Fix the package\[cq]s library. .RE .sp \fB\-\-bin\fR \fIname\fR\[u2026] .RS 4 Fix the specified binary. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-bins\fR .RS 4 Fix all binary targets. .RE .sp \fB\-\-example\fR \fIname\fR\[u2026] .RS 4 Fix the specified example. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-examples\fR .RS 4 Fix all example targets. .RE .sp \fB\-\-test\fR \fIname\fR\[u2026] .RS 4 Fix the specified integration test. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-tests\fR .RS 4 Fix all targets that have the \fBtest = true\fR manifest flag set. By default this includes the library and binaries built as unittests, and integration tests. Be aware that this will also build any required dependencies, so the lib target may be built twice (once as a unittest, and once as a dependency for binaries, integration tests, etc.). Targets may be enabled or disabled by setting the \fBtest\fR flag in the manifest settings for the target. .RE .sp \fB\-\-bench\fR \fIname\fR\[u2026] .RS 4 Fix the specified benchmark. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-benches\fR .RS 4 Fix all targets that have the \fBbench = true\fR manifest flag set. By default this includes the library and binaries built as benchmarks, and bench targets. Be aware that this will also build any required dependencies, so the lib target may be built twice (once as a benchmark, and once as a dependency for binaries, benchmarks, etc.). Targets may be enabled or disabled by setting the \fBbench\fR flag in the manifest settings for the target. .RE .sp \fB\-\-all\-targets\fR .RS 4 Fix all targets. This is equivalent to specifying \fB\-\-lib \-\-bins \-\-tests \-\-benches \-\-examples\fR\&. .RE .SS "Feature Selection" The feature flags allow you to control which features are enabled. When no feature options are given, the \fBdefault\fR feature is activated for every selected package. .sp See \fIthe features documentation\fR for more details. .sp \fB\-F\fR \fIfeatures\fR, \fB\-\-features\fR \fIfeatures\fR .RS 4 Space or comma separated list of features to activate. Features of workspace members may be enabled with \fBpackage\-name/feature\-name\fR syntax. This flag may be specified multiple times, which enables all specified features. .RE .sp \fB\-\-all\-features\fR .RS 4 Activate all available features of all selected packages. .RE .sp \fB\-\-no\-default\-features\fR .RS 4 Do not activate the \fBdefault\fR feature of the selected packages. .RE .SS "Compilation Options" .sp \fB\-\-target\fR \fItriple\fR .RS 4 Fix for the given architecture. The default is the host architecture. The general format of the triple is \fB\-\-\-\fR\&. Run \fBrustc \-\-print target\-list\fR for a list of supported targets. This flag may be specified multiple times. .sp This may also be specified with the \fBbuild.target\fR \fIconfig value\fR \&. .sp Note that specifying this flag makes Cargo run in a different mode where the target artifacts are placed in a separate directory. See the \fIbuild cache\fR documentation for more details. .RE .sp \fB\-r\fR, \fB\-\-release\fR .RS 4 Fix optimized artifacts with the \fBrelease\fR profile. See also the \fB\-\-profile\fR option for choosing a specific profile by name. .RE .sp \fB\-\-profile\fR \fIname\fR .RS 4 Fix with the given profile. .sp As a special case, specifying the \fBtest\fR profile will also enable checking in test mode which will enable checking tests and enable the \fBtest\fR cfg option. See \fIrustc tests\fR for more detail. .sp See \fIthe reference\fR for more details on profiles. .RE .sp \fB\-\-timings=\fR\fIfmts\fR .RS 4 Output information how long each compilation takes, and track concurrency information over time. Accepts an optional comma\-separated list of output formats; \fB\-\-timings\fR without an argument will default to \fB\-\-timings=html\fR\&. Specifying an output format (rather than the default) is unstable and requires \fB\-Zunstable\-options\fR\&. Valid output formats: .sp .RS 4 \h'-04'\(bu\h'+03'\fBhtml\fR (unstable, requires \fB\-Zunstable\-options\fR): Write a human\-readable file \fBcargo\-timing.html\fR to the \fBtarget/cargo\-timings\fR directory with a report of the compilation. Also write a report to the same directory with a timestamp in the filename if you want to look at older runs. HTML output is suitable for human consumption only, and does not provide machine\-readable timing data. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\fR (unstable, requires \fB\-Zunstable\-options\fR): Emit machine\-readable JSON information about timing information. .RE .RE .SS "Output Options" .sp \fB\-\-target\-dir\fR \fIdirectory\fR .RS 4 Directory for all generated artifacts and intermediate files. May also be specified with the \fBCARGO_TARGET_DIR\fR environment variable, or the \fBbuild.target\-dir\fR \fIconfig value\fR \&. Defaults to \fBtarget\fR in the root of the workspace. .RE .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .sp \fB\-\-message\-format\fR \fIfmt\fR .RS 4 The output format for diagnostic messages. Can be specified multiple times and consists of comma\-separated values. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBhuman\fR (default): Display in a human\-readable text format. Conflicts with \fBshort\fR and \fBjson\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBshort\fR: Emit shorter, human\-readable text messages. Conflicts with \fBhuman\fR and \fBjson\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\fR: Emit JSON messages to stdout. See \fIthe reference\fR for more details. Conflicts with \fBhuman\fR and \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-diagnostic\-short\fR: Ensure the \fBrendered\fR field of JSON messages contains the \[lq]short\[rq] rendering from rustc. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-diagnostic\-rendered\-ansi\fR: Ensure the \fBrendered\fR field of JSON messages contains embedded ANSI color codes for respecting rustc\[cq]s default color scheme. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-render\-diagnostics\fR: Instruct Cargo to not include rustc diagnostics in JSON messages printed, but instead Cargo itself should render the JSON diagnostics coming from rustc. Cargo\[cq]s own JSON diagnostics and others coming from rustc are still emitted. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .RE .SS "Manifest Options" .sp \fB\-\-manifest\-path\fR \fIpath\fR .RS 4 Path to the \fBCargo.toml\fR file. By default, Cargo searches for the \fBCargo.toml\fR file in the current directory or any parent directory. .RE .sp \fB\-\-ignore\-rust\-version\fR .RS 4 Ignore \fBrust\-version\fR specification in packages. .RE .sp \fB\-\-locked\fR .RS 4 Asserts that the exact same dependencies and versions are used as when the existing \fBCargo.lock\fR file was originally generated. Cargo will exit with an error when either of the following scenarios arises: .sp .RS 4 \h'-04'\(bu\h'+03'The lock file is missing. .RE .sp .RS 4 \h'-04'\(bu\h'+03'Cargo attempted to change the lock file due to a different dependency resolution. .RE .sp It may be used in environments where deterministic builds are desired, such as in CI pipelines. .RE .sp \fB\-\-offline\fR .RS 4 Prevents Cargo from accessing the network for any reason. Without this flag, Cargo will stop with an error if it needs to access the network and the network is not available. With this flag, Cargo will attempt to proceed without the network if possible. .sp Beware that this may result in different dependency resolution than online mode. Cargo will restrict itself to crates that are downloaded locally, even if there might be a newer version as indicated in the local copy of the index. See the \fBcargo\-fetch\fR(1) command to download dependencies before going offline. .sp May also be specified with the \fBnet.offline\fR \fIconfig value\fR \&. .RE .sp \fB\-\-frozen\fR .RS 4 Equivalent to specifying both \fB\-\-locked\fR and \fB\-\-offline\fR\&. .RE .sp \fB\-\-lockfile\-path\fR \fIPATH\fR .RS 4 Changes the path of the lockfile from the default (\fB/Cargo.lock\fR) to \fIPATH\fR\&. \fIPATH\fR must end with \fBCargo.lock\fR (e.g. \fB\-\-lockfile\-path /tmp/temporary\-lockfile/Cargo.lock\fR). Note that providing \fB\-\-lockfile\-path\fR will ignore existing lockfile at the default path, and instead will either use the lockfile from \fIPATH\fR, or write a new lockfile into the provided \fIPATH\fR if it doesn\[cq]t exist. This flag can be used to run most commands in read\-only directories, writing lockfile into the provided \fIPATH\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#14421\fR ). .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SS "Miscellaneous Options" .sp \fB\-j\fR \fIN\fR, \fB\-\-jobs\fR \fIN\fR .RS 4 Number of parallel jobs to run. May also be specified with the \fBbuild.jobs\fR \fIconfig value\fR \&. Defaults to the number of logical CPUs. If negative, it sets the maximum number of parallel jobs to the number of logical CPUs plus provided value. If a string \fBdefault\fR is provided, it sets the value back to defaults. Should not be 0. .RE .sp \fB\-\-keep\-going\fR .RS 4 Build as many crates in the dependency graph as possible, rather than aborting the build on the first one that fails to build. .sp For example if the current package depends on dependencies \fBfails\fR and \fBworks\fR, one of which fails to build, \fBcargo fix \-j1\fR may or may not build the one that succeeds (depending on which one of the two builds Cargo picked to run first), whereas \fBcargo fix \-j1 \-\-keep\-going\fR would definitely run both builds, even if the one run first fails. .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Apply compiler suggestions to the local package: .sp .RS 4 .nf cargo fix .fi .RE .RE .sp .RS 4 \h'-04' 2.\h'+01'Update a package to prepare it for the next edition: .sp .RS 4 .nf cargo fix \-\-edition .fi .RE .RE .sp .RS 4 \h'-04' 3.\h'+01'Apply suggested idioms for the current edition: .sp .RS 4 .nf cargo fix \-\-edition\-idioms .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-check\fR(1) cargo-0.91.0/src/etc/man/cargo-generate-lockfile.1000064400000000000000000000143211046102023000176570ustar 00000000000000'\" t .TH "CARGO\-GENERATE\-LOCKFILE" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-generate\-lockfile \[em] Generate the lockfile for a package .SH "SYNOPSIS" \fBcargo generate\-lockfile\fR [\fIoptions\fR] .SH "DESCRIPTION" This command will create the \fBCargo.lock\fR lockfile for the current package or workspace. If the lockfile already exists, it will be rebuilt with the latest available version of every package. .sp See also \fBcargo\-update\fR(1) which is also capable of creating a \fBCargo.lock\fR lockfile and has more options for controlling update behavior. .SH "OPTIONS" .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .SS "Manifest Options" .sp \fB\-\-manifest\-path\fR \fIpath\fR .RS 4 Path to the \fBCargo.toml\fR file. By default, Cargo searches for the \fBCargo.toml\fR file in the current directory or any parent directory. .RE .sp \fB\-\-ignore\-rust\-version\fR .RS 4 Ignore \fBrust\-version\fR specification in packages. .RE .sp \fB\-\-locked\fR .RS 4 Asserts that the exact same dependencies and versions are used as when the existing \fBCargo.lock\fR file was originally generated. Cargo will exit with an error when either of the following scenarios arises: .sp .RS 4 \h'-04'\(bu\h'+03'The lock file is missing. .RE .sp .RS 4 \h'-04'\(bu\h'+03'Cargo attempted to change the lock file due to a different dependency resolution. .RE .sp It may be used in environments where deterministic builds are desired, such as in CI pipelines. .RE .sp \fB\-\-offline\fR .RS 4 Prevents Cargo from accessing the network for any reason. Without this flag, Cargo will stop with an error if it needs to access the network and the network is not available. With this flag, Cargo will attempt to proceed without the network if possible. .sp Beware that this may result in different dependency resolution than online mode. Cargo will restrict itself to crates that are downloaded locally, even if there might be a newer version as indicated in the local copy of the index. See the \fBcargo\-fetch\fR(1) command to download dependencies before going offline. .sp May also be specified with the \fBnet.offline\fR \fIconfig value\fR \&. .RE .sp \fB\-\-frozen\fR .RS 4 Equivalent to specifying both \fB\-\-locked\fR and \fB\-\-offline\fR\&. .RE .sp \fB\-\-lockfile\-path\fR \fIPATH\fR .RS 4 Changes the path of the lockfile from the default (\fB/Cargo.lock\fR) to \fIPATH\fR\&. \fIPATH\fR must end with \fBCargo.lock\fR (e.g. \fB\-\-lockfile\-path /tmp/temporary\-lockfile/Cargo.lock\fR). Note that providing \fB\-\-lockfile\-path\fR will ignore existing lockfile at the default path, and instead will either use the lockfile from \fIPATH\fR, or write a new lockfile into the provided \fIPATH\fR if it doesn\[cq]t exist. This flag can be used to run most commands in read\-only directories, writing lockfile into the provided \fIPATH\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#14421\fR ). .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Create or update the lockfile for the current package or workspace: .sp .RS 4 .nf cargo generate\-lockfile .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-update\fR(1) cargo-0.91.0/src/etc/man/cargo-help.1000064400000000000000000000007521046102023000152320ustar 00000000000000'\" t .TH "CARGO\-HELP" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-help \[em] Get help for a Cargo command .SH "SYNOPSIS" \fBcargo help\fR [\fIsubcommand\fR] .SH "DESCRIPTION" Prints a help message for the given command. .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Get help for a command: .sp .RS 4 .nf cargo help build .fi .RE .RE .sp .RS 4 \h'-04' 2.\h'+01'Help is also available with the \fB\-\-help\fR flag: .sp .RS 4 .nf cargo build \-\-help .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1) cargo-0.91.0/src/etc/man/cargo-info.1000064400000000000000000000137451046102023000152430ustar 00000000000000'\" t .TH "CARGO\-INFO" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-info \[em] Display information about a package. .SH "SYNOPSIS" \fBcargo info\fR [\fIoptions\fR] \fIspec\fR .SH "DESCRIPTION" This command displays information about a package. It fetches data from the package\[cq]s Cargo.toml file and presents it in a human\-readable format. .SH "OPTIONS" .SS "Info Options" .sp \fIspec\fR .RS 4 Fetch information about the specified package. The \fIspec\fR can be a package ID, see \fBcargo\-pkgid\fR(1) for the SPEC format. If the specified package is part of the current workspace, information from the local Cargo.toml file will be displayed. If the \fBCargo.lock\fR file does not exist, it will be created. If no version is specified, the appropriate version will be selected based on the Minimum Supported Rust Version (MSRV). .RE .sp \fB\-\-index\fR \fIindex\fR .RS 4 The URL of the registry index to use. .RE .sp \fB\-\-registry\fR \fIregistry\fR .RS 4 Name of the registry to use. Registry names are defined in \fICargo config files\fR \&. If not specified, the default registry is used, which is defined by the \fBregistry.default\fR config key which defaults to \fBcrates\-io\fR\&. .RE .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .SS "Manifest Options" .sp \fB\-\-locked\fR .RS 4 Asserts that the exact same dependencies and versions are used as when the existing \fBCargo.lock\fR file was originally generated. Cargo will exit with an error when either of the following scenarios arises: .sp .RS 4 \h'-04'\(bu\h'+03'The lock file is missing. .RE .sp .RS 4 \h'-04'\(bu\h'+03'Cargo attempted to change the lock file due to a different dependency resolution. .RE .sp It may be used in environments where deterministic builds are desired, such as in CI pipelines. .RE .sp \fB\-\-offline\fR .RS 4 Prevents Cargo from accessing the network for any reason. Without this flag, Cargo will stop with an error if it needs to access the network and the network is not available. With this flag, Cargo will attempt to proceed without the network if possible. .sp Beware that this may result in different dependency resolution than online mode. Cargo will restrict itself to crates that are downloaded locally, even if there might be a newer version as indicated in the local copy of the index. See the \fBcargo\-fetch\fR(1) command to download dependencies before going offline. .sp May also be specified with the \fBnet.offline\fR \fIconfig value\fR \&. .RE .sp \fB\-\-frozen\fR .RS 4 Equivalent to specifying both \fB\-\-locked\fR and \fB\-\-offline\fR\&. .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Inspect the \fBserde\fR package from crates.io: .sp .RS 4 .nf cargo info serde .fi .RE .RE .sp .RS 4 \h'-04' 2.\h'+01'Inspect the \fBserde\fR package with version \fB1.0.0\fR: .sp .RS 4 .nf cargo info serde@1.0.0 .fi .RE .RE .sp .RS 4 \h'-04' 3.\h'+01'Inspect the \fBserde\fR package form the local registry: .sp .RS 4 .nf cargo info serde \-\-registry my\-registry .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-search\fR(1) cargo-0.91.0/src/etc/man/cargo-init.1000064400000000000000000000123141046102023000152420ustar 00000000000000'\" t .TH "CARGO\-INIT" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-init \[em] Create a new Cargo package in an existing directory .SH "SYNOPSIS" \fBcargo init\fR [\fIoptions\fR] [\fIpath\fR] .SH "DESCRIPTION" This command will create a new Cargo manifest in the current directory. Give a path as an argument to create in the given directory. .sp If there are typically\-named Rust source files already in the directory, those will be used. If not, then a sample \fBsrc/main.rs\fR file will be created, or \fBsrc/lib.rs\fR if \fB\-\-lib\fR is passed. .sp If the directory is not already in a VCS repository, then a new repository is created (see \fB\-\-vcs\fR below). .sp See \fBcargo\-new\fR(1) for a similar command which will create a new package in a new directory. .SH "OPTIONS" .SS "Init Options" .sp \fB\-\-bin\fR .RS 4 Create a package with a binary target (\fBsrc/main.rs\fR). This is the default behavior. .RE .sp \fB\-\-lib\fR .RS 4 Create a package with a library target (\fBsrc/lib.rs\fR). .RE .sp \fB\-\-edition\fR \fIedition\fR .RS 4 Specify the Rust edition to use. Default is 2024. Possible values: 2015, 2018, 2021, 2024 .RE .sp \fB\-\-name\fR \fIname\fR .RS 4 Set the package name. Defaults to the directory name. .RE .sp \fB\-\-vcs\fR \fIvcs\fR .RS 4 Initialize a new VCS repository for the given version control system (git, hg, pijul, or fossil) or do not initialize any version control at all (none). If not specified, defaults to \fBgit\fR or the configuration value \fBcargo\-new.vcs\fR, or \fBnone\fR if already inside a VCS repository. .RE .sp \fB\-\-registry\fR \fIregistry\fR .RS 4 This sets the \fBpublish\fR field in \fBCargo.toml\fR to the given registry name which will restrict publishing only to that registry. .sp Registry names are defined in \fICargo config files\fR \&. If not specified, the default registry defined by the \fBregistry.default\fR config key is used. If the default registry is not set and \fB\-\-registry\fR is not used, the \fBpublish\fR field will not be set which means that publishing will not be restricted. .RE .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Create a binary Cargo package in the current directory: .sp .RS 4 .nf cargo init .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-new\fR(1) cargo-0.91.0/src/etc/man/cargo-install.1000064400000000000000000000435101046102023000157470ustar 00000000000000'\" t .TH "CARGO\-INSTALL" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-install \[em] Build and install a Rust binary .SH "SYNOPSIS" \fBcargo install\fR [\fIoptions\fR] \fIcrate\fR[@\fIversion\fR]\[u2026] .br \fBcargo install\fR [\fIoptions\fR] \fB\-\-path\fR \fIpath\fR .br \fBcargo install\fR [\fIoptions\fR] \fB\-\-git\fR \fIurl\fR [\fIcrate\fR\[u2026]] .br \fBcargo install\fR [\fIoptions\fR] \fB\-\-list\fR .SH "DESCRIPTION" This command manages Cargo\[cq]s local set of installed binary crates. Only packages which have executable \fB[[bin]]\fR or \fB[[example]]\fR targets can be installed, and all executables are installed into the installation root\[cq]s \fBbin\fR folder. By default only binaries, not examples, are installed. .sp The installation root is determined, in order of precedence: .sp .RS 4 \h'-04'\(bu\h'+03'\fB\-\-root\fR option .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBCARGO_INSTALL_ROOT\fR environment variable .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBinstall.root\fR Cargo \fIconfig value\fR .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBCARGO_HOME\fR environment variable .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB$HOME/.cargo\fR .RE .sp There are multiple sources from which a crate can be installed. The default source location is crates.io but the \fB\-\-git\fR, \fB\-\-path\fR, and \fB\-\-registry\fR flags can change this source. If the source contains more than one package (such as crates.io or a git repository with multiple crates) the \fIcrate\fR argument is required to indicate which crate should be installed. .sp Crates from crates.io can optionally specify the version they wish to install via the \fB\-\-version\fR flags, and similarly packages from git repositories can optionally specify the branch, tag, or revision that should be installed. If a crate has multiple binaries, the \fB\-\-bin\fR argument can selectively install only one of them, and if you\[cq]d rather install examples the \fB\-\-example\fR argument can be used as well. .sp If the package is already installed, Cargo will reinstall it if the installed version does not appear to be up\-to\-date. If any of the following values change, then Cargo will reinstall the package: .sp .RS 4 \h'-04'\(bu\h'+03'The package version and source. .RE .sp .RS 4 \h'-04'\(bu\h'+03'The set of binary names installed. .RE .sp .RS 4 \h'-04'\(bu\h'+03'The chosen features. .RE .sp .RS 4 \h'-04'\(bu\h'+03'The profile (\fB\-\-profile\fR). .RE .sp .RS 4 \h'-04'\(bu\h'+03'The target (\fB\-\-target\fR). .RE .sp Installing with \fB\-\-path\fR will always build and install, unless there are conflicting binaries from another package. The \fB\-\-force\fR flag may be used to force Cargo to always reinstall the package. .sp If the source is crates.io or \fB\-\-git\fR then by default the crate will be built in a temporary target directory. To avoid this, the target directory can be specified by setting the \fBCARGO_TARGET_DIR\fR environment variable to a relative path. In particular, this can be useful for caching build artifacts on continuous integration systems. .SS "Dealing with the Lockfile" By default, the \fBCargo.lock\fR file that is included with the package will be ignored. This means that Cargo will recompute which versions of dependencies to use, possibly using newer versions that have been released since the package was published. The \fB\-\-locked\fR flag can be used to force Cargo to use the packaged \fBCargo.lock\fR file if it is available. This may be useful for ensuring reproducible builds, to use the exact same set of dependencies that were available when the package was published. It may also be useful if a newer version of a dependency is published that no longer builds on your system, or has other problems. The downside to using \fB\-\-locked\fR is that you will not receive any fixes or updates to any dependency. Note that Cargo did not start publishing \fBCargo.lock\fR files until version 1.37, which means packages published with prior versions will not have a \fBCargo.lock\fR file available. .SS "Configuration Discovery" This command operates on system or user level, not project level. This means that the local \fIconfiguration discovery\fR is ignored. Instead, the configuration discovery begins at \fB$CARGO_HOME/config.toml\fR\&. If the package is installed with \fB\-\-path $PATH\fR, the local configuration will be used, beginning discovery at \fB$PATH/.cargo/config.toml\fR\&. .SH "OPTIONS" .SS "Install Options" .sp \fB\-\-vers\fR \fIversion\fR, \fB\-\-version\fR \fIversion\fR .RS 4 Specify a version to install. This may be a \fIversion requirement\fR , like \fB~1.2\fR, to have Cargo select the newest version from the given requirement. If the version does not have a requirement operator (such as \fB^\fR or \fB~\fR), then it must be in the form \fIMAJOR.MINOR.PATCH\fR, and will install exactly that version; it is \fInot\fR treated as a caret requirement like Cargo dependencies are. .RE .sp \fB\-\-git\fR \fIurl\fR .RS 4 Git URL to install the specified crate from. .RE .sp \fB\-\-branch\fR \fIbranch\fR .RS 4 Branch to use when installing from git. .RE .sp \fB\-\-tag\fR \fItag\fR .RS 4 Tag to use when installing from git. .RE .sp \fB\-\-rev\fR \fIsha\fR .RS 4 Specific commit to use when installing from git. .RE .sp \fB\-\-path\fR \fIpath\fR .RS 4 Filesystem path to local crate to install from. .RE .sp \fB\-\-list\fR .RS 4 List all installed packages and their versions. .RE .sp \fB\-n\fR, \fB\-\-dry\-run\fR .RS 4 (unstable) Perform all checks without installing. .RE .sp \fB\-f\fR, \fB\-\-force\fR .RS 4 Force overwriting existing crates or binaries. This can be used if a package has installed a binary with the same name as another package. This is also useful if something has changed on the system that you want to rebuild with, such as a newer version of \fBrustc\fR\&. .RE .sp \fB\-\-no\-track\fR .RS 4 By default, Cargo keeps track of the installed packages with a metadata file stored in the installation root directory. This flag tells Cargo not to use or create that file. With this flag, Cargo will refuse to overwrite any existing files unless the \fB\-\-force\fR flag is used. This also disables Cargo\[cq]s ability to protect against multiple concurrent invocations of Cargo installing at the same time. .RE .sp \fB\-\-bin\fR \fIname\fR\[u2026] .RS 4 Install only the specified binary. .RE .sp \fB\-\-bins\fR .RS 4 Install all binaries. This is the default behavior. .RE .sp \fB\-\-example\fR \fIname\fR\[u2026] .RS 4 Install only the specified example. .RE .sp \fB\-\-examples\fR .RS 4 Install all examples. .RE .sp \fB\-\-root\fR \fIdir\fR .RS 4 Directory to install packages into. .RE .sp \fB\-\-registry\fR \fIregistry\fR .RS 4 Name of the registry to use. Registry names are defined in \fICargo config files\fR \&. If not specified, the default registry is used, which is defined by the \fBregistry.default\fR config key which defaults to \fBcrates\-io\fR\&. .RE .sp \fB\-\-index\fR \fIindex\fR .RS 4 The URL of the registry index to use. .RE .SS "Feature Selection" The feature flags allow you to control which features are enabled. When no feature options are given, the \fBdefault\fR feature is activated for every selected package. .sp See \fIthe features documentation\fR for more details. .sp \fB\-F\fR \fIfeatures\fR, \fB\-\-features\fR \fIfeatures\fR .RS 4 Space or comma separated list of features to activate. Features of workspace members may be enabled with \fBpackage\-name/feature\-name\fR syntax. This flag may be specified multiple times, which enables all specified features. .RE .sp \fB\-\-all\-features\fR .RS 4 Activate all available features of all selected packages. .RE .sp \fB\-\-no\-default\-features\fR .RS 4 Do not activate the \fBdefault\fR feature of the selected packages. .RE .SS "Compilation Options" .sp \fB\-\-target\fR \fItriple\fR .RS 4 Install for the given architecture. The default is the host architecture. The general format of the triple is \fB\-\-\-\fR\&. Run \fBrustc \-\-print target\-list\fR for a list of supported targets. .sp This may also be specified with the \fBbuild.target\fR \fIconfig value\fR \&. .sp Note that specifying this flag makes Cargo run in a different mode where the target artifacts are placed in a separate directory. See the \fIbuild cache\fR documentation for more details. .RE .sp \fB\-\-target\-dir\fR \fIdirectory\fR .RS 4 Directory for all generated artifacts and intermediate files. May also be specified with the \fBCARGO_TARGET_DIR\fR environment variable, or the \fBbuild.target\-dir\fR \fIconfig value\fR \&. Defaults to a new temporary folder located in the temporary directory of the platform. .sp When using \fB\-\-path\fR, by default it will use \fBtarget\fR directory in the workspace of the local crate unless \fB\-\-target\-dir\fR is specified. .RE .sp \fB\-\-debug\fR .RS 4 Build with the \fBdev\fR profile instead of the \fBrelease\fR profile. See also the \fB\-\-profile\fR option for choosing a specific profile by name. .RE .sp \fB\-\-profile\fR \fIname\fR .RS 4 Install with the given profile. See \fIthe reference\fR for more details on profiles. .RE .sp \fB\-\-timings=\fR\fIfmts\fR .RS 4 Output information how long each compilation takes, and track concurrency information over time. Accepts an optional comma\-separated list of output formats; \fB\-\-timings\fR without an argument will default to \fB\-\-timings=html\fR\&. Specifying an output format (rather than the default) is unstable and requires \fB\-Zunstable\-options\fR\&. Valid output formats: .sp .RS 4 \h'-04'\(bu\h'+03'\fBhtml\fR (unstable, requires \fB\-Zunstable\-options\fR): Write a human\-readable file \fBcargo\-timing.html\fR to the \fBtarget/cargo\-timings\fR directory with a report of the compilation. Also write a report to the same directory with a timestamp in the filename if you want to look at older runs. HTML output is suitable for human consumption only, and does not provide machine\-readable timing data. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\fR (unstable, requires \fB\-Zunstable\-options\fR): Emit machine\-readable JSON information about timing information. .RE .RE .SS "Manifest Options" .sp \fB\-\-ignore\-rust\-version\fR .RS 4 Ignore \fBrust\-version\fR specification in packages. .RE .sp \fB\-\-locked\fR .RS 4 Asserts that the exact same dependencies and versions are used as when the existing \fBCargo.lock\fR file was originally generated. Cargo will exit with an error when either of the following scenarios arises: .sp .RS 4 \h'-04'\(bu\h'+03'The lock file is missing. .RE .sp .RS 4 \h'-04'\(bu\h'+03'Cargo attempted to change the lock file due to a different dependency resolution. .RE .sp It may be used in environments where deterministic builds are desired, such as in CI pipelines. .RE .sp \fB\-\-offline\fR .RS 4 Prevents Cargo from accessing the network for any reason. Without this flag, Cargo will stop with an error if it needs to access the network and the network is not available. With this flag, Cargo will attempt to proceed without the network if possible. .sp Beware that this may result in different dependency resolution than online mode. Cargo will restrict itself to crates that are downloaded locally, even if there might be a newer version as indicated in the local copy of the index. See the \fBcargo\-fetch\fR(1) command to download dependencies before going offline. .sp May also be specified with the \fBnet.offline\fR \fIconfig value\fR \&. .RE .sp \fB\-\-frozen\fR .RS 4 Equivalent to specifying both \fB\-\-locked\fR and \fB\-\-offline\fR\&. .RE .SS "Miscellaneous Options" .sp \fB\-j\fR \fIN\fR, \fB\-\-jobs\fR \fIN\fR .RS 4 Number of parallel jobs to run. May also be specified with the \fBbuild.jobs\fR \fIconfig value\fR \&. Defaults to the number of logical CPUs. If negative, it sets the maximum number of parallel jobs to the number of logical CPUs plus provided value. If a string \fBdefault\fR is provided, it sets the value back to defaults. Should not be 0. .RE .sp \fB\-\-keep\-going\fR .RS 4 Build as many crates in the dependency graph as possible, rather than aborting the build on the first one that fails to build. .sp For example if the current package depends on dependencies \fBfails\fR and \fBworks\fR, one of which fails to build, \fBcargo install \-j1\fR may or may not build the one that succeeds (depending on which one of the two builds Cargo picked to run first), whereas \fBcargo install \-j1 \-\-keep\-going\fR would definitely run both builds, even if the one run first fails. .RE .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .sp \fB\-\-message\-format\fR \fIfmt\fR .RS 4 The output format for diagnostic messages. Can be specified multiple times and consists of comma\-separated values. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBhuman\fR (default): Display in a human\-readable text format. Conflicts with \fBshort\fR and \fBjson\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBshort\fR: Emit shorter, human\-readable text messages. Conflicts with \fBhuman\fR and \fBjson\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\fR: Emit JSON messages to stdout. See \fIthe reference\fR for more details. Conflicts with \fBhuman\fR and \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-diagnostic\-short\fR: Ensure the \fBrendered\fR field of JSON messages contains the \[lq]short\[rq] rendering from rustc. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-diagnostic\-rendered\-ansi\fR: Ensure the \fBrendered\fR field of JSON messages contains embedded ANSI color codes for respecting rustc\[cq]s default color scheme. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-render\-diagnostics\fR: Instruct Cargo to not include rustc diagnostics in JSON messages printed, but instead Cargo itself should render the JSON diagnostics coming from rustc. Cargo\[cq]s own JSON diagnostics and others coming from rustc are still emitted. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Install or upgrade a package from crates.io: .sp .RS 4 .nf cargo install ripgrep .fi .RE .RE .sp .RS 4 \h'-04' 2.\h'+01'Install or reinstall the package in the current directory: .sp .RS 4 .nf cargo install \-\-path . .fi .RE .RE .sp .RS 4 \h'-04' 3.\h'+01'View the list of installed packages: .sp .RS 4 .nf cargo install \-\-list .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-uninstall\fR(1), \fBcargo\-search\fR(1), \fBcargo\-publish\fR(1) cargo-0.91.0/src/etc/man/cargo-locate-project.1000064400000000000000000000110711046102023000172110ustar 00000000000000'\" t .TH "CARGO\-LOCATE\-PROJECT" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-locate\-project \[em] Print a JSON representation of a Cargo.toml file\[cq]s location .SH "SYNOPSIS" \fBcargo locate\-project\fR [\fIoptions\fR] .SH "DESCRIPTION" This command will print a JSON object to stdout with the full path to the manifest. The manifest is found by searching upward for a file named \fBCargo.toml\fR starting from the current working directory. .sp If the project happens to be a part of a workspace, the manifest of the project, rather than the workspace root, is output. This can be overridden by the \fB\-\-workspace\fR flag. The root workspace is found by traversing further upward or by using the field \fBpackage.workspace\fR after locating the manifest of a workspace member. .SH "OPTIONS" .sp \fB\-\-workspace\fR .RS 4 Locate the \fBCargo.toml\fR at the root of the workspace, as opposed to the current workspace member. .RE .SS "Display Options" .sp \fB\-\-message\-format\fR \fIfmt\fR .RS 4 The representation in which to print the project location. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\fR (default): JSON object with the path under the key \[lq]root\[rq]\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBplain\fR: Just the path. .RE .RE .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .SS "Manifest Options" .sp \fB\-\-manifest\-path\fR \fIpath\fR .RS 4 Path to the \fBCargo.toml\fR file. By default, Cargo searches for the \fBCargo.toml\fR file in the current directory or any parent directory. .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Display the path to the manifest based on the current directory: .sp .RS 4 .nf cargo locate\-project .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-metadata\fR(1) cargo-0.91.0/src/etc/man/cargo-login.1000064400000000000000000000112671046102023000154150ustar 00000000000000'\" t .TH "CARGO\-LOGIN" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-login \[em] Log in to a registry .SH "SYNOPSIS" \fBcargo login\fR [\fIoptions\fR] [\fB\-\-\fR \fIargs\fR] .SH "DESCRIPTION" This command will run a credential provider to save a token so that commands that require authentication, such as \fBcargo\-publish\fR(1), will be automatically authenticated. .sp All the arguments following the two dashes (\fB\-\-\fR) are passed to the credential provider. .sp For the default \fBcargo:token\fR credential provider, the token is saved in \fB$CARGO_HOME/credentials.toml\fR\&. \fBCARGO_HOME\fR defaults to \fB\&.cargo\fR in your home directory. .sp If a registry has a credential\-provider specified, it will be used. Otherwise, the providers from the config value \fBregistry.global\-credential\-providers\fR will be attempted, starting from the end of the list. .sp The \fItoken\fR will be read from stdin. .sp The API token for crates.io may be retrieved from \&. .sp Take care to keep the token secret, it should not be shared with anyone else. .SH "OPTIONS" .SS "Login Options" .sp \fB\-\-registry\fR \fIregistry\fR .RS 4 Name of the registry to use. Registry names are defined in \fICargo config files\fR \&. If not specified, the default registry is used, which is defined by the \fBregistry.default\fR config key which defaults to \fBcrates\-io\fR\&. .RE .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Save the token for the default registry: .sp .RS 4 .nf cargo login .fi .RE .RE .sp .RS 4 \h'-04' 2.\h'+01'Save the token for a specific registry: .sp .RS 4 .nf cargo login \-\-registry my\-registry .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-logout\fR(1), \fBcargo\-publish\fR(1) cargo-0.91.0/src/etc/man/cargo-logout.1000064400000000000000000000113531046102023000156120ustar 00000000000000'\" t .TH "CARGO\-LOGOUT" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-logout \[em] Remove an API token from the registry locally .SH "SYNOPSIS" \fBcargo logout\fR [\fIoptions\fR] .SH "DESCRIPTION" This command will run a credential provider to remove a saved token. .sp For the default \fBcargo:token\fR credential provider, credentials are stored in \fB$CARGO_HOME/credentials.toml\fR where \fB$CARGO_HOME\fR defaults to \fB\&.cargo\fR in your home directory. .sp If a registry has a credential\-provider specified, it will be used. Otherwise, the providers from the config value \fBregistry.global\-credential\-providers\fR will be attempted, starting from the end of the list. .sp If \fB\-\-registry\fR is not specified, then the credentials for the default registry will be removed (configured by \fI\f(BIregistry.default\fI\fR , which defaults to ). .sp This will not revoke the token on the server. If you need to revoke the token, visit the registry website and follow its instructions (see to revoke the token for ). .SH "OPTIONS" .SS "Logout Options" .sp \fB\-\-registry\fR \fIregistry\fR .RS 4 Name of the registry to use. Registry names are defined in \fICargo config files\fR \&. If not specified, the default registry is used, which is defined by the \fBregistry.default\fR config key which defaults to \fBcrates\-io\fR\&. .RE .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Remove the default registry token: .sp .RS 4 .nf cargo logout .fi .RE .RE .sp .RS 4 \h'-04' 2.\h'+01'Remove the token for a specific registry: .sp .RS 4 .nf cargo logout \-\-registry my\-registry .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-login\fR(1) cargo-0.91.0/src/etc/man/cargo-metadata.1000064400000000000000000000543461046102023000160720ustar 00000000000000'\" t .TH "CARGO\-METADATA" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-metadata \[em] Machine\-readable metadata about the current package .SH "SYNOPSIS" \fBcargo metadata\fR [\fIoptions\fR] .SH "DESCRIPTION" Output JSON to stdout containing information about the workspace members and resolved dependencies of the current package. .sp The output format is subject to change in future versions of Cargo. It is recommended to include the \fB\-\-format\-version\fR flag to future\-proof your code and ensure the output is in the format you are expecting. For more on the expectations, see \[lq]Compatibility\[rq]\&. .sp See the \fIcargo_metadata crate\fR for a Rust API for reading the metadata. .SH "OUTPUT FORMAT" .SS "Compatibility" Within the same output format version, the compatibility is maintained, except some scenarios. The following is a non\-exhaustive list of changes that are not considered as incompatible: .sp .RS 4 \h'-04'\(bu\h'+03'\fBAdding new fields\fR \[em] New fields will be added when needed. Reserving this helps Cargo evolve without bumping the format version too often. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBAdding new values for enum\-like fields\fR \[em] Same as adding new fields. It keeps metadata evolving without stagnation. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBChanging opaque representations\fR \[em] The inner representations of some fields are implementation details. For example, fields related to \[lq]Source ID\[rq] are treated as opaque identifiers to differentiate packages or sources. Consumers shouldn\[cq]t rely on those representations unless specified. .RE .SS "JSON format" The JSON output has the following format: .sp .RS 4 .nf { /* Array of all packages in the workspace. It also includes all feature\-enabled dependencies unless \-\-no\-deps is used. */ "packages": [ { /* The name of the package. */ "name": "my\-package", /* The version of the package. */ "version": "0.1.0", /* The Package ID for referring to the package within the document and as the `\-\-package` argument to many commands */ "id": "file:///path/to/my\-package#0.1.0", /* The license value from the manifest, or null. */ "license": "MIT/Apache\-2.0", /* The license\-file value from the manifest, or null. */ "license_file": "LICENSE", /* The description value from the manifest, or null. */ "description": "Package description.", /* The source ID of the package, an "opaque" identifier representing where a package is retrieved from. See "Compatibility" above for the stability guarantee. This is null for path dependencies and workspace members. For other dependencies, it is a string with the format: \- "registry+URL" for registry\-based dependencies. Example: "registry+https://github.com/rust\-lang/crates.io\-index" \- "git+URL" for git\-based dependencies. Example: "git+https://github.com/rust\-lang/cargo?rev=5e85ba14aaa20f8133863373404cb0af69eeef2c#5e85ba14aaa20f8133863373404cb0af69eeef2c" \- "sparse+URL" for dependencies from a sparse registry Example: "sparse+https://my\-sparse\-registry.org" The value after the `+` is not explicitly defined, and may change between versions of Cargo and may not directly correlate to other things, such as registry definitions in a config file. New source kinds may be added in the future which will have different `+` prefixed identifiers. */ "source": null, /* Array of dependencies declared in the package's manifest. */ "dependencies": [ { /* The name of the dependency. */ "name": "bitflags", /* The source ID of the dependency. May be null, see description for the package source. */ "source": "registry+https://github.com/rust\-lang/crates.io\-index", /* The version requirement for the dependency. Dependencies without a version requirement have a value of "*". */ "req": "^1.0", /* The dependency kind. "dev", "build", or null for a normal dependency. */ "kind": null, /* If the dependency is renamed, this is the new name for the dependency as a string. null if it is not renamed. */ "rename": null, /* Boolean of whether or not this is an optional dependency. */ "optional": false, /* Boolean of whether or not default features are enabled. */ "uses_default_features": true, /* Array of features enabled. */ "features": [], /* The target platform for the dependency. null if not a target dependency. */ "target": "cfg(windows)", /* The file system path for a local path dependency. not present if not a path dependency. */ "path": "/path/to/dep", /* A string of the URL of the registry this dependency is from. If not specified or null, the dependency is from the default registry (crates.io). */ "registry": null, /* (unstable) Boolean flag of whether or not this is a pulbic dependency. This field is only present when `\-Zpublic\-dependency` is enabled. */ "public": false } ], /* Array of Cargo targets. */ "targets": [ { /* Array of target kinds. \- lib targets list the `crate\-type` values from the manifest such as "lib", "rlib", "dylib", "proc\-macro", etc. (default ["lib"]) \- binary is ["bin"] \- example is ["example"] \- integration test is ["test"] \- benchmark is ["bench"] \- build script is ["custom\-build"] */ "kind": [ "bin" ], /* Array of crate types. \- lib and example libraries list the `crate\-type` values from the manifest such as "lib", "rlib", "dylib", "proc\-macro", etc. (default ["lib"]) \- all other target kinds are ["bin"] */ "crate_types": [ "bin" ], /* The name of the target. For lib targets, dashes will be replaced with underscores. */ "name": "my\-package", /* Absolute path to the root source file of the target. */ "src_path": "/path/to/my\-package/src/main.rs", /* The Rust edition of the target. Defaults to the package edition. */ "edition": "2018", /* Array of required features. This property is not included if no required features are set. */ "required\-features": ["feat1"], /* Whether the target should be documented by `cargo doc`. */ "doc": true, /* Whether or not this target has doc tests enabled, and the target is compatible with doc testing. */ "doctest": false, /* Whether or not this target should be built and run with `\-\-test` */ "test": true } ], /* Set of features defined for the package. Each feature maps to an array of features or dependencies it enables. */ "features": { "default": [ "feat1" ], "feat1": [], "feat2": [] }, /* Absolute path to this package's manifest. */ "manifest_path": "/path/to/my\-package/Cargo.toml", /* Package metadata. This is null if no metadata is specified. */ "metadata": { "docs": { "rs": { "all\-features": true } } }, /* List of registries to which this package may be published. Publishing is unrestricted if null, and forbidden if an empty array. */ "publish": [ "crates\-io" ], /* Array of authors from the manifest. Empty array if no authors specified. */ "authors": [ "Jane Doe " ], /* Array of categories from the manifest. */ "categories": [ "command\-line\-utilities" ], /* Optional string that is the default binary picked by cargo run. */ "default_run": null, /* Optional string that is the minimum supported rust version */ "rust_version": "1.56", /* Array of keywords from the manifest. */ "keywords": [ "cli" ], /* The readme value from the manifest or null if not specified. */ "readme": "README.md", /* The repository value from the manifest or null if not specified. */ "repository": "https://github.com/rust\-lang/cargo", /* The homepage value from the manifest or null if not specified. */ "homepage": "https://rust\-lang.org", /* The documentation value from the manifest or null if not specified. */ "documentation": "https://doc.rust\-lang.org/stable/std", /* The default edition of the package. Note that individual targets may have different editions. */ "edition": "2018", /* Optional string that is the name of a native library the package is linking to. */ "links": null, } ], /* Array of members of the workspace. Each entry is the Package ID for the package. */ "workspace_members": [ "file:///path/to/my\-package#0.1.0", ], /* Array of default members of the workspace. Each entry is the Package ID for the package. */ "workspace_default_members": [ "file:///path/to/my\-package#0.1.0", ], // The resolved dependency graph for the entire workspace. The enabled // features are based on the enabled features for the "current" package. // Inactivated optional dependencies are not listed. // // This is null if \-\-no\-deps is specified. // // By default, this includes all dependencies for all target platforms. // The `\-\-filter\-platform` flag may be used to narrow to a specific // target triple. "resolve": { /* Array of nodes within the dependency graph. Each node is a package. */ "nodes": [ { /* The Package ID of this node. */ "id": "file:///path/to/my\-package#0.1.0", /* The dependencies of this package, an array of Package IDs. */ "dependencies": [ "https://github.com/rust\-lang/crates.io\-index#bitflags@1.0.4" ], /* The dependencies of this package. This is an alternative to "dependencies" which contains additional information. In particular, this handles renamed dependencies. */ "deps": [ { /* The name of the dependency's library target. If this is a renamed dependency, this is the new name. */ "name": "bitflags", /* The Package ID of the dependency. */ "pkg": "https://github.com/rust\-lang/crates.io\-index#bitflags@1.0.4" /* Array of dependency kinds. Added in Cargo 1.40. */ "dep_kinds": [ { /* The dependency kind. "dev", "build", or null for a normal dependency. */ "kind": null, /* The target platform for the dependency. null if not a target dependency. */ "target": "cfg(windows)" } ] } ], /* Array of features enabled on this package. */ "features": [ "default" ] } ], /* The package in the current working directory (if \-\-manifest\-path is not given). This is null if there is a virtual workspace. Otherwise it is the Package ID of the package. */ "root": "file:///path/to/my\-package#0.1.0", }, /* The absolute path to the target directory where Cargo places its output. */ "target_directory": "/path/to/my\-package/target", /* The absolute path to the build directory where Cargo places intermediate build artifacts. (unstable) */ "build_directory": "/path/to/my\-package/build\-dir", /* The version of the schema for this metadata structure. This will be changed if incompatible changes are ever made. */ "version": 1, /* The absolute path to the root of the workspace. */ "workspace_root": "/path/to/my\-package" /* Workspace metadata. This is null if no metadata is specified. */ "metadata": { "docs": { "rs": { "all\-features": true } } } } .fi .RE .sp Notes: .sp .RS 4 \h'-04'\(bu\h'+03'For \fB"id"\fR field syntax, see \fIPackage ID Specifications\fR in the reference. .RE .SH "OPTIONS" .SS "Output Options" .sp \fB\-\-no\-deps\fR .RS 4 Output information only about the workspace members and don\[cq]t fetch dependencies. .RE .sp \fB\-\-format\-version\fR \fIversion\fR .RS 4 Specify the version of the output format to use. Currently \fB1\fR is the only possible value. .RE .sp \fB\-\-filter\-platform\fR \fItriple\fR .RS 4 This filters the \fBresolve\fR output to only include dependencies for the given \fItarget triple\fR \&. Without this flag, the resolve includes all targets. .sp Note that the dependencies listed in the \[lq]packages\[rq] array still includes all dependencies. Each package definition is intended to be an unaltered reproduction of the information within \fBCargo.toml\fR\&. .RE .SS "Feature Selection" The feature flags allow you to control which features are enabled. When no feature options are given, the \fBdefault\fR feature is activated for every selected package. .sp See \fIthe features documentation\fR for more details. .sp \fB\-F\fR \fIfeatures\fR, \fB\-\-features\fR \fIfeatures\fR .RS 4 Space or comma separated list of features to activate. Features of workspace members may be enabled with \fBpackage\-name/feature\-name\fR syntax. This flag may be specified multiple times, which enables all specified features. .RE .sp \fB\-\-all\-features\fR .RS 4 Activate all available features of all selected packages. .RE .sp \fB\-\-no\-default\-features\fR .RS 4 Do not activate the \fBdefault\fR feature of the selected packages. .RE .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .SS "Manifest Options" .sp \fB\-\-manifest\-path\fR \fIpath\fR .RS 4 Path to the \fBCargo.toml\fR file. By default, Cargo searches for the \fBCargo.toml\fR file in the current directory or any parent directory. .RE .sp \fB\-\-locked\fR .RS 4 Asserts that the exact same dependencies and versions are used as when the existing \fBCargo.lock\fR file was originally generated. Cargo will exit with an error when either of the following scenarios arises: .sp .RS 4 \h'-04'\(bu\h'+03'The lock file is missing. .RE .sp .RS 4 \h'-04'\(bu\h'+03'Cargo attempted to change the lock file due to a different dependency resolution. .RE .sp It may be used in environments where deterministic builds are desired, such as in CI pipelines. .RE .sp \fB\-\-offline\fR .RS 4 Prevents Cargo from accessing the network for any reason. Without this flag, Cargo will stop with an error if it needs to access the network and the network is not available. With this flag, Cargo will attempt to proceed without the network if possible. .sp Beware that this may result in different dependency resolution than online mode. Cargo will restrict itself to crates that are downloaded locally, even if there might be a newer version as indicated in the local copy of the index. See the \fBcargo\-fetch\fR(1) command to download dependencies before going offline. .sp May also be specified with the \fBnet.offline\fR \fIconfig value\fR \&. .RE .sp \fB\-\-frozen\fR .RS 4 Equivalent to specifying both \fB\-\-locked\fR and \fB\-\-offline\fR\&. .RE .sp \fB\-\-lockfile\-path\fR \fIPATH\fR .RS 4 Changes the path of the lockfile from the default (\fB/Cargo.lock\fR) to \fIPATH\fR\&. \fIPATH\fR must end with \fBCargo.lock\fR (e.g. \fB\-\-lockfile\-path /tmp/temporary\-lockfile/Cargo.lock\fR). Note that providing \fB\-\-lockfile\-path\fR will ignore existing lockfile at the default path, and instead will either use the lockfile from \fIPATH\fR, or write a new lockfile into the provided \fIPATH\fR if it doesn\[cq]t exist. This flag can be used to run most commands in read\-only directories, writing lockfile into the provided \fIPATH\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#14421\fR ). .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Output JSON about the current package: .sp .RS 4 .nf cargo metadata \-\-format\-version=1 .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-pkgid\fR(1), \fIPackage ID Specifications\fR , \fIJSON messages\fR cargo-0.91.0/src/etc/man/cargo-new.1000064400000000000000000000120201046102023000150620ustar 00000000000000'\" t .TH "CARGO\-NEW" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-new \[em] Create a new Cargo package .SH "SYNOPSIS" \fBcargo new\fR [\fIoptions\fR] \fIpath\fR .SH "DESCRIPTION" This command will create a new Cargo package in the given directory. This includes a simple template with a \fBCargo.toml\fR manifest, sample source file, and a VCS ignore file. If the directory is not already in a VCS repository, then a new repository is created (see \fB\-\-vcs\fR below). .sp See \fBcargo\-init\fR(1) for a similar command which will create a new manifest in an existing directory. .SH "OPTIONS" .SS "New Options" .sp \fB\-\-bin\fR .RS 4 Create a package with a binary target (\fBsrc/main.rs\fR). This is the default behavior. .RE .sp \fB\-\-lib\fR .RS 4 Create a package with a library target (\fBsrc/lib.rs\fR). .RE .sp \fB\-\-edition\fR \fIedition\fR .RS 4 Specify the Rust edition to use. Default is 2024. Possible values: 2015, 2018, 2021, 2024 .RE .sp \fB\-\-name\fR \fIname\fR .RS 4 Set the package name. Defaults to the directory name. .RE .sp \fB\-\-vcs\fR \fIvcs\fR .RS 4 Initialize a new VCS repository for the given version control system (git, hg, pijul, or fossil) or do not initialize any version control at all (none). If not specified, defaults to \fBgit\fR or the configuration value \fBcargo\-new.vcs\fR, or \fBnone\fR if already inside a VCS repository. .RE .sp \fB\-\-registry\fR \fIregistry\fR .RS 4 This sets the \fBpublish\fR field in \fBCargo.toml\fR to the given registry name which will restrict publishing only to that registry. .sp Registry names are defined in \fICargo config files\fR \&. If not specified, the default registry defined by the \fBregistry.default\fR config key is used. If the default registry is not set and \fB\-\-registry\fR is not used, the \fBpublish\fR field will not be set which means that publishing will not be restricted. .RE .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Create a binary Cargo package in the given directory: .sp .RS 4 .nf cargo new foo .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-init\fR(1) cargo-0.91.0/src/etc/man/cargo-owner.1000064400000000000000000000131661046102023000154370ustar 00000000000000'\" t .TH "CARGO\-OWNER" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-owner \[em] Manage the owners of a crate on the registry .SH "SYNOPSIS" \fBcargo owner\fR [\fIoptions\fR] \fB\-\-add\fR \fIlogin\fR [\fIcrate\fR] .br \fBcargo owner\fR [\fIoptions\fR] \fB\-\-remove\fR \fIlogin\fR [\fIcrate\fR] .br \fBcargo owner\fR [\fIoptions\fR] \fB\-\-list\fR [\fIcrate\fR] .SH "DESCRIPTION" This command will modify the owners for a crate on the registry. Owners of a crate can upload new versions and yank old versions. Non\-team owners can also modify the set of owners, so take care! .sp This command requires you to be authenticated with either the \fB\-\-token\fR option or using \fBcargo\-login\fR(1). .sp If the crate name is not specified, it will use the package name from the current directory. .sp See \fIthe reference\fR for more information about owners and publishing. .SH "OPTIONS" .SS "Owner Options" .sp \fB\-a\fR, \fB\-\-add\fR \fIlogin\fR\[u2026] .RS 4 Invite the given user or team as an owner. .RE .sp \fB\-r\fR, \fB\-\-remove\fR \fIlogin\fR\[u2026] .RS 4 Remove the given user or team as an owner. .RE .sp \fB\-l\fR, \fB\-\-list\fR .RS 4 List owners of a crate. .RE .sp \fB\-\-token\fR \fItoken\fR .RS 4 API token to use when authenticating. This overrides the token stored in the credentials file (which is created by \fBcargo\-login\fR(1)). .sp \fICargo config\fR environment variables can be used to override the tokens stored in the credentials file. The token for crates.io may be specified with the \fBCARGO_REGISTRY_TOKEN\fR environment variable. Tokens for other registries may be specified with environment variables of the form \fBCARGO_REGISTRIES_NAME_TOKEN\fR where \fBNAME\fR is the name of the registry in all capital letters. .RE .sp \fB\-\-index\fR \fIindex\fR .RS 4 The URL of the registry index to use. .RE .sp \fB\-\-registry\fR \fIregistry\fR .RS 4 Name of the registry to use. Registry names are defined in \fICargo config files\fR \&. If not specified, the default registry is used, which is defined by the \fBregistry.default\fR config key which defaults to \fBcrates\-io\fR\&. .RE .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'List owners of a package: .sp .RS 4 .nf cargo owner \-\-list foo .fi .RE .RE .sp .RS 4 \h'-04' 2.\h'+01'Invite an owner to a package: .sp .RS 4 .nf cargo owner \-\-add username foo .fi .RE .RE .sp .RS 4 \h'-04' 3.\h'+01'Remove an owner from a package: .sp .RS 4 .nf cargo owner \-\-remove username foo .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-login\fR(1), \fBcargo\-publish\fR(1) cargo-0.91.0/src/etc/man/cargo-package.1000064400000000000000000000373121046102023000156770ustar 00000000000000'\" t .TH "CARGO\-PACKAGE" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-package \[em] Assemble the local package into a distributable tarball .SH "SYNOPSIS" \fBcargo package\fR [\fIoptions\fR] .SH "DESCRIPTION" This command will create a distributable, compressed \fB\&.crate\fR file with the source code of the package in the current directory. The resulting file will be stored in the \fBtarget/package\fR directory. This performs the following steps: .sp .RS 4 \h'-04' 1.\h'+01'Load and check the current workspace, performing some basic checks. .sp .RS 4 \h'-04'\(bu\h'+03'Path dependencies are not allowed unless they have a version key. Cargo will ignore the path key for dependencies in published packages. \fBdev\-dependencies\fR do not have this restriction. .RE .RE .sp .RS 4 \h'-04' 2.\h'+01'Create the compressed \fB\&.crate\fR file. .sp .RS 4 \h'-04'\(bu\h'+03'The original \fBCargo.toml\fR file is rewritten and normalized. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB[patch]\fR, \fB[replace]\fR, and \fB[workspace]\fR sections are removed from the manifest. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBCargo.lock\fR is always included. When missing, a new lock file will be generated unless the \fB\-\-exclude\-lockfile\fR flag is used. \fBcargo\-install\fR(1) will use the packaged lock file if the \fB\-\-locked\fR flag is used. .RE .sp .RS 4 \h'-04'\(bu\h'+03'A \fB\&.cargo_vcs_info.json\fR file is included that contains information about the current VCS checkout hash if available, as well as a flag if the worktree is dirty. .RE .sp .RS 4 \h'-04'\(bu\h'+03'Symlinks are flattened to their target files. .RE .sp .RS 4 \h'-04'\(bu\h'+03'Files and directories are included or excluded based on rules mentioned in \fIthe \f(BI[include]\fI and \f(BI[exclude]\fI fields\fR \&. .RE .RE .sp .RS 4 \h'-04' 3.\h'+01'Extract the \fB\&.crate\fR file and build it to verify it can build. .sp .RS 4 \h'-04'\(bu\h'+03'This will rebuild your package from scratch to ensure that it can be built from a pristine state. The \fB\-\-no\-verify\fR flag can be used to skip this step. .RE .RE .sp .RS 4 \h'-04' 4.\h'+01'Check that build scripts did not modify any source files. .RE .sp The list of files included can be controlled with the \fBinclude\fR and \fBexclude\fR fields in the manifest. .sp See \fIthe reference\fR for more details about packaging and publishing. .SS ".cargo_vcs_info.json format" Will generate a \fB\&.cargo_vcs_info.json\fR in the following format .sp .RS 4 .nf { "git": { "sha1": "aac20b6e7e543e6dd4118b246c77225e3a3a1302", "dirty": true }, "path_in_vcs": "" } .fi .RE .sp \fBdirty\fR indicates that the Git worktree was dirty when the package was built. .sp \fBpath_in_vcs\fR will be set to a repo\-relative path for packages in subdirectories of the version control repository. .sp The compatibility of this file is maintained under the same policy as the JSON output of \fBcargo\-metadata\fR(1). .sp Note that this file provides a best\-effort snapshot of the VCS information. However, the provenance of the package is not verified. There is no guarantee that the source code in the tarball matches the VCS information. .SH "OPTIONS" .SS "Package Options" .sp \fB\-l\fR, \fB\-\-list\fR .RS 4 Print files included in a package without making one. .RE .sp \fB\-\-no\-verify\fR .RS 4 Don\[cq]t verify the contents by building them. .RE .sp \fB\-\-no\-metadata\fR .RS 4 Ignore warnings about a lack of human\-usable metadata (such as the description or the license). .RE .sp \fB\-\-allow\-dirty\fR .RS 4 Allow working directories with uncommitted VCS changes to be packaged. .RE .sp \fB\-\-exclude\-lockfile\fR .RS 4 Don\[cq]t include the lock file when packaging. .sp This flag is not for general use. Some tools may expect a lock file to be present (e.g. \fBcargo install \-\-locked\fR). Consider other options before using this. .RE .sp \fB\-\-index\fR \fIindex\fR .RS 4 The URL of the registry index to use. .RE .sp \fB\-\-registry\fR \fIregistry\fR .RS 4 Name of the registry to package for; see \fBcargo publish \-\-help\fR for more details about configuration of registry names. The packages will not be published to this registry, but if we are packaging multiple inter\-dependent crates, lock\-files will be generated under the assumption that dependencies will be published to this registry. .RE .sp \fB\-\-message\-format\fR \fIfmt\fR .RS 4 Specifies the output message format. Currently, it only works with \fB\-\-list\fR and affects the file listing format. This is unstable and requires \fB\-Zunstable\-options\fR\&. Valid output formats: .sp .RS 4 \h'-04'\(bu\h'+03'\fBhuman\fR (default): Display in a file\-per\-line format. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\fR: Emit machine\-readable JSON information about each package. One package per JSON line (Newline delimited JSON). .sp .RS 4 .nf { /* The Package ID Spec of the package. */ "id": "path+file:///home/foo#0.0.0", /* Files of this package */ "files" { /* Relative path in the archive file. */ "Cargo.toml.orig": { /* Where the file is from. \- "generate" for file being generated during packaging \- "copy" for file being copied from another location. */ "kind": "copy", /* For the "copy" kind, it is an absolute path to the actual file content. For the "generate" kind, it is the original file the generated one is based on. */ "path": "/home/foo/Cargo.toml" }, "Cargo.toml": { "kind": "generate", "path": "/home/foo/Cargo.toml" }, "src/main.rs": { "kind": "copy", "path": "/home/foo/src/main.rs" } } } .fi .RE .RE .RE .SS "Package Selection" By default, when no package selection options are given, the packages selected depend on the selected manifest file (based on the current working directory if \fB\-\-manifest\-path\fR is not given). If the manifest is the root of a workspace then the workspaces default members are selected, otherwise only the package defined by the manifest will be selected. .sp The default members of a workspace can be set explicitly with the \fBworkspace.default\-members\fR key in the root manifest. If this is not set, a virtual workspace will include all workspace members (equivalent to passing \fB\-\-workspace\fR), and a non\-virtual workspace will include only the root crate itself. .sp \fB\-p\fR \fIspec\fR\[u2026], \fB\-\-package\fR \fIspec\fR\[u2026] .RS 4 Package only the specified packages. See \fBcargo\-pkgid\fR(1) for the SPEC format. This flag may be specified multiple times and supports common Unix glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell accidentally expanding glob patterns before Cargo handles them, you must use single quotes or double quotes around each pattern. .RE .sp \fB\-\-workspace\fR .RS 4 Package all members in the workspace. .RE .sp \fB\-\-exclude\fR \fISPEC\fR\[u2026] .RS 4 Exclude the specified packages. Must be used in conjunction with the \fB\-\-workspace\fR flag. This flag may be specified multiple times and supports common Unix glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell accidentally expanding glob patterns before Cargo handles them, you must use single quotes or double quotes around each pattern. .RE .SS "Compilation Options" .sp \fB\-\-target\fR \fItriple\fR .RS 4 Package for the given architecture. The default is the host architecture. The general format of the triple is \fB\-\-\-\fR\&. Run \fBrustc \-\-print target\-list\fR for a list of supported targets. This flag may be specified multiple times. .sp This may also be specified with the \fBbuild.target\fR \fIconfig value\fR \&. .sp Note that specifying this flag makes Cargo run in a different mode where the target artifacts are placed in a separate directory. See the \fIbuild cache\fR documentation for more details. .RE .sp \fB\-\-target\-dir\fR \fIdirectory\fR .RS 4 Directory for all generated artifacts and intermediate files. May also be specified with the \fBCARGO_TARGET_DIR\fR environment variable, or the \fBbuild.target\-dir\fR \fIconfig value\fR \&. Defaults to \fBtarget\fR in the root of the workspace. .RE .SS "Feature Selection" The feature flags allow you to control which features are enabled. When no feature options are given, the \fBdefault\fR feature is activated for every selected package. .sp See \fIthe features documentation\fR for more details. .sp \fB\-F\fR \fIfeatures\fR, \fB\-\-features\fR \fIfeatures\fR .RS 4 Space or comma separated list of features to activate. Features of workspace members may be enabled with \fBpackage\-name/feature\-name\fR syntax. This flag may be specified multiple times, which enables all specified features. .RE .sp \fB\-\-all\-features\fR .RS 4 Activate all available features of all selected packages. .RE .sp \fB\-\-no\-default\-features\fR .RS 4 Do not activate the \fBdefault\fR feature of the selected packages. .RE .SS "Manifest Options" .sp \fB\-\-manifest\-path\fR \fIpath\fR .RS 4 Path to the \fBCargo.toml\fR file. By default, Cargo searches for the \fBCargo.toml\fR file in the current directory or any parent directory. .RE .sp \fB\-\-locked\fR .RS 4 Asserts that the exact same dependencies and versions are used as when the existing \fBCargo.lock\fR file was originally generated. Cargo will exit with an error when either of the following scenarios arises: .sp .RS 4 \h'-04'\(bu\h'+03'The lock file is missing. .RE .sp .RS 4 \h'-04'\(bu\h'+03'Cargo attempted to change the lock file due to a different dependency resolution. .RE .sp It may be used in environments where deterministic builds are desired, such as in CI pipelines. .RE .sp \fB\-\-offline\fR .RS 4 Prevents Cargo from accessing the network for any reason. Without this flag, Cargo will stop with an error if it needs to access the network and the network is not available. With this flag, Cargo will attempt to proceed without the network if possible. .sp Beware that this may result in different dependency resolution than online mode. Cargo will restrict itself to crates that are downloaded locally, even if there might be a newer version as indicated in the local copy of the index. See the \fBcargo\-fetch\fR(1) command to download dependencies before going offline. .sp May also be specified with the \fBnet.offline\fR \fIconfig value\fR \&. .RE .sp \fB\-\-frozen\fR .RS 4 Equivalent to specifying both \fB\-\-locked\fR and \fB\-\-offline\fR\&. .RE .sp \fB\-\-lockfile\-path\fR \fIPATH\fR .RS 4 Changes the path of the lockfile from the default (\fB/Cargo.lock\fR) to \fIPATH\fR\&. \fIPATH\fR must end with \fBCargo.lock\fR (e.g. \fB\-\-lockfile\-path /tmp/temporary\-lockfile/Cargo.lock\fR). Note that providing \fB\-\-lockfile\-path\fR will ignore existing lockfile at the default path, and instead will either use the lockfile from \fIPATH\fR, or write a new lockfile into the provided \fIPATH\fR if it doesn\[cq]t exist. This flag can be used to run most commands in read\-only directories, writing lockfile into the provided \fIPATH\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#14421\fR ). .RE .SS "Miscellaneous Options" .sp \fB\-j\fR \fIN\fR, \fB\-\-jobs\fR \fIN\fR .RS 4 Number of parallel jobs to run. May also be specified with the \fBbuild.jobs\fR \fIconfig value\fR \&. Defaults to the number of logical CPUs. If negative, it sets the maximum number of parallel jobs to the number of logical CPUs plus provided value. If a string \fBdefault\fR is provided, it sets the value back to defaults. Should not be 0. .RE .sp \fB\-\-keep\-going\fR .RS 4 Build as many crates in the dependency graph as possible, rather than aborting the build on the first one that fails to build. .sp For example if the current package depends on dependencies \fBfails\fR and \fBworks\fR, one of which fails to build, \fBcargo package \-j1\fR may or may not build the one that succeeds (depending on which one of the two builds Cargo picked to run first), whereas \fBcargo package \-j1 \-\-keep\-going\fR would definitely run both builds, even if the one run first fails. .RE .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Create a compressed \fB\&.crate\fR file of the current package: .sp .RS 4 .nf cargo package .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-publish\fR(1) cargo-0.91.0/src/etc/man/cargo-pkgid.1000064400000000000000000000202151046102023000153740ustar 00000000000000'\" t .TH "CARGO\-PKGID" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-pkgid \[em] Print a fully qualified package specification .SH "SYNOPSIS" \fBcargo pkgid\fR [\fIoptions\fR] [\fIspec\fR] .SH "DESCRIPTION" Given a \fIspec\fR argument, print out the fully qualified package ID specifier for a package or dependency in the current workspace. This command will generate an error if \fIspec\fR is ambiguous as to which package it refers to in the dependency graph. If no \fIspec\fR is given, then the specifier for the local package is printed. .sp This command requires that a lockfile is available and dependencies have been fetched. .sp A package specifier consists of a name, version, and source URL. You are allowed to use partial specifiers to succinctly match a specific package as long as it matches only one package. This specifier is also used by other parts in Cargo, such as \fBcargo\-metadata\fR(1) and \fIJSON messages\fR emitted by Cargo. .sp The format of a \fIspec\fR can be one of the following: .TS allbox tab(:); lt lt. T{ SPEC Structure T}:T{ Example SPEC T} T{ \fIname\fR T}:T{ \fBbitflags\fR T} T{ \fIname\fR\fB@\fR\fIversion\fR T}:T{ \fBbitflags@1.0.4\fR T} T{ \fIurl\fR T}:T{ \fBhttps://github.com/rust\-lang/cargo\fR T} T{ \fIurl\fR\fB#\fR\fIversion\fR T}:T{ \fBhttps://github.com/rust\-lang/cargo#0.33.0\fR T} T{ \fIurl\fR\fB#\fR\fIname\fR T}:T{ \fBhttps://github.com/rust\-lang/crates.io\-index#bitflags\fR T} T{ \fIurl\fR\fB#\fR\fIname\fR\fB@\fR\fIversion\fR T}:T{ \fBhttps://github.com/rust\-lang/cargo#crates\-io@0.21.0\fR T} .TE .sp .sp The specification grammar can be found in chapter \fIPackage ID Specifications\fR \&. .SH "OPTIONS" .SS "Package Selection" .sp \fB\-p\fR \fIspec\fR, \fB\-\-package\fR \fIspec\fR .RS 4 Get the package ID for the given package instead of the current package. .RE .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .SS "Manifest Options" .sp \fB\-\-manifest\-path\fR \fIpath\fR .RS 4 Path to the \fBCargo.toml\fR file. By default, Cargo searches for the \fBCargo.toml\fR file in the current directory or any parent directory. .RE .sp \fB\-\-locked\fR .RS 4 Asserts that the exact same dependencies and versions are used as when the existing \fBCargo.lock\fR file was originally generated. Cargo will exit with an error when either of the following scenarios arises: .sp .RS 4 \h'-04'\(bu\h'+03'The lock file is missing. .RE .sp .RS 4 \h'-04'\(bu\h'+03'Cargo attempted to change the lock file due to a different dependency resolution. .RE .sp It may be used in environments where deterministic builds are desired, such as in CI pipelines. .RE .sp \fB\-\-offline\fR .RS 4 Prevents Cargo from accessing the network for any reason. Without this flag, Cargo will stop with an error if it needs to access the network and the network is not available. With this flag, Cargo will attempt to proceed without the network if possible. .sp Beware that this may result in different dependency resolution than online mode. Cargo will restrict itself to crates that are downloaded locally, even if there might be a newer version as indicated in the local copy of the index. See the \fBcargo\-fetch\fR(1) command to download dependencies before going offline. .sp May also be specified with the \fBnet.offline\fR \fIconfig value\fR \&. .RE .sp \fB\-\-frozen\fR .RS 4 Equivalent to specifying both \fB\-\-locked\fR and \fB\-\-offline\fR\&. .RE .sp \fB\-\-lockfile\-path\fR \fIPATH\fR .RS 4 Changes the path of the lockfile from the default (\fB/Cargo.lock\fR) to \fIPATH\fR\&. \fIPATH\fR must end with \fBCargo.lock\fR (e.g. \fB\-\-lockfile\-path /tmp/temporary\-lockfile/Cargo.lock\fR). Note that providing \fB\-\-lockfile\-path\fR will ignore existing lockfile at the default path, and instead will either use the lockfile from \fIPATH\fR, or write a new lockfile into the provided \fIPATH\fR if it doesn\[cq]t exist. This flag can be used to run most commands in read\-only directories, writing lockfile into the provided \fIPATH\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#14421\fR ). .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Retrieve package specification for \fBfoo\fR package: .sp .RS 4 .nf cargo pkgid foo .fi .RE .RE .sp .RS 4 \h'-04' 2.\h'+01'Retrieve package specification for version 1.0.0 of \fBfoo\fR: .sp .RS 4 .nf cargo pkgid foo@1.0.0 .fi .RE .RE .sp .RS 4 \h'-04' 3.\h'+01'Retrieve package specification for \fBfoo\fR from crates.io: .sp .RS 4 .nf cargo pkgid https://github.com/rust\-lang/crates.io\-index#foo .fi .RE .RE .sp .RS 4 \h'-04' 4.\h'+01'Retrieve package specification for \fBfoo\fR from a local package: .sp .RS 4 .nf cargo pkgid file:///path/to/local/package#foo .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-generate\-lockfile\fR(1), \fBcargo\-metadata\fR(1), \fIPackage ID Specifications\fR , \fIJSON messages\fR cargo-0.91.0/src/etc/man/cargo-publish.1000064400000000000000000000320441046102023000157470ustar 00000000000000'\" t .TH "CARGO\-PUBLISH" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-publish \[em] Upload a package to the registry .SH "SYNOPSIS" \fBcargo publish\fR [\fIoptions\fR] .SH "DESCRIPTION" This command will create a distributable, compressed \fB\&.crate\fR file with the source code of the package in the current directory and upload it to a registry. The default registry is \&. This performs the following steps: .sp .RS 4 \h'-04' 1.\h'+01'Performs a few checks, including: .sp .RS 4 \h'-04'\(bu\h'+03'Checks the \fBpackage.publish\fR key in the manifest for restrictions on which registries you are allowed to publish to. .RE .RE .sp .RS 4 \h'-04' 2.\h'+01'Create a \fB\&.crate\fR file by following the steps in \fBcargo\-package\fR(1). .RE .sp .RS 4 \h'-04' 3.\h'+01'Upload the crate to the registry. The server will perform additional checks on the crate. .RE .sp .RS 4 \h'-04' 4.\h'+01'The client will poll waiting for the package to appear in the index, and may timeout. In that case, you will need to check for completion manually. This timeout does not affect the upload. .RE .sp This command requires you to be authenticated with either the \fB\-\-token\fR option or using \fBcargo\-login\fR(1). .sp See \fIthe reference\fR for more details about packaging and publishing. .SH "OPTIONS" .SS "Publish Options" .sp \fB\-\-dry\-run\fR .RS 4 Perform all checks without uploading. .RE .sp \fB\-\-token\fR \fItoken\fR .RS 4 API token to use when authenticating. This overrides the token stored in the credentials file (which is created by \fBcargo\-login\fR(1)). .sp \fICargo config\fR environment variables can be used to override the tokens stored in the credentials file. The token for crates.io may be specified with the \fBCARGO_REGISTRY_TOKEN\fR environment variable. Tokens for other registries may be specified with environment variables of the form \fBCARGO_REGISTRIES_NAME_TOKEN\fR where \fBNAME\fR is the name of the registry in all capital letters. .RE .sp \fB\-\-no\-verify\fR .RS 4 Don\[cq]t verify the contents by building them. .RE .sp \fB\-\-allow\-dirty\fR .RS 4 Allow working directories with uncommitted VCS changes to be packaged. .RE .sp \fB\-\-index\fR \fIindex\fR .RS 4 The URL of the registry index to use. .RE .sp \fB\-\-registry\fR \fIregistry\fR .RS 4 Name of the registry to publish to. Registry names are defined in \fICargo config files\fR \&. If not specified, and there is a \fI\f(BIpackage.publish\fI\fR field in \fBCargo.toml\fR with a single registry, then it will publish to that registry. Otherwise it will use the default registry, which is defined by the \fI\f(BIregistry.default\fI\fR config key which defaults to \fBcrates\-io\fR\&. .RE .SS "Package Selection" By default, when no package selection options are given, the packages selected depend on the selected manifest file (based on the current working directory if \fB\-\-manifest\-path\fR is not given). If the manifest is the root of a workspace then the workspaces default members are selected, otherwise only the package defined by the manifest will be selected. .sp The default members of a workspace can be set explicitly with the \fBworkspace.default\-members\fR key in the root manifest. If this is not set, a virtual workspace will include all workspace members (equivalent to passing \fB\-\-workspace\fR), and a non\-virtual workspace will include only the root crate itself. .sp \fB\-p\fR \fIspec\fR\[u2026], \fB\-\-package\fR \fIspec\fR\[u2026] .RS 4 Publish only the specified packages. See \fBcargo\-pkgid\fR(1) for the SPEC format. This flag may be specified multiple times and supports common Unix glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell accidentally expanding glob patterns before Cargo handles them, you must use single quotes or double quotes around each pattern. .RE .sp \fB\-\-workspace\fR .RS 4 Publish all members in the workspace. .RE .sp \fB\-\-all\fR .RS 4 Deprecated alias for \fB\-\-workspace\fR\&. .RE .sp \fB\-\-exclude\fR \fISPEC\fR\[u2026] .RS 4 Exclude the specified packages. Must be used in conjunction with the \fB\-\-workspace\fR flag. This flag may be specified multiple times and supports common Unix glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell accidentally expanding glob patterns before Cargo handles them, you must use single quotes or double quotes around each pattern. .RE .SS "Compilation Options" .sp \fB\-\-target\fR \fItriple\fR .RS 4 Publish for the given architecture. The default is the host architecture. The general format of the triple is \fB\-\-\-\fR\&. Run \fBrustc \-\-print target\-list\fR for a list of supported targets. This flag may be specified multiple times. .sp This may also be specified with the \fBbuild.target\fR \fIconfig value\fR \&. .sp Note that specifying this flag makes Cargo run in a different mode where the target artifacts are placed in a separate directory. See the \fIbuild cache\fR documentation for more details. .RE .sp \fB\-\-target\-dir\fR \fIdirectory\fR .RS 4 Directory for all generated artifacts and intermediate files. May also be specified with the \fBCARGO_TARGET_DIR\fR environment variable, or the \fBbuild.target\-dir\fR \fIconfig value\fR \&. Defaults to \fBtarget\fR in the root of the workspace. .RE .SS "Feature Selection" The feature flags allow you to control which features are enabled. When no feature options are given, the \fBdefault\fR feature is activated for every selected package. .sp See \fIthe features documentation\fR for more details. .sp \fB\-F\fR \fIfeatures\fR, \fB\-\-features\fR \fIfeatures\fR .RS 4 Space or comma separated list of features to activate. Features of workspace members may be enabled with \fBpackage\-name/feature\-name\fR syntax. This flag may be specified multiple times, which enables all specified features. .RE .sp \fB\-\-all\-features\fR .RS 4 Activate all available features of all selected packages. .RE .sp \fB\-\-no\-default\-features\fR .RS 4 Do not activate the \fBdefault\fR feature of the selected packages. .RE .SS "Manifest Options" .sp \fB\-\-manifest\-path\fR \fIpath\fR .RS 4 Path to the \fBCargo.toml\fR file. By default, Cargo searches for the \fBCargo.toml\fR file in the current directory or any parent directory. .RE .sp \fB\-\-locked\fR .RS 4 Asserts that the exact same dependencies and versions are used as when the existing \fBCargo.lock\fR file was originally generated. Cargo will exit with an error when either of the following scenarios arises: .sp .RS 4 \h'-04'\(bu\h'+03'The lock file is missing. .RE .sp .RS 4 \h'-04'\(bu\h'+03'Cargo attempted to change the lock file due to a different dependency resolution. .RE .sp It may be used in environments where deterministic builds are desired, such as in CI pipelines. .RE .sp \fB\-\-offline\fR .RS 4 Prevents Cargo from accessing the network for any reason. Without this flag, Cargo will stop with an error if it needs to access the network and the network is not available. With this flag, Cargo will attempt to proceed without the network if possible. .sp Beware that this may result in different dependency resolution than online mode. Cargo will restrict itself to crates that are downloaded locally, even if there might be a newer version as indicated in the local copy of the index. See the \fBcargo\-fetch\fR(1) command to download dependencies before going offline. .sp May also be specified with the \fBnet.offline\fR \fIconfig value\fR \&. .RE .sp \fB\-\-frozen\fR .RS 4 Equivalent to specifying both \fB\-\-locked\fR and \fB\-\-offline\fR\&. .RE .sp \fB\-\-lockfile\-path\fR \fIPATH\fR .RS 4 Changes the path of the lockfile from the default (\fB/Cargo.lock\fR) to \fIPATH\fR\&. \fIPATH\fR must end with \fBCargo.lock\fR (e.g. \fB\-\-lockfile\-path /tmp/temporary\-lockfile/Cargo.lock\fR). Note that providing \fB\-\-lockfile\-path\fR will ignore existing lockfile at the default path, and instead will either use the lockfile from \fIPATH\fR, or write a new lockfile into the provided \fIPATH\fR if it doesn\[cq]t exist. This flag can be used to run most commands in read\-only directories, writing lockfile into the provided \fIPATH\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#14421\fR ). .RE .SS "Miscellaneous Options" .sp \fB\-j\fR \fIN\fR, \fB\-\-jobs\fR \fIN\fR .RS 4 Number of parallel jobs to run. May also be specified with the \fBbuild.jobs\fR \fIconfig value\fR \&. Defaults to the number of logical CPUs. If negative, it sets the maximum number of parallel jobs to the number of logical CPUs plus provided value. If a string \fBdefault\fR is provided, it sets the value back to defaults. Should not be 0. .RE .sp \fB\-\-keep\-going\fR .RS 4 Build as many crates in the dependency graph as possible, rather than aborting the build on the first one that fails to build. .sp For example if the current package depends on dependencies \fBfails\fR and \fBworks\fR, one of which fails to build, \fBcargo publish \-j1\fR may or may not build the one that succeeds (depending on which one of the two builds Cargo picked to run first), whereas \fBcargo publish \-j1 \-\-keep\-going\fR would definitely run both builds, even if the one run first fails. .RE .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Publish the current package: .sp .RS 4 .nf cargo publish .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-package\fR(1), \fBcargo\-login\fR(1) cargo-0.91.0/src/etc/man/cargo-remove.1000064400000000000000000000157751046102023000156120ustar 00000000000000'\" t .TH "CARGO\-REMOVE" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-remove \[em] Remove dependencies from a Cargo.toml manifest file .SH "SYNOPSIS" \fBcargo remove\fR [\fIoptions\fR] \fIdependency\fR\[u2026] .SH "DESCRIPTION" Remove one or more dependencies from a \fBCargo.toml\fR manifest. .SH "OPTIONS" .SS "Section options" .sp \fB\-\-dev\fR .RS 4 Remove as a \fIdevelopment dependency\fR \&. .RE .sp \fB\-\-build\fR .RS 4 Remove as a \fIbuild dependency\fR \&. .RE .sp \fB\-\-target\fR \fItarget\fR .RS 4 Remove as a dependency to the \fIgiven target platform\fR \&. .sp To avoid unexpected shell expansions, you may use quotes around each target, e.g., \fB\-\-target 'cfg(unix)'\fR\&. .RE .SS "Miscellaneous Options" .sp \fB\-\-dry\-run\fR .RS 4 Don\[cq]t actually write to the manifest. .RE .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .SS "Manifest Options" .sp \fB\-\-manifest\-path\fR \fIpath\fR .RS 4 Path to the \fBCargo.toml\fR file. By default, Cargo searches for the \fBCargo.toml\fR file in the current directory or any parent directory. .RE .sp \fB\-\-locked\fR .RS 4 Asserts that the exact same dependencies and versions are used as when the existing \fBCargo.lock\fR file was originally generated. Cargo will exit with an error when either of the following scenarios arises: .sp .RS 4 \h'-04'\(bu\h'+03'The lock file is missing. .RE .sp .RS 4 \h'-04'\(bu\h'+03'Cargo attempted to change the lock file due to a different dependency resolution. .RE .sp It may be used in environments where deterministic builds are desired, such as in CI pipelines. .RE .sp \fB\-\-offline\fR .RS 4 Prevents Cargo from accessing the network for any reason. Without this flag, Cargo will stop with an error if it needs to access the network and the network is not available. With this flag, Cargo will attempt to proceed without the network if possible. .sp Beware that this may result in different dependency resolution than online mode. Cargo will restrict itself to crates that are downloaded locally, even if there might be a newer version as indicated in the local copy of the index. See the \fBcargo\-fetch\fR(1) command to download dependencies before going offline. .sp May also be specified with the \fBnet.offline\fR \fIconfig value\fR \&. .RE .sp \fB\-\-frozen\fR .RS 4 Equivalent to specifying both \fB\-\-locked\fR and \fB\-\-offline\fR\&. .RE .sp \fB\-\-lockfile\-path\fR \fIPATH\fR .RS 4 Changes the path of the lockfile from the default (\fB/Cargo.lock\fR) to \fIPATH\fR\&. \fIPATH\fR must end with \fBCargo.lock\fR (e.g. \fB\-\-lockfile\-path /tmp/temporary\-lockfile/Cargo.lock\fR). Note that providing \fB\-\-lockfile\-path\fR will ignore existing lockfile at the default path, and instead will either use the lockfile from \fIPATH\fR, or write a new lockfile into the provided \fIPATH\fR if it doesn\[cq]t exist. This flag can be used to run most commands in read\-only directories, writing lockfile into the provided \fIPATH\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#14421\fR ). .RE .SS "Package Selection" .sp \fB\-p\fR \fIspec\fR\[u2026], \fB\-\-package\fR \fIspec\fR\[u2026] .RS 4 Package to remove from. .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Remove \fBregex\fR as a dependency .sp .RS 4 .nf cargo remove regex .fi .RE .RE .sp .RS 4 \h'-04' 2.\h'+01'Remove \fBtrybuild\fR as a dev\-dependency .sp .RS 4 .nf cargo remove \-\-dev trybuild .fi .RE .RE .sp .RS 4 \h'-04' 3.\h'+01'Remove \fBnom\fR from the \fBx86_64\-pc\-windows\-gnu\fR dependencies table .sp .RS 4 .nf cargo remove \-\-target x86_64\-pc\-windows\-gnu nom .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-add\fR(1) cargo-0.91.0/src/etc/man/cargo-report.1000064400000000000000000000017511046102023000156150ustar 00000000000000'\" t .TH "CARGO\-REPORT" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-report \[em] Generate and display various kinds of reports .SH "SYNOPSIS" \fBcargo report\fR \fItype\fR [\fIoptions\fR] .SS "DESCRIPTION" Displays a report of the given \fItype\fR \[em] currently, only \fBfuture\-incompat\fR is supported .SH "OPTIONS" .sp \fB\-\-id\fR \fIid\fR .RS 4 Show the report with the specified Cargo\-generated id .RE .sp \fB\-p\fR \fIspec\fR\[u2026], \fB\-\-package\fR \fIspec\fR\[u2026] .RS 4 Only display a report for the specified package .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Display the latest future\-incompat report: .sp .RS 4 .nf cargo report future\-incompat .fi .RE .RE .sp .RS 4 \h'-04' 2.\h'+01'Display the latest future\-incompat report for a specific package: .sp .RS 4 .nf cargo report future\-incompat \-\-package my\-dep:0.0.1 .fi .RE .RE .SH "SEE ALSO" \fIFuture incompat report\fR .sp \fBcargo\fR(1) cargo-0.91.0/src/etc/man/cargo-run.1000064400000000000000000000316231046102023000151070ustar 00000000000000'\" t .TH "CARGO\-RUN" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-run \[em] Run the current package .SH "SYNOPSIS" \fBcargo run\fR [\fIoptions\fR] [\fB\-\-\fR \fIargs\fR] .SH "DESCRIPTION" Run a binary or example of the local package. .sp All the arguments following the two dashes (\fB\-\-\fR) are passed to the binary to run. If you\[cq]re passing arguments to both Cargo and the binary, the ones after \fB\-\-\fR go to the binary, the ones before go to Cargo. .sp Unlike \fBcargo\-test\fR(1) and \fBcargo\-bench\fR(1), \fBcargo run\fR sets the working directory of the binary executed to the current working directory, same as if it was executed in the shell directly. .SH "OPTIONS" .SS "Package Selection" By default, the package in the current working directory is selected. The \fB\-p\fR flag can be used to choose a different package in a workspace. .sp \fB\-p\fR \fIspec\fR, \fB\-\-package\fR \fIspec\fR .RS 4 The package to run. See \fBcargo\-pkgid\fR(1) for the SPEC format. .RE .SS "Target Selection" When no target selection options are given, \fBcargo run\fR will run the binary target. If there are multiple binary targets, you must pass a target flag to choose one. Or, the \fBdefault\-run\fR field may be specified in the \fB[package]\fR section of \fBCargo.toml\fR to choose the name of the binary to run by default. .sp \fB\-\-bin\fR \fIname\fR .RS 4 Run the specified binary. .RE .sp \fB\-\-example\fR \fIname\fR .RS 4 Run the specified example. .RE .SS "Feature Selection" The feature flags allow you to control which features are enabled. When no feature options are given, the \fBdefault\fR feature is activated for every selected package. .sp See \fIthe features documentation\fR for more details. .sp \fB\-F\fR \fIfeatures\fR, \fB\-\-features\fR \fIfeatures\fR .RS 4 Space or comma separated list of features to activate. Features of workspace members may be enabled with \fBpackage\-name/feature\-name\fR syntax. This flag may be specified multiple times, which enables all specified features. .RE .sp \fB\-\-all\-features\fR .RS 4 Activate all available features of all selected packages. .RE .sp \fB\-\-no\-default\-features\fR .RS 4 Do not activate the \fBdefault\fR feature of the selected packages. .RE .SS "Compilation Options" .sp \fB\-\-target\fR \fItriple\fR .RS 4 Run for the given architecture. The default is the host architecture. The general format of the triple is \fB\-\-\-\fR\&. Run \fBrustc \-\-print target\-list\fR for a list of supported targets. .sp This may also be specified with the \fBbuild.target\fR \fIconfig value\fR \&. .sp Note that specifying this flag makes Cargo run in a different mode where the target artifacts are placed in a separate directory. See the \fIbuild cache\fR documentation for more details. .RE .sp \fB\-r\fR, \fB\-\-release\fR .RS 4 Run optimized artifacts with the \fBrelease\fR profile. See also the \fB\-\-profile\fR option for choosing a specific profile by name. .RE .sp \fB\-\-profile\fR \fIname\fR .RS 4 Run with the given profile. See \fIthe reference\fR for more details on profiles. .RE .sp \fB\-\-timings=\fR\fIfmts\fR .RS 4 Output information how long each compilation takes, and track concurrency information over time. Accepts an optional comma\-separated list of output formats; \fB\-\-timings\fR without an argument will default to \fB\-\-timings=html\fR\&. Specifying an output format (rather than the default) is unstable and requires \fB\-Zunstable\-options\fR\&. Valid output formats: .sp .RS 4 \h'-04'\(bu\h'+03'\fBhtml\fR (unstable, requires \fB\-Zunstable\-options\fR): Write a human\-readable file \fBcargo\-timing.html\fR to the \fBtarget/cargo\-timings\fR directory with a report of the compilation. Also write a report to the same directory with a timestamp in the filename if you want to look at older runs. HTML output is suitable for human consumption only, and does not provide machine\-readable timing data. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\fR (unstable, requires \fB\-Zunstable\-options\fR): Emit machine\-readable JSON information about timing information. .RE .RE .SS "Output Options" .sp \fB\-\-target\-dir\fR \fIdirectory\fR .RS 4 Directory for all generated artifacts and intermediate files. May also be specified with the \fBCARGO_TARGET_DIR\fR environment variable, or the \fBbuild.target\-dir\fR \fIconfig value\fR \&. Defaults to \fBtarget\fR in the root of the workspace. .RE .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .sp \fB\-\-message\-format\fR \fIfmt\fR .RS 4 The output format for diagnostic messages. Can be specified multiple times and consists of comma\-separated values. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBhuman\fR (default): Display in a human\-readable text format. Conflicts with \fBshort\fR and \fBjson\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBshort\fR: Emit shorter, human\-readable text messages. Conflicts with \fBhuman\fR and \fBjson\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\fR: Emit JSON messages to stdout. See \fIthe reference\fR for more details. Conflicts with \fBhuman\fR and \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-diagnostic\-short\fR: Ensure the \fBrendered\fR field of JSON messages contains the \[lq]short\[rq] rendering from rustc. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-diagnostic\-rendered\-ansi\fR: Ensure the \fBrendered\fR field of JSON messages contains embedded ANSI color codes for respecting rustc\[cq]s default color scheme. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-render\-diagnostics\fR: Instruct Cargo to not include rustc diagnostics in JSON messages printed, but instead Cargo itself should render the JSON diagnostics coming from rustc. Cargo\[cq]s own JSON diagnostics and others coming from rustc are still emitted. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .RE .SS "Manifest Options" .sp \fB\-\-manifest\-path\fR \fIpath\fR .RS 4 Path to the \fBCargo.toml\fR file. By default, Cargo searches for the \fBCargo.toml\fR file in the current directory or any parent directory. .RE .sp \fB\-\-ignore\-rust\-version\fR .RS 4 Ignore \fBrust\-version\fR specification in packages. .RE .sp \fB\-\-locked\fR .RS 4 Asserts that the exact same dependencies and versions are used as when the existing \fBCargo.lock\fR file was originally generated. Cargo will exit with an error when either of the following scenarios arises: .sp .RS 4 \h'-04'\(bu\h'+03'The lock file is missing. .RE .sp .RS 4 \h'-04'\(bu\h'+03'Cargo attempted to change the lock file due to a different dependency resolution. .RE .sp It may be used in environments where deterministic builds are desired, such as in CI pipelines. .RE .sp \fB\-\-offline\fR .RS 4 Prevents Cargo from accessing the network for any reason. Without this flag, Cargo will stop with an error if it needs to access the network and the network is not available. With this flag, Cargo will attempt to proceed without the network if possible. .sp Beware that this may result in different dependency resolution than online mode. Cargo will restrict itself to crates that are downloaded locally, even if there might be a newer version as indicated in the local copy of the index. See the \fBcargo\-fetch\fR(1) command to download dependencies before going offline. .sp May also be specified with the \fBnet.offline\fR \fIconfig value\fR \&. .RE .sp \fB\-\-frozen\fR .RS 4 Equivalent to specifying both \fB\-\-locked\fR and \fB\-\-offline\fR\&. .RE .sp \fB\-\-lockfile\-path\fR \fIPATH\fR .RS 4 Changes the path of the lockfile from the default (\fB/Cargo.lock\fR) to \fIPATH\fR\&. \fIPATH\fR must end with \fBCargo.lock\fR (e.g. \fB\-\-lockfile\-path /tmp/temporary\-lockfile/Cargo.lock\fR). Note that providing \fB\-\-lockfile\-path\fR will ignore existing lockfile at the default path, and instead will either use the lockfile from \fIPATH\fR, or write a new lockfile into the provided \fIPATH\fR if it doesn\[cq]t exist. This flag can be used to run most commands in read\-only directories, writing lockfile into the provided \fIPATH\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#14421\fR ). .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SS "Miscellaneous Options" .sp \fB\-j\fR \fIN\fR, \fB\-\-jobs\fR \fIN\fR .RS 4 Number of parallel jobs to run. May also be specified with the \fBbuild.jobs\fR \fIconfig value\fR \&. Defaults to the number of logical CPUs. If negative, it sets the maximum number of parallel jobs to the number of logical CPUs plus provided value. If a string \fBdefault\fR is provided, it sets the value back to defaults. Should not be 0. .RE .sp \fB\-\-keep\-going\fR .RS 4 Build as many crates in the dependency graph as possible, rather than aborting the build on the first one that fails to build. .sp For example if the current package depends on dependencies \fBfails\fR and \fBworks\fR, one of which fails to build, \fBcargo run \-j1\fR may or may not build the one that succeeds (depending on which one of the two builds Cargo picked to run first), whereas \fBcargo run \-j1 \-\-keep\-going\fR would definitely run both builds, even if the one run first fails. .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Build the local package and run its main target (assuming only one binary): .sp .RS 4 .nf cargo run .fi .RE .RE .sp .RS 4 \h'-04' 2.\h'+01'Run an example with extra arguments: .sp .RS 4 .nf cargo run \-\-example exname \-\- \-\-exoption exarg1 exarg2 .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-build\fR(1) cargo-0.91.0/src/etc/man/cargo-rustc.1000064400000000000000000000435451046102023000154510ustar 00000000000000'\" t .TH "CARGO\-RUSTC" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-rustc \[em] Compile the current package, and pass extra options to the compiler .SH "SYNOPSIS" \fBcargo rustc\fR [\fIoptions\fR] [\fB\-\-\fR \fIargs\fR] .SH "DESCRIPTION" The specified target for the current package (or package specified by \fB\-p\fR if provided) will be compiled along with all of its dependencies. The specified \fIargs\fR will all be passed to the final compiler invocation, not any of the dependencies. Note that the compiler will still unconditionally receive arguments such as \fB\-L\fR, \fB\-\-extern\fR, and \fB\-\-crate\-type\fR, and the specified \fIargs\fR will simply be added to the compiler invocation. .sp See for documentation on rustc flags. .sp This command requires that only one target is being compiled when additional arguments are provided. If more than one target is available for the current package the filters of \fB\-\-lib\fR, \fB\-\-bin\fR, etc, must be used to select which target is compiled. .sp To pass flags to all compiler processes spawned by Cargo, use the \fBRUSTFLAGS\fR \fIenvironment variable\fR or the \fBbuild.rustflags\fR \fIconfig value\fR \&. .SH "OPTIONS" .SS "Package Selection" By default, the package in the current working directory is selected. The \fB\-p\fR flag can be used to choose a different package in a workspace. .sp \fB\-p\fR \fIspec\fR, \fB\-\-package\fR \fIspec\fR .RS 4 The package to build. See \fBcargo\-pkgid\fR(1) for the SPEC format. .RE .SS "Target Selection" When no target selection options are given, \fBcargo rustc\fR will build all binary and library targets of the selected package. .sp Binary targets are automatically built if there is an integration test or benchmark being selected to build. This allows an integration test to execute the binary to exercise and test its behavior. The \fBCARGO_BIN_EXE_\fR \fIenvironment variable\fR is set when the integration test is built so that it can use the \fI\f(BIenv\fI macro\fR to locate the executable. .sp Passing target selection flags will build only the specified targets. .sp Note that \fB\-\-bin\fR, \fB\-\-example\fR, \fB\-\-test\fR and \fB\-\-bench\fR flags also support common Unix glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell accidentally expanding glob patterns before Cargo handles them, you must use single quotes or double quotes around each glob pattern. .sp \fB\-\-lib\fR .RS 4 Build the package\[cq]s library. .RE .sp \fB\-\-bin\fR \fIname\fR\[u2026] .RS 4 Build the specified binary. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-bins\fR .RS 4 Build all binary targets. .RE .sp \fB\-\-example\fR \fIname\fR\[u2026] .RS 4 Build the specified example. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-examples\fR .RS 4 Build all example targets. .RE .sp \fB\-\-test\fR \fIname\fR\[u2026] .RS 4 Build the specified integration test. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-tests\fR .RS 4 Build all targets that have the \fBtest = true\fR manifest flag set. By default this includes the library and binaries built as unittests, and integration tests. Be aware that this will also build any required dependencies, so the lib target may be built twice (once as a unittest, and once as a dependency for binaries, integration tests, etc.). Targets may be enabled or disabled by setting the \fBtest\fR flag in the manifest settings for the target. .RE .sp \fB\-\-bench\fR \fIname\fR\[u2026] .RS 4 Build the specified benchmark. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-benches\fR .RS 4 Build all targets that have the \fBbench = true\fR manifest flag set. By default this includes the library and binaries built as benchmarks, and bench targets. Be aware that this will also build any required dependencies, so the lib target may be built twice (once as a benchmark, and once as a dependency for binaries, benchmarks, etc.). Targets may be enabled or disabled by setting the \fBbench\fR flag in the manifest settings for the target. .RE .sp \fB\-\-all\-targets\fR .RS 4 Build all targets. This is equivalent to specifying \fB\-\-lib \-\-bins \-\-tests \-\-benches \-\-examples\fR\&. .RE .SS "Feature Selection" The feature flags allow you to control which features are enabled. When no feature options are given, the \fBdefault\fR feature is activated for every selected package. .sp See \fIthe features documentation\fR for more details. .sp \fB\-F\fR \fIfeatures\fR, \fB\-\-features\fR \fIfeatures\fR .RS 4 Space or comma separated list of features to activate. Features of workspace members may be enabled with \fBpackage\-name/feature\-name\fR syntax. This flag may be specified multiple times, which enables all specified features. .RE .sp \fB\-\-all\-features\fR .RS 4 Activate all available features of all selected packages. .RE .sp \fB\-\-no\-default\-features\fR .RS 4 Do not activate the \fBdefault\fR feature of the selected packages. .RE .SS "Compilation Options" .sp \fB\-\-target\fR \fItriple\fR .RS 4 Build for the given architecture. The default is the host architecture. The general format of the triple is \fB\-\-\-\fR\&. Run \fBrustc \-\-print target\-list\fR for a list of supported targets. This flag may be specified multiple times. .sp This may also be specified with the \fBbuild.target\fR \fIconfig value\fR \&. .sp Note that specifying this flag makes Cargo run in a different mode where the target artifacts are placed in a separate directory. See the \fIbuild cache\fR documentation for more details. .RE .sp \fB\-r\fR, \fB\-\-release\fR .RS 4 Build optimized artifacts with the \fBrelease\fR profile. See also the \fB\-\-profile\fR option for choosing a specific profile by name. .RE .sp \fB\-\-profile\fR \fIname\fR .RS 4 Build with the given profile. .sp The \fBrustc\fR subcommand will treat the following named profiles with special behaviors: .sp .RS 4 \h'-04'\(bu\h'+03'\fBcheck\fR \[em] Builds in the same way as the \fBcargo\-check\fR(1) command with the \fBdev\fR profile. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBtest\fR \[em] Builds in the same way as the \fBcargo\-test\fR(1) command, enabling building in test mode which will enable tests and enable the \fBtest\fR cfg option. See \fIrustc tests\fR for more detail. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBbench\fR \[em] Builds in the same was as the \fBcargo\-bench\fR(1) command, similar to the \fBtest\fR profile. .RE .sp See \fIthe reference\fR for more details on profiles. .RE .sp \fB\-\-timings=\fR\fIfmts\fR .RS 4 Output information how long each compilation takes, and track concurrency information over time. Accepts an optional comma\-separated list of output formats; \fB\-\-timings\fR without an argument will default to \fB\-\-timings=html\fR\&. Specifying an output format (rather than the default) is unstable and requires \fB\-Zunstable\-options\fR\&. Valid output formats: .sp .RS 4 \h'-04'\(bu\h'+03'\fBhtml\fR (unstable, requires \fB\-Zunstable\-options\fR): Write a human\-readable file \fBcargo\-timing.html\fR to the \fBtarget/cargo\-timings\fR directory with a report of the compilation. Also write a report to the same directory with a timestamp in the filename if you want to look at older runs. HTML output is suitable for human consumption only, and does not provide machine\-readable timing data. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\fR (unstable, requires \fB\-Zunstable\-options\fR): Emit machine\-readable JSON information about timing information. .RE .RE .sp \fB\-\-crate\-type\fR \fIcrate\-type\fR .RS 4 Build for the given crate type. This flag accepts a comma\-separated list of 1 or more crate types, of which the allowed values are the same as \fBcrate\-type\fR field in the manifest for configuring a Cargo target. See \fI\f(BIcrate\-type\fI field\fR for possible values. .sp If the manifest contains a list, and \fB\-\-crate\-type\fR is provided, the command\-line argument value will override what is in the manifest. .sp This flag only works when building a \fBlib\fR or \fBexample\fR library target. .RE .SS "Output Options" .sp \fB\-\-target\-dir\fR \fIdirectory\fR .RS 4 Directory for all generated artifacts and intermediate files. May also be specified with the \fBCARGO_TARGET_DIR\fR environment variable, or the \fBbuild.target\-dir\fR \fIconfig value\fR \&. Defaults to \fBtarget\fR in the root of the workspace. .RE .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .sp \fB\-\-message\-format\fR \fIfmt\fR .RS 4 The output format for diagnostic messages. Can be specified multiple times and consists of comma\-separated values. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBhuman\fR (default): Display in a human\-readable text format. Conflicts with \fBshort\fR and \fBjson\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBshort\fR: Emit shorter, human\-readable text messages. Conflicts with \fBhuman\fR and \fBjson\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\fR: Emit JSON messages to stdout. See \fIthe reference\fR for more details. Conflicts with \fBhuman\fR and \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-diagnostic\-short\fR: Ensure the \fBrendered\fR field of JSON messages contains the \[lq]short\[rq] rendering from rustc. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-diagnostic\-rendered\-ansi\fR: Ensure the \fBrendered\fR field of JSON messages contains embedded ANSI color codes for respecting rustc\[cq]s default color scheme. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-render\-diagnostics\fR: Instruct Cargo to not include rustc diagnostics in JSON messages printed, but instead Cargo itself should render the JSON diagnostics coming from rustc. Cargo\[cq]s own JSON diagnostics and others coming from rustc are still emitted. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .RE .SS "Manifest Options" .sp \fB\-\-manifest\-path\fR \fIpath\fR .RS 4 Path to the \fBCargo.toml\fR file. By default, Cargo searches for the \fBCargo.toml\fR file in the current directory or any parent directory. .RE .sp \fB\-\-ignore\-rust\-version\fR .RS 4 Ignore \fBrust\-version\fR specification in packages. .RE .sp \fB\-\-locked\fR .RS 4 Asserts that the exact same dependencies and versions are used as when the existing \fBCargo.lock\fR file was originally generated. Cargo will exit with an error when either of the following scenarios arises: .sp .RS 4 \h'-04'\(bu\h'+03'The lock file is missing. .RE .sp .RS 4 \h'-04'\(bu\h'+03'Cargo attempted to change the lock file due to a different dependency resolution. .RE .sp It may be used in environments where deterministic builds are desired, such as in CI pipelines. .RE .sp \fB\-\-offline\fR .RS 4 Prevents Cargo from accessing the network for any reason. Without this flag, Cargo will stop with an error if it needs to access the network and the network is not available. With this flag, Cargo will attempt to proceed without the network if possible. .sp Beware that this may result in different dependency resolution than online mode. Cargo will restrict itself to crates that are downloaded locally, even if there might be a newer version as indicated in the local copy of the index. See the \fBcargo\-fetch\fR(1) command to download dependencies before going offline. .sp May also be specified with the \fBnet.offline\fR \fIconfig value\fR \&. .RE .sp \fB\-\-frozen\fR .RS 4 Equivalent to specifying both \fB\-\-locked\fR and \fB\-\-offline\fR\&. .RE .sp \fB\-\-lockfile\-path\fR \fIPATH\fR .RS 4 Changes the path of the lockfile from the default (\fB/Cargo.lock\fR) to \fIPATH\fR\&. \fIPATH\fR must end with \fBCargo.lock\fR (e.g. \fB\-\-lockfile\-path /tmp/temporary\-lockfile/Cargo.lock\fR). Note that providing \fB\-\-lockfile\-path\fR will ignore existing lockfile at the default path, and instead will either use the lockfile from \fIPATH\fR, or write a new lockfile into the provided \fIPATH\fR if it doesn\[cq]t exist. This flag can be used to run most commands in read\-only directories, writing lockfile into the provided \fIPATH\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#14421\fR ). .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SS "Miscellaneous Options" .sp \fB\-j\fR \fIN\fR, \fB\-\-jobs\fR \fIN\fR .RS 4 Number of parallel jobs to run. May also be specified with the \fBbuild.jobs\fR \fIconfig value\fR \&. Defaults to the number of logical CPUs. If negative, it sets the maximum number of parallel jobs to the number of logical CPUs plus provided value. If a string \fBdefault\fR is provided, it sets the value back to defaults. Should not be 0. .RE .sp \fB\-\-keep\-going\fR .RS 4 Build as many crates in the dependency graph as possible, rather than aborting the build on the first one that fails to build. .sp For example if the current package depends on dependencies \fBfails\fR and \fBworks\fR, one of which fails to build, \fBcargo rustc \-j1\fR may or may not build the one that succeeds (depending on which one of the two builds Cargo picked to run first), whereas \fBcargo rustc \-j1 \-\-keep\-going\fR would definitely run both builds, even if the one run first fails. .RE .sp \fB\-\-future\-incompat\-report\fR .RS 4 Displays a future\-incompat report for any future\-incompatible warnings produced during execution of this command .sp See \fBcargo\-report\fR(1) .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Check if your package (not including dependencies) uses unsafe code: .sp .RS 4 .nf cargo rustc \-\-lib \-\- \-D unsafe\-code .fi .RE .RE .sp .RS 4 \h'-04' 2.\h'+01'Try an experimental flag on the nightly compiler, such as this which prints the size of every type: .sp .RS 4 .nf cargo rustc \-\-lib \-\- \-Z print\-type\-sizes .fi .RE .RE .sp .RS 4 \h'-04' 3.\h'+01'Override \fBcrate\-type\fR field in Cargo.toml with command\-line option: .sp .RS 4 .nf cargo rustc \-\-lib \-\-crate\-type lib,cdylib .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-build\fR(1), \fBrustc\fR(1) cargo-0.91.0/src/etc/man/cargo-rustdoc.1000064400000000000000000000410661046102023000157700ustar 00000000000000'\" t .TH "CARGO\-RUSTDOC" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-rustdoc \[em] Build a package\[cq]s documentation, using specified custom flags .SH "SYNOPSIS" \fBcargo rustdoc\fR [\fIoptions\fR] [\fB\-\-\fR \fIargs\fR] .SH "DESCRIPTION" The specified target for the current package (or package specified by \fB\-p\fR if provided) will be documented with the specified \fIargs\fR being passed to the final rustdoc invocation. Dependencies will not be documented as part of this command. Note that rustdoc will still unconditionally receive arguments such as \fB\-L\fR, \fB\-\-extern\fR, and \fB\-\-crate\-type\fR, and the specified \fIargs\fR will simply be added to the rustdoc invocation. .sp See for documentation on rustdoc flags. .sp This command requires that only one target is being compiled when additional arguments are provided. If more than one target is available for the current package the filters of \fB\-\-lib\fR, \fB\-\-bin\fR, etc, must be used to select which target is compiled. .sp To pass flags to all rustdoc processes spawned by Cargo, use the \fBRUSTDOCFLAGS\fR \fIenvironment variable\fR or the \fBbuild.rustdocflags\fR \fIconfig value\fR \&. .SH "OPTIONS" .SS "Documentation Options" .sp \fB\-\-open\fR .RS 4 Open the docs in a browser after building them. This will use your default browser unless you define another one in the \fBBROWSER\fR environment variable or use the \fI\f(BIdoc.browser\fI\fR configuration option. .RE .SS "Package Selection" By default, the package in the current working directory is selected. The \fB\-p\fR flag can be used to choose a different package in a workspace. .sp \fB\-p\fR \fIspec\fR, \fB\-\-package\fR \fIspec\fR .RS 4 The package to document. See \fBcargo\-pkgid\fR(1) for the SPEC format. .RE .SS "Target Selection" When no target selection options are given, \fBcargo rustdoc\fR will document all binary and library targets of the selected package. The binary will be skipped if its name is the same as the lib target. Binaries are skipped if they have \fBrequired\-features\fR that are missing. .sp Passing target selection flags will document only the specified targets. .sp Note that \fB\-\-bin\fR, \fB\-\-example\fR, \fB\-\-test\fR and \fB\-\-bench\fR flags also support common Unix glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell accidentally expanding glob patterns before Cargo handles them, you must use single quotes or double quotes around each glob pattern. .sp \fB\-\-lib\fR .RS 4 Document the package\[cq]s library. .RE .sp \fB\-\-bin\fR \fIname\fR\[u2026] .RS 4 Document the specified binary. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-bins\fR .RS 4 Document all binary targets. .RE .sp \fB\-\-example\fR \fIname\fR\[u2026] .RS 4 Document the specified example. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-examples\fR .RS 4 Document all example targets. .RE .sp \fB\-\-test\fR \fIname\fR\[u2026] .RS 4 Document the specified integration test. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-tests\fR .RS 4 Document all targets that have the \fBtest = true\fR manifest flag set. By default this includes the library and binaries built as unittests, and integration tests. Be aware that this will also build any required dependencies, so the lib target may be built twice (once as a unittest, and once as a dependency for binaries, integration tests, etc.). Targets may be enabled or disabled by setting the \fBtest\fR flag in the manifest settings for the target. .RE .sp \fB\-\-bench\fR \fIname\fR\[u2026] .RS 4 Document the specified benchmark. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-benches\fR .RS 4 Document all targets that have the \fBbench = true\fR manifest flag set. By default this includes the library and binaries built as benchmarks, and bench targets. Be aware that this will also build any required dependencies, so the lib target may be built twice (once as a benchmark, and once as a dependency for binaries, benchmarks, etc.). Targets may be enabled or disabled by setting the \fBbench\fR flag in the manifest settings for the target. .RE .sp \fB\-\-all\-targets\fR .RS 4 Document all targets. This is equivalent to specifying \fB\-\-lib \-\-bins \-\-tests \-\-benches \-\-examples\fR\&. .RE .SS "Feature Selection" The feature flags allow you to control which features are enabled. When no feature options are given, the \fBdefault\fR feature is activated for every selected package. .sp See \fIthe features documentation\fR for more details. .sp \fB\-F\fR \fIfeatures\fR, \fB\-\-features\fR \fIfeatures\fR .RS 4 Space or comma separated list of features to activate. Features of workspace members may be enabled with \fBpackage\-name/feature\-name\fR syntax. This flag may be specified multiple times, which enables all specified features. .RE .sp \fB\-\-all\-features\fR .RS 4 Activate all available features of all selected packages. .RE .sp \fB\-\-no\-default\-features\fR .RS 4 Do not activate the \fBdefault\fR feature of the selected packages. .RE .SS "Compilation Options" .sp \fB\-\-target\fR \fItriple\fR .RS 4 Document for the given architecture. The default is the host architecture. The general format of the triple is \fB\-\-\-\fR\&. Run \fBrustc \-\-print target\-list\fR for a list of supported targets. This flag may be specified multiple times. .sp This may also be specified with the \fBbuild.target\fR \fIconfig value\fR \&. .sp Note that specifying this flag makes Cargo run in a different mode where the target artifacts are placed in a separate directory. See the \fIbuild cache\fR documentation for more details. .RE .sp \fB\-r\fR, \fB\-\-release\fR .RS 4 Document optimized artifacts with the \fBrelease\fR profile. See also the \fB\-\-profile\fR option for choosing a specific profile by name. .RE .sp \fB\-\-profile\fR \fIname\fR .RS 4 Document with the given profile. See \fIthe reference\fR for more details on profiles. .RE .sp \fB\-\-timings=\fR\fIfmts\fR .RS 4 Output information how long each compilation takes, and track concurrency information over time. Accepts an optional comma\-separated list of output formats; \fB\-\-timings\fR without an argument will default to \fB\-\-timings=html\fR\&. Specifying an output format (rather than the default) is unstable and requires \fB\-Zunstable\-options\fR\&. Valid output formats: .sp .RS 4 \h'-04'\(bu\h'+03'\fBhtml\fR (unstable, requires \fB\-Zunstable\-options\fR): Write a human\-readable file \fBcargo\-timing.html\fR to the \fBtarget/cargo\-timings\fR directory with a report of the compilation. Also write a report to the same directory with a timestamp in the filename if you want to look at older runs. HTML output is suitable for human consumption only, and does not provide machine\-readable timing data. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\fR (unstable, requires \fB\-Zunstable\-options\fR): Emit machine\-readable JSON information about timing information. .RE .RE .SS "Output Options" .sp \fB\-\-target\-dir\fR \fIdirectory\fR .RS 4 Directory for all generated artifacts and intermediate files. May also be specified with the \fBCARGO_TARGET_DIR\fR environment variable, or the \fBbuild.target\-dir\fR \fIconfig value\fR \&. Defaults to \fBtarget\fR in the root of the workspace. .RE .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .sp \fB\-\-message\-format\fR \fIfmt\fR .RS 4 The output format for diagnostic messages. Can be specified multiple times and consists of comma\-separated values. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBhuman\fR (default): Display in a human\-readable text format. Conflicts with \fBshort\fR and \fBjson\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBshort\fR: Emit shorter, human\-readable text messages. Conflicts with \fBhuman\fR and \fBjson\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\fR: Emit JSON messages to stdout. See \fIthe reference\fR for more details. Conflicts with \fBhuman\fR and \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-diagnostic\-short\fR: Ensure the \fBrendered\fR field of JSON messages contains the \[lq]short\[rq] rendering from rustc. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-diagnostic\-rendered\-ansi\fR: Ensure the \fBrendered\fR field of JSON messages contains embedded ANSI color codes for respecting rustc\[cq]s default color scheme. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-render\-diagnostics\fR: Instruct Cargo to not include rustc diagnostics in JSON messages printed, but instead Cargo itself should render the JSON diagnostics coming from rustc. Cargo\[cq]s own JSON diagnostics and others coming from rustc are still emitted. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .RE .SS "Manifest Options" .sp \fB\-\-manifest\-path\fR \fIpath\fR .RS 4 Path to the \fBCargo.toml\fR file. By default, Cargo searches for the \fBCargo.toml\fR file in the current directory or any parent directory. .RE .sp \fB\-\-ignore\-rust\-version\fR .RS 4 Ignore \fBrust\-version\fR specification in packages. .RE .sp \fB\-\-locked\fR .RS 4 Asserts that the exact same dependencies and versions are used as when the existing \fBCargo.lock\fR file was originally generated. Cargo will exit with an error when either of the following scenarios arises: .sp .RS 4 \h'-04'\(bu\h'+03'The lock file is missing. .RE .sp .RS 4 \h'-04'\(bu\h'+03'Cargo attempted to change the lock file due to a different dependency resolution. .RE .sp It may be used in environments where deterministic builds are desired, such as in CI pipelines. .RE .sp \fB\-\-offline\fR .RS 4 Prevents Cargo from accessing the network for any reason. Without this flag, Cargo will stop with an error if it needs to access the network and the network is not available. With this flag, Cargo will attempt to proceed without the network if possible. .sp Beware that this may result in different dependency resolution than online mode. Cargo will restrict itself to crates that are downloaded locally, even if there might be a newer version as indicated in the local copy of the index. See the \fBcargo\-fetch\fR(1) command to download dependencies before going offline. .sp May also be specified with the \fBnet.offline\fR \fIconfig value\fR \&. .RE .sp \fB\-\-frozen\fR .RS 4 Equivalent to specifying both \fB\-\-locked\fR and \fB\-\-offline\fR\&. .RE .sp \fB\-\-lockfile\-path\fR \fIPATH\fR .RS 4 Changes the path of the lockfile from the default (\fB/Cargo.lock\fR) to \fIPATH\fR\&. \fIPATH\fR must end with \fBCargo.lock\fR (e.g. \fB\-\-lockfile\-path /tmp/temporary\-lockfile/Cargo.lock\fR). Note that providing \fB\-\-lockfile\-path\fR will ignore existing lockfile at the default path, and instead will either use the lockfile from \fIPATH\fR, or write a new lockfile into the provided \fIPATH\fR if it doesn\[cq]t exist. This flag can be used to run most commands in read\-only directories, writing lockfile into the provided \fIPATH\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#14421\fR ). .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SS "Miscellaneous Options" .sp \fB\-j\fR \fIN\fR, \fB\-\-jobs\fR \fIN\fR .RS 4 Number of parallel jobs to run. May also be specified with the \fBbuild.jobs\fR \fIconfig value\fR \&. Defaults to the number of logical CPUs. If negative, it sets the maximum number of parallel jobs to the number of logical CPUs plus provided value. If a string \fBdefault\fR is provided, it sets the value back to defaults. Should not be 0. .RE .sp \fB\-\-keep\-going\fR .RS 4 Build as many crates in the dependency graph as possible, rather than aborting the build on the first one that fails to build. .sp For example if the current package depends on dependencies \fBfails\fR and \fBworks\fR, one of which fails to build, \fBcargo rustdoc \-j1\fR may or may not build the one that succeeds (depending on which one of the two builds Cargo picked to run first), whereas \fBcargo rustdoc \-j1 \-\-keep\-going\fR would definitely run both builds, even if the one run first fails. .RE .sp \fB\-\-output\-format\fR .RS 4 The output type for the documentation emitted. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBhtml\fR (default): Emit the documentation in HTML format. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\fR: Emit the documentation in the \fIexperimental JSON format\fR \&. .RE .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable. .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Build documentation with custom CSS included from a given file: .sp .RS 4 .nf cargo rustdoc \-\-lib \-\- \-\-extend\-css extra.css .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-doc\fR(1), \fBrustdoc\fR(1) cargo-0.91.0/src/etc/man/cargo-search.1000064400000000000000000000101501046102023000155400ustar 00000000000000'\" t .TH "CARGO\-SEARCH" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-search \[em] Search packages in the registry. Default registry is crates.io .SH "SYNOPSIS" \fBcargo search\fR [\fIoptions\fR] [\fIquery\fR\[u2026]] .SH "DESCRIPTION" This performs a textual search for crates on \&. The matching crates will be displayed along with their description in TOML format suitable for copying into a \fBCargo.toml\fR manifest. .SH "OPTIONS" .SS "Search Options" .sp \fB\-\-limit\fR \fIlimit\fR .RS 4 Limit the number of results (default: 10, max: 100). .RE .sp \fB\-\-index\fR \fIindex\fR .RS 4 The URL of the registry index to use. .RE .sp \fB\-\-registry\fR \fIregistry\fR .RS 4 Name of the registry to use. Registry names are defined in \fICargo config files\fR \&. If not specified, the default registry is used, which is defined by the \fBregistry.default\fR config key which defaults to \fBcrates\-io\fR\&. .RE .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Search for a package from crates.io: .sp .RS 4 .nf cargo search serde .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-install\fR(1), \fBcargo\-publish\fR(1) cargo-0.91.0/src/etc/man/cargo-test.1000064400000000000000000000542771046102023000152740ustar 00000000000000'\" t .TH "CARGO\-TEST" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-test \[em] Execute unit and integration tests of a package .SH "SYNOPSIS" \fBcargo test\fR [\fIoptions\fR] [\fItestname\fR] [\fB\-\-\fR \fItest\-options\fR] .SH "DESCRIPTION" Compile and execute unit, integration, and documentation tests. .sp The test filtering argument \fBTESTNAME\fR and all the arguments following the two dashes (\fB\-\-\fR) are passed to the test binaries and thus to \fIlibtest\fR (rustc\[cq]s built in unit\-test and micro\-benchmarking framework). If you\[cq]re passing arguments to both Cargo and the binary, the ones after \fB\-\-\fR go to the binary, the ones before go to Cargo. For details about libtest\[cq]s arguments see the output of \fBcargo test \-\- \-\-help\fR and check out the rustc book\[cq]s chapter on how tests work at \&. .sp As an example, this will filter for tests with \fBfoo\fR in their name and run them on 3 threads in parallel: .sp .RS 4 .nf cargo test foo \-\- \-\-test\-threads 3 .fi .RE .sp Tests are built with the \fB\-\-test\fR option to \fBrustc\fR which creates a special executable by linking your code with libtest. The executable automatically runs all functions annotated with the \fB#[test]\fR attribute in multiple threads. \fB#[bench]\fR annotated functions will also be run with one iteration to verify that they are functional. .sp If the package contains multiple test targets, each target compiles to a special executable as aforementioned, and then is run serially. .sp The libtest harness may be disabled by setting \fBharness = false\fR in the target manifest settings, in which case your code will need to provide its own \fBmain\fR function to handle running tests. .SS "Documentation tests" Documentation tests are also run by default, which is handled by \fBrustdoc\fR\&. It extracts code samples from documentation comments of the library target, and then executes them. .sp Different from normal test targets, each code block compiles to a doctest executable on the fly with \fBrustc\fR\&. These executables run in parallel in separate processes. The compilation of a code block is in fact a part of test function controlled by libtest, so some options such as \fB\-\-jobs\fR might not take effect. Note that this execution model of doctests is not guaranteed and may change in the future; beware of depending on it. .sp See the \fIrustdoc book\fR for more information on writing doc tests. .SS "Working directory of tests" The working directory when running each unit and integration test is set to the root directory of the package the test belongs to. Setting the working directory of tests to the package\[cq]s root directory makes it possible for tests to reliably access the package\[cq]s files using relative paths, regardless from where \fBcargo test\fR was executed from. .sp For documentation tests, the working directory when invoking \fBrustdoc\fR is set to the workspace root directory, and is also the directory \fBrustdoc\fR uses as the compilation directory of each documentation test. The working directory when running each documentation test is set to the root directory of the package the test belongs to, and is controlled via \fBrustdoc\fR\[cq]s \fB\-\-test\-run\-directory\fR option. .SH "OPTIONS" .SS "Test Options" .sp \fB\-\-no\-run\fR .RS 4 Compile, but don\[cq]t run tests. .RE .sp \fB\-\-no\-fail\-fast\fR .RS 4 Run all tests regardless of failure. Without this flag, Cargo will exit after the first executable fails. The Rust test harness will run all tests within the executable to completion, this flag only applies to the executable as a whole. .RE .SS "Package Selection" By default, when no package selection options are given, the packages selected depend on the selected manifest file (based on the current working directory if \fB\-\-manifest\-path\fR is not given). If the manifest is the root of a workspace then the workspaces default members are selected, otherwise only the package defined by the manifest will be selected. .sp The default members of a workspace can be set explicitly with the \fBworkspace.default\-members\fR key in the root manifest. If this is not set, a virtual workspace will include all workspace members (equivalent to passing \fB\-\-workspace\fR), and a non\-virtual workspace will include only the root crate itself. .sp \fB\-p\fR \fIspec\fR\[u2026], \fB\-\-package\fR \fIspec\fR\[u2026] .RS 4 Test only the specified packages. See \fBcargo\-pkgid\fR(1) for the SPEC format. This flag may be specified multiple times and supports common Unix glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell accidentally expanding glob patterns before Cargo handles them, you must use single quotes or double quotes around each pattern. .RE .sp \fB\-\-workspace\fR .RS 4 Test all members in the workspace. .RE .sp \fB\-\-all\fR .RS 4 Deprecated alias for \fB\-\-workspace\fR\&. .RE .sp \fB\-\-exclude\fR \fISPEC\fR\[u2026] .RS 4 Exclude the specified packages. Must be used in conjunction with the \fB\-\-workspace\fR flag. This flag may be specified multiple times and supports common Unix glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell accidentally expanding glob patterns before Cargo handles them, you must use single quotes or double quotes around each pattern. .RE .SS "Target Selection" When no target selection options are given, \fBcargo test\fR will build the following targets of the selected packages: .sp .RS 4 \h'-04'\(bu\h'+03'lib \[em] used to link with binaries, examples, integration tests, and doc tests .RE .sp .RS 4 \h'-04'\(bu\h'+03'bins (only if integration tests are built and required features are available) .RE .sp .RS 4 \h'-04'\(bu\h'+03'examples \[em] to ensure they compile .RE .sp .RS 4 \h'-04'\(bu\h'+03'lib as a unit test .RE .sp .RS 4 \h'-04'\(bu\h'+03'bins as unit tests .RE .sp .RS 4 \h'-04'\(bu\h'+03'integration tests .RE .sp .RS 4 \h'-04'\(bu\h'+03'doc tests for the lib target .RE .sp The default behavior can be changed by setting the \fBtest\fR flag for the target in the manifest settings. Setting examples to \fBtest = true\fR will build and run the example as a test, replacing the example\[cq]s \fBmain\fR function with the libtest harness. If you don\[cq]t want the \fBmain\fR function replaced, also include \fBharness = false\fR, in which case the example will be built and executed as\-is. .sp Setting targets to \fBtest = false\fR will stop them from being tested by default. Target selection options that take a target by name (such as \fB\-\-example foo\fR) ignore the \fBtest\fR flag and will always test the given target. .sp Doc tests for libraries may be disabled by setting \fBdoctest = false\fR for the library in the manifest. .sp See \fIConfiguring a target\fR for more information on per\-target settings. .sp Binary targets are automatically built if there is an integration test or benchmark being selected to test. This allows an integration test to execute the binary to exercise and test its behavior. The \fBCARGO_BIN_EXE_\fR \fIenvironment variable\fR is set when the integration test is built so that it can use the \fI\f(BIenv\fI macro\fR to locate the executable. .sp Passing target selection flags will test only the specified targets. .sp Note that \fB\-\-bin\fR, \fB\-\-example\fR, \fB\-\-test\fR and \fB\-\-bench\fR flags also support common Unix glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell accidentally expanding glob patterns before Cargo handles them, you must use single quotes or double quotes around each glob pattern. .sp \fB\-\-lib\fR .RS 4 Test the package\[cq]s library. .RE .sp \fB\-\-bin\fR \fIname\fR\[u2026] .RS 4 Test the specified binary. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-bins\fR .RS 4 Test all binary targets. .RE .sp \fB\-\-example\fR \fIname\fR\[u2026] .RS 4 Test the specified example. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-examples\fR .RS 4 Test all example targets. .RE .sp \fB\-\-test\fR \fIname\fR\[u2026] .RS 4 Test the specified integration test. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-tests\fR .RS 4 Test all targets that have the \fBtest = true\fR manifest flag set. By default this includes the library and binaries built as unittests, and integration tests. Be aware that this will also build any required dependencies, so the lib target may be built twice (once as a unittest, and once as a dependency for binaries, integration tests, etc.). Targets may be enabled or disabled by setting the \fBtest\fR flag in the manifest settings for the target. .RE .sp \fB\-\-bench\fR \fIname\fR\[u2026] .RS 4 Test the specified benchmark. This flag may be specified multiple times and supports common Unix glob patterns. .RE .sp \fB\-\-benches\fR .RS 4 Test all targets that have the \fBbench = true\fR manifest flag set. By default this includes the library and binaries built as benchmarks, and bench targets. Be aware that this will also build any required dependencies, so the lib target may be built twice (once as a benchmark, and once as a dependency for binaries, benchmarks, etc.). Targets may be enabled or disabled by setting the \fBbench\fR flag in the manifest settings for the target. .RE .sp \fB\-\-all\-targets\fR .RS 4 Test all targets. This is equivalent to specifying \fB\-\-lib \-\-bins \-\-tests \-\-benches \-\-examples\fR\&. .RE .sp \fB\-\-doc\fR .RS 4 Test only the library\[cq]s documentation. This cannot be mixed with other target options. .RE .SS "Feature Selection" The feature flags allow you to control which features are enabled. When no feature options are given, the \fBdefault\fR feature is activated for every selected package. .sp See \fIthe features documentation\fR for more details. .sp \fB\-F\fR \fIfeatures\fR, \fB\-\-features\fR \fIfeatures\fR .RS 4 Space or comma separated list of features to activate. Features of workspace members may be enabled with \fBpackage\-name/feature\-name\fR syntax. This flag may be specified multiple times, which enables all specified features. .RE .sp \fB\-\-all\-features\fR .RS 4 Activate all available features of all selected packages. .RE .sp \fB\-\-no\-default\-features\fR .RS 4 Do not activate the \fBdefault\fR feature of the selected packages. .RE .SS "Compilation Options" .sp \fB\-\-target\fR \fItriple\fR .RS 4 Test for the given architecture. The default is the host architecture. The general format of the triple is \fB\-\-\-\fR\&. Run \fBrustc \-\-print target\-list\fR for a list of supported targets. This flag may be specified multiple times. .sp This may also be specified with the \fBbuild.target\fR \fIconfig value\fR \&. .sp Note that specifying this flag makes Cargo run in a different mode where the target artifacts are placed in a separate directory. See the \fIbuild cache\fR documentation for more details. .RE .sp \fB\-r\fR, \fB\-\-release\fR .RS 4 Test optimized artifacts with the \fBrelease\fR profile. See also the \fB\-\-profile\fR option for choosing a specific profile by name. .RE .sp \fB\-\-profile\fR \fIname\fR .RS 4 Test with the given profile. See \fIthe reference\fR for more details on profiles. .RE .sp \fB\-\-timings=\fR\fIfmts\fR .RS 4 Output information how long each compilation takes, and track concurrency information over time. Accepts an optional comma\-separated list of output formats; \fB\-\-timings\fR without an argument will default to \fB\-\-timings=html\fR\&. Specifying an output format (rather than the default) is unstable and requires \fB\-Zunstable\-options\fR\&. Valid output formats: .sp .RS 4 \h'-04'\(bu\h'+03'\fBhtml\fR (unstable, requires \fB\-Zunstable\-options\fR): Write a human\-readable file \fBcargo\-timing.html\fR to the \fBtarget/cargo\-timings\fR directory with a report of the compilation. Also write a report to the same directory with a timestamp in the filename if you want to look at older runs. HTML output is suitable for human consumption only, and does not provide machine\-readable timing data. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\fR (unstable, requires \fB\-Zunstable\-options\fR): Emit machine\-readable JSON information about timing information. .RE .RE .SS "Output Options" .sp \fB\-\-target\-dir\fR \fIdirectory\fR .RS 4 Directory for all generated artifacts and intermediate files. May also be specified with the \fBCARGO_TARGET_DIR\fR environment variable, or the \fBbuild.target\-dir\fR \fIconfig value\fR \&. Defaults to \fBtarget\fR in the root of the workspace. .RE .SS "Display Options" By default the Rust test harness hides output from test execution to keep results readable. Test output can be recovered (e.g., for debugging) by passing \fB\-\-nocapture\fR to the test binaries: .sp .RS 4 .nf cargo test \-\- \-\-nocapture .fi .RE .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .sp \fB\-\-message\-format\fR \fIfmt\fR .RS 4 The output format for diagnostic messages. Can be specified multiple times and consists of comma\-separated values. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBhuman\fR (default): Display in a human\-readable text format. Conflicts with \fBshort\fR and \fBjson\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBshort\fR: Emit shorter, human\-readable text messages. Conflicts with \fBhuman\fR and \fBjson\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\fR: Emit JSON messages to stdout. See \fIthe reference\fR for more details. Conflicts with \fBhuman\fR and \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-diagnostic\-short\fR: Ensure the \fBrendered\fR field of JSON messages contains the \[lq]short\[rq] rendering from rustc. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-diagnostic\-rendered\-ansi\fR: Ensure the \fBrendered\fR field of JSON messages contains embedded ANSI color codes for respecting rustc\[cq]s default color scheme. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBjson\-render\-diagnostics\fR: Instruct Cargo to not include rustc diagnostics in JSON messages printed, but instead Cargo itself should render the JSON diagnostics coming from rustc. Cargo\[cq]s own JSON diagnostics and others coming from rustc are still emitted. Cannot be used with \fBhuman\fR or \fBshort\fR\&. .RE .RE .SS "Manifest Options" .sp \fB\-\-manifest\-path\fR \fIpath\fR .RS 4 Path to the \fBCargo.toml\fR file. By default, Cargo searches for the \fBCargo.toml\fR file in the current directory or any parent directory. .RE .sp \fB\-\-ignore\-rust\-version\fR .RS 4 Ignore \fBrust\-version\fR specification in packages. .RE .sp \fB\-\-locked\fR .RS 4 Asserts that the exact same dependencies and versions are used as when the existing \fBCargo.lock\fR file was originally generated. Cargo will exit with an error when either of the following scenarios arises: .sp .RS 4 \h'-04'\(bu\h'+03'The lock file is missing. .RE .sp .RS 4 \h'-04'\(bu\h'+03'Cargo attempted to change the lock file due to a different dependency resolution. .RE .sp It may be used in environments where deterministic builds are desired, such as in CI pipelines. .RE .sp \fB\-\-offline\fR .RS 4 Prevents Cargo from accessing the network for any reason. Without this flag, Cargo will stop with an error if it needs to access the network and the network is not available. With this flag, Cargo will attempt to proceed without the network if possible. .sp Beware that this may result in different dependency resolution than online mode. Cargo will restrict itself to crates that are downloaded locally, even if there might be a newer version as indicated in the local copy of the index. See the \fBcargo\-fetch\fR(1) command to download dependencies before going offline. .sp May also be specified with the \fBnet.offline\fR \fIconfig value\fR \&. .RE .sp \fB\-\-frozen\fR .RS 4 Equivalent to specifying both \fB\-\-locked\fR and \fB\-\-offline\fR\&. .RE .sp \fB\-\-lockfile\-path\fR \fIPATH\fR .RS 4 Changes the path of the lockfile from the default (\fB/Cargo.lock\fR) to \fIPATH\fR\&. \fIPATH\fR must end with \fBCargo.lock\fR (e.g. \fB\-\-lockfile\-path /tmp/temporary\-lockfile/Cargo.lock\fR). Note that providing \fB\-\-lockfile\-path\fR will ignore existing lockfile at the default path, and instead will either use the lockfile from \fIPATH\fR, or write a new lockfile into the provided \fIPATH\fR if it doesn\[cq]t exist. This flag can be used to run most commands in read\-only directories, writing lockfile into the provided \fIPATH\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#14421\fR ). .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SS "Miscellaneous Options" The \fB\-\-jobs\fR argument affects the building of the test executable but does not affect how many threads are used when running the tests. The Rust test harness includes an option to control the number of threads used: .sp .RS 4 .nf cargo test \-j 2 \-\- \-\-test\-threads=2 .fi .RE .sp \fB\-j\fR \fIN\fR, \fB\-\-jobs\fR \fIN\fR .RS 4 Number of parallel jobs to run. May also be specified with the \fBbuild.jobs\fR \fIconfig value\fR \&. Defaults to the number of logical CPUs. If negative, it sets the maximum number of parallel jobs to the number of logical CPUs plus provided value. If a string \fBdefault\fR is provided, it sets the value back to defaults. Should not be 0. .RE .sp \fB\-\-future\-incompat\-report\fR .RS 4 Displays a future\-incompat report for any future\-incompatible warnings produced during execution of this command .sp See \fBcargo\-report\fR(1) .RE .sp While \fBcargo test\fR involves compilation, it does not provide a \fB\-\-keep\-going\fR flag. Use \fB\-\-no\-fail\-fast\fR to run as many tests as possible without stopping at the first failure. To \[lq]compile\[rq] as many tests as possible, use \fB\-\-tests\fR to build test binaries separately. For example: .sp .RS 4 .nf cargo build \-\-tests \-\-keep\-going cargo test \-\-tests \-\-no\-fail\-fast .fi .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Execute all the unit and integration tests of the current package: .sp .RS 4 .nf cargo test .fi .RE .RE .sp .RS 4 \h'-04' 2.\h'+01'Run only tests whose names match against a filter string: .sp .RS 4 .nf cargo test name_filter .fi .RE .RE .sp .RS 4 \h'-04' 3.\h'+01'Run only a specific test within a specific integration test: .sp .RS 4 .nf cargo test \-\-test int_test_name \-\- modname::test_name .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-bench\fR(1), \fItypes of tests\fR , \fIhow to write tests\fR cargo-0.91.0/src/etc/man/cargo-tree.1000064400000000000000000000427001046102023000152400ustar 00000000000000'\" t .TH "CARGO\-TREE" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-tree \[em] Display a tree visualization of a dependency graph .SH "SYNOPSIS" \fBcargo tree\fR [\fIoptions\fR] .SH "DESCRIPTION" This command will display a tree of dependencies to the terminal. An example of a simple project that depends on the \[lq]rand\[rq] package: .sp .RS 4 .nf myproject v0.1.0 (/myproject) `\-\- rand v0.7.3 |\-\- getrandom v0.1.14 | |\-\- cfg\-if v0.1.10 | `\-\- libc v0.2.68 |\-\- libc v0.2.68 (*) |\-\- rand_chacha v0.2.2 | |\-\- ppv\-lite86 v0.2.6 | `\-\- rand_core v0.5.1 | `\-\- getrandom v0.1.14 (*) `\-\- rand_core v0.5.1 (*) [build\-dependencies] `\-\- cc v1.0.50 .fi .RE .sp Packages marked with \fB(*)\fR have been \[lq]de\-duplicated\[rq]\&. The dependencies for the package have already been shown elsewhere in the graph, and so are not repeated. Use the \fB\-\-no\-dedupe\fR option to repeat the duplicates. .sp The \fB\-e\fR flag can be used to select the dependency kinds to display. The \[lq]features\[rq] kind changes the output to display the features enabled by each dependency. For example, \fBcargo tree \-e features\fR: .sp .RS 4 .nf myproject v0.1.0 (/myproject) `\-\- log feature "serde" `\-\- log v0.4.8 |\-\- serde v1.0.106 `\-\- cfg\-if feature "default" `\-\- cfg\-if v0.1.10 .fi .RE .sp In this tree, \fBmyproject\fR depends on \fBlog\fR with the \fBserde\fR feature. \fBlog\fR in turn depends on \fBcfg\-if\fR with \[lq]default\[rq] features. When using \fB\-e features\fR it can be helpful to use \fB\-i\fR flag to show how the features flow into a package. See the examples below for more detail. .SS "Feature Unification" This command shows a graph much closer to a feature\-unified graph Cargo will build, rather than what you list in \fBCargo.toml\fR\&. For instance, if you specify the same dependency in both \fB[dependencies]\fR and \fB[dev\-dependencies]\fR but with different features on. This command may merge all features and show a \fB(*)\fR on one of the dependency to indicate the duplicate. .sp As a result, for a mostly equivalent overview of what \fBcargo build\fR does, \fBcargo tree \-e normal,build\fR is pretty close; for a mostly equivalent overview of what \fBcargo test\fR does, \fBcargo tree\fR is pretty close. However, it doesn\[cq]t guarantee the exact equivalence to what Cargo is going to build, since a compilation is complex and depends on lots of different factors. .sp To learn more about feature unification, check out this \fIdedicated section\fR \&. .SH "OPTIONS" .SS "Tree Options" .sp \fB\-i\fR \fIspec\fR, \fB\-\-invert\fR \fIspec\fR .RS 4 Show the reverse dependencies for the given package. This flag will invert the tree and display the packages that depend on the given package. .sp Note that in a workspace, by default it will only display the package\[cq]s reverse dependencies inside the tree of the workspace member in the current directory. The \fB\-\-workspace\fR flag can be used to extend it so that it will show the package\[cq]s reverse dependencies across the entire workspace. The \fB\-p\fR flag can be used to display the package\[cq]s reverse dependencies only with the subtree of the package given to \fB\-p\fR\&. .RE .sp \fB\-\-prune\fR \fIspec\fR .RS 4 Prune the given package from the display of the dependency tree. .RE .sp \fB\-\-depth\fR \fIdepth\fR .RS 4 Maximum display depth of the dependency tree. A depth of 1 displays the direct dependencies, for example. .sp If the given value is \fBworkspace\fR, only shows the dependencies that are member of the current workspace, instead. .RE .sp \fB\-\-no\-dedupe\fR .RS 4 Do not de\-duplicate repeated dependencies. Usually, when a package has already displayed its dependencies, further occurrences will not re\-display its dependencies, and will include a \fB(*)\fR to indicate it has already been shown. This flag will cause those duplicates to be repeated. .RE .sp \fB\-d\fR, \fB\-\-duplicates\fR .RS 4 Show only dependencies which come in multiple versions (implies \fB\-\-invert\fR). When used with the \fB\-p\fR flag, only shows duplicates within the subtree of the given package. .sp It can be beneficial for build times and executable sizes to avoid building that same package multiple times. This flag can help identify the offending packages. You can then investigate if the package that depends on the duplicate with the older version can be updated to the newer version so that only one instance is built. .RE .sp \fB\-e\fR \fIkinds\fR, \fB\-\-edges\fR \fIkinds\fR .RS 4 The dependency kinds to display. Takes a comma separated list of values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBall\fR \[em] Show all edge kinds. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnormal\fR \[em] Show normal dependencies. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBbuild\fR \[em] Show build dependencies. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBdev\fR \[em] Show development dependencies. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBfeatures\fR \[em] Show features enabled by each dependency. If this is the only kind given, then it will automatically include the other dependency kinds. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBno\-normal\fR \[em] Do not include normal dependencies. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBno\-build\fR \[em] Do not include build dependencies. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBno\-dev\fR \[em] Do not include development dependencies. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBno\-proc\-macro\fR \[em] Do not include procedural macro dependencies. .RE .sp The \fBnormal\fR, \fBbuild\fR, \fBdev\fR, and \fBall\fR dependency kinds cannot be mixed with \fBno\-normal\fR, \fBno\-build\fR, or \fBno\-dev\fR dependency kinds. .sp The default is \fBnormal,build,dev\fR\&. .RE .sp \fB\-\-target\fR \fItriple\fR .RS 4 Filter dependencies matching the given \fItarget triple\fR \&. The default is the host platform. Use the value \fBall\fR to include \fIall\fR targets. .RE .SS "Tree Formatting Options" .sp \fB\-\-charset\fR \fIcharset\fR .RS 4 Chooses the character set to use for the tree. Valid values are \[lq]utf8\[rq] or \[lq]ascii\[rq]\&. When unspecified, cargo will auto\-select a value. .RE .sp \fB\-f\fR \fIformat\fR, \fB\-\-format\fR \fIformat\fR .RS 4 Set the format string for each package. The default is \[lq]{p}\[rq]\&. .sp This is an arbitrary string which will be used to display each package. The following strings will be replaced with the corresponding value: .sp .RS 4 \h'-04'\(bu\h'+03'\fB{p}\fR \[em] The package name. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB{l}\fR \[em] The package license. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB{r}\fR \[em] The package repository URL. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB{f}\fR \[em] Comma\-separated list of package features that are enabled. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB{lib}\fR \[em] The name, as used in a \fBuse\fR statement, of the package\[cq]s library. .RE .RE .sp \fB\-\-prefix\fR \fIprefix\fR .RS 4 Sets how each line is displayed. The \fIprefix\fR value can be one of: .sp .RS 4 \h'-04'\(bu\h'+03'\fBindent\fR (default) \[em] Shows each line indented as a tree. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBdepth\fR \[em] Show as a list, with the numeric depth printed before each entry. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnone\fR \[em] Show as a flat list. .RE .RE .SS "Package Selection" By default, when no package selection options are given, the packages selected depend on the selected manifest file (based on the current working directory if \fB\-\-manifest\-path\fR is not given). If the manifest is the root of a workspace then the workspaces default members are selected, otherwise only the package defined by the manifest will be selected. .sp The default members of a workspace can be set explicitly with the \fBworkspace.default\-members\fR key in the root manifest. If this is not set, a virtual workspace will include all workspace members (equivalent to passing \fB\-\-workspace\fR), and a non\-virtual workspace will include only the root crate itself. .sp \fB\-p\fR \fIspec\fR\[u2026], \fB\-\-package\fR \fIspec\fR\[u2026] .RS 4 Display only the specified packages. See \fBcargo\-pkgid\fR(1) for the SPEC format. This flag may be specified multiple times and supports common Unix glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell accidentally expanding glob patterns before Cargo handles them, you must use single quotes or double quotes around each pattern. .RE .sp \fB\-\-workspace\fR .RS 4 Display all members in the workspace. .RE .sp \fB\-\-exclude\fR \fISPEC\fR\[u2026] .RS 4 Exclude the specified packages. Must be used in conjunction with the \fB\-\-workspace\fR flag. This flag may be specified multiple times and supports common Unix glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell accidentally expanding glob patterns before Cargo handles them, you must use single quotes or double quotes around each pattern. .RE .SS "Manifest Options" .sp \fB\-\-manifest\-path\fR \fIpath\fR .RS 4 Path to the \fBCargo.toml\fR file. By default, Cargo searches for the \fBCargo.toml\fR file in the current directory or any parent directory. .RE .sp \fB\-\-locked\fR .RS 4 Asserts that the exact same dependencies and versions are used as when the existing \fBCargo.lock\fR file was originally generated. Cargo will exit with an error when either of the following scenarios arises: .sp .RS 4 \h'-04'\(bu\h'+03'The lock file is missing. .RE .sp .RS 4 \h'-04'\(bu\h'+03'Cargo attempted to change the lock file due to a different dependency resolution. .RE .sp It may be used in environments where deterministic builds are desired, such as in CI pipelines. .RE .sp \fB\-\-offline\fR .RS 4 Prevents Cargo from accessing the network for any reason. Without this flag, Cargo will stop with an error if it needs to access the network and the network is not available. With this flag, Cargo will attempt to proceed without the network if possible. .sp Beware that this may result in different dependency resolution than online mode. Cargo will restrict itself to crates that are downloaded locally, even if there might be a newer version as indicated in the local copy of the index. See the \fBcargo\-fetch\fR(1) command to download dependencies before going offline. .sp May also be specified with the \fBnet.offline\fR \fIconfig value\fR \&. .RE .sp \fB\-\-frozen\fR .RS 4 Equivalent to specifying both \fB\-\-locked\fR and \fB\-\-offline\fR\&. .RE .sp \fB\-\-lockfile\-path\fR \fIPATH\fR .RS 4 Changes the path of the lockfile from the default (\fB/Cargo.lock\fR) to \fIPATH\fR\&. \fIPATH\fR must end with \fBCargo.lock\fR (e.g. \fB\-\-lockfile\-path /tmp/temporary\-lockfile/Cargo.lock\fR). Note that providing \fB\-\-lockfile\-path\fR will ignore existing lockfile at the default path, and instead will either use the lockfile from \fIPATH\fR, or write a new lockfile into the provided \fIPATH\fR if it doesn\[cq]t exist. This flag can be used to run most commands in read\-only directories, writing lockfile into the provided \fIPATH\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#14421\fR ). .RE .SS "Feature Selection" The feature flags allow you to control which features are enabled. When no feature options are given, the \fBdefault\fR feature is activated for every selected package. .sp See \fIthe features documentation\fR for more details. .sp \fB\-F\fR \fIfeatures\fR, \fB\-\-features\fR \fIfeatures\fR .RS 4 Space or comma separated list of features to activate. Features of workspace members may be enabled with \fBpackage\-name/feature\-name\fR syntax. This flag may be specified multiple times, which enables all specified features. .RE .sp \fB\-\-all\-features\fR .RS 4 Activate all available features of all selected packages. .RE .sp \fB\-\-no\-default\-features\fR .RS 4 Do not activate the \fBdefault\fR feature of the selected packages. .RE .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Display the tree for the package in the current directory: .sp .RS 4 .nf cargo tree .fi .RE .RE .sp .RS 4 \h'-04' 2.\h'+01'Display all the packages that depend on the \fBsyn\fR package: .sp .RS 4 .nf cargo tree \-i syn .fi .RE .RE .sp .RS 4 \h'-04' 3.\h'+01'Show the features enabled on each package: .sp .RS 4 .nf cargo tree \-\-format "{p} {f}" .fi .RE .RE .sp .RS 4 \h'-04' 4.\h'+01'Show all packages that are built multiple times. This can happen if multiple semver\-incompatible versions appear in the tree (like 1.0.0 and 2.0.0). .sp .RS 4 .nf cargo tree \-d .fi .RE .RE .sp .RS 4 \h'-04' 5.\h'+01'Explain why features are enabled for the \fBsyn\fR package: .sp .RS 4 .nf cargo tree \-e features \-i syn .fi .RE .sp The \fB\-e features\fR flag is used to show features. The \fB\-i\fR flag is used to invert the graph so that it displays the packages that depend on \fBsyn\fR\&. An example of what this would display: .sp .RS 4 .nf syn v1.0.17 |\-\- syn feature "clone\-impls" | `\-\- syn feature "default" | `\-\- rustversion v1.0.2 | `\-\- rustversion feature "default" | `\-\- myproject v0.1.0 (/myproject) | `\-\- myproject feature "default" (command\-line) |\-\- syn feature "default" (*) |\-\- syn feature "derive" | `\-\- syn feature "default" (*) |\-\- syn feature "full" | `\-\- rustversion v1.0.2 (*) |\-\- syn feature "parsing" | `\-\- syn feature "default" (*) |\-\- syn feature "printing" | `\-\- syn feature "default" (*) |\-\- syn feature "proc\-macro" | `\-\- syn feature "default" (*) `\-\- syn feature "quote" |\-\- syn feature "printing" (*) `\-\- syn feature "proc\-macro" (*) .fi .RE .sp To read this graph, you can follow the chain for each feature from the root to see why it is included. For example, the \[lq]full\[rq] feature is added by the \fBrustversion\fR crate which is included from \fBmyproject\fR (with the default features), and \fBmyproject\fR is the package selected on the command\-line. All of the other \fBsyn\fR features are added by the \[lq]default\[rq] feature (\[lq]quote\[rq] is added by \[lq]printing\[rq] and \[lq]proc\-macro\[rq], both of which are default features). .sp If you\[cq]re having difficulty cross\-referencing the de\-duplicated \fB(*)\fR entries, try with the \fB\-\-no\-dedupe\fR flag to get the full output. .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-metadata\fR(1) cargo-0.91.0/src/etc/man/cargo-uninstall.1000064400000000000000000000105631046102023000163140ustar 00000000000000'\" t .TH "CARGO\-UNINSTALL" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-uninstall \[em] Remove a Rust binary .SH "SYNOPSIS" \fBcargo uninstall\fR [\fIoptions\fR] [\fIspec\fR\[u2026]] .SH "DESCRIPTION" This command removes a package installed with \fBcargo\-install\fR(1). The \fIspec\fR argument is a package ID specification of the package to remove (see \fBcargo\-pkgid\fR(1)). .sp By default all binaries are removed for a crate but the \fB\-\-bin\fR and \fB\-\-example\fR flags can be used to only remove particular binaries. .sp The installation root is determined, in order of precedence: .sp .RS 4 \h'-04'\(bu\h'+03'\fB\-\-root\fR option .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBCARGO_INSTALL_ROOT\fR environment variable .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBinstall.root\fR Cargo \fIconfig value\fR .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBCARGO_HOME\fR environment variable .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB$HOME/.cargo\fR .RE .SH "OPTIONS" .SS "Uninstall Options" .sp \fB\-p\fR, \fB\-\-package\fR \fIspec\fR\[u2026] .RS 4 Package to uninstall. .RE .sp \fB\-\-bin\fR \fIname\fR\[u2026] .RS 4 Only uninstall the binary \fIname\fR\&. .RE .sp \fB\-\-root\fR \fIdir\fR .RS 4 Directory to uninstall packages from. .RE .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Uninstall a previously installed package. .sp .RS 4 .nf cargo uninstall ripgrep .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-install\fR(1) cargo-0.91.0/src/etc/man/cargo-update.1000064400000000000000000000214741046102023000155700ustar 00000000000000'\" t .TH "CARGO\-UPDATE" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-update \[em] Update dependencies as recorded in the local lock file .SH "SYNOPSIS" \fBcargo update\fR [\fIoptions\fR] \fIspec\fR .SH "DESCRIPTION" This command will update dependencies in the \fBCargo.lock\fR file to the latest version. If the \fBCargo.lock\fR file does not exist, it will be created with the latest available versions. .SH "OPTIONS" .SS "Update Options" .sp \fIspec\fR\[u2026] .RS 4 Update only the specified packages. This flag may be specified multiple times. See \fBcargo\-pkgid\fR(1) for the SPEC format. .sp If packages are specified with \fIspec\fR, then a conservative update of the lockfile will be performed. This means that only the dependency specified by SPEC will be updated. Its transitive dependencies will be updated only if SPEC cannot be updated without updating dependencies. All other dependencies will remain locked at their currently recorded versions. .sp If \fIspec\fR is not specified, all dependencies are updated. .RE .sp \fB\-\-recursive\fR .RS 4 When used with \fIspec\fR, dependencies of \fIspec\fR are forced to update as well. Cannot be used with \fB\-\-precise\fR\&. .RE .sp \fB\-\-precise\fR \fIprecise\fR .RS 4 When used with \fIspec\fR, allows you to specify a specific version number to set the package to. If the package comes from a git repository, this can be a git revision (such as a SHA hash or tag). .sp While not recommended, you can specify a yanked version of a package. When possible, try other non\-yanked SemVer\-compatible versions or seek help from the maintainers of the package. .sp A compatible \fBpre\-release\fR version can also be specified even when the version requirement in \fBCargo.toml\fR doesn\[cq]t contain any pre\-release identifier (nightly only). .RE .sp \fB\-\-breaking\fR \fIdirectory\fR .RS 4 Update \fIspec\fR to latest SemVer\-breaking version. .sp Version requirements will be modified to allow this update. .sp This only applies to dependencies when .sp .RS 4 \h'-04'\(bu\h'+03'The package is a dependency of a workspace member .RE .sp .RS 4 \h'-04'\(bu\h'+03'The dependency is not renamed .RE .sp .RS 4 \h'-04'\(bu\h'+03'A SemVer\-incompatible version is available .RE .sp .RS 4 \h'-04'\(bu\h'+03'The \[lq]SemVer operator\[rq] is used (\fB^\fR which is the default) .RE .sp This option is unstable and available only on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable. See for more information. .RE .sp \fB\-w\fR, \fB\-\-workspace\fR .RS 4 Attempt to update only packages defined in the workspace. Other packages are updated only if they don\[cq]t already exist in the lockfile. This option is useful for updating \fBCargo.lock\fR after you\[cq]ve changed version numbers in \fBCargo.toml\fR\&. .RE .sp \fB\-\-dry\-run\fR .RS 4 Displays what would be updated, but doesn\[cq]t actually write the lockfile. .RE .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .SS "Manifest Options" .sp \fB\-\-manifest\-path\fR \fIpath\fR .RS 4 Path to the \fBCargo.toml\fR file. By default, Cargo searches for the \fBCargo.toml\fR file in the current directory or any parent directory. .RE .sp \fB\-\-ignore\-rust\-version\fR .RS 4 Ignore \fBrust\-version\fR specification in packages. .RE .sp \fB\-\-locked\fR .RS 4 Asserts that the exact same dependencies and versions are used as when the existing \fBCargo.lock\fR file was originally generated. Cargo will exit with an error when either of the following scenarios arises: .sp .RS 4 \h'-04'\(bu\h'+03'The lock file is missing. .RE .sp .RS 4 \h'-04'\(bu\h'+03'Cargo attempted to change the lock file due to a different dependency resolution. .RE .sp It may be used in environments where deterministic builds are desired, such as in CI pipelines. .RE .sp \fB\-\-offline\fR .RS 4 Prevents Cargo from accessing the network for any reason. Without this flag, Cargo will stop with an error if it needs to access the network and the network is not available. With this flag, Cargo will attempt to proceed without the network if possible. .sp Beware that this may result in different dependency resolution than online mode. Cargo will restrict itself to crates that are downloaded locally, even if there might be a newer version as indicated in the local copy of the index. See the \fBcargo\-fetch\fR(1) command to download dependencies before going offline. .sp May also be specified with the \fBnet.offline\fR \fIconfig value\fR \&. .RE .sp \fB\-\-frozen\fR .RS 4 Equivalent to specifying both \fB\-\-locked\fR and \fB\-\-offline\fR\&. .RE .sp \fB\-\-lockfile\-path\fR \fIPATH\fR .RS 4 Changes the path of the lockfile from the default (\fB/Cargo.lock\fR) to \fIPATH\fR\&. \fIPATH\fR must end with \fBCargo.lock\fR (e.g. \fB\-\-lockfile\-path /tmp/temporary\-lockfile/Cargo.lock\fR). Note that providing \fB\-\-lockfile\-path\fR will ignore existing lockfile at the default path, and instead will either use the lockfile from \fIPATH\fR, or write a new lockfile into the provided \fIPATH\fR if it doesn\[cq]t exist. This flag can be used to run most commands in read\-only directories, writing lockfile into the provided \fIPATH\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#14421\fR ). .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Update all dependencies in the lockfile: .sp .RS 4 .nf cargo update .fi .RE .RE .sp .RS 4 \h'-04' 2.\h'+01'Update only specific dependencies: .sp .RS 4 .nf cargo update foo bar .fi .RE .RE .sp .RS 4 \h'-04' 3.\h'+01'Set a specific dependency to a specific version: .sp .RS 4 .nf cargo update foo \-\-precise 1.2.3 .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-generate\-lockfile\fR(1) cargo-0.91.0/src/etc/man/cargo-vendor.1000064400000000000000000000201751046102023000156000ustar 00000000000000'\" t .TH "CARGO\-VENDOR" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-vendor \[em] Vendor all dependencies locally .SH "SYNOPSIS" \fBcargo vendor\fR [\fIoptions\fR] [\fIpath\fR] .SH "DESCRIPTION" This cargo subcommand will vendor all crates.io and git dependencies for a project into the specified directory at \fB\fR\&. After this command completes the vendor directory specified by \fB\fR will contain all remote sources from dependencies specified. Additional manifests beyond the default one can be specified with the \fB\-s\fR option. .sp The configuration necessary to use the vendored sources would be printed to stdout after \fBcargo vendor\fR completes the vendoring process. You will need to add or redirect it to your Cargo configuration file, which is usually \fB\&.cargo/config.toml\fR locally for the current package. .sp Cargo treats vendored sources as read\-only as it does to registry and git sources. If you intend to modify a crate from a remote source, use \fB[patch]\fR or a \fBpath\fR dependency pointing to a local copy of that crate. Cargo will then correctly handle the crate on incremental rebuilds, as it knows that it is no longer a read\-only dependency. .SH "OPTIONS" .SS "Vendor Options" .sp \fB\-s\fR \fImanifest\fR, \fB\-\-sync\fR \fImanifest\fR .RS 4 Specify an extra \fBCargo.toml\fR manifest to workspaces which should also be vendored and synced to the output. May be specified multiple times. .RE .sp \fB\-\-no\-delete\fR .RS 4 Don\[cq]t delete the \[lq]vendor\[rq] directory when vendoring, but rather keep all existing contents of the vendor directory .RE .sp \fB\-\-respect\-source\-config\fR .RS 4 Instead of ignoring \fB[source]\fR configuration by default in \fB\&.cargo/config.toml\fR read it and use it when downloading crates from crates.io, for example .RE .sp \fB\-\-versioned\-dirs\fR .RS 4 Normally versions are only added to disambiguate multiple versions of the same package. This option causes all directories in the \[lq]vendor\[rq] directory to be versioned, which makes it easier to track the history of vendored packages over time, and can help with the performance of re\-vendoring when only a subset of the packages have changed. .RE .SS "Manifest Options" .sp \fB\-\-manifest\-path\fR \fIpath\fR .RS 4 Path to the \fBCargo.toml\fR file. By default, Cargo searches for the \fBCargo.toml\fR file in the current directory or any parent directory. .RE .sp \fB\-\-locked\fR .RS 4 Asserts that the exact same dependencies and versions are used as when the existing \fBCargo.lock\fR file was originally generated. Cargo will exit with an error when either of the following scenarios arises: .sp .RS 4 \h'-04'\(bu\h'+03'The lock file is missing. .RE .sp .RS 4 \h'-04'\(bu\h'+03'Cargo attempted to change the lock file due to a different dependency resolution. .RE .sp It may be used in environments where deterministic builds are desired, such as in CI pipelines. .RE .sp \fB\-\-offline\fR .RS 4 Prevents Cargo from accessing the network for any reason. Without this flag, Cargo will stop with an error if it needs to access the network and the network is not available. With this flag, Cargo will attempt to proceed without the network if possible. .sp Beware that this may result in different dependency resolution than online mode. Cargo will restrict itself to crates that are downloaded locally, even if there might be a newer version as indicated in the local copy of the index. See the \fBcargo\-fetch\fR(1) command to download dependencies before going offline. .sp May also be specified with the \fBnet.offline\fR \fIconfig value\fR \&. .RE .sp \fB\-\-frozen\fR .RS 4 Equivalent to specifying both \fB\-\-locked\fR and \fB\-\-offline\fR\&. .RE .sp \fB\-\-lockfile\-path\fR \fIPATH\fR .RS 4 Changes the path of the lockfile from the default (\fB/Cargo.lock\fR) to \fIPATH\fR\&. \fIPATH\fR must end with \fBCargo.lock\fR (e.g. \fB\-\-lockfile\-path /tmp/temporary\-lockfile/Cargo.lock\fR). Note that providing \fB\-\-lockfile\-path\fR will ignore existing lockfile at the default path, and instead will either use the lockfile from \fIPATH\fR, or write a new lockfile into the provided \fIPATH\fR if it doesn\[cq]t exist. This flag can be used to run most commands in read\-only directories, writing lockfile into the provided \fIPATH\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#14421\fR ). .RE .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Vendor all dependencies into a local \[lq]vendor\[rq] folder .sp .RS 4 .nf cargo vendor .fi .RE .RE .sp .RS 4 \h'-04' 2.\h'+01'Vendor all dependencies into a local \[lq]third\-party/vendor\[rq] folder .sp .RS 4 .nf cargo vendor third\-party/vendor .fi .RE .RE .sp .RS 4 \h'-04' 3.\h'+01'Vendor the current workspace as well as another to \[lq]vendor\[rq] .sp .RS 4 .nf cargo vendor \-s ../path/to/Cargo.toml .fi .RE .RE .sp .RS 4 \h'-04' 4.\h'+01'Vendor and redirect the necessary vendor configs to a config file. .sp .RS 4 .nf cargo vendor > path/to/my/cargo/config.toml .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1) cargo-0.91.0/src/etc/man/cargo-version.1000064400000000000000000000012301046102023000157570ustar 00000000000000'\" t .TH "CARGO\-VERSION" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-version \[em] Show version information .SH "SYNOPSIS" \fBcargo version\fR [\fIoptions\fR] .SH "DESCRIPTION" Displays the version of Cargo. .SH "OPTIONS" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Display additional version information. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Display the version: .sp .RS 4 .nf cargo version .fi .RE .RE .sp .RS 4 \h'-04' 2.\h'+01'The version is also available via flags: .sp .RS 4 .nf cargo \-\-version cargo \-V .fi .RE .RE .sp .RS 4 \h'-04' 3.\h'+01'Display extra version information: .sp .RS 4 .nf cargo \-Vv .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1) cargo-0.91.0/src/etc/man/cargo-yank.1000064400000000000000000000200421046102023000152360ustar 00000000000000'\" t .TH "CARGO\-YANK" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo\-yank \[em] Remove a pushed crate from the index .SH "SYNOPSIS" \fBcargo yank\fR [\fIoptions\fR] \fIcrate\fR@\fIversion\fR .br \fBcargo yank\fR [\fIoptions\fR] \fB\-\-version\fR \fIversion\fR [\fIcrate\fR] .SH "DESCRIPTION" The yank command removes a previously published crate\[cq]s version from the server\[cq]s index. This command does not delete any data, and the crate will still be available for download via the registry\[cq]s download link. .sp Cargo will not use a yanked version for any new project or checkout without a pre\-existing lockfile, and will generate an error if there are no longer any compatible versions for your crate. .sp This command requires you to be authenticated with either the \fB\-\-token\fR option or using \fBcargo\-login\fR(1). .sp If the crate name is not specified, it will use the package name from the current directory. .SS "How yank works" For example, the \fBfoo\fR crate published version \fB1.5.0\fR and another crate \fBbar\fR declared a dependency on version \fBfoo = "1.5"\fR\&. Now \fBfoo\fR releases a new, but not semver compatible, version \fB2.0.0\fR, and finds a critical issue with \fB1.5.0\fR\&. If \fB1.5.0\fR is yanked, no new project or checkout without an existing lockfile will be able to use crate \fBbar\fR as it relies on \fB1.5\fR\&. .sp In this case, the maintainers of \fBfoo\fR should first publish a semver compatible version such as \fB1.5.1\fR prior to yanking \fB1.5.0\fR so that \fBbar\fR and all projects that depend on \fBbar\fR will continue to work. .sp As another example, consider a crate \fBbar\fR with published versions \fB1.5.0\fR, \fB1.5.1\fR, \fB1.5.2\fR, \fB2.0.0\fR and \fB3.0.0\fR\&. The following table identifies the versions cargo could use in the absence of a lockfile for different SemVer requirements, following a given release being yanked: .TS allbox tab(:); lt lt lt lt. T{ Yanked Version / SemVer requirement T}:T{ \fBbar = "1.5.0"\fR T}:T{ \fBbar = "=1.5.0"\fR T}:T{ \fBbar = "2.0.0"\fR T} T{ \fB1.5.0\fR T}:T{ Use either \fB1.5.1\fR or \fB1.5.2\fR T}:T{ \fBReturn Error\fR T}:T{ Use \fB2.0.0\fR T} T{ \fB1.5.1\fR T}:T{ Use either \fB1.5.0\fR or \fB1.5.2\fR T}:T{ Use \fB1.5.0\fR T}:T{ Use \fB2.0.0\fR T} T{ \fB2.0.0\fR T}:T{ Use either \fB1.5.0\fR, \fB1.5.1\fR or \fB1.5.2\fR T}:T{ Use \fB1.5.0\fR T}:T{ \fBReturn Error\fR T} .TE .sp .SS "When to yank" Crates should only be yanked in exceptional circumstances, for example, an accidental publish, an unintentional SemVer breakages, or a significantly broken and unusable crate. In the case of security vulnerabilities, \fIRustSec\fR is typically a less disruptive mechanism to inform users and encourage them to upgrade, and avoids the possibility of significant downstream disruption irrespective of susceptibility to the vulnerability in question. .sp A common workflow is to yank a crate having already published a semver compatible version, to reduce the probability of preventing dependent crates from compiling. .sp When addressing copyright, licensing, or personal data issues with a published crate, simply yanking it may not suffice. In such cases, contact the maintainers of the registry you used. For crates.io, refer to their \fIpolicies\fR and contact them at \&. .sp If credentials have been leaked, the recommended course of action is to revoke them immediately. Once a crate has been published, it is impossible to determine if the leaked credentials have been copied. Yanking the crate only prevents new users from downloading it, but cannot stop those who have already downloaded it from keeping or even spreading the leaked credentials. .SH "OPTIONS" .SS "Yank Options" .sp \fB\-\-vers\fR \fIversion\fR, \fB\-\-version\fR \fIversion\fR .RS 4 The version to yank or un\-yank. .RE .sp \fB\-\-undo\fR .RS 4 Undo a yank, putting a version back into the index. .RE .sp \fB\-\-token\fR \fItoken\fR .RS 4 API token to use when authenticating. This overrides the token stored in the credentials file (which is created by \fBcargo\-login\fR(1)). .sp \fICargo config\fR environment variables can be used to override the tokens stored in the credentials file. The token for crates.io may be specified with the \fBCARGO_REGISTRY_TOKEN\fR environment variable. Tokens for other registries may be specified with environment variables of the form \fBCARGO_REGISTRIES_NAME_TOKEN\fR where \fBNAME\fR is the name of the registry in all capital letters. .RE .sp \fB\-\-index\fR \fIindex\fR .RS 4 The URL of the registry index to use. .RE .sp \fB\-\-registry\fR \fIregistry\fR .RS 4 Name of the registry to use. Registry names are defined in \fICargo config files\fR \&. If not specified, the default registry is used, which is defined by the \fBregistry.default\fR config key which defaults to \fBcrates\-io\fR\&. .RE .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Yank a crate from the index: .sp .RS 4 .nf cargo yank foo@1.0.7 .fi .RE .RE .SH "SEE ALSO" \fBcargo\fR(1), \fBcargo\-login\fR(1), \fBcargo\-publish\fR(1) cargo-0.91.0/src/etc/man/cargo.1000064400000000000000000000245621046102023000143110ustar 00000000000000'\" t .TH "CARGO" "1" .nh .ad l .ss \n[.ss] 0 .SH "NAME" cargo \[em] The Rust package manager .SH "SYNOPSIS" \fBcargo\fR [\fIoptions\fR] \fIcommand\fR [\fIargs\fR] .br \fBcargo\fR [\fIoptions\fR] \fB\-\-version\fR .br \fBcargo\fR [\fIoptions\fR] \fB\-\-list\fR .br \fBcargo\fR [\fIoptions\fR] \fB\-\-help\fR .br \fBcargo\fR [\fIoptions\fR] \fB\-\-explain\fR \fIcode\fR .SH "DESCRIPTION" This program is a package manager and build tool for the Rust language, available at \&. .SH "COMMANDS" .SS "Build Commands" \fBcargo\-bench\fR(1) .br \ \ \ \ Execute benchmarks of a package. .sp \fBcargo\-build\fR(1) .br \ \ \ \ Compile a package. .sp \fBcargo\-check\fR(1) .br \ \ \ \ Check a local package and all of its dependencies for errors. .sp \fBcargo\-clean\fR(1) .br \ \ \ \ Remove artifacts that Cargo has generated in the past. .sp \fBcargo\-doc\fR(1) .br \ \ \ \ Build a package\[cq]s documentation. .sp \fBcargo\-fetch\fR(1) .br \ \ \ \ Fetch dependencies of a package from the network. .sp \fBcargo\-fix\fR(1) .br \ \ \ \ Automatically fix lint warnings reported by rustc. .sp \fBcargo\-run\fR(1) .br \ \ \ \ Run a binary or example of the local package. .sp \fBcargo\-rustc\fR(1) .br \ \ \ \ Compile a package, and pass extra options to the compiler. .sp \fBcargo\-rustdoc\fR(1) .br \ \ \ \ Build a package\[cq]s documentation, using specified custom flags. .sp \fBcargo\-test\fR(1) .br \ \ \ \ Execute unit and integration tests of a package. .SS "Manifest Commands" \fBcargo\-add\fR(1) .br \ \ \ \ Add dependencies to a \fBCargo.toml\fR manifest file. .sp \fBcargo\-generate\-lockfile\fR(1) .br \ \ \ \ Generate \fBCargo.lock\fR for a project. .sp \fBcargo\-info\fR(1) .br \ \ \ \ Display information about a package in the registry. Default registry is crates.io. .sp \fBcargo\-locate\-project\fR(1) .br \ \ \ \ Print a JSON representation of a \fBCargo.toml\fR file\[cq]s location. .sp \fBcargo\-metadata\fR(1) .br \ \ \ \ Output the resolved dependencies of a package in machine\-readable format. .sp \fBcargo\-pkgid\fR(1) .br \ \ \ \ Print a fully qualified package specification. .sp \fBcargo\-remove\fR(1) .br \ \ \ \ Remove dependencies from a \fBCargo.toml\fR manifest file. .sp \fBcargo\-tree\fR(1) .br \ \ \ \ Display a tree visualization of a dependency graph. .sp \fBcargo\-update\fR(1) .br \ \ \ \ Update dependencies as recorded in the local lock file. .sp \fBcargo\-vendor\fR(1) .br \ \ \ \ Vendor all dependencies locally. .SS "Package Commands" \fBcargo\-init\fR(1) .br \ \ \ \ Create a new Cargo package in an existing directory. .sp \fBcargo\-install\fR(1) .br \ \ \ \ Build and install a Rust binary. .sp \fBcargo\-new\fR(1) .br \ \ \ \ Create a new Cargo package. .sp \fBcargo\-search\fR(1) .br \ \ \ \ Search packages in crates.io. .sp \fBcargo\-uninstall\fR(1) .br \ \ \ \ Remove a Rust binary. .SS "Publishing Commands" \fBcargo\-login\fR(1) .br \ \ \ \ Save an API token from the registry locally. .sp \fBcargo\-logout\fR(1) .br \ \ \ \ Remove an API token from the registry locally. .sp \fBcargo\-owner\fR(1) .br \ \ \ \ Manage the owners of a crate on the registry. .sp \fBcargo\-package\fR(1) .br \ \ \ \ Assemble the local package into a distributable tarball. .sp \fBcargo\-publish\fR(1) .br \ \ \ \ Upload a package to the registry. .sp \fBcargo\-yank\fR(1) .br \ \ \ \ Remove a pushed crate from the index. .SS "General Commands" \fBcargo\-help\fR(1) .br \ \ \ \ Display help information about Cargo. .sp \fBcargo\-version\fR(1) .br \ \ \ \ Show version information. .SH "OPTIONS" .SS "Special Options" .sp \fB\-V\fR, \fB\-\-version\fR .RS 4 Print version info and exit. If used with \fB\-\-verbose\fR, prints extra information. .RE .sp \fB\-\-list\fR .RS 4 List all installed Cargo subcommands. If used with \fB\-\-verbose\fR, prints extra information. .RE .sp \fB\-\-explain\fR \fIcode\fR .RS 4 Run \fBrustc \-\-explain CODE\fR which will print out a detailed explanation of an error message (for example, \fBE0004\fR). .RE .SS "Display Options" .sp \fB\-v\fR, \fB\-\-verbose\fR .RS 4 Use verbose output. May be specified twice for \[lq]very verbose\[rq] output which includes extra output such as dependency warnings and build script output. May also be specified with the \fBterm.verbose\fR \fIconfig value\fR \&. .RE .sp \fB\-q\fR, \fB\-\-quiet\fR .RS 4 Do not print cargo log messages. May also be specified with the \fBterm.quiet\fR \fIconfig value\fR \&. .RE .sp \fB\-\-color\fR \fIwhen\fR .RS 4 Control when colored output is used. Valid values: .sp .RS 4 \h'-04'\(bu\h'+03'\fBauto\fR (default): Automatically detect if color support is available on the terminal. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBalways\fR: Always display colors. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fBnever\fR: Never display colors. .RE .sp May also be specified with the \fBterm.color\fR \fIconfig value\fR \&. .RE .SS "Manifest Options" .sp \fB\-\-locked\fR .RS 4 Asserts that the exact same dependencies and versions are used as when the existing \fBCargo.lock\fR file was originally generated. Cargo will exit with an error when either of the following scenarios arises: .sp .RS 4 \h'-04'\(bu\h'+03'The lock file is missing. .RE .sp .RS 4 \h'-04'\(bu\h'+03'Cargo attempted to change the lock file due to a different dependency resolution. .RE .sp It may be used in environments where deterministic builds are desired, such as in CI pipelines. .RE .sp \fB\-\-offline\fR .RS 4 Prevents Cargo from accessing the network for any reason. Without this flag, Cargo will stop with an error if it needs to access the network and the network is not available. With this flag, Cargo will attempt to proceed without the network if possible. .sp Beware that this may result in different dependency resolution than online mode. Cargo will restrict itself to crates that are downloaded locally, even if there might be a newer version as indicated in the local copy of the index. See the \fBcargo\-fetch\fR(1) command to download dependencies before going offline. .sp May also be specified with the \fBnet.offline\fR \fIconfig value\fR \&. .RE .sp \fB\-\-frozen\fR .RS 4 Equivalent to specifying both \fB\-\-locked\fR and \fB\-\-offline\fR\&. .RE .SS "Common Options" .sp \fB+\fR\fItoolchain\fR .RS 4 If Cargo has been installed with rustup, and the first argument to \fBcargo\fR begins with \fB+\fR, it will be interpreted as a rustup toolchain name (such as \fB+stable\fR or \fB+nightly\fR). See the \fIrustup documentation\fR for more information about how toolchain overrides work. .RE .sp \fB\-\-config\fR \fIKEY=VALUE\fR or \fIPATH\fR .RS 4 Overrides a Cargo configuration value. The argument should be in TOML syntax of \fBKEY=VALUE\fR, or provided as a path to an extra configuration file. This flag may be specified multiple times. See the \fIcommand\-line overrides section\fR for more information. .RE .sp \fB\-C\fR \fIPATH\fR .RS 4 Changes the current working directory before executing any specified operations. This affects things like where cargo looks by default for the project manifest (\fBCargo.toml\fR), as well as the directories searched for discovering \fB\&.cargo/config.toml\fR, for example. This option must appear before the command name, for example \fBcargo \-C path/to/my\-project build\fR\&. .sp This option is only available on the \fInightly channel\fR and requires the \fB\-Z unstable\-options\fR flag to enable (see \fI#10098\fR ). .RE .sp \fB\-h\fR, \fB\-\-help\fR .RS 4 Prints help information. .RE .sp \fB\-Z\fR \fIflag\fR .RS 4 Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. .RE .SH "ENVIRONMENT" See \fIthe reference\fR for details on environment variables that Cargo reads. .SH "EXIT STATUS" .sp .RS 4 \h'-04'\(bu\h'+03'\fB0\fR: Cargo succeeded. .RE .sp .RS 4 \h'-04'\(bu\h'+03'\fB101\fR: Cargo failed to complete. .RE .SH "FILES" \fB~/.cargo/\fR .br \ \ \ \ Default location for Cargo\[cq]s \[lq]home\[rq] directory where it stores various files. The location can be changed with the \fBCARGO_HOME\fR environment variable. .sp \fB$CARGO_HOME/bin/\fR .br \ \ \ \ Binaries installed by \fBcargo\-install\fR(1) will be located here. If using \fIrustup\fR , executables distributed with Rust are also located here. .sp \fB$CARGO_HOME/config.toml\fR .br \ \ \ \ The global configuration file. See \fIthe reference\fR for more information about configuration files. .sp \fB\&.cargo/config.toml\fR .br \ \ \ \ Cargo automatically searches for a file named \fB\&.cargo/config.toml\fR in the current directory, and all parent directories. These configuration files will be merged with the global configuration file. .sp \fB$CARGO_HOME/credentials.toml\fR .br \ \ \ \ Private authentication information for logging in to a registry. .sp \fB$CARGO_HOME/registry/\fR .br \ \ \ \ This directory contains cached downloads of the registry index and any downloaded dependencies. .sp \fB$CARGO_HOME/git/\fR .br \ \ \ \ This directory contains cached downloads of git dependencies. .sp Please note that the internal structure of the \fB$CARGO_HOME\fR directory is not stable yet and may be subject to change. .SH "EXAMPLES" .sp .RS 4 \h'-04' 1.\h'+01'Build a local package and all of its dependencies: .sp .RS 4 .nf cargo build .fi .RE .RE .sp .RS 4 \h'-04' 2.\h'+01'Build a package with optimizations: .sp .RS 4 .nf cargo build \-\-release .fi .RE .RE .sp .RS 4 \h'-04' 3.\h'+01'Run tests for a cross\-compiled target: .sp .RS 4 .nf cargo test \-\-target i686\-unknown\-linux\-gnu .fi .RE .RE .sp .RS 4 \h'-04' 4.\h'+01'Create a new package that builds an executable: .sp .RS 4 .nf cargo new foobar .fi .RE .RE .sp .RS 4 \h'-04' 5.\h'+01'Create a package in the current directory: .sp .RS 4 .nf mkdir foo && cd foo cargo init . .fi .RE .RE .sp .RS 4 \h'-04' 6.\h'+01'Learn about a command\[cq]s options and usage: .sp .RS 4 .nf cargo help clean .fi .RE .RE .SH "BUGS" See for issues. .SH "SEE ALSO" \fBrustc\fR(1), \fBrustdoc\fR(1) cargo-0.91.0/tests/build-std/main.rs000064400000000000000000000322061046102023000153340ustar 00000000000000//! A test suite for `-Zbuild-std` which is much more expensive than the //! standard test suite. //! //! This test suite attempts to perform a full integration test where we //! actually compile the standard library from source (like the real one) and //! the various tests associated with that. //! //! YOU SHOULD IDEALLY NOT WRITE TESTS HERE. //! //! If possible, use `tests/testsuite/standard_lib.rs` instead. That uses a //! 'mock' sysroot which is much faster to compile. The tests here are //! extremely intensive and are only intended to run on CI and are theoretically //! not catching any regressions that `tests/testsuite/standard_lib.rs` isn't //! already catching. //! //! All tests here should use `#[cargo_test(build_std_real)]` to indicate that //! boilerplate should be generated to require the nightly toolchain and the //! `CARGO_RUN_BUILD_STD_TESTS` env var to be set to actually run these tests. //! Otherwise the tests are skipped. #![allow(clippy::disallowed_methods)] use cargo_test_support::{Execs, basic_manifest, paths, project, rustc_host, str}; use cargo_test_support::{Project, prelude::*}; use std::env; use std::path::{Path, PathBuf}; fn enable_build_std(e: &mut Execs, arg: Option<&str>, isolated: bool) { if !isolated { e.env_remove("CARGO_HOME"); e.env_remove("HOME"); } // And finally actually enable `build-std` for now let arg = match arg { Some(s) => format!("-Zbuild-std={}", s), None => "-Zbuild-std".to_string(), }; e.arg(arg).arg("-Zpublic-dependency"); e.masquerade_as_nightly_cargo(&["build-std"]); } // Helper methods used in the tests below trait BuildStd: Sized { /// Set `-Zbuild-std` args and will download dependencies of the standard /// library in users's `CARGO_HOME` (`~/.cargo/`) instead of isolated /// environment `cargo-test-support` usually provides. /// /// The environment is not isolated is to avoid excessive network requests /// and downloads. A side effect is `[BLOCKING]` will show up in stderr, /// as a sign of package cache lock contention when running other build-std /// tests concurrently. fn build_std(&mut self) -> &mut Self; /// Like [`BuildStd::build_std`] and is able to specify what crates to build. fn build_std_arg(&mut self, arg: &str) -> &mut Self; /// Like [`BuildStd::build_std`] but use an isolated `CARGO_HOME` environment /// to avoid package cache lock contention. /// /// Don't use this unless you really need to assert the full stderr /// and avoid any `[BLOCKING]` message. fn build_std_isolated(&mut self) -> &mut Self; fn target_host(&mut self) -> &mut Self; } impl BuildStd for Execs { fn build_std(&mut self) -> &mut Self { enable_build_std(self, None, false); self } fn build_std_arg(&mut self, arg: &str) -> &mut Self { enable_build_std(self, Some(arg), false); self } fn build_std_isolated(&mut self) -> &mut Self { enable_build_std(self, None, true); self } fn target_host(&mut self) -> &mut Self { self.arg("--target").arg(rustc_host()); self } } #[cargo_test(build_std_real)] fn basic() { let p = project() .file( "src/main.rs", " fn main() { foo::f(); } #[test] fn smoke_bin_unit() { foo::f(); } ", ) .file( "src/lib.rs", " extern crate alloc; extern crate proc_macro; /// ``` /// foo::f(); /// ``` pub fn f() { } #[test] fn smoke_lib_unit() { f(); } ", ) .file( "tests/smoke.rs", " #[test] fn smoke_integration() { foo::f(); } ", ) .build(); // HACK: use an isolated the isolated CARGO_HOME environment (`build_std_isolated`) // to avoid `[BLOCKING]` messages (from lock contention with other tests) // from getting in this test's asserts p.cargo("check").build_std_isolated().target_host().run(); p.cargo("build") .build_std_isolated() .target_host() // Importantly, this should not say [UPDATING] // There have been multiple bugs where every build triggers and update. .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("run") .build_std_isolated() .target_host() .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/[HOST_TARGET]/debug/foo` "#]]) .run(); p.cargo("test") .build_std_isolated() .target_host() .with_stderr_data(str![[r#" [COMPILING] [..] ... [COMPILING] test v0.0.0 ([..]) [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/[HOST_TARGET]/debug/deps/foo-[HASH]) [RUNNING] unittests src/main.rs (target/[HOST_TARGET]/debug/deps/foo-[HASH]) [RUNNING] tests/smoke.rs (target/[HOST_TARGET]/debug/deps/smoke-[HASH]) [DOCTEST] foo "#]]) .run(); // Check for hack that removes dylibs. let deps_dir = Path::new("target") .join(rustc_host()) .join("debug") .join("deps"); assert!(p.glob(deps_dir.join("*.rlib")).count() > 0); assert_eq!(p.glob(deps_dir.join("*.dylib")).count(), 0); } #[cargo_test(build_std_real)] fn host_proc_macro() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2021" [dependencies] macro_test = { path = "macro_test" } "#, ) .file( "src/main.rs", r#" extern crate macro_test; use macro_test::make_answer; make_answer!(); fn main() { println!("Hello, World: {}", answer()); } "#, ) .file( "macro_test/Cargo.toml", r#" [package] name = "macro_test" version = "0.1.0" edition = "2021" [lib] proc-macro = true "#, ) .file( "macro_test/src/lib.rs", r#" extern crate proc_macro; use proc_macro::TokenStream; #[proc_macro] pub fn make_answer(_item: TokenStream) -> TokenStream { "fn answer() -> u32 { 42 }".parse().unwrap() } "#, ) .build(); p.cargo("build") .build_std_arg("std") .build_std_arg("proc_macro") .run(); } #[cargo_test(build_std_real)] fn cross_custom() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [target.custom-target.dependencies] dep = { path = "dep" } "#, ) .file( "src/lib.rs", "#![no_std] pub fn f() -> u32 { dep::answer() }", ) .file("dep/Cargo.toml", &basic_manifest("dep", "0.1.0")) .file("dep/src/lib.rs", "#![no_std] pub fn answer() -> u32 { 42 }") .file( "custom-target.json", r#" { "llvm-target": "x86_64-unknown-none-gnu", "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", "arch": "x86_64", "target-endian": "little", "target-pointer-width": "64", "os": "none", "linker-flavor": "ld.lld" } "#, ) .build(); p.cargo("build --target custom-target.json -v") .build_std_arg("core") .run(); } #[cargo_test(build_std_real)] fn custom_test_framework() { let p = project() .file( "src/lib.rs", r#" #![no_std] #![cfg_attr(test, no_main)] #![feature(custom_test_frameworks)] #![test_runner(crate::test_runner)] pub fn test_runner(_tests: &[&dyn Fn()]) {} #[panic_handler] fn panic(_info: &core::panic::PanicInfo) -> ! { loop {} } "#, ) .file( "target.json", r#" { "llvm-target": "x86_64-unknown-none-gnu", "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", "arch": "x86_64", "target-endian": "little", "target-pointer-width": "64", "os": "none", "linker-flavor": "ld.lld", "linker": "rust-lld", "executables": true, "panic-strategy": "abort" } "#, ) .build(); // This is a bit of a hack to use the rust-lld that ships with most toolchains. let sysroot = paths::sysroot(); let sysroot = Path::new(&sysroot); let sysroot_bin = sysroot .join("lib") .join("rustlib") .join(rustc_host()) .join("bin"); let path = env::var_os("PATH").unwrap_or_default(); let mut paths = env::split_paths(&path).collect::>(); paths.insert(0, sysroot_bin); let new_path = env::join_paths(paths).unwrap(); p.cargo("test --target target.json --no-run -v") .env("PATH", new_path) .build_std_arg("core") .run(); } // Fixing rust-lang/rust#117839. // on macOS it never gets remapped. // Might be a separate issue, so only run on Linux. #[cargo_test(build_std_real)] #[cfg(target_os = "linux")] fn remap_path_scope() { let p = project() .file( "src/main.rs", " fn main() { panic!(\"remap to /rustc/\"); } ", ) .file( ".cargo/config.toml", " [profile.release] debug = \"line-tables-only\" ", ) .build(); p.cargo("run --release -Ztrim-paths") .masquerade_as_nightly_cargo(&["-Ztrim-paths"]) .env("RUST_BACKTRACE", "1") .build_std() .target_host() .with_status(101) .with_stderr_data( str![[r#" [FINISHED] `release` profile [optimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/[HOST_TARGET]/release/foo` ... [..]thread '[..]' panicked at [..]src/main.rs:3:[..]: [..]remap to /rustc/[..] [..]at /rustc/[..]/library/std/src/[..] [..]at ./src/main.rs:3:[..] [..]at /rustc/[..]/library/core/src/[..] ... "#]] .unordered(), ) .run(); } #[cargo_test(build_std_real)] fn test_proc_macro() { // See rust-lang/cargo#14735 let p = project() .file( "Cargo.toml", r#" [package] name = "foo" edition = "2021" [lib] proc-macro = true "#, ) .file("src/lib.rs", "") .build(); p.cargo("test --lib") .env_remove(cargo_util::paths::dylib_path_envvar()) .build_std() .with_stderr_data(str![[r#" ... [COMPILING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH]) "#]]) .run(); } #[cargo_test(build_std_real)] fn test_panic_abort() { // See rust-lang/cargo#14935 let p = project() .file( "Cargo.toml", r#" [package] name = "foo" edition = "2021" "#, ) .file("src/lib.rs", "#![no_std]") .build(); p.cargo("check") .build_std_arg("std,panic_abort") .env("RUSTFLAGS", "-C panic=abort") .arg("-Zbuild-std-features=panic_immediate_abort") .run(); } pub trait CargoProjectExt { /// Creates a `ProcessBuilder` to run cargo. /// /// Arguments can be separated by spaces. /// /// For `cargo run`, see [`Project::rename_run`]. /// /// # Example: /// /// ```no_run /// # let p = cargo_test_support::project().build(); /// p.cargo("build --bin foo").run(); /// ``` fn cargo(&self, cmd: &str) -> Execs; } impl CargoProjectExt for Project { fn cargo(&self, cmd: &str) -> Execs { let cargo = cargo_exe(); let mut execs = self.process(&cargo); execs.env("CARGO", cargo); execs.arg_line(cmd); execs } } /// Path to the cargo binary pub fn cargo_exe() -> PathBuf { snapbox::cmd::cargo_bin!("cargo").to_path_buf() } cargo-0.91.0/tests/testsuite/advanced_env.rs000064400000000000000000000016441046102023000171710ustar 00000000000000//! -Zadvanced-env tests use crate::prelude::*; use cargo_test_support::{paths, project, registry::Package}; #[cargo_test] fn source_config_env() { // Try to define [source] with environment variables. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] somedep = "1.0" "#, ) .file("src/lib.rs", "") .build(); Package::new("somedep", "1.0.0") .local(true) .file("src/lib.rs", "") .publish(); let path = paths::root().join("registry"); p.cargo("check -Zadvanced-env") .masquerade_as_nightly_cargo(&["advanced-env"]) .env("CARGO_SOURCE_crates-io_REPLACE_WITH", "my-local-source") .env("CARGO_SOURCE_my-local-source_LOCAL_REGISTRY", path) .run(); } cargo-0.91.0/tests/testsuite/alt_registry.rs000064400000000000000000001465031046102023000172700ustar 00000000000000//! Tests for alternative registries. use std::fs; use crate::prelude::*; use cargo_test_support::compare::assert_e2e; use cargo_test_support::publish::validate_alt_upload; use cargo_test_support::registry::{self, Package, RegistryBuilder}; use cargo_test_support::str; use cargo_test_support::{basic_manifest, paths, project}; #[cargo_test] fn depend_on_alt_registry() { registry::alt_init(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] edition = "2015" [dependencies.bar] version = "0.0.1" registry = "alternative" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("bar", "0.0.1").alternative(true).publish(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `alternative` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `alternative`) [CHECKING] bar v0.0.1 (registry `alternative`) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("clean").run(); // Don't download a second time p.cargo("check") .with_stderr_data(str![[r#" [CHECKING] bar v0.0.1 (registry `alternative`) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn depend_on_alt_registry_depends_on_same_registry_no_index() { registry::alt_init(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] edition = "2015" [dependencies.bar] version = "0.0.1" registry = "alternative" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("baz", "0.0.1").alternative(true).publish(); Package::new("bar", "0.0.1") .registry_dep("baz", "0.0.1") .alternative(true) .publish(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `alternative` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] baz v0.0.1 (registry `alternative`) [DOWNLOADED] bar v0.0.1 (registry `alternative`) [CHECKING] baz v0.0.1 (registry `alternative`) [CHECKING] bar v0.0.1 (registry `alternative`) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn depend_on_alt_registry_depends_on_same_registry() { registry::alt_init(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] edition = "2015" [dependencies.bar] version = "0.0.1" registry = "alternative" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("baz", "0.0.1").alternative(true).publish(); Package::new("bar", "0.0.1") .registry_dep("baz", "0.0.1") .alternative(true) .publish(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `alternative` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] baz v0.0.1 (registry `alternative`) [DOWNLOADED] bar v0.0.1 (registry `alternative`) [CHECKING] baz v0.0.1 (registry `alternative`) [CHECKING] bar v0.0.1 (registry `alternative`) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn depend_on_alt_registry_depends_on_crates_io() { registry::alt_init(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] edition = "2015" [dependencies.bar] version = "0.0.1" registry = "alternative" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("baz", "0.0.1").publish(); Package::new("bar", "0.0.1") .dep("baz", "0.0.1") .alternative(true) .publish(); p.cargo("check") .with_stderr_data( str![[r#" [UPDATING] `alternative` index [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] baz v0.0.1 (registry `dummy-registry`) [DOWNLOADED] bar v0.0.1 (registry `alternative`) [CHECKING] baz v0.0.1 [CHECKING] bar v0.0.1 (registry `alternative`) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [CHECKING] foo v0.0.1 ([ROOT]/foo) "#]] .unordered(), ) .run(); } #[cargo_test] fn registry_and_path_dep_works() { registry::alt_init(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] edition = "2015" [dependencies.bar] path = "bar" registry = "alternative" "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.0.1 ([ROOT]/foo/bar) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn registry_incompatible_with_git() { registry::alt_init(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] edition = "2015" [dependencies.bar] git = "" registry = "alternative" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: dependency (bar) specification is ambiguous. Only one of `git` or `registry` is allowed. "#]]) .run(); } #[cargo_test] fn cannot_publish_to_crates_io_with_registry_dependency() { let crates_io = registry::init(); let _alternative = RegistryBuilder::new().alternative().build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] edition = "2015" [dependencies.bar] version = "0.0.1" registry = "alternative" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("bar", "0.0.1").alternative(true).publish(); p.cargo("publish") .replace_crates_io(crates_io.index_url()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] crates.io index [ERROR] failed to verify manifest at `[ROOT]/foo/Cargo.toml` Caused by: crates cannot be published to crates.io with dependencies sourced from other registries. `bar` needs to be published to crates.io before publishing this crate. (crate `bar` is pulled from registry `alternative`) "#]]) .run(); p.cargo("publish") .replace_crates_io(crates_io.index_url()) .arg("--token") .arg(crates_io.token()) .arg("--index") .arg(crates_io.index_url().as_str()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] crates.io index [ERROR] failed to verify manifest at `[ROOT]/foo/Cargo.toml` Caused by: crates cannot be published to crates.io with dependencies sourced from other registries. `bar` needs to be published to crates.io before publishing this crate. (crate `bar` is pulled from registry `alternative`) "#]]) .run(); } #[cargo_test] fn publish_with_registry_dependency() { let _reg = RegistryBuilder::new() .http_api() .http_index() .alternative() .build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] edition = "2015" [dependencies.bar] version = "0.0.1" registry = "alternative" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("bar", "0.0.1").alternative(true).publish(); p.cargo("publish --registry alternative") .with_stderr_data(str![[r#" [UPDATING] `alternative` index [WARNING] manifest has no description, license, license-file, documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [UPDATING] `alternative` index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `alternative`) [COMPILING] bar v0.0.1 (registry `alternative`) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] foo v0.0.1 ([ROOT]/foo) [UPLOADED] foo v0.0.1 to registry `alternative` [NOTE] waiting for foo v0.0.1 to be available at registry `alternative`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.0.1 at registry `alternative` "#]]) .run(); validate_alt_upload( r#"{ "authors": [], "badges": {}, "categories": [], "deps": [ { "default_features": true, "features": [], "kind": "normal", "name": "bar", "optional": false, "target": null, "version_req": "^0.0.1" } ], "description": null, "documentation": null, "features": {}, "homepage": null, "keywords": [], "license": null, "license_file": null, "links": null, "name": "foo", "readme": null, "readme_file": null, "repository": null, "homepage": null, "documentation": null, "rust_version": null, "vers": "0.0.1" }"#, "foo-0.0.1.crate", &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], ); } #[cargo_test] fn alt_registry_and_crates_io_deps() { registry::alt_init(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] edition = "2015" [dependencies] crates_io_dep = "0.0.1" [dependencies.alt_reg_dep] version = "0.1.0" registry = "alternative" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("crates_io_dep", "0.0.1").publish(); Package::new("alt_reg_dep", "0.1.0") .alternative(true) .publish(); p.cargo("check") .with_stderr_data( str![[r#" [UPDATING] `alternative` index [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] crates_io_dep v0.0.1 (registry `dummy-registry`) [DOWNLOADED] alt_reg_dep v0.1.0 (registry `alternative`) [CHECKING] crates_io_dep v0.0.1 [CHECKING] alt_reg_dep v0.1.0 (registry `alternative`) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [CHECKING] foo v0.0.1 ([ROOT]/foo) "#]] .unordered(), ) .run(); } #[cargo_test] fn block_publish_due_to_no_token() { registry::alt_init(); let p = project().file("src/lib.rs", "").build(); fs::remove_file(paths::home().join(".cargo/credentials.toml")).unwrap(); // Now perform the actual publish p.cargo("publish --registry alternative") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [ERROR] no token found for `alternative`, please run `cargo login --registry alternative` or use environment variable CARGO_REGISTRIES_ALTERNATIVE_TOKEN "#]]) .run(); } #[cargo_test] fn cargo_registries_crates_io_protocol() { let _ = RegistryBuilder::new() .no_configure_token() .alternative() .build(); // Should not produce a warning due to the registries.crates-io.protocol = 'sparse' configuration let p = project() .file("src/lib.rs", "") .file( ".cargo/config.toml", "[registries.crates-io] protocol = 'sparse'", ) .build(); p.cargo("publish --registry alternative") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [ERROR] no token found for `alternative`, please run `cargo login --registry alternative` or use environment variable CARGO_REGISTRIES_ALTERNATIVE_TOKEN "#]]) .run(); } #[cargo_test] fn publish_to_alt_registry() { let _reg = RegistryBuilder::new() .http_api() .http_index() .alternative() .build(); let p = project().file("src/main.rs", "fn main() {}").build(); // Now perform the actual publish p.cargo("publish --registry alternative") .with_stderr_data(str![[r#" [UPDATING] `alternative` index [WARNING] manifest has no description, license, license-file, documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] foo v0.0.1 ([ROOT]/foo) [UPLOADED] foo v0.0.1 to registry `alternative` [NOTE] waiting for foo v0.0.1 to be available at registry `alternative`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.0.1 at registry `alternative` "#]]) .run(); validate_alt_upload( r#"{ "authors": [], "badges": {}, "categories": [], "deps": [], "description": null, "documentation": null, "features": {}, "homepage": null, "keywords": [], "license": null, "license_file": null, "links": null, "name": "foo", "readme": null, "readme_file": null, "repository": null, "homepage": null, "documentation": null, "rust_version": null, "vers": "0.0.1" }"#, "foo-0.0.1.crate", &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], ); } #[cargo_test] fn publish_with_crates_io_dep() { // crates.io registry. let _dummy_reg = registry::init(); // Alternative registry. let _alt_reg = RegistryBuilder::new() .http_api() .http_index() .alternative() .build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = ["me"] edition = "2015" license = "MIT" description = "foo" [dependencies.bar] version = "0.0.1" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("bar", "0.0.1").publish(); p.cargo("publish --registry alternative") .with_stderr_data(str![[r#" [UPDATING] `alternative` index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [UPDATING] `dummy-registry` index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) [COMPILING] bar v0.0.1 [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] foo v0.0.1 ([ROOT]/foo) [UPLOADED] foo v0.0.1 to registry `alternative` [NOTE] waiting for foo v0.0.1 to be available at registry `alternative`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.0.1 at registry `alternative` "#]]) .run(); validate_alt_upload( r#"{ "authors": ["me"], "badges": {}, "categories": [], "deps": [ { "default_features": true, "features": [], "kind": "normal", "name": "bar", "optional": false, "registry": "https://github.com/rust-lang/crates.io-index", "target": null, "version_req": "^0.0.1" } ], "description": "foo", "documentation": null, "features": {}, "homepage": null, "keywords": [], "license": "MIT", "license_file": null, "links": null, "name": "foo", "readme": null, "readme_file": null, "repository": null, "homepage": null, "documentation": null, "rust_version": null, "vers": "0.0.1" }"#, "foo-0.0.1.crate", &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], ); } #[cargo_test] fn passwords_in_registries_index_url_forbidden() { registry::alt_init(); let config = paths::home().join(".cargo/config.toml"); fs::write( config, r#" [registries.alternative] index = "ssh://git:secret@foobar.com" "#, ) .unwrap(); let p = project().file("src/main.rs", "fn main() {}").build(); p.cargo("publish --registry alternative") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid index URL for registry `alternative` defined in [ROOT]/home/.cargo/config.toml Caused by: registry URLs may not contain passwords "#]]) .run(); } #[cargo_test] fn patch_alt_reg() { registry::alt_init(); Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = { version = "0.1.0", registry = "alternative" } [patch.alternative] bar = { path = "bar" } "#, ) .file( "src/lib.rs", " extern crate bar; pub fn f() { bar::bar(); } ", ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `alternative` index [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn bad_registry_name() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] edition = "2015" [dependencies.bar] version = "0.0.1" registry = "bad name" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid character ` ` in registry name: `bad name`, characters must be Unicode XID characters (numbers, `-`, `_`, or most letters) --> Cargo.toml:8:17 | 8 | [dependencies.bar] | ^^^^^^^^^^^^^^^^^^ | "#]]) .run(); for cmd in &[ "init", "install foo", "login", "owner", "publish", "search", "yank --version 0.0.1", ] { p.cargo(cmd) .arg("--registry") .arg("bad name") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid character ` ` in registry name: `bad name`, characters must be Unicode XID characters (numbers, `-`, `_`, or most letters) "#]]) .run(); } } #[cargo_test] fn no_api() { let _registry = RegistryBuilder::new().alternative().no_api().build(); Package::new("bar", "0.0.1").alternative(true).publish(); // First check that a dependency works. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies.bar] version = "0.0.1" registry = "alternative" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `alternative` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `alternative`) [CHECKING] bar v0.0.1 (registry `alternative`) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("login --registry alternative") .with_stdin("TOKEN") .with_status(101) .with_stderr_data(str![[r#" [ERROR] registry `alternative` does not support API commands "#]]) .run(); p.cargo("publish --registry alternative") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [ERROR] registry `alternative` does not support API commands "#]]) .run(); p.cargo("search --registry alternative") .with_status(101) .with_stderr_data(str![[r#" [ERROR] registry `alternative` does not support API commands "#]]) .run(); p.cargo("owner --registry alternative --list") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [ERROR] registry `alternative` does not support API commands "#]]) .run(); p.cargo("yank --registry alternative --version=0.0.1 bar") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [ERROR] registry `alternative` does not support API commands "#]]) .run(); p.cargo("yank --registry alternative --version=0.0.1 bar") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [ERROR] registry `alternative` does not support API commands "#]]) .run(); } #[cargo_test] fn alt_reg_metadata() { // Check for "registry" entries in `cargo metadata` with alternative registries. registry::alt_init(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] altdep = { version = "0.0.1", registry = "alternative" } iodep = { version = "0.0.1" } "#, ) .file("src/lib.rs", "") .build(); Package::new("bar", "0.0.1").publish(); Package::new("altdep", "0.0.1") .dep("bar", "0.0.1") .alternative(true) .publish(); Package::new("altdep2", "0.0.1").alternative(true).publish(); Package::new("iodep", "0.0.1") .registry_dep("altdep2", "0.0.1") .publish(); // The important thing to check here is the "registry" value in `deps`. // They should be: // foo -> altdep: alternative-registry // foo -> iodep: null (because it is in crates.io) // altdep -> bar: null (because it is in crates.io) // iodep -> altdep2: alternative-registry p.cargo("metadata --format-version=1 --no-deps") .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [], "categories": [], "default_run": null, "dependencies": [ { "features": [], "kind": null, "name": "altdep", "optional": false, "registry": "[ROOTURL]/alternative-registry", "rename": null, "req": "^0.0.1", "source": "registry+[ROOTURL]/alternative-registry", "target": null, "uses_default_features": true }, { "features": [], "kind": null, "name": "iodep", "optional": false, "registry": null, "rename": null, "req": "^0.0.1", "source": "registry+https://github.com/rust-lang/crates.io-index", "target": null, "uses_default_features": true } ], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo#0.0.1", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/Cargo.toml", "metadata": null, "name": "foo", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "foo", "src_path": "[ROOT]/foo/src/lib.rs", "test": true } ], "version": "0.0.1" } ], "resolve": null, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo#0.0.1" ], "workspace_members": [ "path+[ROOTURL]/foo#0.0.1" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); // --no-deps uses a different code path, make sure both work. p.cargo("metadata --format-version=1") .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [], "categories": [], "default_run": null, "dependencies": [ { "features": [], "kind": null, "name": "bar", "optional": false, "registry": null, "rename": null, "req": "^0.0.1", "source": "registry+https://github.com/rust-lang/crates.io-index", "target": null, "uses_default_features": true } ], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "registry+[ROOTURL]/alternative-registry#altdep@0.0.1", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/altdep-0.0.1/Cargo.toml", "metadata": null, "name": "altdep", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": "registry+[ROOTURL]/alternative-registry", "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "altdep", "src_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/altdep-0.0.1/src/lib.rs", "test": true } ], "version": "0.0.1" }, { "authors": [], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "registry+[ROOTURL]/alternative-registry#altdep2@0.0.1", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/altdep2-0.0.1/Cargo.toml", "metadata": null, "name": "altdep2", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": "registry+[ROOTURL]/alternative-registry", "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "altdep2", "src_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/altdep2-0.0.1/src/lib.rs", "test": true } ], "version": "0.0.1" }, { "authors": [], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "registry+https://github.com/rust-lang/crates.io-index#bar@0.0.1", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/bar-0.0.1/Cargo.toml", "metadata": null, "name": "bar", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": "registry+https://github.com/rust-lang/crates.io-index", "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "bar", "src_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/bar-0.0.1/src/lib.rs", "test": true } ], "version": "0.0.1" }, { "authors": [], "categories": [], "default_run": null, "dependencies": [ { "features": [], "kind": null, "name": "altdep", "optional": false, "registry": "[ROOTURL]/alternative-registry", "rename": null, "req": "^0.0.1", "source": "registry+[ROOTURL]/alternative-registry", "target": null, "uses_default_features": true }, { "features": [], "kind": null, "name": "iodep", "optional": false, "registry": null, "rename": null, "req": "^0.0.1", "source": "registry+https://github.com/rust-lang/crates.io-index", "target": null, "uses_default_features": true } ], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo#0.0.1", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/Cargo.toml", "metadata": null, "name": "foo", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "foo", "src_path": "[ROOT]/foo/src/lib.rs", "test": true } ], "version": "0.0.1" }, { "authors": [], "categories": [], "default_run": null, "dependencies": [ { "features": [], "kind": null, "name": "altdep2", "optional": false, "registry": "[ROOTURL]/alternative-registry", "rename": null, "req": "^0.0.1", "source": "registry+[ROOTURL]/alternative-registry", "target": null, "uses_default_features": true } ], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "registry+https://github.com/rust-lang/crates.io-index#iodep@0.0.1", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/iodep-0.0.1/Cargo.toml", "metadata": null, "name": "iodep", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": "registry+https://github.com/rust-lang/crates.io-index", "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "iodep", "src_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/iodep-0.0.1/src/lib.rs", "test": true } ], "version": "0.0.1" } ], "resolve": { "nodes": [ { "dependencies": [ "registry+https://github.com/rust-lang/crates.io-index#bar@0.0.1" ], "deps": [ { "dep_kinds": [ { "kind": null, "target": null } ], "name": "bar", "pkg": "registry+https://github.com/rust-lang/crates.io-index#bar@0.0.1" } ], "features": [], "id": "registry+[ROOTURL]/alternative-registry#altdep@0.0.1" }, { "dependencies": [], "deps": [], "features": [], "id": "registry+[ROOTURL]/alternative-registry#altdep2@0.0.1" }, { "dependencies": [], "deps": [], "features": [], "id": "registry+https://github.com/rust-lang/crates.io-index#bar@0.0.1" }, { "dependencies": [ "registry+[ROOTURL]/alternative-registry#altdep@0.0.1", "registry+https://github.com/rust-lang/crates.io-index#iodep@0.0.1" ], "deps": [ { "dep_kinds": [ { "kind": null, "target": null } ], "name": "altdep", "pkg": "registry+[ROOTURL]/alternative-registry#altdep@0.0.1" }, { "dep_kinds": [ { "kind": null, "target": null } ], "name": "iodep", "pkg": "registry+https://github.com/rust-lang/crates.io-index#iodep@0.0.1" } ], "features": [], "id": "path+[ROOTURL]/foo#0.0.1" }, { "dependencies": [ "registry+[ROOTURL]/alternative-registry#altdep2@0.0.1" ], "deps": [ { "dep_kinds": [ { "kind": null, "target": null } ], "name": "altdep2", "pkg": "registry+[ROOTURL]/alternative-registry#altdep2@0.0.1" } ], "features": [], "id": "registry+https://github.com/rust-lang/crates.io-index#iodep@0.0.1" } ], "root": "path+[ROOTURL]/foo#0.0.1" }, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo#0.0.1" ], "workspace_members": [ "path+[ROOTURL]/foo#0.0.1" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); } #[cargo_test] fn unknown_registry() { // A known registry refers to an unknown registry. // foo -> bar(crates.io) -> baz(alt) registry::alt_init(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] edition = "2015" [dependencies.bar] version = "0.0.1" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("baz", "0.0.1").alternative(true).publish(); Package::new("bar", "0.0.1") .registry_dep("baz", "0.0.1") .publish(); // Remove "alternative" from config. let cfg_path = paths::home().join(".cargo/config.toml"); let mut config = fs::read_to_string(&cfg_path).unwrap(); let start = config.find("[registries.alternative]").unwrap(); config.insert(start, '#'); let start_index = &config[start..].find("index =").unwrap(); config.insert(start + start_index, '#'); fs::write(&cfg_path, config).unwrap(); p.cargo("check").run(); // Important parts: // foo -> bar registry = null // bar -> baz registry = alternate p.cargo("metadata --format-version=1") .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [], "categories": [], "default_run": null, "dependencies": [ { "features": [], "kind": null, "name": "baz", "optional": false, "registry": "[ROOTURL]/alternative-registry", "rename": null, "req": "^0.0.1", "source": "registry+[ROOTURL]/alternative-registry", "target": null, "uses_default_features": true } ], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "registry+https://github.com/rust-lang/crates.io-index#bar@0.0.1", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/bar-0.0.1/Cargo.toml", "metadata": null, "name": "bar", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": "registry+https://github.com/rust-lang/crates.io-index", "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "bar", "src_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/bar-0.0.1/src/lib.rs", "test": true } ], "version": "0.0.1" }, { "authors": [], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "registry+[ROOTURL]/alternative-registry#baz@0.0.1", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/baz-0.0.1/Cargo.toml", "metadata": null, "name": "baz", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": "registry+[ROOTURL]/alternative-registry", "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "baz", "src_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/baz-0.0.1/src/lib.rs", "test": true } ], "version": "0.0.1" }, { "authors": [], "categories": [], "default_run": null, "dependencies": [ { "features": [], "kind": null, "name": "bar", "optional": false, "registry": null, "rename": null, "req": "^0.0.1", "source": "registry+https://github.com/rust-lang/crates.io-index", "target": null, "uses_default_features": true } ], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo#0.0.1", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/Cargo.toml", "metadata": null, "name": "foo", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "bin" ], "doc": true, "doctest": false, "edition": "2015", "kind": [ "bin" ], "name": "foo", "src_path": "[ROOT]/foo/src/main.rs", "test": true } ], "version": "0.0.1" } ], "resolve": { "nodes": [ { "dependencies": [ "registry+[ROOTURL]/alternative-registry#baz@0.0.1" ], "deps": [ { "dep_kinds": [ { "kind": null, "target": null } ], "name": "baz", "pkg": "registry+[ROOTURL]/alternative-registry#baz@0.0.1" } ], "features": [], "id": "registry+https://github.com/rust-lang/crates.io-index#bar@0.0.1" }, { "dependencies": [], "deps": [], "features": [], "id": "registry+[ROOTURL]/alternative-registry#baz@0.0.1" }, { "dependencies": [ "registry+https://github.com/rust-lang/crates.io-index#bar@0.0.1" ], "deps": [ { "dep_kinds": [ { "kind": null, "target": null } ], "name": "bar", "pkg": "registry+https://github.com/rust-lang/crates.io-index#bar@0.0.1" } ], "features": [], "id": "path+[ROOTURL]/foo#0.0.1" } ], "root": "path+[ROOTURL]/foo#0.0.1" }, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo#0.0.1" ], "workspace_members": [ "path+[ROOTURL]/foo#0.0.1" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); } #[cargo_test] fn registries_index_relative_url() { registry::alt_init(); let config = paths::root().join(".cargo/config.toml"); fs::create_dir_all(config.parent().unwrap()).unwrap(); fs::write( &config, r#" [registries.relative] index = "file:alternative-registry" "#, ) .unwrap(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] edition = "2015" [dependencies.bar] version = "0.0.1" registry = "relative" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("bar", "0.0.1").alternative(true).publish(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `relative` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `relative`) [CHECKING] bar v0.0.1 (registry `relative`) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn registries_index_relative_path_not_allowed() { registry::alt_init(); let config = paths::root().join(".cargo/config.toml"); fs::create_dir_all(config.parent().unwrap()).unwrap(); fs::write( &config, r#" [registries.relative] index = "alternative-registry" "#, ) .unwrap(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] edition = "2015" [dependencies.bar] version = "0.0.1" registry = "relative" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("bar", "0.0.1").alternative(true).publish(); p.cargo("check") .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: invalid index URL for registry `relative` defined in [ROOT]/.cargo/config.toml Caused by: invalid url `alternative-registry`: relative URL without a base "#]]) .with_status(101) .run(); } #[cargo_test] fn both_index_and_registry() { let p = project().file("src/lib.rs", "").build(); for cmd in &["publish", "owner", "search", "yank --version 1.0.0"] { p.cargo(cmd) .arg("--registry=foo") .arg("--index=foo") .with_status(1) .with_stderr_data(str![[r#" [ERROR] the argument '--registry ' cannot be used with '--index ' Usage: [..] For more information, try '--help'. "#]]) .run(); } } #[cargo_test] fn both_index_and_default() { let p = project().file("src/lib.rs", "").build(); for cmd in &[ "publish", "owner", "search", "yank --version 1.0.0", "install foo", ] { p.cargo(cmd) .env("CARGO_REGISTRY_DEFAULT", "undefined") .arg(format!("--index=index_url")) .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid url `index_url`: relative URL without a base "#]]) .run(); } } #[cargo_test] fn sparse_lockfile() { let _registry = registry::RegistryBuilder::new() .http_index() .alternative() .build(); Package::new("foo", "0.1.0").alternative(true).publish(); let p = project() .file( "Cargo.toml", r#" [project] name = "a" version = "0.5.0" authors = [] edition = "2015" [dependencies] foo = { registry = 'alternative', version = '0.1.0'} "#, ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); assert_e2e().eq( &p.read_lockfile(), str![[r##" # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "a" version = "0.5.0" dependencies = [ "foo", ] [[package]] name = "foo" version = "0.1.0" source = "sparse+http://127.0.0.1:[..]/index/" checksum = "458c1addb23fde7dfbca0410afdbcc0086f96197281ec304d9e0e10def3cb899" "##]], ); } #[cargo_test] fn publish_with_transitive_dep() { let _alt1 = RegistryBuilder::new() .http_api() .http_index() .alternative_named("Alt-1") .build(); let _alt2 = RegistryBuilder::new() .http_api() .http_index() .alternative_named("Alt-2") .build(); let p1 = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.5.0" edition = "2015" "#, ) .file("src/lib.rs", "") .build(); p1.cargo("publish --registry Alt-1").run(); let p2 = project() .file( "Cargo.toml", r#" [package] name = "b" version = "0.6.0" publish = ["Alt-2"] edition = "2015" [dependencies] a = { version = "0.5.0", registry = "Alt-1" } "#, ) .file("src/lib.rs", "") .build(); p2.cargo("publish").run(); } #[cargo_test] fn warn_for_unused_fields() { let _ = RegistryBuilder::new() .no_configure_token() .alternative() .build(); let p = project() .file("src/lib.rs", "") .file( ".cargo/config.toml", "[registry] unexpected-field = 'foo' [registries.alternative] unexpected-field = 'foo' ", ) .build(); p.cargo("publish --registry alternative") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [WARNING] unused config key `registries.alternative.unexpected-field` in `[ROOT]/foo/.cargo/config.toml` [ERROR] no token found for `alternative`, please run `cargo login --registry alternative` or use environment variable CARGO_REGISTRIES_ALTERNATIVE_TOKEN "#]]) .run(); let crates_io = registry::RegistryBuilder::new() .no_configure_token() .build(); p.cargo("publish --registry crates-io") .replace_crates_io(crates_io.index_url()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] unused config key `registry.unexpected-field` in `[ROOT]/foo/.cargo/config.toml` [ERROR] no token found, please run `cargo login` or use environment variable CARGO_REGISTRY_TOKEN "#]]) .run(); } #[cargo_test] fn config_empty_registry_name() { let _ = RegistryBuilder::new() .no_configure_token() .alternative() .build(); let p = project() .file("src/lib.rs", "") .file( ".cargo/config.toml", "[registry.''] ", ) .build(); p.cargo("publish") .arg("--registry") .arg("") .with_status(101) .with_stderr_data(str![[r#" [ERROR] registry name cannot be empty "#]]) .run(); } #[cargo_test] fn empty_registry_flag() { let p = project().file("src/lib.rs", "").build(); p.cargo("publish") .arg("--registry") .arg("") .with_status(101) .with_stderr_data(str![[r#" [ERROR] registry name cannot be empty "#]]) .run(); } #[cargo_test] fn empty_dependency_registry() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = { version = "0.1.0", registry = "" } "#, ) .file( "src/lib.rs", " extern crate bar; pub fn f() { bar::bar(); } ", ) .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] registry name cannot be empty --> Cargo.toml:8:23 | 8 | bar = { version = "0.1.0", registry = "" } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | "#]]) .run(); } cargo-0.91.0/tests/testsuite/artifact_dep.rs000064400000000000000000003034621046102023000172040ustar 00000000000000//! Tests specific to artifact dependencies, designated using //! the new `dep = { artifact = "bin", … }` syntax in manifests. use crate::prelude::*; use crate::utils::cross_compile::{ can_run_on_host as cross_compile_can_run_on_host, disabled as cross_compile_disabled, }; use cargo_test_support::compare::assert_e2e; use cargo_test_support::registry::{Package, RegistryBuilder}; use cargo_test_support::str; use cargo_test_support::{ Project, basic_bin_manifest, basic_manifest, cross_compile, project, publish, registry, rustc_host, }; #[cargo_test] fn check_with_invalid_artifact_dependency() { // invalid name let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] resolver = "2" [dependencies] bar = { path = "bar/", artifact = "unknown" } "#, ) .file("src/lib.rs", "extern crate bar;") // this would fail but we don't get there, artifacts are no libs .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "") .build(); p.cargo("check -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: 'unknown' is not a valid artifact specifier "#]]) .with_status(101) .run(); fn run_cargo_with_and_without_bindeps_feature( p: &Project, cmd: &str, assert: &dyn Fn(&mut cargo_test_support::Execs), ) { assert( p.cargo(&format!("{} -Z bindeps", cmd)) .masquerade_as_nightly_cargo(&["bindeps"]), ); assert(&mut p.cargo(cmd)); } // lib specified without artifact let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [dependencies] bar = { path = "bar/", lib = true } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "") .build(); run_cargo_with_and_without_bindeps_feature(&p, "check", &|cargo| { cargo .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: 'lib' specifier cannot be used without an 'artifact = …' value (bar) "#]]) .with_status(101) .run(); }); // target specified without artifact let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [dependencies] bar = { path = "bar/", target = "target" } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "") .build(); run_cargo_with_and_without_bindeps_feature(&p, "check", &|cargo| { cargo .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: 'target' specifier cannot be used without an 'artifact = …' value (bar) "#]]) .with_status(101) .run(); }) } #[cargo_test] fn check_with_invalid_target_triple() { // invalid name let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] resolver = "2" [dependencies] bar = { path = "bar/", artifact = "bin", target = "unknown-target-triple" } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("check -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: process didn't exit successfully: `rustc - --crate-name ___ --print=file-names --target unknown-target-triple [..]` ([EXIT_STATUS]: 1) --- stderr ... "#]]) .with_status(101) .run(); } #[cargo_test] fn build_without_nightly_aborts_with_error() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] resolver = "2" [dependencies] bar = { path = "bar/", artifact = "bin" } "#, ) .file("src/lib.rs", "extern crate bar;") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: `artifact = …` requires `-Z bindeps` (bar) "#]]) .run(); } #[cargo_test] fn disallow_artifact_and_no_artifact_dep_to_same_package_within_the_same_dep_category() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] resolver = "2" [dependencies] bar = { path = "bar/", artifact = "bin" } bar_stable = { path = "bar/", package = "bar" } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_bin_manifest("bar")) .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("check -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_status(101) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [WARNING] foo v0.0.0 ([ROOT]/foo) ignoring invalid dependency `bar_stable` which is missing a lib target [ERROR] the crate `foo v0.0.0 ([ROOT]/foo)` depends on crate `bar v0.5.0 ([ROOT]/foo/bar)` multiple times with different names "#]]) .run(); } #[cargo_test] fn features_are_unified_among_lib_and_bin_dep_of_same_target() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] resolver = "2" [dependencies.d1] path = "d1" features = ["d1f1"] artifact = "bin" lib = true [dependencies.d2] path = "d2" features = ["d2f2"] "#, ) .file( "src/main.rs", r#" fn main() { d1::f1(); d1::f2(); d2::f1(); d2::f2(); } "#, ) .file( "d1/Cargo.toml", r#" [package] name = "d1" version = "0.0.1" edition = "2015" authors = [] [features] d1f1 = ["d2"] [dependencies.d2] path = "../d2" features = ["d2f1"] optional = true "#, ) .file( "d1/src/main.rs", r#"fn main() { #[cfg(feature = "d1f1")] d2::f1(); // Using f2 is only possible as features are unififed across the same target. // Our own manifest would only enable f1, and f2 comes in because a parent crate // enables the feature in its manifest. #[cfg(feature = "d1f1")] d2::f2(); }"#, ) .file( "d1/src/lib.rs", r#" #[cfg(feature = "d2")] extern crate d2; /// Importing f2 here shouldn't be possible as unless features are unified. #[cfg(feature = "d1f1")] pub use d2::{f1, f2}; "#, ) .file( "d2/Cargo.toml", r#" [package] name = "d2" version = "0.0.1" edition = "2015" authors = [] [features] d2f1 = [] d2f2 = [] "#, ) .file( "d2/src/lib.rs", r#" #[cfg(feature = "d2f1")] pub fn f1() {} #[cfg(feature = "d2f2")] pub fn f2() {} "#, ) .build(); p.cargo("build -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] d2 v0.0.1 ([ROOT]/foo/d2) [COMPILING] d1 v0.0.1 ([ROOT]/foo/d1) [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn features_are_not_unified_among_lib_and_bin_dep_of_different_target() { if cross_compile_disabled() { return; } let target = cross_compile::alternate(); let p = project() .file( "Cargo.toml", &r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] resolver = "2" [dependencies.d1] path = "d1" features = ["d1f1"] artifact = "bin" lib = true target = "$TARGET" [dependencies.d2] path = "d2" features = ["d2f2"] "# .replace("$TARGET", target), ) .file( "src/main.rs", r#" fn main() { // the lib = true part always builds for our current target, unifying dependencies d1::d2::f1(); d1::d2::f2(); d2::f1(); d2::f2(); } "#, ) .file( "d1/Cargo.toml", r#" [package] name = "d1" version = "0.0.1" edition = "2015" authors = [] [features] d1f1 = ["d2"] [dependencies.d2] path = "../d2" features = ["d2f1"] optional = true "#, ) .file("d1/src/main.rs", r#"fn main() { // f1 we set ourselves d2::f1(); // As 'main' is only compiled as part of the artifact dependency and since that is not unified // if the target differs, trying to access f2 is a compile time error as the feature isn't enabled in our dependency tree. d2::f2(); }"#) .file( "d1/src/lib.rs", r#" #[cfg(feature = "d2")] pub extern crate d2; "#, ) .file( "d2/Cargo.toml", r#" [package] name = "d2" version = "0.0.1" edition = "2015" authors = [] [features] d2f1 = [] d2f2 = [] "#, ) .file( "d2/src/lib.rs", r#" #[cfg(feature = "d2f1")] pub fn f1() {} #[cfg(feature = "d2f2")] pub fn f2() {} "#, ) .build(); p.cargo("build -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_status(101) .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] d2 v0.0.1 ([ROOT]/foo/d2) [COMPILING] d1 v0.0.1 ([ROOT]/foo/d1) error[E0425]: cannot find function `f2` in crate `d2` ... For more information about this error, try `rustc --explain E0425`. [ERROR] could not compile `d1` (bin "d1") due to 1 previous error ... "#]]) .run(); } #[cargo_test] fn feature_resolution_works_for_cfg_target_specification() { if cross_compile_disabled() { return; } let target = cross_compile::alternate(); let p = project() .file( "Cargo.toml", &r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] resolver = "2" [dependencies.d1] path = "d1" artifact = "bin" target = "$TARGET" "# .replace("$TARGET", target), ) .file( "src/main.rs", r#" fn main() { let _b = include_bytes!(env!("CARGO_BIN_FILE_D1")); } "#, ) .file( "d1/Cargo.toml", &r#" [package] name = "d1" version = "0.0.1" edition = "2015" authors = [] [target.'$TARGET'.dependencies] d2 = { path = "../d2" } "# .replace("$TARGET", target), ) .file( "d1/src/main.rs", r#"fn main() { d1::f(); }"#, ) .file("d1/build.rs", r#"fn main() { }"#) .file( "d1/src/lib.rs", &r#"pub fn f() { #[cfg(target = "$TARGET")] d2::f(); } "# .replace("$TARGET", target), ) .file( "d2/Cargo.toml", r#" [package] name = "d2" version = "0.0.1" edition = "2015" authors = [] "#, ) .file("d2/build.rs", r#"fn main() { }"#) .file("d2/src/lib.rs", "pub fn f() {}") .build(); p.cargo("test -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .run(); } #[cargo_test] fn build_script_with_bin_artifacts() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] resolver = "2" [build-dependencies] bar = { path = "bar/", artifact = ["bin", "staticlib", "cdylib"] } "#, ) .file("src/lib.rs", "") .file("build.rs", r#" fn main() { let baz: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR_baz").expect("CARGO_BIN_FILE_BAR_baz").into(); println!("{}", baz.display()); assert!(&baz.is_file()); let lib: std::path::PathBuf = std::env::var("CARGO_STATICLIB_FILE_BAR_bar").expect("CARGO_STATICLIB_FILE_BAR_bar").into(); println!("{}", lib.display()); assert!(&lib.is_file()); let lib: std::path::PathBuf = std::env::var("CARGO_CDYLIB_FILE_BAR_bar").expect("CARGO_CDYLIB_FILE_BAR_bar").into(); println!("{}", lib.display()); assert!(&lib.is_file()); let dir: std::path::PathBuf = std::env::var("CARGO_BIN_DIR_BAR").expect("CARGO_BIN_DIR_BAR").into(); println!("{}", dir.display()); assert!(dir.is_dir()); let bar: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR").expect("CARGO_BIN_FILE_BAR").into(); println!("{}", bar.display()); assert!(&bar.is_file()); let bar2: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR_bar").expect("CARGO_BIN_FILE_BAR_bar").into(); println!("{}", bar2.display()); assert_eq!(bar, bar2); } "#) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = [] [lib] crate-type = ["staticlib", "cdylib"] "#, ) // compilation target is native for build scripts unless overridden .file("bar/src/bin/bar.rs", &format!(r#"fn main() {{ assert_eq!(std::env::var("TARGET").unwrap(), "{}"); }}"#, cross_compile::native())) .file("bar/src/bin/baz.rs", "fn main() {}") .file("bar/src/lib.rs", "") .build(); p.cargo("build -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data( str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [COMPILING] foo v0.0.0 ([ROOT]/foo) "#]] .unordered(), ) .run(); let build_script_output = build_script_output_string(&p, "foo"); // we need the binary directory for this artifact along with all binary paths if cfg!(target_env = "msvc") { assert_e2e().eq( &build_script_output, str![[r#" [ROOT]/foo/target/debug/deps/artifact/bar-[HASH]/bin/baz[EXE] [ROOT]/foo/target/debug/deps/artifact/bar-[HASH]/staticlib/bar-[HASH].lib [ROOT]/foo/target/debug/deps/artifact/bar-[HASH]/cdylib/bar.dll [ROOT]/foo/target/debug/deps/artifact/bar-[HASH]/bin [ROOT]/foo/target/debug/deps/artifact/bar-[HASH]/bin/bar[EXE] [ROOT]/foo/target/debug/deps/artifact/bar-[HASH]/bin/bar[EXE] "#]], ); } else { assert_e2e().eq( &build_script_output, str![[r#" [ROOT]/foo/target/debug/deps/artifact/bar-[HASH]/bin/baz-[HASH][EXE] [ROOT]/foo/target/debug/deps/artifact/bar-[HASH]/staticlib/libbar-[HASH].a [ROOT]/foo/target/debug/deps/artifact/bar-[HASH]/cdylib/[..]bar.[..] [ROOT]/foo/target/debug/deps/artifact/bar-[HASH]/bin [ROOT]/foo/target/debug/deps/artifact/bar-[HASH]/bin/bar-[HASH][EXE] [ROOT]/foo/target/debug/deps/artifact/bar-[HASH]/bin/bar-[HASH][EXE] "#]], ); } assert!( !p.bin("bar").is_file(), "artifacts are located in their own directory, exclusively, and won't be lifted up" ); assert!(!p.bin("baz").is_file(),); assert_artifact_executable_output(&p, "debug", "bar", "bar"); } #[cargo_test] fn build_script_with_bin_artifact_and_lib_false() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] resolver = "2" [build-dependencies] bar = { path = "bar/", artifact = "bin" } "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { bar::doit() } "#, ) .file("bar/Cargo.toml", &basic_bin_manifest("bar")) .file("bar/src/main.rs", "fn main() { bar::doit(); }") .file( "bar/src/lib.rs", r#" pub fn doit() { panic!("sentinel"); } "#, ) .build(); p.cargo("build -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_status(101) .with_stderr_does_not_contain("[..]sentinel[..]") .run(); } #[cargo_test] fn lib_with_bin_artifact_and_lib_false() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] resolver = "2" [dependencies] bar = { path = "bar/", artifact = "bin" } "#, ) .file( "src/lib.rs", r#" pub fn foo() { bar::doit() }"#, ) .file("bar/Cargo.toml", &basic_bin_manifest("bar")) .file("bar/src/main.rs", "fn main() { bar::doit(); }") .file( "bar/src/lib.rs", r#" pub fn doit() { panic!("sentinel"); } "#, ) .build(); p.cargo("build -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_status(101) .with_stderr_does_not_contain("[..]sentinel[..]") .run(); } #[cargo_test] fn build_script_with_selected_dashed_bin_artifact_and_lib_true() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] resolver = "2" [build-dependencies] bar-baz = { path = "bar/", artifact = "bin:baz-suffix", lib = true } "#, ) .file("src/lib.rs", "") .file("build.rs", r#" fn main() { bar_baz::print_env() } "#) .file( "bar/Cargo.toml", r#" [package] name = "bar-baz" version = "0.5.0" edition = "2015" authors = [] [[bin]] name = "bar" [[bin]] name = "baz-suffix" "#, ) .file("bar/src/main.rs", "fn main() {}") .file("bar/src/lib.rs", r#" pub fn print_env() { let dir: std::path::PathBuf = std::env::var("CARGO_BIN_DIR_BAR_BAZ").expect("CARGO_BIN_DIR_BAR_BAZ").into(); let bin: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR_BAZ_baz-suffix").expect("CARGO_BIN_FILE_BAR_BAZ_baz-suffix").into(); println!("{}", dir.display()); println!("{}", bin.display()); assert!(dir.is_dir()); assert!(&bin.is_file()); assert!(std::env::var("CARGO_BIN_FILE_BAR_BAZ").is_err(), "CARGO_BIN_FILE_BAR_BAZ isn't set due to name mismatch"); assert!(std::env::var("CARGO_BIN_FILE_BAR_BAZ_bar").is_err(), "CARGO_BIN_FILE_BAR_BAZ_bar isn't set as binary isn't selected"); } "#) .build(); p.cargo("build -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar-baz v0.5.0 ([ROOT]/foo/bar) [COMPILING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let build_script_output = build_script_output_string(&p, "foo"); // we need the binary directory for this artifact and the binary itself if cfg!(target_env = "msvc") { assert_e2e().eq( &build_script_output, str![[r#" [ROOT]/foo/target/debug/deps/artifact/bar-baz-[HASH]/bin [ROOT]/foo/target/debug/deps/artifact/bar-baz-[HASH]/bin/baz_suffix[EXE] "#]], ); } else { assert_e2e().eq( &build_script_output, str![[r#" [ROOT]/foo/target/debug/deps/artifact/bar-baz-[HASH]/bin [ROOT]/foo/target/debug/deps/artifact/bar-baz-[HASH]/bin/baz_suffix-[HASH][EXE] "#]], ); } assert!( !p.bin("bar").is_file(), "artifacts are located in their own directory, exclusively, and won't be lifted up" ); assert_artifact_executable_output(&p, "debug", "bar", "baz_suffix"); } #[cargo_test] fn lib_with_selected_dashed_bin_artifact_and_lib_true() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] resolver = "2" [dependencies] bar-baz = { path = "bar/", artifact = ["bin:baz-suffix", "staticlib", "cdylib"], lib = true } "#, ) .file( "src/lib.rs", r#" pub fn foo() { bar_baz::exists(); env!("CARGO_BIN_DIR_BAR_BAZ"); let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR_BAZ_baz-suffix")); let _b = include_bytes!(env!("CARGO_STATICLIB_FILE_BAR_BAZ")); let _b = include_bytes!(env!("CARGO_STATICLIB_FILE_BAR_BAZ_bar-baz")); let _b = include_bytes!(env!("CARGO_STATICLIB_FILE_BAR_BAZ_bar_baz")); let _b = include_bytes!(env!("CARGO_CDYLIB_FILE_BAR_BAZ")); let _b = include_bytes!(env!("CARGO_CDYLIB_FILE_BAR_BAZ_bar-baz")); let _b = include_bytes!(env!("CARGO_CDYLIB_FILE_BAR_BAZ_bar_baz")); } "#, ) .file( "bar/Cargo.toml", r#" [package] name = "bar-baz" version = "0.5.0" edition = "2015" authors = [] [lib] crate-type = ["rlib", "staticlib", "cdylib"] [[bin]] name = "bar" [[bin]] name = "baz-suffix" "#, ) .file("bar/src/main.rs", "fn main() {}") .file("bar/src/lib.rs", "pub fn exists() {}") .build(); p.cargo("build -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar-baz v0.5.0 ([ROOT]/foo/bar) [COMPILING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert!( !p.bin("bar").is_file(), "artifacts are located in their own directory, exclusively, and won't be lifted up" ); assert_artifact_executable_output(&p, "debug", "bar", "baz_suffix"); } #[cargo_test] fn allow_artifact_and_no_artifact_dep_to_same_package_within_different_dep_categories() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] resolver = "2" [dependencies] bar = { path = "bar/", artifact = "bin" } [dev-dependencies] bar = { path = "bar/", package = "bar" } "#, ) .file( "src/lib.rs", r#" #[cfg(test)] extern crate bar; pub fn foo() { env!("CARGO_BIN_DIR_BAR"); let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR")); }"#, ) .file("bar/Cargo.toml", &basic_bin_manifest("bar")) .file("bar/src/main.rs", "fn main() {}") .file("bar/src/lib.rs", "") .build(); p.cargo("test -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [COMPILING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) [DOCTEST] foo "#]]) .run(); } #[cargo_test] fn normal_build_deps_are_picked_up_in_presence_of_an_artifact_build_dep_to_the_same_package() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] resolver = "2" [dependencies] bar = { path = "bar", artifact = "bin:bar" } [build-dependencies] bar = { path = "bar" } "#, ) .file("build.rs", "fn main() { bar::f(); }") .file( "src/lib.rs", r#" pub fn foo() { env!("CARGO_BIN_DIR_BAR"); let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR")); }"#, ) .file("bar/Cargo.toml", &basic_bin_manifest("bar")) .file("bar/src/main.rs", "fn main() {}") .file("bar/src/lib.rs", "pub fn f() {}") .build(); p.cargo("check -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .run(); } #[cargo_test] fn disallow_using_example_binaries_as_artifacts() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] resolver = "2" [dependencies] bar = { path = "bar/", artifact = "bin:one-example" } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_bin_manifest("bar")) .file("bar/src/main.rs", "fn main() {}") .file("bar/examples/one-example.rs", "fn main() {}") .build(); p.cargo("build -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_status(101) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [ERROR] dependency `bar` in package `foo` requires a `bin:one-example` artifact to be present. "#]]) .run(); } /// From RFC 3028 /// /// > You may also specify separate dependencies with different artifact values, as well as /// dependencies on the same crate without artifact specified; for instance, you may have a /// build dependency on the binary of a crate and a normal dependency on the Rust library of the same crate. #[cargo_test] fn allow_artifact_and_non_artifact_dependency_to_same_crate() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] resolver = "2" [build-dependencies] bar = { path = "bar/", artifact = "bin" } [dependencies] bar = { path = "bar/" } "#, ) .file("src/lib.rs", r#" pub fn foo() { bar::doit(); assert!(option_env!("CARGO_BIN_FILE_BAR").is_none()); }"#) .file( "build.rs", r#" fn main() { assert!(option_env!("CARGO_BIN_FILE_BAR").is_none(), "no environment variables at build time"); std::process::Command::new(std::env::var("CARGO_BIN_FILE_BAR").expect("BAR present")).status().unwrap(); }"#, ) .file("bar/Cargo.toml", &basic_bin_manifest("bar")) .file("bar/src/main.rs", "fn main() {}") .file("bar/src/lib.rs", "pub fn doit() {}") .build(); p.cargo("check -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [COMPILING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn build_script_deps_adopt_specified_target_unconditionally() { if cross_compile_disabled() { return; } let target = cross_compile::alternate(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] resolver = "2" [build-dependencies.bar] path = "bar/" artifact = "bin" target = "{}" "#, target ), ) .file("src/lib.rs", "") .file("build.rs", r#" fn main() { let bar: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR").expect("CARGO_BIN_FILE_BAR").into(); assert!(&bar.is_file()); }"#) .file("bar/Cargo.toml", &basic_bin_manifest("bar")) .file("bar/src/main.rs", "fn main() {}") .file("bar/src/lib.rs", "pub fn doit() {}") .build(); p.cargo("check -v -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_does_not_contain( "[RUNNING] `rustc --crate-name build_script_build --edition=2015 build.rs [..]--target [ALT_TARGET] [..]", ) .with_stderr_contains("[RUNNING] `rustc --crate-name build_script_build --edition=2015 build.rs [..]") .with_stderr_contains( "[RUNNING] `rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--target [ALT_TARGET] [..]", ) .with_stderr_contains( "[RUNNING] `rustc --crate-name bar --edition=2015 bar/src/main.rs [..]--target [ALT_TARGET] [..]", ) .with_stderr_does_not_contain( "[RUNNING] `rustc --crate-name foo [..]--target [ALT_TARGET] [..]", ) .with_stderr_contains("[RUNNING] `rustc --crate-name foo [..]") .run(); } /// inverse RFC-3176 #[cargo_test] fn build_script_deps_adopt_do_not_allow_multiple_targets_under_different_name_and_same_version() { if cross_compile_disabled() { return; } let alternate = cross_compile::alternate(); let native = cross_compile::native(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] resolver = "2" [build-dependencies.bar] path = "bar/" artifact = "bin" target = "{}" [build-dependencies.bar-native] package = "bar" path = "bar/" artifact = "bin" target = "{}" "#, alternate, native ), ) .file("src/lib.rs", "") .file("build.rs", r#" fn main() { let bar: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR").expect("CARGO_BIN_FILE_BAR").into(); assert!(&bar.is_file()); let bar_native: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR_NATIVE_bar").expect("CARGO_BIN_FILE_BAR_NATIVE_bar").into(); assert!(&bar_native.is_file()); assert_ne!(bar_native, bar, "should build different binaries due to different targets"); }"#) .file("bar/Cargo.toml", &basic_bin_manifest("bar")) .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("check -v -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_status(101) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [ERROR] the crate `foo v0.0.0 ([ROOT]/foo)` depends on crate `bar v0.5.0 ([ROOT]/foo/bar)` multiple times with different names "#]]) .run(); } #[cargo_test] fn non_build_script_deps_adopt_specified_target_unconditionally() { if cross_compile_disabled() { return; } let target = cross_compile::alternate(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] resolver = "2" [dependencies.bar] path = "bar/" artifact = "bin" target = "{}" "#, target ), ) .file( "src/lib.rs", r#"pub fn foo() { let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR")); }"#, ) .file("bar/Cargo.toml", &basic_bin_manifest("bar")) .file("bar/src/main.rs", "fn main() {}") .file("bar/src/lib.rs", "pub fn doit() {}") .build(); p.cargo("check -v -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_contains( "[RUNNING] `rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--target [ALT_TARGET] [..]", ) .with_stderr_contains( "[RUNNING] `rustc --crate-name bar --edition=2015 bar/src/main.rs [..]--target [ALT_TARGET] [..]", ) .with_stderr_does_not_contain( "[RUNNING] `rustc --crate-name foo [..]--target [ALT_TARGET] [..]", ) .with_stderr_contains("[RUNNING] `rustc --crate-name foo [..]") .run(); } #[cargo_test] fn cross_doctests_works_with_artifacts() { if cross_compile_disabled() { return; } let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] resolver = "2" [dependencies] bar = { path = "bar/", artifact = "bin", lib = true } "#, ) .file( "src/lib.rs", r#" //! ``` //! env!("CARGO_BIN_DIR_BAR"); //! let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR")); //! ``` pub fn foo() { env!("CARGO_BIN_DIR_BAR"); let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR")); } "#, ) .file("bar/Cargo.toml", &basic_bin_manifest("bar")) .file("bar/src/lib.rs", r#"pub extern "C" fn c() {}"#) .file("bar/src/main.rs", "fn main() {}") .build(); let target = rustc_host(); p.cargo("test -Z bindeps --target") .arg(&target) .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/[HOST_TARGET]/debug/deps/foo-[HASH][EXE]) [DOCTEST] foo "#]]) .run(); println!("c"); let target = cross_compile::alternate(); if !cross_compile_can_run_on_host() { return; } p.cargo("test -Z bindeps -v --target") .arg(&target) .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [RUNNING] `rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--target [ALT_TARGET] [..] [RUNNING] `rustc --crate-name bar --edition=2015 bar/src/main.rs [..]--target [ALT_TARGET] [..] [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] [RUNNING] `rustc --crate-name foo [..] [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/[ALT_TARGET]/debug/deps/foo-[HASH][EXE]` [DOCTEST] foo [RUNNING] `rustdoc [..]--test src/lib.rs --test-run-directory [ROOT]/foo --target [ALT_TARGET] [..] "#]]) .run(); } #[cargo_test] fn build_script_deps_adopts_target_platform_if_target_equals_target() { if cross_compile_disabled() { return; } let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] resolver = "2" [build-dependencies] bar = { path = "bar/", artifact = "bin", target = "target" } "#, ) .file("src/lib.rs", "") .file("build.rs", r#" fn main() { let bar: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR").expect("CARGO_BIN_FILE_BAR").into(); assert!(&bar.is_file()); }"#) .file("bar/Cargo.toml", &basic_bin_manifest("bar")) .file("bar/src/main.rs", "fn main() {}") .file("bar/src/lib.rs", "pub fn doit() {}") .build(); let alternate_target = cross_compile::alternate(); p.cargo("check -v -Z bindeps --target") .arg(alternate_target) .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_does_not_contain( "[RUNNING] `rustc --crate-name build_script_build --edition=2015 build.rs [..]--target [ALT_TARGET] [..]", ) .with_stderr_contains("[RUNNING] `rustc --crate-name build_script_build --edition=2015 build.rs [..]") .with_stderr_contains( "[RUNNING] `rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--target [ALT_TARGET] [..]", ) .with_stderr_contains( "[RUNNING] `rustc --crate-name bar --edition=2015 bar/src/main.rs [..]--target [ALT_TARGET] [..]", ) .with_stderr_contains( "[RUNNING] `rustc --crate-name foo [..]--target [ALT_TARGET] [..]", ) .run(); } #[cargo_test] // TODO(ST): rename bar (dependency) to something else and un-ignore this with RFC-3176 #[cfg_attr(target_env = "msvc", ignore = "msvc not working")] fn profile_override_basic() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [build-dependencies] bar = { path = "bar", artifact = "bin" } [dependencies] bar = { path = "bar", artifact = "bin" } [profile.dev.build-override] opt-level = 1 [profile.dev] opt-level = 3 "#, ) .file("build.rs", "fn main() {}") .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_bin_manifest("bar")) .file("bar/src/main.rs", "fn main() {}") .file("bar/src/lib.rs", "pub fn bar() {}") .build(); p.cargo("build -v -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data( str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [RUNNING] `rustc --crate-name build_script_build [..] -C opt-level=1 [..]` [RUNNING] `rustc --crate-name bar --edition=2015 bar/src/main.rs [..] -C opt-level=3 [..]` [RUNNING] `rustc --crate-name bar --edition=2015 bar/src/main.rs [..] -C opt-level=1 [..]` [RUNNING] `rustc --crate-name bar --edition=2015 bar/src/lib.rs [..] -C opt-level=1 [..]` [RUNNING] `rustc --crate-name bar --edition=2015 bar/src/lib.rs [..] -C opt-level=3 [..]` [RUNNING] `rustc --crate-name foo [..] -C opt-level=3 [..]` [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [FINISHED] `dev` profile [optimized + debuginfo] target(s) in [ELAPSED]s [COMPILING] foo v0.0.1 ([ROOT]/foo) "#]] .unordered(), ) .run(); } #[cargo_test] fn dependencies_of_dependencies_work_in_artifacts() { Package::new("baz", "1.0.0") .file("src/lib.rs", "pub fn baz() {}") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] resolver = "2" [build-dependencies] bar = { path = "bar/", artifact = "bin" } "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { std::process::Command::new(std::env::var("CARGO_BIN_FILE_BAR").expect("BAR present")).status().unwrap(); } "#, ) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = [] [dependencies] baz = "1.0.0" "#, ) .file("bar/src/lib.rs", r#"pub fn bar() {baz::baz()}"#) .file("bar/src/main.rs", r#"fn main() {bar::bar()}"#) .build(); p.cargo("build -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .run(); // cargo tree sees artifacts as the dependency kind they are in and doesn't do anything special with it. p.cargo("tree -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stdout_data(str![[r#" foo v0.0.0 ([ROOT]/foo) [build-dependencies] └── bar v0.5.0 ([ROOT]/foo/bar) └── baz v1.0.0 "#]]) .run(); } #[cargo_test] fn artifact_dep_target_specified() { if cross_compile_disabled() { return; } let target = cross_compile::alternate(); let p = project() .file( "Cargo.toml", &r#" [package] name = "foo" version = "0.0.0" authors = [] resolver = "2" edition = "2015" [dependencies] bindep = { path = "bindep", artifact = "bin", target = "$TARGET" } "# .replace("$TARGET", target), ) .file("src/lib.rs", "") .file("bindep/Cargo.toml", &basic_manifest("bindep", "0.0.0")) .file("bindep/src/main.rs", "fn main() {}") .build(); p.cargo("check -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bindep v0.0.0 ([ROOT]/foo/bindep) [CHECKING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .with_status(0) .run(); p.cargo("tree -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stdout_data(str![[r#" foo v0.0.0 ([ROOT]/foo) └── bindep v0.0.0 ([ROOT]/foo/bindep) "#]]) .with_status(0) .run(); } /// From issue #10593 /// The case where: /// * artifact dep is { target = } /// * dependency of that artifact dependency specifies the same target /// * the target is not activated. #[cargo_test] fn dep_of_artifact_dep_same_target_specified() { if cross_compile_disabled() { return; } let target = cross_compile::alternate(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2015" resolver = "2" [dependencies] bar = {{ path = "bar", artifact = "bin", target = "{target}" }} "#, ), ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", &format!( r#" [package] name = "bar" version = "0.1.0" [target.{target}.dependencies] baz = {{ path = "../baz" }} "#, ), ) .file("bar/src/main.rs", "fn main() {}") .file( "baz/Cargo.toml", r#" [package] name = "baz" version = "0.1.0" "#, ) .file("baz/src/lib.rs", "") .build(); p.cargo("check -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] baz v0.1.0 ([ROOT]/foo/baz) [COMPILING] bar v0.1.0 ([ROOT]/foo/bar) [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .with_status(0) .run(); p.cargo("tree -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stdout_data( r#"... foo v0.1.0 ([ROOT]/foo) └── bar v0.1.0 ([ROOT]/foo/bar) └── baz v0.1.0 ([ROOT]/foo/baz) "#, ) .with_status(0) .run(); } #[cargo_test] fn targets_are_picked_up_from_non_workspace_artifact_deps() { if cross_compile_disabled() { return; } let target = cross_compile::alternate(); Package::new("artifact", "1.0.0") .file("src/main.rs", r#"fn main() {}"#) .file("src/lib.rs", r#"pub fn lib() {}"#) .publish(); let mut dep = registry::Dependency::new("artifact", "1.0.0"); Package::new("uses-artifact", "1.0.0") .schema_version(3) .file( "src/lib.rs", r#"pub fn uses_artifact() { let _b = include_bytes!(env!("CARGO_BIN_FILE_ARTIFACT")); }"#, ) .add_dep(dep.artifact("bin", Some(target.to_string()))) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [dependencies] uses-artifact = { version = "1.0.0" } "#, ) .file( "src/lib.rs", r#"pub fn foo() { uses_artifact::uses_artifact(); }"#, ) .build(); p.cargo("build -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .run(); } #[cargo_test] fn index_version_filtering() { if cross_compile_disabled() { return; } let target = cross_compile::alternate(); Package::new("artifact", "1.0.0") .file("src/main.rs", r#"fn main() {}"#) .file("src/lib.rs", r#"pub fn lib() {}"#) .publish(); let mut dep = registry::Dependency::new("artifact", "1.0.0"); Package::new("bar", "1.0.0").publish(); Package::new("bar", "1.0.1") .schema_version(3) .add_dep(dep.artifact("bin", Some(target.to_string()))) .publish(); // Verify that without `-Zbindeps` that it does not use 1.0.1. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── bar v1.0.0 "#]]) .run(); // And with -Zbindeps it can use 1.0.1. p.cargo("update -Zbindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [ADDING] artifact v1.0.0 [UPDATING] bar v1.0.0 -> v1.0.1 "#]]) .run(); // And without -Zbindeps, now that 1.0.1 is in Cargo.lock, it should fail. p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for the requirement `bar = "^1.0"` (locked to 1.0.1) version 1.0.1 requires a Cargo version that supports index version 3 location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.1.0 ([ROOT]/foo)` "#]]) .run(); } #[cargo_test] fn proc_macro_in_artifact_dep() { // Forcing FeatureResolver to check a proc-macro for a dependency behind a // target dependency. if cross_compile_disabled() { return; } Package::new("pm", "1.0.0") .file("src/lib.rs", "") .file( "Cargo.toml", r#" [package] name = "pm" version = "1.0.0" edition = "2015" [lib] proc-macro = true "#, ) .publish(); let alternate = cross_compile::alternate(); Package::new("bin-uses-pm", "1.0.0") .target_dep("pm", "1.0", alternate) .file("src/main.rs", "fn main() {}") .publish(); // Simulate a network error downloading the proc-macro. std::fs::remove_file(cargo_test_support::paths::root().join("dl/pm/1.0.0/download")).unwrap(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2021" [dependencies] bin-uses-pm = {{ version = "1.0", artifact = "bin", target = "{alternate}"}} "# ), ) .file("src/lib.rs", "") .build(); p.cargo("check -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data( r#"... [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [ERROR] failed to download from `[ROOTURL]/dl/pm/1.0.0/download` Caused by: [37] Could[..]t read a file:// file (Couldn't open file [ROOT]/dl/pm/1.0.0/download) "#, ) .with_status(101) .run(); } #[cargo_test] fn allow_dep_renames_with_multiple_versions() { Package::new("bar", "1.0.0") .file("src/main.rs", r#"fn main() {println!("1.0.0")}"#) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] resolver = "2" [build-dependencies] bar = { path = "bar/", artifact = "bin" } bar_stable = { package = "bar", version = "1.0.0", artifact = "bin" } "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { std::process::Command::new(std::env::var("CARGO_BIN_FILE_BAR").expect("BAR present")).status().unwrap(); std::process::Command::new(std::env::var("CARGO_BIN_FILE_BAR_STABLE_bar").expect("BAR STABLE present")).status().unwrap(); } "#, ) .file("bar/Cargo.toml", &basic_bin_manifest("bar")) .file("bar/src/main.rs", r#"fn main() {println!("0.5.0")}"#) .build(); p.cargo("check -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [COMPILING] bar v1.0.0 [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [COMPILING] foo v0.0.0 ([ROOT]/foo) "#]] .unordered(), ) .run(); let build_script_output = build_script_output_string(&p, "foo"); assert_e2e().eq( &build_script_output, str![[r#" 0.5.0 1.0.0 "#]], ); } #[cargo_test] fn allow_artifact_and_non_artifact_dependency_to_same_crate_if_these_are_not_the_same_dep_kind() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] resolver = "2" [build-dependencies] bar = { path = "bar/", artifact = "bin", lib = false } [dependencies] bar = { path = "bar/" } "#, ) .file("src/lib.rs", r#" pub fn foo() { bar::doit(); assert!(option_env!("CARGO_BIN_FILE_BAR").is_none()); }"#) .file( "build.rs", r#"fn main() { println!("{}", std::env::var("CARGO_BIN_FILE_BAR").expect("CARGO_BIN_FILE_BAR")); println!("{}", std::env::var("CARGO_BIN_FILE_BAR_bar").expect("CARGO_BIN_FILE_BAR_bar")); }"#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "pub fn doit() {}") .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("build -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [COMPILING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn prevent_no_lib_warning_with_artifact_dependencies() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] resolver = "2" [dependencies] bar = { path = "bar/", artifact = "bin" } "#, ) .file( "src/lib.rs", r#"pub fn foo() { let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR")); }"#, ) .file("bar/Cargo.toml", &basic_bin_manifest("bar")) .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("check -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [CHECKING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn show_no_lib_warning_with_artifact_dependencies_that_have_no_lib_but_lib_true() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] resolver = "2" [build-dependencies] bar = { path = "bar/", artifact = "bin" } [dependencies] bar = { path = "bar/", artifact = "bin", lib = true } "#, ) .file("src/lib.rs", "") .file("src/build.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_bin_manifest("bar")) .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("check -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [WARNING] foo v0.0.0 ([ROOT]/foo) ignoring invalid dependency `bar` which is missing a lib target [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [CHECKING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn resolver_2_build_dep_without_lib() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" authors = [] edition = "2021" [build-dependencies] bar = { path = "bar/", artifact = "bin" } "#, ) .file("src/lib.rs", "") .file("build.rs", r#" fn main() { let bar: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR").expect("CARGO_BIN_FILE_BAR").into(); assert!(&bar.is_file()); }"#) .file("bar/Cargo.toml", &basic_bin_manifest("bar")) .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("check -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .run(); } #[cargo_test] fn check_missing_crate_type_in_package_fails() { for crate_type in &["cdylib", "staticlib", "bin"] { let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [dependencies] bar = {{ path = "bar/", artifact = "{}" }} "#, crate_type ), ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) //no bin, just rlib .file("bar/src/lib.rs", "") .build(); p.cargo("check -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_status(101) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [ERROR] dependency `bar` in package `foo` requires a [..] artifact to be present. "#]]) .run(); } } #[cargo_test] fn check_target_equals_target_in_non_build_dependency_errors() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] resolver = "2" [dependencies] bar = { path = "bar/", artifact = "bin", target = "target" } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("check -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: `target = "target"` in normal- or dev-dependencies has no effect (bar) "#]]) .run(); } #[cargo_test] fn env_vars_and_build_products_for_various_build_targets() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] resolver = "2" [lib] doctest = true [build-dependencies] bar = { path = "bar/", artifact = ["cdylib", "staticlib"] } [dependencies] bar = { path = "bar/", artifact = "bin", lib = true } [dev-dependencies] bar = { path = "bar/", artifact = "bin:baz" } "#, ) .file("build.rs", r#" fn main() { let file: std::path::PathBuf = std::env::var("CARGO_CDYLIB_FILE_BAR").expect("CARGO_CDYLIB_FILE_BAR").into(); assert!(&file.is_file()); let file: std::path::PathBuf = std::env::var("CARGO_STATICLIB_FILE_BAR").expect("CARGO_STATICLIB_FILE_BAR").into(); assert!(&file.is_file()); assert!(std::env::var("CARGO_BIN_FILE_BAR").is_err()); assert!(std::env::var("CARGO_BIN_FILE_BAR_baz").is_err()); } "#) .file( "src/lib.rs", r#" //! ``` //! bar::c(); //! env!("CARGO_BIN_DIR_BAR"); //! let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR")); //! let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR_bar")); //! let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR_baz")); //! assert!(option_env!("CARGO_STATICLIB_FILE_BAR").is_none()); //! assert!(option_env!("CARGO_CDYLIB_FILE_BAR").is_none()); //! ``` pub fn foo() { bar::c(); env!("CARGO_BIN_DIR_BAR"); let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR")); let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR_bar")); let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR_baz")); assert!(option_env!("CARGO_STATICLIB_FILE_BAR").is_none()); assert!(option_env!("CARGO_CDYLIB_FILE_BAR").is_none()); } #[cfg(test)] #[test] fn env_unit() { env!("CARGO_BIN_DIR_BAR"); let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR")); let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR_bar")); let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR_baz")); assert!(option_env!("CARGO_STATICLIB_FILE_BAR").is_none()); assert!(option_env!("CARGO_CDYLIB_FILE_BAR").is_none()); } "#, ) .file( "tests/main.rs", r#" #[test] fn env_integration() { env!("CARGO_BIN_DIR_BAR"); let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR")); let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR_bar")); let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR_baz")); }"#, ) .file("build.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = [] [lib] crate-type = ["staticlib", "cdylib", "rlib"] [[bin]] name = "bar" [[bin]] name = "baz" "#, ) .file("bar/src/lib.rs", r#"pub extern "C" fn c() {}"#) .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("test -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [COMPILING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) [RUNNING] tests/main.rs (target/debug/deps/main-[HASH][EXE]) [DOCTEST] foo "#]]) .run(); } #[cargo_test] fn publish_artifact_dep() { let registry = RegistryBuilder::new().http_api().http_index().build(); Package::new("bar", "1.0.0").publish(); Package::new("baz", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] license = "MIT" description = "foo" documentation = "foo" homepage = "foo" repository = "foo" resolver = "2" [dependencies] bar = { version = "1.0", artifact = "bin", lib = true } [build-dependencies] baz = { version = "1.0", artifact = ["bin:a", "cdylib", "staticlib"], target = "target" } "#, ) .file("src/lib.rs", "") .build(); p.cargo("publish -Z bindeps --no-verify") .replace_crates_io(registry.index_url()) .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [UPDATING] crates.io index [PACKAGING] foo v0.1.0 ([ROOT]/foo) [UPDATING] crates.io index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] foo v0.1.0 ([ROOT]/foo) [UPLOADED] foo v0.1.0 to registry `crates-io` [NOTE] waiting for foo v0.1.0 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.1.0 at registry `crates-io` "#]]) .run(); publish::validate_upload_with_contents( r#" { "authors": [], "badges": {}, "categories": [], "deps": [{ "artifact": ["bin"], "default_features": true, "features": [], "kind": "normal", "lib": true, "name": "bar", "optional": false, "target": null, "version_req": "^1.0" }, { "artifact": [ "bin:a", "cdylib", "staticlib" ], "bindep_target": "target", "default_features": true, "features": [], "kind": "build", "name": "baz", "optional": false, "target": null, "version_req": "^1.0" } ], "description": "foo", "documentation": "foo", "features": {}, "homepage": "foo", "keywords": [], "license": "MIT", "license_file": null, "links": null, "name": "foo", "readme": null, "readme_file": null, "repository": "foo", "rust_version": null, "vers": "0.1.0" } "#, "foo-0.1.0.crate", &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs", "Cargo.lock"], [( "Cargo.toml", str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "foo" version = "0.1.0" authors = [] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" homepage = "foo" documentation = "foo" readme = false license = "MIT" repository = "foo" resolver = "2" [lib] name = "foo" path = "src/lib.rs" [dependencies.bar] version = "1.0" artifact = ["bin"] lib = true [build-dependencies.baz] version = "1.0" artifact = [ "bin:a", "cdylib", "staticlib", ] target = "target" "##]], )], ); } #[cargo_test] fn doc_lib_true() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] resolver = "2" [dependencies.bar] path = "bar" artifact = "bin" lib = true "#, ) .file("src/lib.rs", "extern crate bar; pub fn foo() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "pub fn bar() {}") .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("doc -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [DOCUMENTING] bar v0.0.1 ([ROOT]/foo/bar) [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); assert!(p.root().join("target/doc").is_dir()); assert!(p.root().join("target/doc/foo/index.html").is_file()); assert!(p.root().join("target/doc/bar/index.html").is_file()); // Verify that it emits rmeta for the bin and lib dependency. assert_eq!(p.glob("target/debug/artifact/*.rlib").count(), 0); assert_eq!(p.glob("target/debug/deps/libbar-*.rmeta").count(), 2); p.cargo("doc -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); assert!(p.root().join("target/doc").is_dir()); assert!(p.root().join("target/doc/foo/index.html").is_file()); assert!(p.root().join("target/doc/bar/index.html").is_file()); } #[cargo_test] fn rustdoc_works_on_libs_with_artifacts_and_lib_false() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] resolver = "2" [dependencies.bar] path = "bar" artifact = ["bin", "staticlib", "cdylib"] "#, ) .file( "src/lib.rs", r#" pub fn foo() { env!("CARGO_BIN_DIR_BAR"); let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR")); let _b = include_bytes!(env!("CARGO_CDYLIB_FILE_BAR")); let _b = include_bytes!(env!("CARGO_CDYLIB_FILE_BAR_bar")); let _b = include_bytes!(env!("CARGO_STATICLIB_FILE_BAR")); let _b = include_bytes!(env!("CARGO_STATICLIB_FILE_BAR_bar")); }"#, ) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = [] [lib] crate-type = ["staticlib", "cdylib"] "#, ) .file("bar/src/lib.rs", "pub fn bar() {}") .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("doc -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); assert!(p.root().join("target/doc").is_dir()); assert!(p.root().join("target/doc/foo/index.html").is_file()); assert!( !p.root().join("target/doc/bar/index.html").is_file(), "bar is not a lib dependency and thus remains undocumented" ); } fn assert_artifact_executable_output( p: &Project, target_name: &str, dep_name: &str, bin_name: &str, ) { if cfg!(target_env = "msvc") { assert_eq!( p.glob(format!( "target/{}/deps/artifact/{}-*/bin/{}{}", target_name, dep_name, bin_name, std::env::consts::EXE_SUFFIX )) .count(), 1, "artifacts are placed into their own output directory to not possibly clash" ); } else { assert_eq!( p.glob(format!( "target/{}/deps/artifact/{}-*/bin/{}-*{}", target_name, dep_name, bin_name, std::env::consts::EXE_SUFFIX )) .filter_map(Result::ok) .filter(|f| f.extension().map_or(true, |ext| ext != "o" && ext != "d")) .count(), 1, "artifacts are placed into their own output directory to not possibly clash" ); } } fn build_script_output_string(p: &Project, package_name: &str) -> String { let paths = p .glob(format!("target/debug/build/{}-*/output", package_name)) .collect::, _>>() .unwrap(); assert_eq!(paths.len(), 1); std::fs::read_to_string(&paths[0]).unwrap() } #[cargo_test] fn build_script_features_for_shared_dependency() { // When a build script is built and run, its features should match. Here: // // foo // -> artifact on d1 with target // -> common with features f1 // // d1 // -> common with features f2 // // common has features f1 and f2, with a build script. // // When common is built as a dependency of d1, it should have features // `f2` (for the library and the build script). // // When common is built as a dependency of foo, it should have features // `f1` (for the library and the build script). if cross_compile_disabled() { return; } let target = cross_compile::alternate(); let p = project() .file( "Cargo.toml", &r#" [package] name = "foo" version = "0.0.1" edition = "2015" resolver = "2" [dependencies] d1 = { path = "d1", artifact = "bin", target = "$TARGET" } common = { path = "common", features = ["f1"] } "# .replace("$TARGET", target), ) .file( "src/main.rs", r#" fn main() { let _b = include_bytes!(env!("CARGO_BIN_FILE_D1")); common::f1(); } "#, ) .file( "d1/Cargo.toml", r#" [package] name = "d1" version = "0.0.1" edition = "2015" [dependencies] common = { path = "../common", features = ["f2"] } "#, ) .file( "d1/src/main.rs", r#"fn main() { common::f2(); }"#, ) .file( "common/Cargo.toml", r#" [package] name = "common" version = "0.0.1" edition = "2015" [features] f1 = [] f2 = [] "#, ) .file( "common/src/lib.rs", r#" #[cfg(feature = "f1")] pub fn f1() {} #[cfg(feature = "f2")] pub fn f2() {} "#, ) .file( "common/build.rs", &r#" use std::env::var_os; fn main() { assert_eq!(var_os("CARGO_FEATURE_F1").is_some(), cfg!(feature="f1")); assert_eq!(var_os("CARGO_FEATURE_F2").is_some(), cfg!(feature="f2")); if std::env::var("TARGET").unwrap() == "$TARGET" { assert!(var_os("CARGO_FEATURE_F1").is_none()); assert!(var_os("CARGO_FEATURE_F2").is_some()); } else { assert!(var_os("CARGO_FEATURE_F1").is_some()); assert!(var_os("CARGO_FEATURE_F2").is_none()); } } "# .replace("$TARGET", target), ) .build(); p.cargo("build -Z bindeps -v") .masquerade_as_nightly_cargo(&["bindeps"]) .run(); } #[cargo_test] fn calc_bin_artifact_fingerprint() { // See rust-lang/cargo#10527 let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" resolver = "2" [dependencies] bar = { path = "bar/", artifact = "bin" } "#, ) .file( "src/main.rs", r#" fn main() { let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR")); } "#, ) .file("bar/Cargo.toml", &basic_bin_manifest("bar")) .file("bar/src/main.rs", r#"fn main() { println!("foo") }"#) .build(); p.cargo("check -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.change_file("bar/src/main.rs", r#"fn main() { println!("bar") }"#); // Change in artifact bin dep `bar` propagates to `foo`, triggering recompile. p.cargo("check -v -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [DIRTY] bar v0.5.0 ([ROOT]/foo/bar): the file `bar/src/main.rs` has changed ([..]) [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [RUNNING] `rustc --crate-name bar [..]` [DIRTY] foo v0.1.0 ([ROOT]/foo): the dependency bar was rebuilt [CHECKING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // All units are fresh. No recompile. p.cargo("check -v -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [FRESH] bar v0.5.0 ([ROOT]/foo/bar) [FRESH] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn with_target_and_optional() { // See rust-lang/cargo#10526 if cross_compile_disabled() { return; } let target = cross_compile::alternate(); let p = project() .file( "Cargo.toml", &r#" [package] name = "foo" version = "0.0.1" edition = "2021" [dependencies] d1 = { path = "d1", artifact = "bin", optional = true, target = "$TARGET" } "# .replace("$TARGET", target), ) .file( "src/main.rs", r#" fn main() { let _b = include_bytes!(env!("CARGO_BIN_FILE_D1")); } "#, ) .file( "d1/Cargo.toml", r#" [package] name = "d1" version = "0.0.1" edition = "2021" "#, ) .file("d1/src/main.rs", "fn main() {}") .build(); p.cargo("check -Z bindeps -F d1 -v") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] d1 v0.0.1 ([ROOT]/foo/d1) [RUNNING] `rustc --crate-name d1 [..]--crate-type bin[..] [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]--cfg[..]d1[..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn with_assumed_host_target_and_optional_build_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2021" [build-dependencies] d1 = { path = "d1", artifact = "bin", optional = true, target = "target" } "#, ) .file("src/main.rs", "fn main() {}") .file( "build.rs", r#" fn main() { std::env::var("CARGO_BIN_FILE_D1").unwrap(); } "#, ) .file( "d1/Cargo.toml", r#" [package] name = "d1" version = "0.0.1" edition = "2021" "#, ) .file("d1/src/main.rs", "fn main() {}") .build(); p.cargo("check -Z bindeps -F d1 -v") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data( str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] d1 v0.0.1 ([ROOT]/foo/d1) [RUNNING] `rustc --crate-name build_script_build --edition=2021 [..]--crate-type bin[..] [RUNNING] `rustc --crate-name d1 --edition=2021 [..]--crate-type bin[..] [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc --crate-name foo --edition=2021 [..]--cfg[..]d1[..] [FINISHED] `dev` profile [..] [COMPILING] foo v0.0.1 ([ROOT]/foo) "#]] .unordered(), ) .run(); } #[cargo_test] fn decouple_same_target_transitive_dep_from_artifact_dep() { // See https://github.com/rust-lang/cargo/issues/11463 let target = rustc_host(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2021" [dependencies] a = {{ path = "a" }} bar = {{ path = "bar", artifact = "bin", target = "{target}" }} "# ), ) .file( "src/main.rs", r#" fn main() {} "#, ) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" [dependencies] a = { path = "../a", features = ["feature"] } "#, ) .file( "bar/src/main.rs", r#" fn main() {} "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2021" [dependencies] b = { path = "../b" } c = { path = "../c" } [features] feature = ["c/feature"] "#, ) .file( "a/src/lib.rs", r#" use b::Trait as _; pub fn use_b_trait(x: &impl c::Trait) { x.b(); } "#, ) .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" edition = "2015" [dependencies] c = { path = "../c" } "#, ) .file( "b/src/lib.rs", r#" pub trait Trait { fn b(&self) {} } impl Trait for T {} "#, ) .file( "c/Cargo.toml", r#" [package] name = "c" version = "0.1.0" edition = "2015" [features] feature = [] "#, ) .file( "c/src/lib.rs", r#" pub trait Trait {} "#, ) .build(); p.cargo("build -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [LOCKING] 4 packages to latest compatible versions [COMPILING] c v0.1.0 ([ROOT]/foo/c) [COMPILING] b v0.1.0 ([ROOT]/foo/b) [COMPILING] a v0.1.0 ([ROOT]/foo/a) [COMPILING] bar v0.1.0 ([ROOT]/foo/bar) [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn decouple_same_target_transitive_dep_from_artifact_dep_lib() { // See https://github.com/rust-lang/cargo/issues/10837 let target = rustc_host(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2021" [dependencies] a = {{ path = "a" }} b = {{ path = "b", features = ["feature"] }} bar = {{ path = "bar", artifact = "bin", lib = true, target = "{target}" }} "# ), ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2021" [dependencies] a = { path = "../a", features = ["b"] } b = { path = "../b" } "#, ) .file("bar/src/lib.rs", "") .file( "bar/src/main.rs", r#" use b::Trait; fn main() { a::A.b() } "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" [dependencies] b = { path = "../b", optional = true } "#, ) .file( "a/src/lib.rs", r#" pub struct A; #[cfg(feature = "b")] impl b::Trait for A {} "#, ) .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" edition = "2015" [features] feature = [] "#, ) .file( "b/src/lib.rs", r#" pub trait Trait { fn b(&self) {} } "#, ) .build(); p.cargo("build -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [LOCKING] 3 packages to latest compatible versions [COMPILING] b v0.1.0 ([ROOT]/foo/b) [COMPILING] a v0.1.0 ([ROOT]/foo/a) [COMPILING] bar v0.1.0 ([ROOT]/foo/bar) [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn decouple_same_target_transitive_dep_from_artifact_dep_and_proc_macro() { let target = rustc_host(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2021" [dependencies] c = {{ path = "c" }} bar = {{ path = "bar", artifact = "bin", target = "{target}" }} "# ), ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" [dependencies] b = { path = "../b" } "#, ) .file("bar/src/main.rs", "fn main() {}") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" edition = "2021" [dependencies] a = { path = "../a" } [lib] proc-macro = true "#, ) .file("b/src/lib.rs", "") .file( "c/Cargo.toml", r#" [package] name = "c" version = "0.1.0" edition = "2021" [dependencies] d = { path = "../d", features = ["feature"] } a = { path = "../a" } [lib] proc-macro = true "#, ) .file( "c/src/lib.rs", r#" use a::Trait; fn _c() { d::D.a() } "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" [dependencies] d = { path = "../d" } "#, ) .file( "a/src/lib.rs", r#" pub trait Trait { fn a(&self) {} } impl Trait for d::D {} "#, ) .file( "d/Cargo.toml", r#" [package] name = "d" version = "0.1.0" edition = "2015" [features] feature = [] "#, ) .file("d/src/lib.rs", "pub struct D;") .build(); p.cargo("build -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data( str![[r#" [LOCKING] 5 packages to latest compatible versions [COMPILING] d v0.1.0 ([ROOT]/foo/d) [COMPILING] a v0.1.0 ([ROOT]/foo/a) [COMPILING] b v0.1.0 ([ROOT]/foo/b) [COMPILING] c v0.1.0 ([ROOT]/foo/c) [COMPILING] bar v0.1.0 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [..] [COMPILING] foo v0.1.0 ([ROOT]/foo) "#]] .unordered(), ) .run(); } #[cargo_test] fn same_target_artifact_dep_sharing() { let target = rustc_host(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] a = {{ path = "a" }} bar = {{ path = "bar", artifact = "bin", target = "{target}" }} "# ), ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" [dependencies] a = { path = "../a" } "#, ) .file( "bar/src/main.rs", r#" fn main() {} "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" "#, ) .file("a/src/lib.rs", "") .build(); p.cargo(&format!("build -Z bindeps --target {target}")) .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] a v0.1.0 ([ROOT]/foo/a) [COMPILING] bar v0.1.0 ([ROOT]/foo/bar) [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn check_transitive_artifact_dependency_with_different_target() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" [dependencies] bar = { path = "bar/" } "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.0" edition = "2015" [dependencies] baz = { path = "baz/", artifact = "bin", target = "custom-target" } "#, ) .file("bar/src/lib.rs", "") .file( "bar/baz/Cargo.toml", r#" [package] name = "baz" version = "0.0.0" edition = "2015" [dependencies] "#, ) .file("bar/baz/src/main.rs", "fn main() {}") .build(); p.cargo("check -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [ERROR] failed to determine target information for target `custom-target`. Artifact dependency `baz` in package `bar v0.0.0 ([ROOT]/foo/bar)` requires building for `custom-target` Caused by: failed to run `rustc` to learn about target-specific information Caused by: process didn't exit successfully: `rustc [..] ([EXIT_STATUS]: 1) --- stderr ... "#]]) .with_status(101) .run(); } #[cargo_test] fn build_only_specified_artifact_library() { // Create a project with: // - A crate `bar` with both `staticlib` and `cdylib` as crate-types. // - A crate `foo` which depends on either the `staticlib` or `cdylib` artifact of bar, // whose build-script simply checks which library artifacts are present. let create_project = |artifact_lib| { project() .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "1.0.0" [lib] crate-type = ["staticlib", "cdylib"] "#, ) .file("bar/src/lib.rs", "") .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "1.0.0" [build-dependencies] bar = {{ path = "bar", artifact = "{artifact_lib}" }} "#), ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { println!("cdylib present: {}", std::env::var_os("CARGO_CDYLIB_FILE_BAR").is_some()); println!("staticlib present: {}", std::env::var_os("CARGO_STATICLIB_FILE_BAR").is_some()); } "#, ) .build() }; let cdylib = create_project("cdylib"); cdylib .cargo("build -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .run(); assert_e2e().eq( &build_script_output_string(&cdylib, "foo"), str![[r#" cdylib present: true staticlib present: false "#]], ); let staticlib = create_project("staticlib"); staticlib .cargo("build -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .run(); assert_e2e().eq( &build_script_output_string(&staticlib, "foo"), str![[r#" cdylib present: false staticlib present: true "#]], ); } cargo-0.91.0/tests/testsuite/artifact_dir.rs000064400000000000000000000242111046102023000172020ustar 00000000000000//! Tests for --artifact-dir flag. use std::env; use std::fs; use std::path::Path; use crate::prelude::*; use cargo_test_support::sleep_ms; use cargo_test_support::str; use cargo_test_support::{basic_manifest, project}; #[cargo_test] fn binary_with_debug() { let p = project() .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) .build(); p.cargo("build -Z unstable-options --artifact-dir out") .masquerade_as_nightly_cargo(&["artifact-dir"]) .enable_mac_dsym() .run(); check_dir_contents( &p.root().join("out"), &["foo"], &["foo", "foo.dSYM"], &["foo.exe", "foo.pdb"], &["foo.exe"], ); } #[cargo_test] fn static_library_with_debug() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [lib] crate-type = ["staticlib"] "#, ) .file( "src/lib.rs", r#" #[no_mangle] pub extern "C" fn foo() { println!("Hello, World!") } "#, ) .build(); p.cargo("build -Z unstable-options --artifact-dir out") .masquerade_as_nightly_cargo(&["artifact-dir"]) .run(); check_dir_contents( &p.root().join("out"), &["libfoo.a"], &["libfoo.a"], &["foo.lib"], &["libfoo.a"], ); } #[cargo_test] fn dynamic_library_with_debug() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [lib] crate-type = ["cdylib"] "#, ) .file( "src/lib.rs", r#" #[no_mangle] pub extern "C" fn foo() { println!("Hello, World!") } "#, ) .build(); p.cargo("build -Z unstable-options --artifact-dir out") .masquerade_as_nightly_cargo(&["artifact-dir"]) .enable_mac_dsym() .run(); check_dir_contents( &p.root().join("out"), &["libfoo.so"], &["libfoo.dylib", "libfoo.dylib.dSYM"], &["foo.dll", "foo.dll.exp", "foo.dll.lib", "foo.pdb"], &["foo.dll", "libfoo.dll.a"], ); } #[cargo_test] fn rlib_with_debug() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [lib] crate-type = ["rlib"] "#, ) .file( "src/lib.rs", r#" pub fn foo() { println!("Hello, World!") } "#, ) .build(); p.cargo("build -Z unstable-options --artifact-dir out") .masquerade_as_nightly_cargo(&["artifact-dir"]) .run(); check_dir_contents( &p.root().join("out"), &["libfoo.rlib"], &["libfoo.rlib"], &["libfoo.rlib"], &["libfoo.rlib"], ); } #[cargo_test] fn include_only_the_binary_from_the_current_package() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [workspace] [dependencies] utils = { path = "./utils" } "#, ) .file("src/lib.rs", "extern crate utils;") .file( "src/main.rs", r#" extern crate foo; extern crate utils; fn main() { println!("Hello, World!") } "#, ) .file("utils/Cargo.toml", &basic_manifest("utils", "0.0.1")) .file("utils/src/lib.rs", "") .build(); p.cargo("build -Z unstable-options --bin foo --artifact-dir out") .masquerade_as_nightly_cargo(&["artifact-dir"]) .enable_mac_dsym() .run(); check_dir_contents( &p.root().join("out"), &["foo"], &["foo", "foo.dSYM"], &["foo.exe", "foo.pdb"], &["foo.exe"], ); } #[cargo_test] fn artifact_dir_is_a_file() { let p = project() .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) .file("out", "") .build(); p.cargo("build -Z unstable-options --artifact-dir out") .masquerade_as_nightly_cargo(&["artifact-dir"]) .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [ERROR] failed to create directory `[ROOT]/foo/out` Caused by: ... "#]]) .run(); } #[cargo_test] fn replaces_artifacts() { let p = project() .file("src/main.rs", r#"fn main() { println!("foo") }"#) .build(); p.cargo("build -Z unstable-options --artifact-dir out") .masquerade_as_nightly_cargo(&["artifact-dir"]) .run(); p.process( &p.root() .join(&format!("out/foo{}", env::consts::EXE_SUFFIX)), ) .with_stdout_data(str![[r#" foo "#]]) .run(); sleep_ms(1000); p.change_file("src/main.rs", r#"fn main() { println!("bar") }"#); p.cargo("build -Z unstable-options --artifact-dir out") .masquerade_as_nightly_cargo(&["artifact-dir"]) .run(); p.process( &p.root() .join(&format!("out/foo{}", env::consts::EXE_SUFFIX)), ) .with_stdout_data(str![[r#" bar "#]]) .run(); } #[cargo_test] fn avoid_build_scripts() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] "#, ) .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) .file("a/src/main.rs", "fn main() {}") .file("a/build.rs", r#"fn main() { println!("hello-build-a"); }"#) .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) .file("b/src/main.rs", "fn main() {}") .file("b/build.rs", r#"fn main() { println!("hello-build-b"); }"#) .build(); p.cargo("build -Z unstable-options --artifact-dir out -vv") .masquerade_as_nightly_cargo(&["artifact-dir"]) .enable_mac_dsym() .with_stdout_data( str![[r#" [a 0.0.1] hello-build-a [b 0.0.1] hello-build-b "#]] .unordered(), ) .run(); check_dir_contents( &p.root().join("out"), &["a", "b"], &["a", "a.dSYM", "b", "b.dSYM"], &["a.exe", "a.pdb", "b.exe", "b.pdb"], &["a.exe", "b.exe"], ); } #[cargo_test] fn cargo_build_artifact_dir() { let p = project() .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) .file( ".cargo/config.toml", r#" [build] artifact-dir = "out" "#, ) .build(); p.cargo("build -Z unstable-options") .masquerade_as_nightly_cargo(&["artifact-dir"]) .enable_mac_dsym() .run(); check_dir_contents( &p.root().join("out"), &["foo"], &["foo", "foo.dSYM"], &["foo.exe", "foo.pdb"], &["foo.exe"], ); } #[cargo_test] fn unsupported_short_artifact_dir_flag() { let p = project() .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) .build(); p.cargo("build -Z unstable-options -O") .masquerade_as_nightly_cargo(&["artifact-dir"]) .with_stderr_data(str![[r#" [ERROR] unexpected argument '-O' found tip: a similar argument exists: '--artifact-dir' Usage: cargo[EXE] build [OPTIONS] For more information, try '--help'. "#]]) .with_status(1) .run(); } #[cargo_test] fn deprecated_out_dir() { let p = project() .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) .build(); p.cargo("build -Z unstable-options --out-dir out") .masquerade_as_nightly_cargo(&["out-dir"]) .enable_mac_dsym() .with_stderr_data(str![[r#" [WARNING] the --out-dir flag has been changed to --artifact-dir [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); check_dir_contents( &p.root().join("out"), &["foo"], &["foo", "foo.dSYM"], &["foo.exe", "foo.pdb"], &["foo.exe"], ); } #[cargo_test] fn cargo_build_deprecated_out_dir() { let p = project() .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) .file( ".cargo/config.toml", r#" [build] out-dir = "out" "#, ) .build(); p.cargo("build -Z unstable-options") .masquerade_as_nightly_cargo(&["out-dir"]) .enable_mac_dsym() .with_stderr_data(str![[r#" [WARNING] the out-dir config option has been changed to artifact-dir [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); check_dir_contents( &p.root().join("out"), &["foo"], &["foo", "foo.dSYM"], &["foo.exe", "foo.pdb"], &["foo.exe"], ); } fn check_dir_contents( artifact_dir: &Path, expected_linux: &[&str], expected_mac: &[&str], expected_win_msvc: &[&str], expected_win_gnu: &[&str], ) { let expected = if cfg!(target_os = "windows") { if cfg!(target_env = "msvc") { expected_win_msvc } else { expected_win_gnu } } else if cfg!(target_os = "macos") { expected_mac } else { expected_linux }; let actual = list_dir(artifact_dir); let mut expected = expected.iter().map(|s| s.to_string()).collect::>(); expected.sort_unstable(); assert_eq!(actual, expected); } fn list_dir(dir: &Path) -> Vec { let mut res = Vec::new(); for entry in fs::read_dir(dir).unwrap() { let entry = entry.unwrap(); res.push(entry.file_name().into_string().unwrap()); } res.sort_unstable(); res } cargo-0.91.0/tests/testsuite/bad_config.rs000064400000000000000000002623271046102023000166360ustar 00000000000000//! Tests for some invalid .cargo/config files. use crate::prelude::*; use cargo_test_support::git::cargo_uses_gitoxide; use cargo_test_support::registry::{self, Package}; use cargo_test_support::{Project, basic_bin_manifest, basic_manifest, project, rustc_host, str}; #[cargo_test] fn bad1() { let p = project() .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [target] nonexistent-target = "foo" "#, ) .build(); p.cargo("check -v --target=nonexistent-target") .with_status(101) .with_stderr_data(str![[r#" [ERROR] expected table for configuration key `target.nonexistent-target`, but found string in [ROOT]/foo/.cargo/config.toml "#]]) .run(); } #[cargo_test] fn bad2() { let p = project() .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [http] proxy = 3.0 "#, ) .build(); p.cargo("publish -v") .with_status(101) .with_stderr_data(str![[r#" [ERROR] could not load Cargo configuration Caused by: failed to load TOML configuration from `[ROOT]/foo/.cargo/config.toml` Caused by: failed to parse key `http` Caused by: failed to parse key `proxy` Caused by: found TOML configuration value of unknown type `float` "#]]) .run(); } #[cargo_test] fn bad3() { let registry = registry::init(); let p = project() .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [http] proxy = true "#, ) .build(); Package::new("foo", "1.0.0").publish(); p.cargo("publish -v") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to update registry `crates-io` Caused by: error in [ROOT]/foo/.cargo/config.toml: `http.proxy` expected a string, but found a boolean "#]]) .run(); } #[cargo_test] fn bad4() { let p = project() .file( ".cargo/config.toml", r#" [cargo-new] vcs = false "#, ) .build(); p.cargo("new -v foo") .with_status(101) .with_stderr_data(str![[r#" [CREATING] binary (application) `foo` package [ERROR] Failed to create package `foo` at `[ROOT]/foo/foo` Caused by: error in [ROOT]/foo/.cargo/config.toml: `cargo-new.vcs` expected a string, but found a boolean "#]]) .run(); } #[cargo_test] fn bad6() { let registry = registry::init(); let p = project() .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [http] user-agent = true "#, ) .build(); Package::new("foo", "1.0.0").publish(); p.cargo("publish -v") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to update registry `crates-io` Caused by: error in [ROOT]/foo/.cargo/config.toml: `http.user-agent` expected a string, but found a boolean "#]]) .run(); } #[cargo_test] fn invalid_global_config() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [dependencies] foo = "0.1.0" "#, ) .file(".cargo/config.toml", "4") .file("src/lib.rs", "") .build(); p.cargo("check -v") .with_status(101) .with_stderr_data(str![[r#" [ERROR] could not load Cargo configuration Caused by: could not parse TOML configuration in `[ROOT]/foo/.cargo/config.toml` Caused by: TOML parse error at line 1, column 2 | 1 | 4 | ^ key with no value, expected `=` "#]]) .run(); } #[cargo_test] fn bad_cargo_lock() { let p = project() .file("Cargo.lock", "[[package]]\nfoo = 92") .file("src/lib.rs", "") .build(); p.cargo("check -v") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse lock file at: [ROOT]/foo/Cargo.lock Caused by: TOML parse error at line 1, column 1 | 1 | [[package]] | ^^^^^^^^^^^ missing field `name` "#]]) .run(); } #[cargo_test] fn duplicate_packages_in_cargo_lock() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .file( "Cargo.lock", r#" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" "#, ) .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse lock file at: [ROOT]/foo/Cargo.lock Caused by: package `bar` is specified twice in the lockfile "#]]) .run(); } #[cargo_test] fn bad_source_in_cargo_lock() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .file( "Cargo.lock", r#" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bar" version = "0.1.0" source = "You shall not parse" "#, ) .build(); p.cargo("check --verbose") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse lock file at: [ROOT]/foo/Cargo.lock Caused by: TOML parse error at line 12, column 26 | 12 | source = "You shall not parse" | ^^^^^^^^^^^^^^^^^^^^^ invalid source `You shall not parse` "#]]) .run(); } #[cargo_test] fn bad_dependency_in_lockfile() { let p = project() .file("src/lib.rs", "") .file( "Cargo.lock", r#" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] "#, ) .build(); p.cargo("check").run(); } #[cargo_test] fn bad_git_dependency() { let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [dependencies] foo = {{ git = "{url}" }} "#, url = if cargo_uses_gitoxide() { "git://host.xz" } else { "file:.." } ), ) .file("src/lib.rs", "") .build(); if cargo_uses_gitoxide() { p.cargo("check -v") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] git repository `git://host.xz` [ERROR] failed to get `foo` as a dependency of package `foo v0.0.0 ([ROOT]/foo)` Caused by: failed to load source for dependency `foo` Caused by: Unable to update git://host.xz Caused by: failed to clone into: [ROOT]/home/.cargo/git/db/_empty-[HASH] Caused by: URL "git://host.xz" does not specify a path to a repository "#]]) .run(); } else { p.cargo("check -v") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] git repository `file:///` [ERROR] failed to get `foo` as a dependency of package `foo v0.0.0 ([ROOT]/foo)` Caused by: failed to load source for dependency `foo` Caused by: Unable to update file:/// Caused by: failed to clone into: [ROOT]/home/.cargo/git/db/_empty-[HASH] Caused by: 'file:///' is not a valid local file URI; class=Config (7) "#]]) .run(); }; } #[cargo_test] fn bad_crate_type() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [lib] crate-type = ["bad_type", "rlib"] "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [CHECKING] foo v0.0.0 ([ROOT]/foo) [ERROR] unknown crate type: `bad_type`, expected one of: `lib`, `rlib`, `staticlib`, `dylib`, `cdylib`, `bin`, `proc-macro` [ERROR] could not compile `foo` (lib) due to 1 previous error "#]]) .run(); } #[cargo_test] fn malformed_override() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [target.x86_64-apple-darwin.freetype] native = { foo: "bar" } "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] newlines are unsupported in inline tables, expected nothing --> Cargo.toml:9:27 | 9 | native = { | ^ | "#]]) .run(); } #[cargo_test] fn cargo_toml_missing_package_name() { let p = project() .file( "Cargo.toml", r#" [package] "#, ) .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: missing field `package.name` "#]]) .run(); } #[cargo_test] fn duplicate_binary_names() { let p = project() .file( "Cargo.toml", r#" [package] name = "qqq" version = "0.1.0" edition = "2015" authors = ["A "] [[bin]] name = "e" path = "a.rs" [[bin]] name = "e" path = "b.rs" "#, ) .file("a.rs", r#"fn main() -> () {}"#) .file("b.rs", r#"fn main() -> () {}"#) .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: found duplicate binary name e, but all binary targets must have a unique name "#]]) .run(); } #[cargo_test] fn duplicate_example_names() { let p = project() .file( "Cargo.toml", r#" [package] name = "qqq" version = "0.1.0" edition = "2015" authors = ["A "] [[example]] name = "ex" path = "examples/ex.rs" [[example]] name = "ex" path = "examples/ex2.rs" "#, ) .file("examples/ex.rs", r#"fn main () -> () {}"#) .file("examples/ex2.rs", r#"fn main () -> () {}"#) .build(); p.cargo("check --example ex") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: found duplicate example name ex, but all example targets must have a unique name "#]]) .run(); } #[cargo_test] fn duplicate_bench_names() { let p = project() .file( "Cargo.toml", r#" [package] name = "qqq" version = "0.1.0" edition = "2015" authors = ["A "] [[bench]] name = "ex" path = "benches/ex.rs" [[bench]] name = "ex" path = "benches/ex2.rs" "#, ) .file("benches/ex.rs", r#"fn main () {}"#) .file("benches/ex2.rs", r#"fn main () {}"#) .build(); p.cargo("bench") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: found duplicate bench name ex, but all bench targets must have a unique name "#]]) .run(); } #[cargo_test] fn duplicate_deps() { let p = project() .file("shim-bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("shim-bar/src/lib.rs", "pub fn a() {}") .file("linux-bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("linux-bar/src/lib.rs", "pub fn a() {}") .file( "Cargo.toml", r#" [package] name = "qqq" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = { path = "shim-bar" } [target.x86_64-unknown-linux-gnu.dependencies] bar = { path = "linux-bar" } "#, ) .file("src/main.rs", r#"fn main () {}"#) .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: Dependency 'bar' has different source paths depending on the build target. Each dependency must have a single canonical source path irrespective of build target. "#]]) .run(); } #[cargo_test] fn duplicate_deps_diff_sources() { let p = project() .file("shim-bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("shim-bar/src/lib.rs", "pub fn a() {}") .file("linux-bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("linux-bar/src/lib.rs", "pub fn a() {}") .file( "Cargo.toml", r#" [package] name = "qqq" version = "0.0.1" edition = "2015" authors = [] [target.i686-unknown-linux-gnu.dependencies] bar = { path = "shim-bar" } [target.x86_64-unknown-linux-gnu.dependencies] bar = { path = "linux-bar" } "#, ) .file("src/main.rs", r#"fn main () {}"#) .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: Dependency 'bar' has different source paths depending on the build target. Each dependency must have a single canonical source path irrespective of build target. "#]]) .run(); } #[cargo_test] fn unused_keys() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [target.foo] bar = "3" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [WARNING] unused manifest key: target.foo.bar [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] bulid = "foo" "#, ) .file("src/lib.rs", "pub fn foo() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [WARNING] unused manifest key: package.bulid [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let p = project() .at("bar") .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [lib] build = "foo" "#, ) .file("src/lib.rs", "pub fn foo() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [WARNING] unused manifest key: lib.build [CHECKING] foo v0.5.0 ([ROOT]/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn unused_keys_in_virtual_manifest() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar"] bulid = "foo" "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "") .build(); p.cargo("check --workspace") .with_stderr_data(str![[r#" [WARNING] [ROOT]/foo/Cargo.toml: unused manifest key: workspace.bulid [CHECKING] bar v0.0.1 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn empty_dependencies() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [dependencies] bar = {} "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("bar", "0.0.1").publish(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: dependency (bar) specified without providing a local path, Git repository, version, or workspace dependency to use "#]]) .run(); } #[cargo_test] fn dev_dependencies2() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [dev_dependencies] a = {path = "a"} "#, ) .file("src/lib.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" "#, ) .file("a/src/lib.rs", "") .build(); p.cargo("check").with_stderr_data(str![[r#" [WARNING] `dev_dependencies` is deprecated in favor of `dev-dependencies` and will not work in the 2024 edition (in the `foo` package) [LOCKING] 1 package to latest compatible version [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn dev_dependencies2_2024() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2024" [dev_dependencies] a = {path = "a"} "#, ) .file("src/lib.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" "#, ) .file("a/src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: `dev_dependencies` is unsupported as of the 2024 edition; instead use `dev-dependencies` (in the `foo` package) "#]]) .run(); } #[cargo_test] fn dev_dependencies2_conflict() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [dev-dependencies] a = {path = "a"} [dev_dependencies] a = {path = "a"} "#, ) .file("src/lib.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" "#, ) .file("a/src/lib.rs", "") .build(); p.cargo("check").with_stderr_data(str![[r#" [WARNING] `dev_dependencies` is redundant with `dev-dependencies`, preferring `dev-dependencies` in the `foo` package [LOCKING] 1 package to latest compatible version [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn build_dependencies2() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [build_dependencies] a = {path = "a"} "#, ) .file("src/lib.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" "#, ) .file("a/src/lib.rs", "") .build(); p.cargo("check").with_stderr_data(str![[r#" [WARNING] `build_dependencies` is deprecated in favor of `build-dependencies` and will not work in the 2024 edition (in the `foo` package) [LOCKING] 1 package to latest compatible version [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn build_dependencies2_2024() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2024" [build_dependencies] a = {path = "a"} "#, ) .file("src/lib.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" "#, ) .file("a/src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: `build_dependencies` is unsupported as of the 2024 edition; instead use `build-dependencies` (in the `foo` package) "#]]) .run(); } #[cargo_test] fn build_dependencies2_conflict() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [build-dependencies] a = {path = "a"} [build_dependencies] a = {path = "a"} "#, ) .file("src/lib.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" "#, ) .file("a/src/lib.rs", "") .build(); p.cargo("check").with_stderr_data(str![[r#" [WARNING] `build_dependencies` is redundant with `build-dependencies`, preferring `build-dependencies` in the `foo` package [LOCKING] 1 package to latest compatible version [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn lib_crate_type2() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [lib] name = "foo" crate_type = ["staticlib", "dylib"] "#, ) .file("src/lib.rs", "pub fn foo() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [WARNING] `crate_type` is deprecated in favor of `crate-type` and will not work in the 2024 edition (in the `foo` library target) [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn lib_crate_type2_2024() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2024" authors = ["wycats@example.com"] [lib] name = "foo" crate_type = ["staticlib", "dylib"] "#, ) .file("src/lib.rs", "pub fn foo() {}") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: `crate_type` is unsupported as of the 2024 edition; instead use `crate-type` (in the `foo` library target) "#]]) .run(); } #[cargo_test] fn lib_crate_type2_conflict() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [lib] name = "foo" crate-type = ["rlib", "dylib"] crate_type = ["staticlib", "dylib"] "#, ) .file("src/lib.rs", "pub fn foo() {}") .build(); p.cargo("check").with_stderr_data(str![[r#" [WARNING] `crate_type` is redundant with `crate-type`, preferring `crate-type` in the `foo` library target [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn bin_crate_type2() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [[bin]] name = "foo" path = "src/main.rs" crate_type = [] "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [WARNING] `crate_type` is deprecated in favor of `crate-type` and will not work in the 2024 edition (in the `foo` binary target) [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn bin_crate_type2_2024() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2024" authors = ["wycats@example.com"] [[bin]] name = "foo" path = "src/main.rs" crate_type = [] "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: `crate_type` is unsupported as of the 2024 edition; instead use `crate-type` (in the `foo` binary target) "#]]) .run(); } #[cargo_test] fn bin_crate_type2_conflict() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [[bin]] name = "foo" path = "src/main.rs" crate_type = [] crate-type = [] "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check").with_stderr_data(str![[r#" [WARNING] `crate_type` is redundant with `crate-type`, preferring `crate-type` in the `foo` binary target [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn examples_crate_type2() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [[example]] name = "ex" path = "examples/ex.rs" crate_type = ["proc_macro"] [[example]] name = "goodbye" path = "examples/ex-goodbye.rs" crate_type = ["rlib", "staticlib"] "#, ) .file("src/lib.rs", "") .file( "examples/ex.rs", r#" fn main() { println!("ex"); } "#, ) .file( "examples/ex-goodbye.rs", r#" fn main() { println!("goodbye"); } "#, ) .build(); p.cargo("check") .with_stderr_data(str![[r#" [WARNING] `crate_type` is deprecated in favor of `crate-type` and will not work in the 2024 edition (in the `ex` example target) [WARNING] `crate_type` is deprecated in favor of `crate-type` and will not work in the 2024 edition (in the `goodbye` example target) [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn examples_crate_type2_2024() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2024" authors = ["wycats@example.com"] [[example]] name = "ex" path = "examples/ex.rs" crate_type = ["proc_macro"] [[example]] name = "goodbye" path = "examples/ex-goodbye.rs" crate_type = ["rlib", "staticlib"] "#, ) .file("src/lib.rs", "") .file( "examples/ex.rs", r#" fn main() { println!("ex"); } "#, ) .file( "examples/ex-goodbye.rs", r#" fn main() { println!("goodbye"); } "#, ) .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: `crate_type` is unsupported as of the 2024 edition; instead use `crate-type` (in the `ex` example target) "#]]) .run(); } #[cargo_test] fn examples_crate_type2_conflict() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [[example]] name = "ex" path = "examples/ex.rs" crate-type = ["rlib", "dylib"] crate_type = ["proc_macro"] [[example]] name = "goodbye" path = "examples/ex-goodbye.rs" crate-type = ["rlib", "dylib"] crate_type = ["rlib", "staticlib"] "#, ) .file("src/lib.rs", "") .file( "examples/ex.rs", r#" fn main() { println!("ex"); } "#, ) .file( "examples/ex-goodbye.rs", r#" fn main() { println!("goodbye"); } "#, ) .build(); p.cargo("check").with_stderr_data(str![[r#" [WARNING] `crate_type` is redundant with `crate-type`, preferring `crate-type` in the `ex` example target [WARNING] `crate_type` is redundant with `crate-type`, preferring `crate-type` in the `goodbye` example target [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn cargo_platform_build_dependencies2() { let host = rustc_host(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] build = "build.rs" [target.{host}.build_dependencies] build = {{ path = "build" }} "#, host = host ), ) .file("src/main.rs", "fn main() { }") .file( "build.rs", "extern crate build; fn main() { build::build(); }", ) .file("build/Cargo.toml", &basic_manifest("build", "0.5.0")) .file("build/src/lib.rs", "pub fn build() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [WARNING] `build_dependencies` is deprecated in favor of `build-dependencies` and will not work in the 2024 edition (in the `[HOST_TARGET]` platform target) [LOCKING] 1 package to latest compatible version [COMPILING] build v0.5.0 ([ROOT]/foo/build) [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] ) .run(); } #[cargo_test] fn cargo_platform_build_dependencies2_2024() { let host = rustc_host(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2024" authors = ["wycats@example.com"] build = "build.rs" [target.{host}.build_dependencies] build = {{ path = "build" }} "#, host = host ), ) .file("src/main.rs", "fn main() { }") .file( "build.rs", "extern crate build; fn main() { build::build(); }", ) .file("build/Cargo.toml", &basic_manifest("build", "0.5.0")) .file("build/src/lib.rs", "pub fn build() {}") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: `build_dependencies` is unsupported as of the 2024 edition; instead use `build-dependencies` (in the `[HOST_TARGET]` platform target) "#]]) .run(); } #[cargo_test] fn cargo_platform_build_dependencies2_conflict() { let host = rustc_host(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] build = "build.rs" [target.{host}.build-dependencies] build = {{ path = "build" }} [target.{host}.build_dependencies] build = {{ path = "build" }} "#, host = host ), ) .file("src/main.rs", "fn main() { }") .file( "build.rs", "extern crate build; fn main() { build::build(); }", ) .file("build/Cargo.toml", &basic_manifest("build", "0.5.0")) .file("build/src/lib.rs", "pub fn build() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [WARNING] `build_dependencies` is redundant with `build-dependencies`, preferring `build-dependencies` in the `[HOST_TARGET]` platform target [LOCKING] 1 package to latest compatible version [COMPILING] build v0.5.0 ([ROOT]/foo/build) [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn cargo_platform_dev_dependencies2() { let host = rustc_host(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [target.{host}.dev_dependencies] dev = {{ path = "dev" }} "#, host = host ), ) .file("src/main.rs", "fn main() { }") .file( "tests/foo.rs", "extern crate dev; #[test] fn foo() { dev::dev() }", ) .file("dev/Cargo.toml", &basic_manifest("dev", "0.5.0")) .file("dev/src/lib.rs", "pub fn dev() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [WARNING] `dev_dependencies` is deprecated in favor of `dev-dependencies` and will not work in the 2024 edition (in the `[HOST_TARGET]` platform target) [LOCKING] 1 package to latest compatible version [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn cargo_platform_dev_dependencies2_2024() { let host = rustc_host(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2024" authors = ["wycats@example.com"] [target.{host}.dev_dependencies] dev = {{ path = "dev" }} "#, host = host ), ) .file("src/main.rs", "fn main() { }") .file( "tests/foo.rs", "extern crate dev; #[test] fn foo() { dev::dev() }", ) .file("dev/Cargo.toml", &basic_manifest("dev", "0.5.0")) .file("dev/src/lib.rs", "pub fn dev() {}") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: `dev_dependencies` is unsupported as of the 2024 edition; instead use `dev-dependencies` (in the `[HOST_TARGET]` platform target) "#]]) .run(); } #[cargo_test] fn cargo_platform_dev_dependencies2_conflict() { let host = rustc_host(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [target.{host}.dev-dependencies] dev = {{ path = "dev" }} [target.{host}.dev_dependencies] dev = {{ path = "dev" }} "#, host = host ), ) .file("src/main.rs", "fn main() { }") .file( "tests/foo.rs", "extern crate dev; #[test] fn foo() { dev::dev() }", ) .file("dev/Cargo.toml", &basic_manifest("dev", "0.5.0")) .file("dev/src/lib.rs", "pub fn dev() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [WARNING] `dev_dependencies` is redundant with `dev-dependencies`, preferring `dev-dependencies` in the `[HOST_TARGET]` platform target [LOCKING] 1 package to latest compatible version [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn default_features2() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [dependencies] a = { path = "a", features = ["f1"], default_features = false } "#, ) .file("src/lib.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" authors = [] [features] default = ["f1"] f1 = [] "#, ) .file("a/src/lib.rs", "") .build(); p.cargo("check").with_stderr_data(str![[r#" [WARNING] `default_features` is deprecated in favor of `default-features` and will not work in the 2024 edition (in the `a` dependency) [LOCKING] 1 package to latest compatible version [CHECKING] a v0.1.0 ([ROOT]/foo/a) [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn default_features2_2024() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2024" authors = [] [dependencies] a = { path = "a", features = ["f1"], default_features = false } "#, ) .file("src/lib.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" authors = [] [features] default = ["f1"] f1 = [] "#, ) .file("a/src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: `default_features` is unsupported as of the 2024 edition; instead use `default-features` (in the `a` dependency) "#]]) .run(); } #[cargo_test] fn default_features2_conflict() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [dependencies] a = { path = "a", features = ["f1"], default-features = false, default_features = false } "#, ) .file("src/lib.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" authors = [] [features] default = ["f1"] f1 = [] "#, ) .file("a/src/lib.rs", "") .build(); p.cargo("check").with_stderr_data(str![[r#" [WARNING] `default_features` is redundant with `default-features`, preferring `default-features` in the `a` dependency [LOCKING] 1 package to latest compatible version [CHECKING] a v0.1.0 ([ROOT]/foo/a) [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn workspace_default_features2() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["workspace_only", "dep_workspace_only", "package_only", "dep_package_only"] [workspace.dependencies] dep_workspace_only = { path = "dep_workspace_only", default_features = true } dep_package_only = { path = "dep_package_only" } "#, ) .file( "workspace_only/Cargo.toml", r#" [package] name = "workspace_only" version = "0.1.0" edition = "2015" authors = [] [dependencies] dep_workspace_only.workspace = true "#, ) .file("workspace_only/src/lib.rs", "") .file( "dep_workspace_only/Cargo.toml", r#" [package] name = "dep_workspace_only" version = "0.1.0" edition = "2015" authors = [] "#, ) .file("dep_workspace_only/src/lib.rs", "") .file( "package_only/Cargo.toml", r#" [package] name = "package_only" version = "0.1.0" edition = "2015" authors = [] [dependencies] dep_package_only = { workspace = true, default_features = true } "#, ) .file("package_only/src/lib.rs", "") .file( "dep_package_only/Cargo.toml", r#" [package] name = "dep_package_only" version = "0.1.0" edition = "2015" authors = [] "#, ) .file("dep_package_only/src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data( str![[r#" (in the `dep_workspace_only` dependency) [CHECKING] dep_package_only v0.1.0 ([ROOT]/foo/dep_package_only) [CHECKING] dep_workspace_only v0.1.0 ([ROOT]/foo/dep_workspace_only) [CHECKING] package_only v0.1.0 ([ROOT]/foo/package_only) [CHECKING] workspace_only v0.1.0 ([ROOT]/foo/workspace_only) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [WARNING] [ROOT]/foo/workspace_only/Cargo.toml: `default_features` is deprecated in favor of `default-features` and will not work in the 2024 edition "#]] .unordered(), ) .run(); } #[cargo_test] fn workspace_default_features2_2024() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["workspace_only", "dep_workspace_only", "package_only", "dep_package_only"] [workspace.dependencies] dep_workspace_only = { path = "dep_workspace_only", default_features = true } dep_package_only = { path = "dep_package_only" } "#, ) .file( "workspace_only/Cargo.toml", r#" [package] name = "workspace_only" version = "0.1.0" edition = "2024" authors = [] [dependencies] dep_workspace_only.workspace = true "#, ) .file("workspace_only/src/lib.rs", "") .file( "dep_workspace_only/Cargo.toml", r#" [package] name = "dep_workspace_only" version = "0.1.0" edition = "2024" authors = [] "#, ) .file("dep_workspace_only/src/lib.rs", "") .file( "package_only/Cargo.toml", r#" [package] name = "package_only" version = "0.1.0" edition = "2024" authors = [] [dependencies] dep_package_only = { workspace = true, default_features = true } "#, ) .file("package_only/src/lib.rs", "") .file( "dep_package_only/Cargo.toml", r#" [package] name = "dep_package_only" version = "0.1.0" edition = "2024" authors = [] "#, ) .file("dep_package_only/src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to load manifest for workspace member `[ROOT]/foo/workspace_only` referenced by workspace at `[ROOT]/foo/Cargo.toml` Caused by: failed to parse manifest at `[ROOT]/foo/workspace_only/Cargo.toml` Caused by: `default_features` is unsupported as of the 2024 edition; instead use `default-features` (in the `dep_workspace_only` dependency) "#]]) .run(); } #[cargo_test] fn lib_proc_macro2() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [lib] proc_macro = true "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check") .with_stderr_data(str![[r#" [WARNING] `proc_macro` is deprecated in favor of `proc-macro` and will not work in the 2024 edition (in the `foo` library target) [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn lib_proc_macro2_2024() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2024" [lib] proc_macro = true "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: `proc_macro` is unsupported as of the 2024 edition; instead use `proc-macro` (in the `foo` library target) "#]]) .run(); } #[cargo_test] fn lib_proc_macro2_conflict() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [lib] proc-macro = false proc_macro = true "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check").with_stderr_data(str![[r#" [WARNING] `proc_macro` is redundant with `proc-macro`, preferring `proc-macro` in the `foo` library target [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn bin_proc_macro2() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [[bin]] name = "foo" path = "src/main.rs" proc_macro = false "#, ) .file("src/main.rs", "fn main() {}") .build(); foo.cargo("check") .with_stderr_data(str![[r#" [WARNING] `proc_macro` is deprecated in favor of `proc-macro` and will not work in the 2024 edition (in the `foo` binary target) [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn bin_proc_macro2_2024() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2024" authors = ["wycats@example.com"] [[bin]] name = "foo" path = "src/main.rs" proc_macro = false "#, ) .file("src/main.rs", "fn main() {}") .build(); foo.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: `proc_macro` is unsupported as of the 2024 edition; instead use `proc-macro` (in the `foo` binary target) "#]]) .run(); } #[cargo_test] fn bin_proc_macro2_conflict() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [[bin]] name = "foo" path = "src/main.rs" proc-macro = false proc_macro = false "#, ) .file("src/main.rs", "fn main() {}") .build(); foo.cargo("check").with_stderr_data(str![[r#" [WARNING] `proc_macro` is redundant with `proc-macro`, preferring `proc-macro` in the `foo` binary target [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn invalid_toml_historically_allowed_fails() { let p = project() .file(".cargo/config.toml", "[bar] baz = 2") .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] could not load Cargo configuration Caused by: could not parse TOML configuration in `[ROOT]/foo/.cargo/config.toml` Caused by: TOML parse error at line 1, column 7 | 1 | [bar] baz = 2 | ^ unexpected key or value, expected newline, `#` "#]]) .run(); } #[cargo_test] fn ambiguous_git_reference() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [dependencies.bar] git = "http://127.0.0.1" branch = "master" tag = "some-tag" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check -v") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: dependency (bar) specification is ambiguous. Only one of `branch`, `tag` or `rev` is allowed. "#]]) .run(); } #[cargo_test] fn fragment_in_git_url() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [dependencies.bar] git = "http://127.0.0.1#foo" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check -v") .with_status(101) // the following is needed as gitoxide has a different error message // ... // [..]127.0.0.1[..] .with_stderr_data(str![[r#" [WARNING] URL fragment `#foo` in git URL is ignored for dependency (bar). If you were trying to specify a specific git revision, use `rev = "foo"` in the dependency declaration. [UPDATING] git repository `http://127.0.0.1/#foo` ... [ERROR] failed to get `bar` as a dependency of package `foo v0.0.0 ([ROOT]/foo)` Caused by: failed to load source for dependency `bar` Caused by: Unable to update http://127.0.0.1/#foo Caused by: failed to clone into: [ROOT]/home/.cargo/git/db/_empty-[HASH] ... "#]]) .run(); } #[cargo_test] fn bad_source_config1() { let p = project() .file("src/lib.rs", "") .file(".cargo/config.toml", "[source.foo]") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no source location specified for `source.foo`, need `registry`, `local-registry`, `directory`, or `git` defined "#]]) .run(); } #[cargo_test] fn bad_source_config2() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [dependencies] bar = "*" "#, ) .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [source.crates-io] registry = 'http://example.com' replace-with = 'bar' "#, ) .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to get `bar` as a dependency of package `foo v0.0.0 ([ROOT]/foo)` Caused by: failed to load source for dependency `bar` Caused by: Unable to update registry `crates-io` Caused by: could not find a configured source with the name `bar` when attempting to lookup `crates-io` (configuration in `[ROOT]/foo/.cargo/config.toml`) "#]]) .run(); } #[cargo_test] fn bad_source_config3() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [dependencies] bar = "*" "#, ) .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [source.crates-io] registry = 'https://example.com' replace-with = 'crates-io' "#, ) .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to get `bar` as a dependency of package `foo v0.0.0 ([ROOT]/foo)` Caused by: failed to load source for dependency `bar` Caused by: Unable to update registry `crates-io` Caused by: detected a cycle of `replace-with` sources, the source `crates-io` is eventually replaced with itself (configuration in `[ROOT]/foo/.cargo/config.toml`) "#]]) .run(); } #[cargo_test] fn bad_source_config4() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [dependencies] bar = "*" "#, ) .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [source.crates-io] replace-with = 'bar' [source.bar] registry = 'https://example.com' replace-with = 'crates-io' "#, ) .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to get `bar` as a dependency of package `foo v0.0.0 ([ROOT]/foo)` Caused by: failed to load source for dependency `bar` Caused by: Unable to update registry `crates-io` Caused by: detected a cycle of `replace-with` sources, the source `crates-io` is eventually replaced with itself (configuration in `[ROOT]/foo/.cargo/config.toml`) "#]]) .run(); } #[cargo_test] fn bad_source_config5() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [dependencies] bar = "*" "#, ) .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [source.crates-io] registry = 'https://example.com' replace-with = 'bar' [source.bar] registry = 'not a url' "#, ) .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] configuration key `source.bar.registry` specified an invalid URL (in [ROOT]/foo/.cargo/config.toml) Caused by: invalid url `not a url`: relative URL without a base "#]]) .run(); } #[cargo_test] fn both_git_and_path_specified() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [dependencies.bar] git = "http://127.0.0.1" path = "bar" "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check -v") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: dependency (bar) specification is ambiguous. Only one of `git` or `path` is allowed. "#]]) .run(); } #[cargo_test] fn bad_source_config6() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [dependencies] bar = "*" "#, ) .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [source.crates-io] registry = 'https://example.com' replace-with = ['not', 'a', 'string'] "#, ) .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] error in [ROOT]/foo/.cargo/config.toml: could not load config key `source.crates-io.replace-with` Caused by: error in [ROOT]/foo/.cargo/config.toml: `source.crates-io.replace-with` expected a string, but found a array "#]]) .run(); } #[cargo_test] fn ignored_git_revision() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [dependencies.bar] path = "bar" branch = "spam" "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check -v") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: key `branch` is ignored for dependency (bar). "#]]) .run(); // #11540, check that [target] dependencies fail the same way. foo.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" [target.some-target.dependencies] bar = { path = "bar", branch = "spam" } "#, ); foo.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: key `branch` is ignored for dependency (bar). "#]]) .run(); } #[cargo_test] fn bad_source_config7() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [dependencies] bar = "*" "#, ) .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [source.foo] registry = 'https://example.com' local-registry = 'file:///another/file' "#, ) .build(); Package::new("bar", "0.1.0").publish(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] more than one source location specified for `source.foo` "#]]) .run(); } #[cargo_test] fn bad_source_config8() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [dependencies] bar = "*" "#, ) .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [source.foo] branch = "somebranch" "#, ) .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] source definition `source.foo` specifies `branch`, but that requires a `git` key to be specified (in [ROOT]/foo/.cargo/config.toml) "#]]) .run(); } #[cargo_test] fn bad_dependency() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [dependencies] bar = 3 "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid type: integer `3`, expected a version string like "0.9.8" or a detailed dependency like { version = "0.9.8" } --> Cargo.toml:9:23 | 9 | bar = 3 | ^ | "#]]) .run(); } #[cargo_test] fn bad_debuginfo() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [profile.dev] debug = 'a' "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid value: string "a", expected a boolean, 0, 1, 2, "none", "limited", "full", "line-tables-only", or "line-directives-only" --> Cargo.toml:9:25 | 9 | debug = 'a' | ^^^ | "#]]) .run(); } #[cargo_test] fn bad_debuginfo2() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [profile.dev] debug = 3.6 "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid type: floating point `3.6`, expected a boolean, 0, 1, 2, "none", "limited", "full", "line-tables-only", or "line-directives-only" --> Cargo.toml:9:25 | 9 | debug = 3.6 | ^^^ | "#]]) .run(); } #[cargo_test] fn bad_opt_level() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] build = 3 "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid type: integer `3`, expected a boolean, string or array --> Cargo.toml:7:25 | 7 | build = 3 | ^ | "#]]) .run(); } #[cargo_test] fn warn_semver_metadata() { Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" edition = "2015" [dependencies] bar = "1.0.0+1234" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check").with_stderr_data(str![[r#" [WARNING] version requirement `1.0.0+1234` for dependency `bar` includes semver metadata which will be ignored, removing the metadata is recommended to avoid confusion [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [CHECKING] bar v1.0.0 [CHECKING] foo v1.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn bad_http_ssl_version() { // Invalid type in SslVersionConfig. let p = project() .file( ".cargo/config.toml", r#" [http] ssl-version = ["tlsv1.2", "tlsv1.3"] "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] error in [ROOT]/foo/.cargo/config.toml: could not load config key `http.ssl-version` Caused by: invalid type: sequence, expected a string or map "#]]) .run(); } #[cargo_test] fn bad_http_ssl_version_range() { // Invalid type in SslVersionConfigRange. let p = project() .file( ".cargo/config.toml", r#" [http] ssl-version.min = false "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] error in [ROOT]/foo/.cargo/config.toml: could not load config key `http.ssl-version` Caused by: error in [ROOT]/foo/.cargo/config.toml: `http.ssl-version.min` expected a string, but found a boolean "#]]) .run(); } #[cargo_test] fn bad_build_jobs() { // Invalid type in JobsConfig. let p = project() .file( ".cargo/config.toml", r#" [build] jobs = { default = true } "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] error in [ROOT]/foo/.cargo/config.toml: could not load config key `build.jobs` Caused by: invalid type: map, expected an integer or string "#]]) .run(); } #[cargo_test] fn bad_build_target() { // Invalid type in BuildTargetConfig. let p = project() .file( ".cargo/config.toml", r#" [build] target.'cfg(unix)' = "x86_64" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] error in [ROOT]/foo/.cargo/config.toml: could not load config key `build.target` Caused by: error in [ROOT]/foo/.cargo/config.toml: could not load config key `build.target` Caused by: invalid type: map, expected a string or array "#]]) .run(); } #[cargo_test] fn bad_target_cfg() { // Invalid type in a StringList. // // The error message is a bit unfortunate here. The type here ends up // being essentially Value>, and each layer of "Value" // adds some context to the error message. Also, untagged enums provide // strange error messages. Hopefully most users will be able to untangle // the message. let p = project() .file( ".cargo/config.toml", r#" [target.'cfg(not(target_os = "none"))'] runner = false "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] error in [ROOT]/foo/.cargo/config.toml: could not load config key `target.'cfg(not(target_os = "none"))'.runner` Caused by: error in [ROOT]/foo/.cargo/config.toml: could not load config key `target.'cfg(not(target_os = "none"))'.runner` Caused by: invalid configuration for key `target.'cfg(not(target_os = "none"))'.runner` expected a string or array of strings, but found a boolean for `target.'cfg(not(target_os = "none"))'.runner` in [ROOT]/foo/.cargo/config.toml "#]]) .run(); } #[cargo_test] fn bad_target_links_overrides() { // Invalid parsing of links overrides. // // This error message is terrible. Nothing in the deserialization path is // using config::Value<>, so nothing is able to report the location. I // think this illustrates how the way things break down with how it // currently is designed with serde. let p = project() .file( ".cargo/config.toml", &format!( r#" [target.{}.somelib] rustc-flags = 'foo' "#, rustc_host() ), ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r" [ERROR] Only `-l` and `-L` flags are allowed in target config `target.[..].rustc-flags` (in [..]foo/.cargo/config.toml): `foo` "]]) .run(); p.change_file( ".cargo/config.toml", &format!( "[target.{}.somelib] warning = \"foo\" ", rustc_host(), ), ); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] `warning` is not supported in build script overrides "#]]) .run(); } #[cargo_test] fn redefined_sources() { // Cannot define a source multiple times. let p = project() .file( ".cargo/config.toml", r#" [source.foo] registry = "https://github.com/rust-lang/crates.io-index" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] source `foo` defines source registry `crates-io`, but that source is already defined by `crates-io` [NOTE] Sources are not allowed to be defined multiple times. "#]]) .run(); p.change_file( ".cargo/config.toml", r#" [source.one] directory = "index" [source.two] directory = "index" "#, ); // Name is `[..]` because we can't guarantee the order. p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] source `[..]` defines source dir [ROOT]/foo/index, but that source is already defined by `[..]` [NOTE] Sources are not allowed to be defined multiple times. "#]]) .run(); } #[cargo_test] fn bad_trim_paths() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" [profile.dev] trim-paths = "split-debuginfo" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check -Ztrim-paths") .masquerade_as_nightly_cargo(&["trim-paths"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] expected a boolean, "none", "diagnostics", "macro", "object", "all", or an array with these options --> Cargo.toml:8:30 | 8 | trim-paths = "split-debuginfo" | ^^^^^^^^^^^^^^^^^ | "#]]) .run(); } fn bad_target_name_project(target: &str, path: &str, name: &str) -> Project { project() .file( "Cargo.toml", &format!( r#" [package] name = "bad-{target}-name" edition = "2024" [[{target}]] name = "{name}" "# ), ) .file("src/lib.rs", "") .file(format!("{path}/{name}"), "") .build() } #[cargo_test] fn bad_bin_name() { bad_target_name_project("bin", "src/bin", "bin.rs") .cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: can't find `bin.rs` bin at `src/bin/bin.rs.rs` or `src/bin/bin.rs/main.rs`. Please specify bin.path if you want to use a non-default path. [HELP] a bin with a similar name exists: `bin` "#]]) .run(); } #[cargo_test] fn bad_example_name() { bad_target_name_project("example", "examples", "example.rs") .cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: can't find `example.rs` example at `examples/example.rs.rs` or `examples/example.rs/main.rs`. Please specify example.path if you want to use a non-default path. [HELP] a example with a similar name exists: `example` "#]]) .run(); } #[cargo_test] fn bad_test_name() { bad_target_name_project("test", "tests", "test.rs") .cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: can't find `test.rs` test at `tests/test.rs.rs` or `tests/test.rs/main.rs`. Please specify test.path if you want to use a non-default path. [HELP] a test with a similar name exists: `test` "#]]) .run(); } #[cargo_test] fn bad_bench_name() { bad_target_name_project("bench", "benches", "bench.rs") .cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: can't find `bench.rs` bench at `benches/bench.rs.rs` or `benches/bench.rs/main.rs`. Please specify bench.path if you want to use a non-default path. [HELP] a bench with a similar name exists: `bench` "#]]) .run(); } #[cargo_test] fn non_existing_test() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" edition = "2015" [lib] name = "foo" path = "src/lib.rs" [[test]] name = "hello" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check --tests -v") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: can't find `hello` test at `tests/hello.rs` or `tests/hello/main.rs`. Please specify test.path if you want to use a non-default path. "#]]) .run(); } #[cargo_test] fn non_existing_example() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" edition = "2015" [lib] name = "foo" path = "src/lib.rs" [[example]] name = "hello" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check --examples -v") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: can't find `hello` example at `examples/hello.rs` or `examples/hello/main.rs`. Please specify example.path if you want to use a non-default path. "#]]) .run(); } #[cargo_test] fn non_existing_benchmark() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" edition = "2015" [lib] name = "foo" path = "src/lib.rs" [[bench]] name = "hello" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check --benches -v") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: can't find `hello` bench at `benches/hello.rs` or `benches/hello/main.rs`. Please specify bench.path if you want to use a non-default path. "#]]) .run(); } #[cargo_test] fn non_existing_binary() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/lib.rs", "") .file("src/bin/ehlo.rs", "") .build(); p.cargo("check -v") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: can't find `foo` bin at `src/bin/foo.rs` or `src/bin/foo/main.rs`. Please specify bin.path if you want to use a non-default path. [HELP] a bin with a similar name exists: `ehlo` "#]]) .run(); } #[cargo_test] fn commonly_wrong_path_of_test() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" edition = "2015" [lib] name = "foo" path = "src/lib.rs" [[test]] name = "foo" "#, ) .file("src/lib.rs", "") .file("test/foo.rs", "") .build(); p.cargo("check --tests -v") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: can't find `foo` test at default paths, but found a file at `test/foo.rs`. Perhaps rename the file to `tests/foo.rs` for target auto-discovery, or specify test.path if you want to use a non-default path. "#]]) .run(); } #[cargo_test] fn commonly_wrong_path_of_example() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" edition = "2015" [lib] name = "foo" path = "src/lib.rs" [[example]] name = "foo" "#, ) .file("src/lib.rs", "") .file("example/foo.rs", "") .build(); p.cargo("check --examples -v") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: can't find `foo` example at default paths, but found a file at `example/foo.rs`. Perhaps rename the file to `examples/foo.rs` for target auto-discovery, or specify example.path if you want to use a non-default path. "#]]) .run(); } #[cargo_test] fn commonly_wrong_path_of_benchmark() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" edition = "2015" [lib] name = "foo" path = "src/lib.rs" [[bench]] name = "foo" "#, ) .file("src/lib.rs", "") .file("bench/foo.rs", "") .build(); p.cargo("check --benches -v") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: can't find `foo` bench at default paths, but found a file at `bench/foo.rs`. Perhaps rename the file to `benches/foo.rs` for target auto-discovery, or specify bench.path if you want to use a non-default path. "#]]) .run(); } #[cargo_test] fn commonly_wrong_path_binary() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/lib.rs", "") .file("src/bins/foo.rs", "") .build(); p.cargo("check -v") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: can't find `foo` bin at default paths, but found a file at `src/bins/foo.rs`. Perhaps rename the file to `src/bin/foo.rs` for target auto-discovery, or specify bin.path if you want to use a non-default path. "#]]) .run(); } #[cargo_test] fn commonly_wrong_path_subdir_binary() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/lib.rs", "") .file("src/bins/foo/main.rs", "") .build(); p.cargo("check -v") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: can't find `foo` bin at default paths, but found a file at `src/bins/foo/main.rs`. Perhaps rename the file to `src/bin/foo/main.rs` for target auto-discovery, or specify bin.path if you want to use a non-default path. "#]]) .run(); } #[cargo_test] fn found_multiple_target_files() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/lib.rs", "") .file("src/bin/foo.rs", "") .file("src/bin/foo/main.rs", "") .build(); p.cargo("check -v") .with_status(101) // Don't assert the inferred paths since the order is non-deterministic. .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: cannot infer path for `foo` bin Cargo doesn't know which to use because multiple target files found at `src/bin/foo[..]rs` and `src/bin/foo[..].rs`. "#]]) .run(); } #[cargo_test] fn legacy_binary_paths_warnings() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" edition = "2015" authors = [] [[bin]] name = "bar" "#, ) .file("src/lib.rs", "") .file("src/main.rs", "fn main() {}") .build(); p.cargo("check -v") .with_stderr_data(str![[r#" [WARNING] An explicit [[bin]] section is specified in Cargo.toml which currently disables Cargo from automatically inferring other binary targets. This inference behavior will change in the Rust 2018 edition and the following files will be included as a binary target: * src/main.rs This is likely to break cargo build or cargo test as these files may not be ready to be compiled as a binary target today. You can future-proof yourself and disable this warning by adding `autobins = false` to your [package] section. You may also move the files to a location where Cargo would not automatically infer them to be a target, such as in subfolders. For more information on this warning you can consult https://github.com/rust-lang/cargo/issues/5330 [WARNING] path `src/main.rs` was erroneously implicitly accepted for binary `bar`, please set bin.path in Cargo.toml [CHECKING] foo v1.0.0 ([ROOT]/foo) [RUNNING] `rustc [..]` [RUNNING] `rustc [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" edition = "2015" authors = [] [[bin]] name = "bar" "#, ) .file("src/lib.rs", "") .file("src/bin/main.rs", "fn main() {}") .build(); p.cargo("check -v") .with_stderr_data(str![[r#" [WARNING] An explicit [[bin]] section is specified in Cargo.toml which currently disables Cargo from automatically inferring other binary targets. This inference behavior will change in the Rust 2018 edition and the following files will be included as a binary target: * src/bin/main.rs This is likely to break cargo build or cargo test as these files may not be ready to be compiled as a binary target today. You can future-proof yourself and disable this warning by adding `autobins = false` to your [package] section. You may also move the files to a location where Cargo would not automatically infer them to be a target, such as in subfolders. For more information on this warning you can consult https://github.com/rust-lang/cargo/issues/5330 [WARNING] path `src/bin/main.rs` was erroneously implicitly accepted for binary `bar`, please set bin.path in Cargo.toml [CHECKING] foo v1.0.0 ([ROOT]/foo) [RUNNING] `rustc [..]` [RUNNING] `rustc [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" edition = "2015" authors = [] [[bin]] name = "bar" "#, ) .file("src/bar.rs", "fn main() {}") .build(); p.cargo("check -v") .with_stderr_data(str![[r#" [WARNING] path `src/bar.rs` was erroneously implicitly accepted for binary `bar`, please set bin.path in Cargo.toml [CHECKING] foo v1.0.0 ([ROOT]/foo) [RUNNING] `rustc [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } cargo-0.91.0/tests/testsuite/bad_manifest_path.rs000064400000000000000000000222741046102023000202060ustar 00000000000000//! Tests for invalid --manifest-path arguments. use crate::prelude::*; use cargo_test_support::{basic_bin_manifest, main_file, project, str}; #[track_caller] fn assert_not_a_cargo_toml(command: &str, manifest_path_argument: &str) { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo(command) .arg("--manifest-path") .arg(manifest_path_argument) .cwd(p.root().parent().unwrap()) .with_status(101) .with_stderr_data("[ERROR] the manifest-path must be a path to a Cargo.toml file\n") .run(); } #[track_caller] fn assert_cargo_toml_doesnt_exist(command: &str, manifest_path_argument: &str) { let p = project().build(); let expected_path = manifest_path_argument .split('/') .collect::>() .join("[..]"); p.cargo(command) .arg("--manifest-path") .arg(manifest_path_argument) .cwd(p.root().parent().unwrap()) .with_status(101) .with_stderr_data(format!( "[ERROR] manifest path `{}` does not exist\n", expected_path )) .run(); } #[cargo_test] fn bench_dir_containing_cargo_toml() { assert_not_a_cargo_toml("bench", "foo"); } #[cargo_test] fn bench_dir_plus_file() { assert_not_a_cargo_toml("bench", "foo/bar"); } #[cargo_test] fn bench_dir_plus_path() { assert_not_a_cargo_toml("bench", "foo/bar/baz"); } #[cargo_test] fn bench_dir_to_nonexistent_cargo_toml() { assert_cargo_toml_doesnt_exist("bench", "foo/bar/baz/Cargo.toml"); } #[cargo_test] fn build_dir_containing_cargo_toml() { assert_not_a_cargo_toml("check", "foo"); } #[cargo_test] fn build_dir_plus_file() { assert_not_a_cargo_toml("bench", "foo/bar"); } #[cargo_test] fn build_dir_plus_path() { assert_not_a_cargo_toml("bench", "foo/bar/baz"); } #[cargo_test] fn build_dir_to_nonexistent_cargo_toml() { assert_cargo_toml_doesnt_exist("check", "foo/bar/baz/Cargo.toml"); } #[cargo_test] fn clean_dir_containing_cargo_toml() { assert_not_a_cargo_toml("clean", "foo"); } #[cargo_test] fn clean_dir_plus_file() { assert_not_a_cargo_toml("clean", "foo/bar"); } #[cargo_test] fn clean_dir_plus_path() { assert_not_a_cargo_toml("clean", "foo/bar/baz"); } #[cargo_test] fn clean_dir_to_nonexistent_cargo_toml() { assert_cargo_toml_doesnt_exist("clean", "foo/bar/baz/Cargo.toml"); } #[cargo_test] fn doc_dir_containing_cargo_toml() { assert_not_a_cargo_toml("doc", "foo"); } #[cargo_test] fn doc_dir_plus_file() { assert_not_a_cargo_toml("doc", "foo/bar"); } #[cargo_test] fn doc_dir_plus_path() { assert_not_a_cargo_toml("doc", "foo/bar/baz"); } #[cargo_test] fn doc_dir_to_nonexistent_cargo_toml() { assert_cargo_toml_doesnt_exist("doc", "foo/bar/baz/Cargo.toml"); } #[cargo_test] fn fetch_dir_containing_cargo_toml() { assert_not_a_cargo_toml("fetch", "foo"); } #[cargo_test] fn fetch_dir_plus_file() { assert_not_a_cargo_toml("fetch", "foo/bar"); } #[cargo_test] fn fetch_dir_plus_path() { assert_not_a_cargo_toml("fetch", "foo/bar/baz"); } #[cargo_test] fn fetch_dir_to_nonexistent_cargo_toml() { assert_cargo_toml_doesnt_exist("fetch", "foo/bar/baz/Cargo.toml"); } #[cargo_test] fn generate_lockfile_dir_containing_cargo_toml() { assert_not_a_cargo_toml("generate-lockfile", "foo"); } #[cargo_test] fn generate_lockfile_dir_plus_file() { assert_not_a_cargo_toml("generate-lockfile", "foo/bar"); } #[cargo_test] fn generate_lockfile_dir_plus_path() { assert_not_a_cargo_toml("generate-lockfile", "foo/bar/baz"); } #[cargo_test] fn generate_lockfile_dir_to_nonexistent_cargo_toml() { assert_cargo_toml_doesnt_exist("generate-lockfile", "foo/bar/baz/Cargo.toml"); } #[cargo_test] fn package_dir_containing_cargo_toml() { assert_not_a_cargo_toml("package", "foo"); } #[cargo_test] fn package_dir_plus_file() { assert_not_a_cargo_toml("package", "foo/bar"); } #[cargo_test] fn package_dir_plus_path() { assert_not_a_cargo_toml("package", "foo/bar/baz"); } #[cargo_test] fn package_dir_to_nonexistent_cargo_toml() { assert_cargo_toml_doesnt_exist("package", "foo/bar/baz/Cargo.toml"); } #[cargo_test] fn pkgid_dir_containing_cargo_toml() { assert_not_a_cargo_toml("pkgid", "foo"); } #[cargo_test] fn pkgid_dir_plus_file() { assert_not_a_cargo_toml("pkgid", "foo/bar"); } #[cargo_test] fn pkgid_dir_plus_path() { assert_not_a_cargo_toml("pkgid", "foo/bar/baz"); } #[cargo_test] fn pkgid_dir_to_nonexistent_cargo_toml() { assert_cargo_toml_doesnt_exist("pkgid", "foo/bar/baz/Cargo.toml"); } #[cargo_test] fn publish_dir_containing_cargo_toml() { assert_not_a_cargo_toml("publish", "foo"); } #[cargo_test] fn publish_dir_plus_file() { assert_not_a_cargo_toml("publish", "foo/bar"); } #[cargo_test] fn publish_dir_plus_path() { assert_not_a_cargo_toml("publish", "foo/bar/baz"); } #[cargo_test] fn publish_dir_to_nonexistent_cargo_toml() { assert_cargo_toml_doesnt_exist("publish", "foo/bar/baz/Cargo.toml"); } #[cargo_test] fn read_manifest_dir_containing_cargo_toml() { assert_not_a_cargo_toml("read-manifest", "foo"); } #[cargo_test] fn read_manifest_dir_plus_file() { assert_not_a_cargo_toml("read-manifest", "foo/bar"); } #[cargo_test] fn read_manifest_dir_plus_path() { assert_not_a_cargo_toml("read-manifest", "foo/bar/baz"); } #[cargo_test] fn read_manifest_dir_to_nonexistent_cargo_toml() { assert_cargo_toml_doesnt_exist("read-manifest", "foo/bar/baz/Cargo.toml"); } #[cargo_test] fn run_dir_containing_cargo_toml() { assert_not_a_cargo_toml("run", "foo"); } #[cargo_test] fn run_dir_plus_file() { assert_not_a_cargo_toml("run", "foo/bar"); } #[cargo_test] fn run_dir_plus_path() { assert_not_a_cargo_toml("run", "foo/bar/baz"); } #[cargo_test] fn run_dir_to_nonexistent_cargo_toml() { assert_cargo_toml_doesnt_exist("run", "foo/bar/baz/Cargo.toml"); } #[cargo_test] fn rustc_dir_containing_cargo_toml() { assert_not_a_cargo_toml("rustc", "foo"); } #[cargo_test] fn rustc_dir_plus_file() { assert_not_a_cargo_toml("rustc", "foo/bar"); } #[cargo_test] fn rustc_dir_plus_path() { assert_not_a_cargo_toml("rustc", "foo/bar/baz"); } #[cargo_test] fn rustc_dir_to_nonexistent_cargo_toml() { assert_cargo_toml_doesnt_exist("rustc", "foo/bar/baz/Cargo.toml"); } #[cargo_test] fn test_dir_containing_cargo_toml() { assert_not_a_cargo_toml("test", "foo"); } #[cargo_test] fn test_dir_plus_file() { assert_not_a_cargo_toml("test", "foo/bar"); } #[cargo_test] fn test_dir_plus_path() { assert_not_a_cargo_toml("test", "foo/bar/baz"); } #[cargo_test] fn test_dir_to_nonexistent_cargo_toml() { assert_cargo_toml_doesnt_exist("test", "foo/bar/baz/Cargo.toml"); } #[cargo_test] fn update_dir_containing_cargo_toml() { assert_not_a_cargo_toml("update", "foo"); } #[cargo_test] fn update_dir_plus_file() { assert_not_a_cargo_toml("update", "foo/bar"); } #[cargo_test] fn update_dir_plus_path() { assert_not_a_cargo_toml("update", "foo/bar/baz"); } #[cargo_test] fn update_dir_to_nonexistent_cargo_toml() { assert_cargo_toml_doesnt_exist("update", "foo/bar/baz/Cargo.toml"); } #[cargo_test] fn verify_project_dir_containing_cargo_toml() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("verify-project --manifest-path foo") .cwd(p.root().parent().unwrap()) .with_status(1) .with_stdout_data( str![[r#" [ { "invalid": "the manifest-path must be a path to a Cargo.toml file" } ] "#]] .is_json() .against_jsonlines(), ) .run(); } #[cargo_test] fn verify_project_dir_plus_file() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("verify-project --manifest-path foo/bar") .cwd(p.root().parent().unwrap()) .with_status(1) .with_stdout_data( str![[r#" [ { "invalid": "the manifest-path must be a path to a Cargo.toml file" } ] "#]] .is_json() .against_jsonlines(), ) .run(); } #[cargo_test] fn verify_project_dir_plus_path() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("verify-project --manifest-path foo/bar/baz") .cwd(p.root().parent().unwrap()) .with_status(1) .with_stdout_data( str![[r#" [ { "invalid": "the manifest-path must be a path to a Cargo.toml file" } ] "#]] .is_json() .against_jsonlines(), ) .run(); } #[cargo_test] fn verify_project_dir_to_nonexistent_cargo_toml() { let p = project().build(); p.cargo("verify-project --manifest-path foo/bar/baz/Cargo.toml") .cwd(p.root().parent().unwrap()) .with_status(1) .with_stdout_data( str![[r#" [ { "invalid": "manifest path `foo/bar/baz/Cargo.toml` does not exist" } ] "#]] .is_json() .against_jsonlines(), ) .run(); } cargo-0.91.0/tests/testsuite/bench.rs000064400000000000000000001530671046102023000156420ustar 00000000000000//! Tests for the `cargo bench` command. use crate::prelude::*; use cargo_test_support::{basic_bin_manifest, basic_lib_manifest, basic_manifest, project, str}; #[cargo_test(nightly, reason = "bench")] fn cargo_bench_simple() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file( "src/main.rs", r#" #![feature(test)] #[cfg(test)] extern crate test; fn hello() -> &'static str { "hello" } pub fn main() { println!("{}", hello()) } #[bench] fn bench_hello(_b: &mut test::Bencher) { assert_eq!(hello(), "hello") } "#, ) .build(); p.cargo("build").run(); assert!(p.bin("foo").is_file()); p.process(&p.bin("foo")) .with_stdout_data(str![[r#" hello "#]]) .run(); p.cargo("bench") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] unittests src/main.rs (target/release/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test bench_hello ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn bench_bench_implicit() { let p = project() .file( "src/main.rs", r#" #![feature(test)] #[cfg(test)] extern crate test; #[bench] fn run1(_ben: &mut test::Bencher) { } fn main() { println!("Hello main!"); } "#, ) .file( "tests/other.rs", r#" #![feature(test)] extern crate test; #[bench] fn run3(_ben: &mut test::Bencher) { } "#, ) .file( "benches/mybench.rs", r#" #![feature(test)] extern crate test; #[bench] fn run2(_ben: &mut test::Bencher) { } "#, ) .build(); p.cargo("bench --benches") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] [..] (target/release/deps/foo-[HASH][EXE]) [RUNNING] [..] (target/release/deps/mybench-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test run1 ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test run2 ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn bench_bin_implicit() { let p = project() .file( "src/main.rs", r#" #![feature(test)] #[cfg(test)] extern crate test; #[bench] fn run1(_ben: &mut test::Bencher) { } fn main() { println!("Hello main!"); } "#, ) .file( "tests/other.rs", r#" #![feature(test)] extern crate test; #[bench] fn run3(_ben: &mut test::Bencher) { } "#, ) .file( "benches/mybench.rs", r#" #![feature(test)] extern crate test; #[bench] fn run2(_ben: &mut test::Bencher) { } "#, ) .build(); p.cargo("bench --bins") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] [..] (target/release/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test run1 ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn bench_tarname() { let p = project() .file( "benches/bin1.rs", r#" #![feature(test)] extern crate test; #[bench] fn run1(_ben: &mut test::Bencher) { } "#, ) .file( "benches/bin2.rs", r#" #![feature(test)] extern crate test; #[bench] fn run2(_ben: &mut test::Bencher) { } "#, ) .build(); p.cargo("bench --bench bin2") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] [..] (target/release/deps/bin2-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test run2 ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn bench_multiple_targets() { let p = project() .file( "benches/bin1.rs", r#" #![feature(test)] extern crate test; #[bench] fn run1(_ben: &mut test::Bencher) { } "#, ) .file( "benches/bin2.rs", r#" #![feature(test)] extern crate test; #[bench] fn run2(_ben: &mut test::Bencher) { } "#, ) .file( "benches/bin3.rs", r#" #![feature(test)] extern crate test; #[bench] fn run3(_ben: &mut test::Bencher) { } "#, ) .build(); // This should not have anything about `run3` in it. p.cargo("bench --bench bin1 --bench bin2") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] benches/bin1.rs (target/release/deps/bin1-[HASH][EXE]) [RUNNING] benches/bin2.rs (target/release/deps/bin2-[HASH][EXE]) "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn cargo_bench_verbose() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file( "src/main.rs", r#" #![feature(test)] #[cfg(test)] extern crate test; fn main() {} #[bench] fn bench_hello(_b: &mut test::Bencher) {} "#, ) .build(); p.cargo("bench -v hello") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc [..] src/main.rs [..]` [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] `[..]target/release/deps/foo-[HASH][EXE] hello --bench` "#]]) .with_stdout_data(str![[r#" running 1 test test bench_hello ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn many_similar_names() { let p = project() .file( "src/lib.rs", " #![feature(test)] #[cfg(test)] extern crate test; pub fn foo() {} #[bench] fn lib_bench(_b: &mut test::Bencher) {} ", ) .file( "src/main.rs", " #![feature(test)] #[cfg(test)] extern crate foo; #[cfg(test)] extern crate test; fn main() {} #[bench] fn bin_bench(_b: &mut test::Bencher) { foo::foo() } ", ) .file( "benches/foo.rs", r#" #![feature(test)] extern crate foo; extern crate test; #[bench] fn bench_bench(_b: &mut test::Bencher) { foo::foo() } "#, ) .build(); p.cargo("bench") .with_stdout_data(str![[r#" running 1 test test lib_bench ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test bin_bench ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test bench_bench ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn cargo_bench_failing_test() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file( "src/main.rs", r#" #![feature(test)] #[cfg(test)] extern crate test; fn hello() -> &'static str { "hello" } pub fn main() { println!("{}", hello()) } #[bench] fn bench_hello(_b: &mut test::Bencher) { assert_eq!(hello(), "nope", "NOPE!") } "#, ) .build(); p.cargo("build").run(); assert!(p.bin("foo").is_file()); p.process(&p.bin("foo")) .with_stdout_data(str![[r#" hello "#]]) .run(); // Force libtest into serial execution so that the test header will be printed. p.cargo("bench -- --test-threads=1") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] [..] (target/release/deps/foo-[HASH][EXE]) [ERROR] bench failed, to rerun pass `--bin foo` "#]]) .with_stdout_data("...\n[..]NOPE![..]\n...") .with_status(101) .run(); } #[cargo_test(nightly, reason = "bench")] fn bench_with_lib_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [[bin]] name = "baz" path = "src/main.rs" "#, ) .file( "src/lib.rs", r#" #![feature(test)] #[cfg(test)] extern crate test; /// /// ```rust /// extern crate foo; /// fn main() { /// println!("{}", foo::foo()); /// } /// ``` /// pub fn foo(){} #[bench] fn lib_bench(_b: &mut test::Bencher) {} "#, ) .file( "src/main.rs", " #![feature(test)] #[allow(unused_extern_crates)] extern crate foo; #[cfg(test)] extern crate test; fn main() {} #[bench] fn bin_bench(_b: &mut test::Bencher) {} ", ) .build(); p.cargo("bench") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] [..] (target/release/deps/foo-[HASH][EXE]) [RUNNING] [..] (target/release/deps/baz-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test lib_bench ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test bin_bench ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn bench_with_deep_lib_dep() { let p = project() .at("bar") .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [dependencies.foo] path = "../foo" "#, ) .file( "src/lib.rs", " #![feature(test)] #[cfg(test)] extern crate foo; #[cfg(test)] extern crate test; #[bench] fn bar_bench(_b: &mut test::Bencher) { foo::foo(); } ", ) .build(); let _p2 = project() .file( "src/lib.rs", " #![feature(test)] #[cfg(test)] extern crate test; pub fn foo() {} #[bench] fn foo_bench(_b: &mut test::Bencher) {} ", ) .build(); p.cargo("bench") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] foo v0.0.1 ([ROOT]/foo) [COMPILING] bar v0.0.1 ([ROOT]/bar) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] [..] (target/release/deps/bar-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test bar_bench ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn external_bench_explicit() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [[bench]] name = "bench" path = "src/bench.rs" "#, ) .file( "src/lib.rs", r#" #![feature(test)] #[cfg(test)] extern crate test; pub fn get_hello() -> &'static str { "Hello" } #[bench] fn internal_bench(_b: &mut test::Bencher) {} "#, ) .file( "src/bench.rs", r#" #![feature(test)] #[allow(unused_extern_crates)] extern crate foo; extern crate test; #[bench] fn external_bench(_b: &mut test::Bencher) {} "#, ) .build(); p.cargo("bench") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] [..] (target/release/deps/foo-[HASH][EXE]) [RUNNING] [..] (target/release/deps/bench-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test internal_bench ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test external_bench ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn external_bench_implicit() { let p = project() .file( "src/lib.rs", r#" #![feature(test)] #[cfg(test)] extern crate test; pub fn get_hello() -> &'static str { "Hello" } #[bench] fn internal_bench(_b: &mut test::Bencher) {} "#, ) .file( "benches/external.rs", r#" #![feature(test)] #[allow(unused_extern_crates)] extern crate foo; extern crate test; #[bench] fn external_bench(_b: &mut test::Bencher) {} "#, ) .build(); p.cargo("bench") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] [..] (target/release/deps/foo-[HASH][EXE]) [RUNNING] [..] (target/release/deps/external-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test internal_bench ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test external_bench ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn bench_autodiscover_2015() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] edition = "2015" [features] magic = [] [[bench]] name = "bench_magic" required-features = ["magic"] "#, ) .file("src/lib.rs", "") .file( "benches/bench_basic.rs", r#" #![feature(test)] #[allow(unused_extern_crates)] extern crate foo; extern crate test; #[bench] fn bench_basic(_b: &mut test::Bencher) {} "#, ) .file( "benches/bench_magic.rs", r#" #![feature(test)] #[allow(unused_extern_crates)] extern crate foo; extern crate test; #[bench] fn bench_magic(_b: &mut test::Bencher) {} "#, ) .build(); p.cargo("bench bench_basic") .with_stderr_data(str![[r#" [WARNING] An explicit [[bench]] section is specified in Cargo.toml which currently disables Cargo from automatically inferring other benchmark targets. This inference behavior will change in the Rust 2018 edition and the following files will be included as a benchmark target: * [..]bench_basic.rs This is likely to break cargo build or cargo test as these files may not be ready to be compiled as a benchmark target today. You can future-proof yourself and disable this warning by adding `autobenches = false` to your [package] section. You may also move the files to a location where Cargo would not automatically infer them to be a target, such as in subfolders. For more information on this warning you can consult https://github.com/rust-lang/cargo/issues/5330 [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] [..] (target/release/deps/foo-[HASH][EXE]) "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn dont_run_examples() { let p = project() .file("src/lib.rs", "") .file( "examples/dont-run-me-i-will-fail.rs", r#"fn main() { panic!("Examples should not be run by 'cargo test'"); }"#, ) .build(); p.cargo("bench").run(); } #[cargo_test(nightly, reason = "bench")] fn pass_through_command_line() { let p = project() .file( "src/lib.rs", " #![feature(test)] #[cfg(test)] extern crate test; #[bench] fn foo(_b: &mut test::Bencher) {} #[bench] fn bar(_b: &mut test::Bencher) {} ", ) .build(); p.cargo("bench bar") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] [..] (target/release/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test bar ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 1 filtered out; finished in [ELAPSED]s "#]]) .run(); p.cargo("bench foo") .with_stderr_data(str![[r#" [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] [..] (target/release/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test foo ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 1 filtered out; finished in [ELAPSED]s "#]]) .run(); } // Regression test for running cargo-bench twice with // tests in an rlib #[cargo_test(nightly, reason = "bench")] fn cargo_bench_twice() { let p = project() .file("Cargo.toml", &basic_lib_manifest("foo")) .file( "src/foo.rs", r#" #![crate_type = "rlib"] #![feature(test)] #[cfg(test)] extern crate test; #[bench] fn dummy_bench(b: &mut test::Bencher) { } "#, ) .build(); for _ in 0..2 { p.cargo("bench").run(); } } #[cargo_test(nightly, reason = "bench")] fn lib_bin_same_name() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lib] name = "foo" [[bin]] name = "foo" "#, ) .file( "src/lib.rs", " #![feature(test)] #[cfg(test)] extern crate test; #[bench] fn lib_bench(_b: &mut test::Bencher) {} ", ) .file( "src/main.rs", " #![feature(test)] #[allow(unused_extern_crates)] extern crate foo; #[cfg(test)] extern crate test; #[bench] fn bin_bench(_b: &mut test::Bencher) {} ", ) .build(); p.cargo("bench") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] [..] (target/release/deps/foo-[HASH][EXE]) [RUNNING] [..] (target/release/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test lib_bench ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test bin_bench ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn lib_with_standard_name() { let p = project() .file("Cargo.toml", &basic_manifest("syntax", "0.0.1")) .file( "src/lib.rs", " #![feature(test)] #[cfg(test)] extern crate test; /// ``` /// syntax::foo(); /// ``` pub fn foo() {} #[bench] fn foo_bench(_b: &mut test::Bencher) {} ", ) .file( "benches/bench.rs", " #![feature(test)] extern crate syntax; extern crate test; #[bench] fn bench(_b: &mut test::Bencher) { syntax::foo() } ", ) .build(); p.cargo("bench") .with_stderr_data(str![[r#" [COMPILING] syntax v0.0.1 ([ROOT]/foo) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] [..] (target/release/deps/syntax-[HASH][EXE]) [RUNNING] [..] (target/release/deps/bench-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test foo_bench ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test bench ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn lib_with_standard_name2() { let p = project() .file( "Cargo.toml", r#" [package] name = "syntax" version = "0.0.1" edition = "2015" authors = [] [lib] name = "syntax" bench = false doctest = false "#, ) .file("src/lib.rs", "pub fn foo() {}") .file( "src/main.rs", " #![feature(test)] #[cfg(test)] extern crate syntax; #[cfg(test)] extern crate test; fn main() {} #[bench] fn bench(_b: &mut test::Bencher) { syntax::foo() } ", ) .build(); p.cargo("bench") .with_stderr_data(str![[r#" [COMPILING] syntax v0.0.1 ([ROOT]/foo) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] [..] (target/release/deps/syntax-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test bench ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn bench_dylib() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lib] name = "foo" crate-type = ["dylib"] [dependencies.bar] path = "bar" "#, ) .file( "src/lib.rs", r#" #![feature(test)] extern crate bar as the_bar; #[cfg(test)] extern crate test; pub fn bar() { the_bar::baz(); } #[bench] fn foo(_b: &mut test::Bencher) {} "#, ) .file( "benches/bench.rs", r#" #![feature(test)] extern crate foo as the_foo; extern crate test; #[bench] fn foo(_b: &mut test::Bencher) { the_foo::bar(); } "#, ) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [lib] name = "bar" crate-type = ["dylib"] "#, ) .file("bar/src/lib.rs", "pub fn baz() {}") .build(); p.cargo("bench -v") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [RUNNING] [..] -C opt-level=3 [..] [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] [..] -C opt-level=3 [..] [RUNNING] [..] -C opt-level=3 [..] [RUNNING] [..] -C opt-level=3 [..] [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] `[..]target/release/deps/foo-[HASH][EXE] --bench` [RUNNING] `[..]target/release/deps/bench-[HASH][EXE] --bench` "#]]) .with_stdout_data(str![[r#" running 1 test test foo ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test foo ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); p.root().move_into_the_past(); p.cargo("bench -v") .with_stderr_data(str![[r#" [FRESH] bar v0.0.1 ([ROOT]/foo/bar) [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] `[..]target/release/deps/foo-[HASH][EXE] --bench` [RUNNING] `[..]target/release/deps/bench-[HASH][EXE] --bench` "#]]) .with_stdout_data(str![[r#" running 1 test test foo ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test foo ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn bench_twice_with_build_cmd() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" "#, ) .file("build.rs", "fn main() {}") .file( "src/lib.rs", " #![feature(test)] #[cfg(test)] extern crate test; #[bench] fn foo(_b: &mut test::Bencher) {} ", ) .build(); p.cargo("bench") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] [..] (target/release/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test foo ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); p.cargo("bench") .with_stderr_data(str![[r#" [FINISHED] `bench` profile [optimized] target(s) in [..] [RUNNING] [..] (target/release/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test foo ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn bench_with_examples() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "6.6.6" edition = "2015" authors = [] [[example]] name = "teste1" [[bench]] name = "testb1" "#, ) .file( "src/lib.rs", r#" #![feature(test)] #[cfg(test)] extern crate test; #[cfg(test)] use test::Bencher; pub fn f1() { println!("f1"); } pub fn f2() {} #[bench] fn bench_bench1(_b: &mut Bencher) { f2(); } "#, ) .file( "benches/testb1.rs", " #![feature(test)] extern crate foo; extern crate test; use test::Bencher; #[bench] fn bench_bench2(_b: &mut Bencher) { foo::f2(); } ", ) .file( "examples/teste1.rs", r#" extern crate foo; fn main() { println!("example1"); foo::f1(); } "#, ) .build(); p.cargo("bench -v") .with_stderr_data(str![[r#" [COMPILING] foo v6.6.6 ([ROOT]/foo) [RUNNING] `rustc [..]` [RUNNING] `rustc [..]` [RUNNING] `rustc [..]` [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/release/deps/foo-[HASH][EXE] --bench` [RUNNING] `[ROOT]/foo/target/release/deps/testb1-[HASH][EXE] --bench` "#]]) .with_stdout_data(str![[r#" running 1 test test bench_bench1 ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test bench_bench2 ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn test_a_bench() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.1.0" edition = "2015" [lib] name = "foo" test = false doctest = false [[bench]] name = "b" test = true "#, ) .file("src/lib.rs", "") .file("benches/b.rs", "#[test] fn foo() {}") .build(); p.cargo("test") .with_stderr_data(str![[r#" [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] [..] (target/debug/deps/b-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test foo ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn test_bench_no_run() { let p = project() .file("src/lib.rs", "") .file( "benches/bbaz.rs", r#" #![feature(test)] extern crate test; use test::Bencher; #[bench] fn bench_baz(_: &mut Bencher) {} "#, ) .build(); p.cargo("bench --no-run") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [EXECUTABLE] benches src/lib.rs (target/release/deps/foo-[HASH][EXE]) [EXECUTABLE] benches/bbaz.rs (target/release/deps/bbaz-[HASH][EXE]) "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn test_bench_no_run_emit_json() { let p = project() .file("src/lib.rs", "") .file( "benches/bbaz.rs", r#" #![feature(test)] extern crate test; use test::Bencher; #[bench] fn bench_baz(_: &mut Bencher) {} "#, ) .build(); p.cargo("bench --no-run --message-format json") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn test_bench_no_fail_fast() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file( "src/main.rs", r#" #![feature(test)] #[cfg(test)] extern crate test; fn hello() -> &'static str { "hello" } pub fn main() { println!("{}", hello()) } #[bench] fn bench_hello(_b: &mut test::Bencher) { assert_eq!(hello(), "hello") } #[bench] fn bench_nope(_b: &mut test::Bencher) { assert_eq!("nope", hello(), "NOPE!") } "#, ) .file( "benches/b1.rs", r#" #![feature(test)] extern crate test; #[bench] fn b1_fail(_b: &mut test::Bencher) { assert_eq!(1, 2, "ONE=TWO"); } "#, ) .build(); p.cargo("bench --no-fail-fast -- --test-threads=1") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] unittests src/main.rs (target/release/deps/foo-[HASH][EXE]) [ERROR] bench failed, to rerun pass `--bin foo` [RUNNING] benches/b1.rs (target/release/deps/b1-[HASH][EXE]) [ERROR] bench failed, to rerun pass `--bench b1` [ERROR] 2 targets failed: `--bin foo` `--bench b1` "#]]) .with_stdout_data( r#" ... [..]NOPE![..] ... [..]ONE=TWO[..] ... "#, ) .run(); } #[cargo_test(nightly, reason = "bench")] fn test_bench_multiple_packages() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.1.0" edition = "2015" [dependencies.bar] path = "../bar" [dependencies.baz] path = "../baz" "#, ) .file("src/lib.rs", "") .build(); let _bar = project() .at("bar") .file( "Cargo.toml", r#" [package] name = "bar" authors = [] version = "0.1.0" edition = "2015" [[bench]] name = "bbar" test = true "#, ) .file("src/lib.rs", "") .file( "benches/bbar.rs", r#" #![feature(test)] extern crate test; use test::Bencher; #[bench] fn bench_bar(_b: &mut Bencher) {} "#, ) .build(); let _baz = project() .at("baz") .file( "Cargo.toml", r#" [package] name = "baz" authors = [] version = "0.1.0" edition = "2015" [[bench]] name = "bbaz" test = true "#, ) .file("src/lib.rs", "") .file( "benches/bbaz.rs", r#" #![feature(test)] extern crate test; use test::Bencher; #[bench] fn bench_baz(_b: &mut Bencher) {} "#, ) .build(); p.cargo("bench -p bar -p baz") .with_stderr_data(str![[r#" [RUNNING] [..] (target/release/deps/bbaz-[HASH][EXE]) [RUNNING] [..] (target/release/deps/bbar-[HASH][EXE]) "#]]) .with_stderr_data( str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] bar v0.1.0 ([ROOT]/bar) [COMPILING] baz v0.1.0 ([ROOT]/baz) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/release/deps/bar-[HASH][EXE]) [RUNNING] benches/bbar.rs (target/release/deps/bbar-[HASH][EXE]) [RUNNING] unittests src/lib.rs (target/release/deps/baz-[HASH][EXE]) [RUNNING] benches/bbaz.rs (target/release/deps/bbaz-[HASH][EXE]) "#]] .unordered(), ) .run(); } #[cargo_test(nightly, reason = "bench")] fn bench_all_workspace() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { path = "bar" } [workspace] "#, ) .file("src/main.rs", "fn main() {}") .file( "benches/foo.rs", r#" #![feature(test)] extern crate test; use test::Bencher; #[bench] fn bench_foo(_: &mut Bencher) -> () { () } "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .file( "bar/benches/bar.rs", r#" #![feature(test)] extern crate test; use test::Bencher; #[bench] fn bench_bar(_: &mut Bencher) -> () { () } "#, ) .build(); p.cargo("bench --workspace") .with_stderr_data(str![[r#" [COMPILING] bar v0.1.0 ([ROOT]/foo/bar) [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/release/deps/bar-[HASH][EXE]) [RUNNING] benches/bar.rs (target/release/deps/bar-[HASH][EXE]) [RUNNING] unittests src/main.rs (target/release/deps/foo-[HASH][EXE]) [RUNNING] benches/foo.rs (target/release/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test bench_bar ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test bench_foo ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn bench_all_exclude() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [workspace] members = ["bar", "baz"] "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file( "bar/src/lib.rs", r#" #![feature(test)] #[cfg(test)] extern crate test; #[bench] pub fn bar(b: &mut test::Bencher) { b.iter(|| {}); } "#, ) .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file( "baz/src/lib.rs", "#[test] pub fn baz() { break_the_build(); }", ) .build(); p.cargo("bench --workspace --exclude baz") .with_stdout_data(str![[r#" running 1 test test bar ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn bench_all_exclude_glob() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [workspace] members = ["bar", "baz"] "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file( "bar/src/lib.rs", r#" #![feature(test)] #[cfg(test)] extern crate test; #[bench] pub fn bar(b: &mut test::Bencher) { b.iter(|| {}); } "#, ) .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file( "baz/src/lib.rs", "#[test] pub fn baz() { break_the_build(); }", ) .build(); p.cargo("bench --workspace --exclude '*z'") .with_stdout_data(str![[r#" running 1 test test bar ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn bench_all_virtual_manifest() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .file( "bar/benches/bar.rs", r#" #![feature(test)] extern crate test; use test::Bencher; #[bench] fn bench_bar(_: &mut Bencher) -> () { () } "#, ) .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "pub fn baz() {}") .file( "baz/benches/baz.rs", r#" #![feature(test)] extern crate test; use test::Bencher; #[bench] fn bench_baz(_: &mut Bencher) -> () { () } "#, ) .build(); // The order in which bar and baz are built is not guaranteed p.cargo("bench --workspace") .with_stderr_data( str![[r#" [COMPILING] bar v0.1.0 ([ROOT]/foo/bar) [COMPILING] baz v0.1.0 ([ROOT]/foo/baz) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/release/deps/bar-[HASH][EXE]) [RUNNING] benches/bar.rs (target/release/deps/bar-[HASH][EXE]) [RUNNING] unittests src/lib.rs (target/release/deps/baz-[HASH][EXE]) [RUNNING] benches/baz.rs (target/release/deps/baz-[HASH][EXE]) "#]] .unordered(), ) .with_stdout_data(str![[r#" running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test bench_bar ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test bench_baz ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn bench_virtual_manifest_glob() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() { break_the_build(); }") .file( "bar/benches/bar.rs", r#" #![feature(test)] extern crate test; use test::Bencher; #[bench] fn bench_bar(_: &mut Bencher) -> () { break_the_build(); } "#, ) .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "pub fn baz() {}") .file( "baz/benches/baz.rs", r#" #![feature(test)] extern crate test; use test::Bencher; #[bench] fn bench_baz(_: &mut Bencher) -> () { () } "#, ) .build(); // This should not have `bar` built or benched p.cargo("bench -p '*z'") .with_stderr_data(str![[r#" [COMPILING] baz v0.1.0 ([ROOT]/foo/baz) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/release/deps/baz-[HASH][EXE]) [RUNNING] benches/baz.rs (target/release/deps/baz-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test bench_baz ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } // https://github.com/rust-lang/cargo/issues/4287 #[cargo_test(nightly, reason = "bench")] fn legacy_bench_name() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [[bench]] name = "bench" "#, ) .file("src/lib.rs", "pub fn foo() {}") .file( "src/bench.rs", r#" #![feature(test)] extern crate test; use test::Bencher; #[bench] fn bench_foo(_: &mut Bencher) -> () { () } "#, ) .build(); p.cargo("bench") .with_stderr_data(str![[r#" [WARNING] path `src/bench.rs` was erroneously implicitly accepted for benchmark `bench`, please set bench.path in Cargo.toml [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/release/deps/foo-[HASH][EXE]) [RUNNING] src/bench.rs (target/release/deps/bench-[HASH][EXE]) "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn bench_virtual_manifest_all_implied() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn foo() {}") .file( "bar/benches/bar.rs", r#" #![feature(test)] extern crate test; use test::Bencher; #[bench] fn bench_bar(_: &mut Bencher) -> () { () } "#, ) .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "pub fn baz() {}") .file( "baz/benches/baz.rs", r#" #![feature(test)] extern crate test; use test::Bencher; #[bench] fn bench_baz(_: &mut Bencher) -> () { () } "#, ) .build(); // The order in which bar and baz are built is not guaranteed p.cargo("bench") .with_stderr_data( str![[r#" [COMPILING] bar v0.1.0 ([ROOT]/foo/bar) [COMPILING] baz v0.1.0 ([ROOT]/foo/baz) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/release/deps/bar-[HASH][EXE]) [RUNNING] benches/bar.rs (target/release/deps/bar-[HASH][EXE]) [RUNNING] unittests src/lib.rs (target/release/deps/baz-[HASH][EXE]) [RUNNING] benches/baz.rs (target/release/deps/baz-[HASH][EXE]) "#]] .unordered(), ) .with_stdout_data(str![[r#" running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test bench_bar ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test bench_baz ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn json_artifact_includes_executable_for_benchmark() { let p = project() .file( "benches/benchmark.rs", r#" #![feature(test)] extern crate test; use test::Bencher; #[bench] fn bench_foo(_: &mut Bencher) -> () { () } "#, ) .build(); p.cargo("bench --no-run --message-format=json") .with_stdout_data( str![[r#" [ { "executable": "[..]", "features": [], "filenames": "{...}", "fresh": false, "manifest_path": "[ROOT]/foo/Cargo.toml", "package_id": "path+[ROOTURL]/foo#0.0.1", "profile": "{...}", "reason": "compiler-artifact", "target": { "crate_types": [ "bin" ], "doc": false, "doctest": false, "edition": "2015", "kind": [ "bench" ], "name": "benchmark", "src_path": "[ROOT]/foo/benches/benchmark.rs", "test": false } }, { "reason": "build-finished", "success": true } ] "#]] .is_json() .against_jsonlines(), ) .run(); } #[cargo_test(nightly, reason = "bench")] fn cargo_bench_print_env_verbose() { let p = project() .file("Cargo.toml", &basic_manifest("foo", "0.0.1")) .file( "src/main.rs", r#" #![feature(test)] #[cfg(test)] extern crate test; fn hello() -> &'static str { "hello" } pub fn main() { println!("{}", hello()) } #[bench] fn bench_hello(_b: &mut test::Bencher) { assert_eq!(hello(), "hello") } "#, ) .build(); p.cargo("bench -vv") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[..]CARGO_MANIFEST_DIR=[ROOT]/foo[..] rustc[..]` [FINISHED] `bench` profile [optimized] target(s) in [..] [RUNNING] `[..]CARGO_MANIFEST_DIR=[ROOT]/foo[..] [ROOT]/foo/target/release/deps/foo-[HASH][EXE] --bench` "#]]) .run(); } cargo-0.91.0/tests/testsuite/binary_name.rs000064400000000000000000000317421046102023000170420ustar 00000000000000//! Tests for `cargo-features = ["different-binary-name"]`. use crate::prelude::*; use cargo_test_support::install::assert_has_installed_exe; use cargo_test_support::install::assert_has_not_installed_exe; use cargo_test_support::is_nightly; use cargo_test_support::paths; use cargo_test_support::project; use cargo_test_support::str; #[cargo_test] fn gated() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [[bin]] name = "foo" filename = "007bar" path = "src/main.rs" "#, ) .file("src/main.rs", "fn main() { assert!(true) }") .build(); // Run cargo build. p.cargo("build") .masquerade_as_nightly_cargo(&["different-binary-name"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: feature `different-binary-name` is required The package requires the Cargo feature called `different-binary-name`, but that feature is not stabilized in this version of Cargo ([..]). Consider adding `cargo-features = ["different-binary-name"]` to the top of Cargo.toml (above the [package] table) to tell Cargo you are opting in to use this unstable feature. See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#different-binary-name for more information about the status of this feature. "#]]) .run(); } #[cargo_test] // This test checks if: // 1. The correct binary is produced // 2. The deps file has the correct content // 3. Fingerprinting works // 4. `cargo clean` command works fn binary_name1() { // Create the project. let p = project() .file( "Cargo.toml", r#" cargo-features = ["different-binary-name"] [package] name = "foo" version = "0.0.1" edition = "2015" [[bin]] name = "foo" filename = "007bar" path = "src/main.rs" "#, ) .file("src/main.rs", "fn main() { assert!(true) }") .build(); // Run cargo build. p.cargo("build") .masquerade_as_nightly_cargo(&["different-binary-name"]) .run(); // Check the name of the binary that cargo has generated. // A binary with the name of the crate should NOT be created. let foo_path = p.bin("foo"); assert!(!foo_path.is_file()); // A binary with the name provided in `filename` parameter should be created. let bar_path = p.bin("007bar"); assert!(bar_path.is_file()); // Check if deps file exists. let deps_path = p.bin("007bar").with_extension("d"); assert!(deps_path.is_file(), "{:?}", bar_path); let depinfo = p.read_file(&deps_path); // Prepare what content we expect to be present in deps file. let deps_exp = format!( "{}: {}", p.bin("007bar").to_str().unwrap(), p.root().join("src").join("main.rs").to_str().unwrap() ); // Compare actual deps content with expected deps content. assert!( depinfo.lines().any(|line| line == deps_exp), "Content of `{}` is incorrect", deps_path.to_string_lossy() ); // Run cargo second time, to verify fingerprint. p.cargo("build -p foo -v") .masquerade_as_nightly_cargo(&["different-binary-name"]) .with_stderr_data(str![[r#" [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Run cargo clean. p.cargo("clean -p foo") .masquerade_as_nightly_cargo(&["different-binary-name"]) .run(); // Check if the appropriate file was removed. assert!( !bar_path.is_file(), "`cargo clean` did not remove the correct files" ); } #[cargo_test] // This test checks if: // 1. Check `cargo run` // 2. Check `cargo test` // 3. Check `cargo install/uninstall` fn binary_name2() { // Create the project. let p = project() .file( "Cargo.toml", r#" cargo-features = ["different-binary-name"] [package] name = "foo" version = "0.0.1" edition = "2015" [[bin]] name = "foo" filename = "007bar" "#, ) .file( "src/main.rs", r#" fn hello(name: &str) -> String { format!("Hello, {}!", name) } fn main() { println!("{}", hello("crabs")); } #[cfg(test)] mod tests { use super::*; #[test] fn check_crabs() { assert_eq!(hello("crabs"), "Hello, crabs!"); } } "#, ) .build(); // Run cargo build. p.cargo("build") .masquerade_as_nightly_cargo(&["different-binary-name"]) .run(); // Check the name of the binary that cargo has generated. // A binary with the name of the crate should NOT be created. let foo_path = p.bin("foo"); assert!(!foo_path.is_file()); // A binary with the name provided in `filename` parameter should be created. let bar_path = p.bin("007bar"); assert!(bar_path.is_file()); // Check if `cargo test` works p.cargo("test") .masquerade_as_nightly_cargo(&["different-binary-name"]) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/main.rs (target/debug/deps/foo-[..][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test tests::check_crabs ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); // Check if `cargo run` is able to execute the binary p.cargo("run") .masquerade_as_nightly_cargo(&["different-binary-name"]) .with_stdout_data(str![[r#" Hello, crabs! "#]]) .run(); p.cargo("install") .masquerade_as_nightly_cargo(&["different-binary-name"]) .run(); assert_has_installed_exe(paths::cargo_home(), "007bar"); p.cargo("uninstall") .with_stderr_data(str![[r#" [REMOVING] [ROOT]/home/.cargo/bin/007bar[EXE] "#]]) .masquerade_as_nightly_cargo(&["different-binary-name"]) .run(); assert_has_not_installed_exe(paths::cargo_home(), "007bar"); } #[cargo_test] fn check_env_vars() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["different-binary-name"] [package] name = "foo" version = "0.0.1" edition = "2015" [[bin]] name = "foo" filename = "007bar" "#, ) .file( "src/main.rs", r#" fn main() { println!("{}", option_env!("CARGO_BIN_NAME").unwrap()); } "#, ) .file( "tests/integration.rs", r#" #[test] fn check_env_vars2() { let value = option_env!("CARGO_BIN_EXE_007bar").expect("Could not find environment variable."); assert!(value.contains("007bar")); } "# ) .build(); // Run cargo build. p.cargo("build") .masquerade_as_nightly_cargo(&["different-binary-name"]) .run(); p.cargo("run") .masquerade_as_nightly_cargo(&["different-binary-name"]) .with_stdout_data(str![[r#" 007bar "#]]) .run(); p.cargo("test") .masquerade_as_nightly_cargo(&["different-binary-name"]) .with_status(0) .run(); } #[cargo_test] fn check_msg_format_json() { // Create the project. let p = project() .file( "Cargo.toml", r#" cargo-features = ["different-binary-name"] [package] name = "foo" version = "0.0.1" edition = "2015" [[bin]] name = "foo" filename = "007bar" path = "src/main.rs" "#, ) .file("src/main.rs", "fn main() { assert!(true) }") .build(); // Run cargo build. p.cargo("build --message-format=json") .masquerade_as_nightly_cargo(&["different-binary-name"]) .with_stdout_data( str![[r#" [ { "executable": "[ROOT]/foo/target/debug/007bar[EXE]", "features": [], "filenames": "{...}", "fresh": false, "manifest_path": "[ROOT]/foo/Cargo.toml", "package_id": "path+[ROOTURL]/foo#0.0.1", "profile": "{...}", "reason": "compiler-artifact", "target": "{...}" }, { "reason": "build-finished", "success": true } ] "#]] .is_json() .against_jsonlines(), ) .run(); } #[cargo_test] fn targets_with_relative_path_in_workspace_members() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["relative-bar"] resolver = "2" "#, ) .file( "relative-bar/Cargo.toml", r#" [package] name = "relative-bar" version = "0.1.0" edition = "2021" build = "./build.rs" [[bin]] name = "bar" path = "./src/main.rs" [lib] name = "lib" path = "./src/lib.rs" [[example]] name = "example" path = "./example.rs" [[test]] name = "test" path = "./test.rs" [[bench]] name = "bench" path = "./bench.rs" "#, ) .file("relative-bar/build.rs", "fn main() { let a = 1; }") .file("relative-bar/src/main.rs", "fn main() { let a = 1; }") .file("relative-bar/src/lib.rs", "fn a() {}") .file("relative-bar/example.rs", "fn main() { let a = 1; }") .file( "relative-bar/test.rs", r#" fn main() {} #[test] fn test_a() { let a = 1; } "#, ) .file( "relative-bar/bench.rs", r#" #![feature(test)] #[cfg(test)] extern crate test; #[bench] fn bench_a(_b: &mut test::Bencher) { let a = 1; } "#, ) .build(); p.cargo("check") .with_stderr_data(str![[r#" ... --> relative-bar/build.rs:1:17 ... --> relative-bar/src/lib.rs:1:4 ... --> relative-bar/src/main.rs:1:17 ... "#]]) .run(); p.cargo("check --example example") .with_stderr_data(str![[r#" ... --> relative-bar/example.rs:1:17 ... "#]]) .run(); p.cargo("check --test test") .with_stderr_data(str![[r#" ... --> relative-bar/test.rs:5:35 ... "#]]) .run(); if is_nightly() { p.cargo("check --bench bench") .with_stderr_data(str![[r#" ... --> relative-bar/bench.rs:7:58 ... "#]]) .run(); } // Disable Cargo target auto-discovery. p.change_file( "relative-bar/Cargo.toml", r#" [package] name = "relative-bar" version = "0.1.0" edition = "2021" autolib = false autobins = false autoexamples = false autotests = false autobenches = false build = "./build.rs" [[bin]] name = "bar" path = "./src/main.rs" [lib] name = "lib" path = "./src/lib.rs" [[example]] name = "example" path = "./example.rs" [[test]] name = "test" path = "./test.rs" [[bench]] name = "bench" path = "./bench.rs" "#, ); p.cargo("check") .with_stderr_data(str![[r#" ... --> relative-bar/build.rs:1:17 ... --> relative-bar/src/lib.rs:1:4 ... --> relative-bar/src/main.rs:1:17 ... "#]]) .run(); p.cargo("check --example example") .with_stderr_data(str![[r#" ... --> relative-bar/example.rs:1:17 ... "#]]) .run(); p.cargo("check --test test") .with_stderr_data(str![[r#" ... --> relative-bar/test.rs:5:35 ... "#]]) .run(); if is_nightly() { p.cargo("check --bench bench") .with_stderr_data(str![[r#" ... --> relative-bar/bench.rs:7:58 ... "#]]) .run(); } } cargo-0.91.0/tests/testsuite/build.rs000064400000000000000000005234241046102023000156600ustar 00000000000000//! Tests for the `cargo build` command. use std::env; use std::fs; use std::io::Read; use std::process::Stdio; use crate::prelude::*; use crate::utils::cargo_exe; use crate::utils::cargo_process; use crate::utils::tools; use cargo::GlobalContext; use cargo::core::Shell; use cargo::core::Workspace; use cargo::core::compiler::UserIntent; use cargo::ops::CompileOptions; use cargo_test_support::compare::assert_e2e; use cargo_test_support::paths::root; use cargo_test_support::registry::Package; use cargo_test_support::str; use cargo_test_support::{ Execs, ProjectBuilder, basic_bin_manifest, basic_lib_manifest, basic_manifest, git, is_nightly, main_file, paths, process, project, rustc_host, sleep_ms, symlink_supported, t, }; use cargo_util::paths::dylib_path_envvar; #[cargo_test] fn cargo_compile_simple() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("build").run(); assert!(p.bin("foo").is_file()); p.process(&p.bin("foo")) .with_stdout_data(str![[r#" i am foo "#]]) .run(); } #[cargo_test] fn build_with_symlink_to_path_dependency_with_build_script_in_git() { if !symlink_supported() { return; } let root = paths::root(); git::repo(&root) .nocommit_file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2021" [dependencies] # the path leads through a symlink, 'symlink-to-original' is a worktree root, # and symlink-to-dir/ is a symlink to a sub-directory to be stepped through. lib = { version = "0.1.0", path = "symlink-to-original/symlink-to-dir/lib" } "#, ) .nocommit_file("src/main.rs", "fn main() { }") .nocommit_file("original/dir/lib/build.rs", "fn main() {}") .nocommit_file( "original/dir/lib/Cargo.toml", r#" [package] name = "lib" version = "0.1.0" edition = "2021" "#, ) .nocommit_file("original/dir/lib/src/lib.rs", "") .nocommit_symlink_dir("original", "symlink-to-original") .nocommit_symlink_dir("original/dir", "original/symlink-to-dir") .build(); // It is necessary to have a sub-repository and to add files so there is an index. let repo = git::init(&root.join("original")); git::add(&repo); cargo_process("build").run(); } #[cargo_test] fn cargo_fail_with_no_stderr() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", &String::from("refusal")) .build(); p.cargo("build --message-format=json") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [ERROR] could not compile `foo` (bin "foo") due to 1 previous error "#]]) .run(); } /// Checks that the `CARGO_INCREMENTAL` environment variable results in /// `rustc` getting `-C incremental` passed to it. #[cargo_test] fn cargo_compile_incremental() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("build -v") .env("CARGO_INCREMENTAL", "1") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc [..] -C incremental=[ROOT]/foo/target/debug/incremental[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("test -v") .env("CARGO_INCREMENTAL", "1") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc [..] -C incremental=[ROOT]/foo/target/debug/incremental[..]` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/debug/deps/foo-[HASH][EXE]` "#]]) .run(); } #[cargo_test] fn incremental_profile() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [profile.dev] incremental = false [profile.release] incremental = true "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build -v") .env_remove("CARGO_INCREMENTAL") .with_stderr_does_not_contain("[..]C incremental=[..]") .run(); p.cargo("build -v") .env("CARGO_INCREMENTAL", "1") .with_stderr_contains("[..]C incremental=[..]") .run(); p.cargo("build --release -v") .env_remove("CARGO_INCREMENTAL") .with_stderr_contains("[..]C incremental=[..]") .run(); p.cargo("build --release -v") .env("CARGO_INCREMENTAL", "0") .with_stderr_does_not_contain("[..]C incremental=[..]") .run(); } #[cargo_test] fn incremental_config() { let p = project() .file("src/main.rs", "fn main() {}") .file( ".cargo/config.toml", r#" [build] incremental = false "#, ) .build(); p.cargo("build -v") .env_remove("CARGO_INCREMENTAL") .with_stderr_does_not_contain("[..]C incremental=[..]") .run(); p.cargo("build -v") .env("CARGO_INCREMENTAL", "1") .with_stderr_contains("[..]C incremental=[..]") .run(); } #[cargo_test] fn cargo_compile_with_redundant_default_mode() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("build --debug") .with_stderr_data(str![[r#" [ERROR] unexpected argument '--debug' found tip: `--debug` is the default for `cargo build`; instead `--release` is supported Usage: cargo[EXE] build [OPTIONS] For more information, try '--help'. "#]]) .with_status(1) .run(); } #[cargo_test] fn cargo_compile_with_unsupported_short_config_flag() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("build -c net.git-fetch-with-cli=true") .with_stderr_data(str![[r#" [ERROR] unexpected argument '-c' found tip: a similar argument exists: '--config' Usage: cargo[EXE] build [OPTIONS] For more information, try '--help'. "#]]) .with_status(1) .run(); } #[cargo_test] fn cargo_compile_with_workspace_excluded() { let p = project().file("src/main.rs", "fn main() {}").build(); p.cargo("build --workspace --exclude foo") .with_stderr_data(str![[r#" [ERROR] no packages to compile "#]]) .with_status(101) .run(); } #[cargo_test] fn cargo_compile_manifest_path() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("build --manifest-path foo/Cargo.toml") .cwd(p.root().parent().unwrap()) .run(); assert!(p.bin("foo").is_file()); } #[cargo_test] fn cargo_compile_with_wrong_manifest_path_flag() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("build --path foo/Cargo.toml") .cwd(p.root().parent().unwrap()) .with_stderr_data(str![[r#" [ERROR] unexpected argument '--path' found tip: a similar argument exists: '--manifest-path' Usage: cargo[EXE] build [OPTIONS] For more information, try '--help'. "#]]) .with_status(1) .run(); } #[cargo_test] fn chdir_gated() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .build(); p.cargo("-C foo build") .cwd(p.root().parent().unwrap()) .with_stderr_data(str![[r#" [ERROR] the `-C` flag is unstable, pass `-Z unstable-options` on the nightly channel to enable it "#]]) .with_status(101) .run(); // No masquerade should also fail. p.cargo("-C foo -Z unstable-options build") .cwd(p.root().parent().unwrap()) .with_stderr_data(str![[r#" [ERROR] the `-C` flag is unstable, pass `-Z unstable-options` on the nightly channel to enable it "#]]) .with_status(101) .run(); } #[cargo_test] fn cargo_compile_directory_not_cwd() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .file(".cargo/config.toml", &"") .build(); p.cargo("-Zunstable-options -C foo build") .masquerade_as_nightly_cargo(&["chdir"]) .cwd(p.root().parent().unwrap()) .run(); assert!(p.bin("foo").is_file()); } #[cargo_test] fn cargo_compile_with_unsupported_short_unstable_feature_flag() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .file(".cargo/config.toml", &"") .build(); p.cargo("-zunstable-options -C foo build") .masquerade_as_nightly_cargo(&["chdir"]) .cwd(p.root().parent().unwrap()) .with_stderr_data(str![[r#" [ERROR] unexpected argument '-z' found tip: a similar argument exists: '-Z' Usage: cargo [..][OPTIONS] [COMMAND] cargo [..][OPTIONS] -Zscript [ARGS]... For more information, try '--help'. "#]]) .with_status(1) .run(); } #[cargo_test] fn cargo_compile_directory_not_cwd_with_invalid_config() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .file(".cargo/config.toml", &"!") .build(); p.cargo("-Zunstable-options -C foo build") .masquerade_as_nightly_cargo(&["chdir"]) .cwd(p.root().parent().unwrap()) .with_status(101) .with_stderr_data(str![[r#" [ERROR] could not load Cargo configuration Caused by: could not parse TOML configuration in `[ROOT]/foo/.cargo/config.toml` Caused by: TOML parse error at line 1, column 2 | 1 | ! | ^ key with no value, expected `=` "#]]) .run(); } #[cargo_test] fn cargo_compile_with_invalid_manifest() { let p = project().file("Cargo.toml", "").build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: manifest is missing either a `[package]` or a `[workspace]` "#]]) .run(); } #[cargo_test] fn cargo_compile_with_invalid_manifest2() { let p = project() .file( "Cargo.toml", " [package] foo = bar ", ) .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] string values must be quoted, expected literal string --> Cargo.toml:3:23 | 3 | foo = bar | ^^^ | "#]]) .run(); } #[cargo_test] fn cargo_compile_with_invalid_manifest3() { let p = project().file("src/Cargo.toml", "a = bar").build(); p.cargo("build --manifest-path src/Cargo.toml") .with_status(101) .with_stderr_data(str![[r#" [ERROR] string values must be quoted, expected literal string --> src/Cargo.toml:1:5 | 1 | a = bar | ^^^ | "#]]) .run(); } #[cargo_test] fn cargo_compile_duplicate_build_targets() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lib] name = "main" path = "src/main.rs" crate-type = ["dylib"] [dependencies] "#, ) .file("src/main.rs", "#![allow(warnings)] fn main() {}") .build(); p.cargo("build") .with_stderr_data(str![[r#" [WARNING] file `[ROOT]/foo/src/main.rs` found to be present in multiple build targets: * `lib` target `main` * `bin` target `foo` [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn cargo_compile_with_invalid_version() { let p = project() .file("Cargo.toml", &basic_manifest("foo", "1.0")) .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] unexpected end of input while parsing minor version number --> Cargo.toml:4:19 | 4 | version = "1.0" | ^^^^^ | "#]]) .run(); } #[cargo_test] fn cargo_compile_with_empty_package_name() { let p = project() .file("Cargo.toml", &basic_manifest("", "0.0.0")) .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package name cannot be empty --> Cargo.toml:3:16 | 3 | name = "" | ^^ | "#]]) .run(); } #[cargo_test] fn cargo_compile_with_invalid_package_name() { let p = project() .file("Cargo.toml", &basic_manifest("foo@bar", "0.0.0")) .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid character `@` in package name: `foo@bar`, characters must be Unicode XID characters (numbers, `-`, `_`, or most letters) --> Cargo.toml:3:16 | 3 | name = "foo@bar" | ^^^^^^^^^ | "#]]) .run(); } #[cargo_test] fn cargo_compile_with_invalid_bin_target_name() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.0" edition = "2015" [[bin]] name = "" "#, ) .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: binary target names cannot be empty "#]]) .run(); } #[cargo_test] fn cargo_compile_with_forbidden_bin_target_name() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.0" edition = "2015" [[bin]] name = "build" "#, ) .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: the binary target name `build` is forbidden, it conflicts with cargo's build directory names "#]]) .run(); } #[cargo_test] fn cargo_compile_with_bin_and_crate_type() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.0" edition = "2015" [[bin]] name = "the_foo_bin" path = "src/foo.rs" crate-type = ["cdylib", "rlib"] "#, ) .file("src/foo.rs", "fn main() {}") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: the target `the_foo_bin` is a binary and can't have any crate-types set (currently "cdylib, rlib") "#]]) .run(); } #[cargo_test] fn cargo_compile_api_exposes_artifact_paths() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.0" edition = "2015" [[bin]] name = "the_foo_bin" path = "src/bin.rs" [lib] name = "the_foo_lib" path = "src/foo.rs" crate-type = ["cdylib", "rlib"] "#, ) .file("src/foo.rs", "pub fn bar() {}") .file("src/bin.rs", "pub fn main() {}") .build(); let shell = Shell::from_write(Box::new(Vec::new())); let gctx = GlobalContext::new(shell, env::current_dir().unwrap(), paths::home()); let ws = Workspace::new(&p.root().join("Cargo.toml"), &gctx).unwrap(); let compile_options = CompileOptions::new(ws.gctx(), UserIntent::Build).unwrap(); let result = cargo::ops::compile(&ws, &compile_options).unwrap(); assert_eq!(1, result.binaries.len()); assert!(result.binaries[0].path.exists()); assert!( result.binaries[0] .path .to_str() .unwrap() .contains("the_foo_bin") ); assert_eq!(1, result.cdylibs.len()); // The exact library path varies by platform, but should certainly exist at least assert!(result.cdylibs[0].path.exists()); assert!( result.cdylibs[0] .path .to_str() .unwrap() .contains("the_foo_lib") ); } #[cargo_test] fn cargo_compile_with_bin_and_proc() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.0" edition = "2015" [[bin]] name = "the_foo_bin" path = "src/foo.rs" proc-macro = true "#, ) .file("src/foo.rs", "fn main() {}") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: the target `the_foo_bin` is a binary and can't have `proc-macro` set `true` "#]]) .run(); } #[cargo_test] fn cargo_compile_with_invalid_lib_target_name() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.0" edition = "2015" [lib] name = "" "#, ) .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: library target names cannot be empty "#]]) .run(); } #[cargo_test] fn cargo_compile_with_invalid_non_numeric_dep_version() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] crossbeam = "y" "#, ) .file("src/lib.rs", "") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: failed to parse the version requirement `y` for dependency `crossbeam` Caused by: unexpected character 'y' while parsing major version number "#]]) .run(); } #[cargo_test] fn cargo_compile_without_manifest() { let p = project().no_manifest().build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] could not find `Cargo.toml` in `[ROOT]/foo` or any parent directory "#]]) .run(); } #[cargo_test] #[cfg(target_os = "linux")] fn cargo_compile_with_lowercase_cargo_toml() { let p = project() .no_manifest() .file("cargo.toml", &basic_manifest("foo", "0.1.0")) .file("src/lib.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] could not find `Cargo.toml` in `[ROOT]/foo` or any parent directory, but found cargo.toml please try to rename it to Cargo.toml "#]]) .run(); } #[cargo_test] fn cargo_compile_with_invalid_code() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", "invalid rust code!") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [ERROR] [..] ... [ERROR] could not compile `foo` (bin "foo") due to 1 previous error "#]]) .run(); assert!(p.root().join("Cargo.lock").is_file()); } #[cargo_test] fn cargo_compile_with_invalid_code_in_deps() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "../bar" [dependencies.baz] path = "../baz" "#, ) .file("src/main.rs", "invalid rust code!") .build(); let _bar = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "invalid rust code!") .build(); let _baz = project() .at("baz") .file("Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("src/lib.rs", "invalid rust code!") .build(); p.cargo("build") .with_status(101) .with_stderr_data( str![[r#" [COMPILING] bar v0.1.0 ([ROOT]/bar) [COMPILING] baz v0.1.0 ([ROOT]/baz) [ERROR] could not compile `bar` (lib) due to 1 previous error [ERROR] could not compile `baz` (lib) due to 1 previous error ... "#]] .unordered(), ) .run(); } #[cargo_test] fn cargo_compile_with_warnings_in_the_root_package() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", "fn main() {} fn dead() {}") .build(); p.cargo("build") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [WARNING] [..]dead[..] ... [WARNING] `foo` (bin "foo") generated 1 warning [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn cargo_compile_with_warnings_in_a_dep_package() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.bar] path = "bar" [[bin]] name = "foo" "#, ) .file("src/main.rs", &main_file(r#""{}", bar::gimme()"#, &["bar"])) .file("bar/Cargo.toml", &basic_lib_manifest("bar")) .file( "bar/src/bar.rs", r#" pub fn gimme() -> &'static str { "test passed" } fn dead() {} "#, ) .build(); p.cargo("build") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [WARNING] [..]dead[..] ... [WARNING] `bar` (lib) generated 1 warning [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert!(p.bin("foo").is_file()); p.process(&p.bin("foo")) .with_stdout_data(str![[r#" test passed "#]]) .run(); } #[cargo_test] fn cargo_compile_with_nested_deps_inferred() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.bar] path = 'bar' [[bin]] name = "foo" "#, ) .file("src/foo.rs", &main_file(r#""{}", bar::gimme()"#, &["bar"])) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.baz] path = "../baz" "#, ) .file( "bar/src/lib.rs", r#" extern crate baz; pub fn gimme() -> String { baz::gimme() } "#, ) .file("baz/Cargo.toml", &basic_manifest("baz", "0.5.0")) .file( "baz/src/lib.rs", r#" pub fn gimme() -> String { "test passed".to_string() } "#, ) .build(); p.cargo("build").run(); assert!(p.bin("foo").is_file()); assert!(!p.bin("libbar.rlib").is_file()); assert!(!p.bin("libbaz.rlib").is_file()); p.process(&p.bin("foo")) .with_stdout_data(str![[r#" test passed "#]]) .run(); } #[cargo_test] fn cargo_compile_with_nested_deps_correct_bin() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.bar] path = "bar" [[bin]] name = "foo" "#, ) .file("src/main.rs", &main_file(r#""{}", bar::gimme()"#, &["bar"])) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.baz] path = "../baz" "#, ) .file( "bar/src/lib.rs", r#" extern crate baz; pub fn gimme() -> String { baz::gimme() } "#, ) .file("baz/Cargo.toml", &basic_manifest("baz", "0.5.0")) .file( "baz/src/lib.rs", r#" pub fn gimme() -> String { "test passed".to_string() } "#, ) .build(); p.cargo("build").run(); assert!(p.bin("foo").is_file()); assert!(!p.bin("libbar.rlib").is_file()); assert!(!p.bin("libbaz.rlib").is_file()); p.process(&p.bin("foo")) .with_stdout_data(str![[r#" test passed "#]]) .run(); } #[cargo_test] fn cargo_compile_with_nested_deps_shorthand() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.bar] path = "bar" "#, ) .file("src/main.rs", &main_file(r#""{}", bar::gimme()"#, &["bar"])) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.baz] path = "../baz" [lib] name = "bar" "#, ) .file( "bar/src/bar.rs", r#" extern crate baz; pub fn gimme() -> String { baz::gimme() } "#, ) .file("baz/Cargo.toml", &basic_lib_manifest("baz")) .file( "baz/src/baz.rs", r#" pub fn gimme() -> String { "test passed".to_string() } "#, ) .build(); p.cargo("build").run(); assert!(p.bin("foo").is_file()); assert!(!p.bin("libbar.rlib").is_file()); assert!(!p.bin("libbaz.rlib").is_file()); p.process(&p.bin("foo")) .with_stdout_data(str![[r#" test passed "#]]) .run(); } #[cargo_test] fn cargo_compile_with_nested_deps_longhand() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.bar] path = "bar" version = "0.5.0" edition = "2015" [[bin]] name = "foo" "#, ) .file("src/foo.rs", &main_file(r#""{}", bar::gimme()"#, &["bar"])) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.baz] path = "../baz" version = "0.5.0" edition = "2015" [lib] name = "bar" "#, ) .file( "bar/src/bar.rs", r#" extern crate baz; pub fn gimme() -> String { baz::gimme() } "#, ) .file("baz/Cargo.toml", &basic_lib_manifest("baz")) .file( "baz/src/baz.rs", r#" pub fn gimme() -> String { "test passed".to_string() } "#, ) .build(); p.cargo("build").run(); assert!(p.bin("foo").is_file()); assert!(!p.bin("libbar.rlib").is_file()); assert!(!p.bin("libbaz.rlib").is_file()); p.process(&p.bin("foo")) .with_stdout_data(str![[r#" test passed "#]]) .run(); } // Check that Cargo gives a sensible error if a dependency can't be found // because of a name mismatch. #[cargo_test] fn cargo_compile_with_dep_name_mismatch() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = ["wycats@example.com"] [[bin]] name = "foo" [dependencies.notquitebar] path = "bar" "#, ) .file("src/bin/foo.rs", &main_file(r#""i am foo""#, &["bar"])) .file("bar/Cargo.toml", &basic_bin_manifest("bar")) .file("bar/src/bar.rs", &main_file(r#""i am bar""#, &[])) .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no matching package named `notquitebar` found location searched: [ROOT]/foo/bar required by package `foo v0.0.1 ([ROOT]/foo)` "#]]) .run(); } // Ensure that renamed deps have a valid name #[cargo_test] fn cargo_compile_with_invalid_dep_rename() { let p = project() .file( "Cargo.toml", r#" [package] name = "buggin" version = "0.1.0" edition = "2015" [dependencies] "haha this isn't a valid name 🐛" = { package = "libc", version = "0.1" } "#, ) .file("src/main.rs", &main_file(r#""What's good?""#, &[])) .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid character ` ` in package name: `haha this isn't a valid name 🐛`, characters must be Unicode XID characters (numbers, `-`, `_`, or most letters) --> Cargo.toml:8:17 | 8 | "haha this isn't a valid name 🐛" = { package = "libc", version = "0.1" } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | "#]]) .run(); } #[cargo_test] fn cargo_compile_with_filename() { let p = project() .file("src/lib.rs", "") .file( "src/bin/a.rs", r#" extern crate foo; fn main() { println!("hello a.rs"); } "#, ) .file("examples/a.rs", r#"fn main() { println!("example"); }"#) .build(); p.cargo("build --bin bin.rs") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no bin target named `bin.rs` in default-run packages [HELP] available bin targets: a "#]]) .run(); p.cargo("build --bin a.rs") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no bin target named `a.rs` in default-run packages [HELP] a target with a similar name exists: `a` "#]]) .run(); p.cargo("build --example example.rs") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no example target named `example.rs` in default-run packages [HELP] available example targets: a "#]]) .run(); p.cargo("build --example a.rs") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no example target named `a.rs` in default-run packages [HELP] a target with a similar name exists: `a` "#]]) .run(); } #[cargo_test] fn incompatible_dependencies() { Package::new("bad", "0.1.0").publish(); Package::new("bad", "1.0.0").publish(); Package::new("bad", "1.0.1").publish(); Package::new("bad", "1.0.2").publish(); Package::new("bar", "0.1.0").dep("bad", "0.1.0").publish(); Package::new("baz", "0.1.1").dep("bad", "=1.0.0").publish(); Package::new("baz", "0.1.0").dep("bad", "=1.0.0").publish(); Package::new("qux", "0.1.2").dep("bad", ">=1.0.1").publish(); Package::new("qux", "0.1.1").dep("bad", ">=1.0.1").publish(); Package::new("qux", "0.1.0").dep("bad", ">=1.0.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = "0.1.0" baz = "0.1.0" qux = "0.1.0" "#, ) .file("src/main.rs", "fn main(){}") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for `bad`. ... required by package `qux v0.1.0` ... which satisfies dependency `qux = "^0.1.0"` of package `foo v0.0.1 ([ROOT]/foo)` versions that meet the requirements `>=1.0.1` are: 1.0.2, 1.0.1 all possible versions conflict with previously selected packages. previously selected package `bad v1.0.0` ... which satisfies dependency `bad = "=1.0.0"` of package `baz v0.1.0` ... which satisfies dependency `baz = "^0.1.0"` of package `foo v0.0.1 ([ROOT]/foo)` failed to select a version for `bad` which could resolve this conflict "#]]) .run(); } #[cargo_test] fn incompatible_dependencies_with_multi_semver() { Package::new("bad", "1.0.0").publish(); Package::new("bad", "1.0.1").publish(); Package::new("bad", "2.0.0").publish(); Package::new("bad", "2.0.1").publish(); Package::new("bar", "0.1.0").dep("bad", "=1.0.0").publish(); Package::new("baz", "0.1.0").dep("bad", ">=2.0.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = "0.1.0" baz = "0.1.0" bad = ">=1.0.1, <=2.0.0" "#, ) .file("src/main.rs", "fn main(){}") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for `bad`. ... required by package `foo v0.0.1 ([ROOT]/foo)` versions that meet the requirements `>=1.0.1, <=2.0.0` are: 2.0.0, 1.0.1 all possible versions conflict with previously selected packages. previously selected package `bad v2.0.1` ... which satisfies dependency `bad = ">=2.0.1"` of package `baz v0.1.0` ... which satisfies dependency `baz = "^0.1.0"` of package `foo v0.0.1 ([ROOT]/foo)` previously selected package `bad v1.0.0` ... which satisfies dependency `bad = "=1.0.0"` of package `bar v0.1.0` ... which satisfies dependency `bar = "^0.1.0"` of package `foo v0.0.1 ([ROOT]/foo)` failed to select a version for `bad` which could resolve this conflict "#]]) .run(); } #[cargo_test] fn compile_path_dep_then_change_version() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "bar" "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "") .build(); p.cargo("build").run(); p.change_file("bar/Cargo.toml", &basic_manifest("bar", "0.0.2")); p.cargo("build").run(); } #[cargo_test] fn ignores_carriage_return_in_lockfile() { let p = project() .file("src/main.rs", "mod a; fn main() {}") .file("src/a.rs", "") .build(); p.cargo("build").run(); let lock = p.read_lockfile(); p.change_file("Cargo.lock", &lock.replace("\n", "\r\n")); p.cargo("build").run(); } #[cargo_test] fn cargo_default_env_metadata_env_var() { // Ensure that path dep + dylib + env_var get metadata // (even though path_dep + dylib should not) let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "bar" "#, ) .file("src/lib.rs", "// hi") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [lib] name = "bar" crate-type = ["dylib"] "#, ) .file("bar/src/lib.rs", "// hello") .build(); let dll_prefix = env::consts::DLL_PREFIX; let dll_suffix = env::consts::DLL_SUFFIX; // No metadata on libbar since it's a dylib path dependency p.cargo("build -v") .with_stderr_data(format!( "\ ... [RUNNING] `rustc --crate-name foo [..]--extern bar=[ROOT]/foo/target/debug/deps/{dll_prefix}bar{dll_suffix}` ... ")) .run(); p.cargo("clean").run(); // If you set the env-var, then we expect metadata on libbar p.cargo("build -v") .env("__CARGO_DEFAULT_LIB_METADATA", "stable") .with_stderr_data(format!( "\ ... [RUNNING] `rustc --crate-name foo [..]--extern bar=[ROOT]/foo/target/debug/deps/{dll_prefix}bar-[..]{dll_suffix}` ... ")) .run(); } #[cargo_test] fn crate_env_vars() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.1-alpha.1" edition = "2015" description = "This is foo" homepage = "https://example.com" repository = "https://example.com/repo.git" authors = ["wycats@example.com"] license = "MIT OR Apache-2.0" license-file = "license.txt" rust-version = "1.61.0" readme = "../../README.md" [[bin]] name = "foo-bar" path = "src/main.rs" "#, ) .file( "src/main.rs", r#" extern crate foo; static VERSION_MAJOR: &'static str = env!("CARGO_PKG_VERSION_MAJOR"); static VERSION_MINOR: &'static str = env!("CARGO_PKG_VERSION_MINOR"); static VERSION_PATCH: &'static str = env!("CARGO_PKG_VERSION_PATCH"); static VERSION_PRE: &'static str = env!("CARGO_PKG_VERSION_PRE"); static VERSION: &'static str = env!("CARGO_PKG_VERSION"); static CARGO_MANIFEST_DIR: &'static str = env!("CARGO_MANIFEST_DIR"); static CARGO_MANIFEST_PATH: &'static str = env!("CARGO_MANIFEST_PATH"); static PKG_NAME: &'static str = env!("CARGO_PKG_NAME"); static HOMEPAGE: &'static str = env!("CARGO_PKG_HOMEPAGE"); static REPOSITORY: &'static str = env!("CARGO_PKG_REPOSITORY"); static LICENSE: &'static str = env!("CARGO_PKG_LICENSE"); static LICENSE_FILE: &'static str = env!("CARGO_PKG_LICENSE_FILE"); static DESCRIPTION: &'static str = env!("CARGO_PKG_DESCRIPTION"); static RUST_VERSION: &'static str = env!("CARGO_PKG_RUST_VERSION"); static README: &'static str = env!("CARGO_PKG_README"); static BIN_NAME: &'static str = env!("CARGO_BIN_NAME"); static CRATE_NAME: &'static str = env!("CARGO_CRATE_NAME"); fn main() { let s = format!("{}-{}-{} @ {} in {} file {}", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, VERSION_PRE, CARGO_MANIFEST_DIR, CARGO_MANIFEST_PATH); assert_eq!(s, foo::version()); println!("{}", s); assert_eq!("foo", PKG_NAME); assert_eq!("foo-bar", BIN_NAME); assert_eq!("foo_bar", CRATE_NAME); assert_eq!("https://example.com", HOMEPAGE); assert_eq!("https://example.com/repo.git", REPOSITORY); assert_eq!("MIT OR Apache-2.0", LICENSE); assert_eq!("license.txt", LICENSE_FILE); assert_eq!("This is foo", DESCRIPTION); assert_eq!("1.61.0", RUST_VERSION); assert_eq!("../../README.md", README); let s = format!("{}.{}.{}-{}", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, VERSION_PRE); assert_eq!(s, VERSION); // Verify CARGO_TARGET_TMPDIR isn't set for bins assert!(option_env!("CARGO_TARGET_TMPDIR").is_none()); } "#, ) .file( "src/lib.rs", r#" use std::env; use std::path::PathBuf; pub fn version() -> String { format!("{}-{}-{} @ {} in {} file {}", env!("CARGO_PKG_VERSION_MAJOR"), env!("CARGO_PKG_VERSION_MINOR"), env!("CARGO_PKG_VERSION_PATCH"), env!("CARGO_PKG_VERSION_PRE"), env!("CARGO_MANIFEST_DIR"), env!("CARGO_MANIFEST_PATH")) } pub fn check_no_int_test_env() { env::var("CARGO_TARGET_DIR").unwrap_err(); } pub fn check_tmpdir(tmp: Option<&'static str>) { let tmpdir: PathBuf = tmp.unwrap().into(); let exe: PathBuf = env::current_exe().unwrap().into(); let mut expected: PathBuf = exe.parent().unwrap() .parent().unwrap() .parent().unwrap() .into(); expected.push("tmp"); assert_eq!(tmpdir, expected); // Check that CARGO_TARGET_TMPDIR isn't set for lib code assert!(option_env!("CARGO_TARGET_TMPDIR").is_none()); env::var("CARGO_TARGET_TMPDIR").unwrap_err(); } #[test] fn unit_env_cargo_target_tmpdir() { // Check that CARGO_TARGET_TMPDIR isn't set for unit tests assert!(option_env!("CARGO_TARGET_TMPDIR").is_none()); env::var("CARGO_TARGET_TMPDIR").unwrap_err(); } "#, ) .file( "examples/ex-env-vars.rs", r#" static PKG_NAME: &'static str = env!("CARGO_PKG_NAME"); static BIN_NAME: &'static str = env!("CARGO_BIN_NAME"); static CRATE_NAME: &'static str = env!("CARGO_CRATE_NAME"); fn main() { assert_eq!("foo", PKG_NAME); assert_eq!("ex-env-vars", BIN_NAME); assert_eq!("ex_env_vars", CRATE_NAME); // Verify CARGO_TARGET_TMPDIR isn't set for examples assert!(option_env!("CARGO_TARGET_TMPDIR").is_none()); } "#, ) .file( "tests/env.rs", r#" #[test] fn integration_env_cargo_target_tmpdir() { foo::check_tmpdir(option_env!("CARGO_TARGET_TMPDIR")); } "#, ); let p = if is_nightly() { p.file( "benches/env.rs", r#" #![feature(test)] extern crate test; use test::Bencher; #[bench] fn bench_env_cargo_target_tmpdir(_: &mut Bencher) { foo::check_tmpdir(option_env!("CARGO_TARGET_TMPDIR")); } "#, ) .build() } else { p.build() }; println!("build"); p.cargo("build -v").run(); println!("bin"); p.process(&p.bin("foo-bar")) .with_stdout_data(str![[r#" 0-5-1 @ alpha.1 in [ROOT]/foo file [ROOT]/foo/Cargo.toml "#]]) .run(); println!("example"); p.cargo("run --example ex-env-vars -v").run(); println!("test"); p.cargo("test -v").run(); if is_nightly() { println!("bench"); p.cargo("bench -v").run(); } } #[cargo_test] fn crate_authors_env_vars() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.1-alpha.1" edition = "2015" authors = ["wycats@example.com", "neikos@example.com"] "#, ) .file( "src/main.rs", r#" extern crate foo; static AUTHORS: &'static str = env!("CARGO_PKG_AUTHORS"); fn main() { let s = "wycats@example.com:neikos@example.com"; assert_eq!(AUTHORS, foo::authors()); println!("{}", AUTHORS); assert_eq!(s, AUTHORS); } "#, ) .file( "src/lib.rs", r#" pub fn authors() -> String { format!("{}", env!("CARGO_PKG_AUTHORS")) } "#, ) .build(); println!("build"); p.cargo("build -v").run(); println!("bin"); p.process(&p.bin("foo")) .with_stdout_data(str![[r#" wycats@example.com:neikos@example.com "#]]) .run(); println!("test"); p.cargo("test -v").run(); } #[cargo_test] fn vv_prints_rustc_env_vars() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = ["escape='\"@example.com"] "#, ) .file("src/main.rs", "fn main() {}") .build(); let mut b = p.cargo("build -vv"); #[cfg(windows)] { b.with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[..] CARGO_PKG_AUTHORS="escape='/"@example.com"&& [..] set CARGO_PKG_NAME=foo&& [..] rustc [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); } #[cfg(not(windows))] { b.with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[..] CARGO_PKG_AUTHORS='escape='/''"@example.com' [..] CARGO_PKG_NAME=foo [..] rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); } b.run(); } // The tester may already have LD_LIBRARY_PATH=::/foo/bar which leads to a false positive error fn setenv_for_removing_empty_component(mut execs: Execs) -> Execs { let v = dylib_path_envvar(); if let Ok(search_path) = env::var(v) { let new_search_path = env::join_paths(env::split_paths(&search_path).filter(|e| !e.as_os_str().is_empty())) .expect("join_paths"); execs.env(v, new_search_path); // build_command() will override LD_LIBRARY_PATH accordingly } execs } // Regression test for #4277 #[cargo_test] fn crate_library_path_env_var() { let p = project() .file( "src/main.rs", &format!( r#" fn main() {{ let search_path = env!("{}"); let paths = std::env::split_paths(&search_path).collect::>(); assert!(!paths.contains(&"".into())); }} "#, dylib_path_envvar() ), ) .build(); setenv_for_removing_empty_component(p.cargo("run")).run(); } // See https://github.com/rust-lang/cargo/issues/14194 #[cargo_test] fn issue_14194_deduplicate_library_path_env_var() { let p = project() .file( "src/main.rs", &format!( r#" use std::process::Command; fn main() {{ let level: i32 = std::env::args().nth(1).unwrap().parse().unwrap(); let txt = "var.txt"; let lib_path = std::env::var("{}").unwrap(); // Make sure we really have something in dylib search path. let count = std::env::split_paths(&lib_path).count(); assert!(count > 0); if level >= 3 {{ std::fs::write(txt, &lib_path).unwrap(); }} else {{ let prev_lib_path = std::fs::read_to_string(txt).unwrap(); // Ensure no duplicate insertion to dylib search paths // when calling `cargo run` recursively. assert_eq!(lib_path, prev_lib_path); }} if level == 0 {{ return; }} let _ = Command::new(std::env!("CARGO")) .arg("run") .arg("--") .arg((level - 1).to_string()) .status() .unwrap(); }} "#, dylib_path_envvar(), ), ) .build(); setenv_for_removing_empty_component(p.cargo("run -- 3")) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE] 3` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE] 2` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE] 1` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE] 0` "#]]) .run(); } // Regression test for #4277 #[cargo_test] fn build_with_fake_libc_not_loading() { let p = project() .file("src/main.rs", "fn main() {}") .file("src/lib.rs", r#" "#) .file("libc.so.6", r#""#) .build(); setenv_for_removing_empty_component(p.cargo("build")).run(); } // this is testing that src/.rs still works (for now) #[cargo_test] fn many_crate_types_old_style_lib_location() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [lib] name = "foo" crate-type = ["rlib", "dylib"] "#, ) .file("src/foo.rs", "pub fn foo() {}") .build(); p.cargo("build") .with_stderr_data(str![[r#" [WARNING] path `src/foo.rs` was erroneously implicitly accepted for library `foo`, please rename the file to `src/lib.rs` or set lib.path in Cargo.toml [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert!(p.root().join("target/debug/libfoo.rlib").is_file()); let fname = format!("{}foo{}", env::consts::DLL_PREFIX, env::consts::DLL_SUFFIX); assert!(p.root().join("target/debug").join(&fname).is_file()); } #[cargo_test] fn many_crate_types_correct() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [lib] name = "foo" crate-type = ["rlib", "dylib"] "#, ) .file("src/lib.rs", "pub fn foo() {}") .build(); p.cargo("build").run(); assert!(p.root().join("target/debug/libfoo.rlib").is_file()); let fname = format!("{}foo{}", env::consts::DLL_PREFIX, env::consts::DLL_SUFFIX); assert!(p.root().join("target/debug").join(&fname).is_file()); } #[cargo_test] fn set_both_dylib_and_cdylib_crate_types() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [lib] name = "foo" crate-type = ["cdylib", "dylib"] "#, ) .file("src/lib.rs", "pub fn foo() {}") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: library `foo` cannot set the crate type of both `dylib` and `cdylib` "#]]) .run(); } #[cargo_test] fn self_dependency() { let p = project() .file( "Cargo.toml", r#" [package] name = "test" version = "0.0.0" edition = "2015" authors = [] [dependencies.test] path = "." [lib] name = "test" path = "src/test.rs" "#, ) .file("src/test.rs", "fn main() {}") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] cyclic package dependency: package `test v0.0.0 ([ROOT]/foo)` depends on itself. Cycle: package `test v0.0.0 ([ROOT]/foo)` ... which satisfies path dependency `test` of package `test v0.0.0 ([ROOT]/foo)` "#]]) .run(); } #[cargo_test] /// Make sure broken and loop symlinks don't break the build /// /// This test requires you to be able to make symlinks. /// For windows, this may require you to enable developer mode. fn ignore_broken_symlinks() { if !symlink_supported() { return; } let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", &main_file(r#""i am foo""#, &[])) .symlink("Notafile", "bar") // To hit the symlink directory, we need a build script // to trigger a full scan of package files. .file("build.rs", &main_file(r#""build script""#, &[])) .symlink_dir("a/b", "a/b/c/d/foo") .build(); p.cargo("build") .with_stderr_data(str![[r#" [WARNING] File system loop found: [ROOT]/foo/a/b/c/d/foo points to an ancestor [ROOT]/foo/a/b [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert!(p.bin("foo").is_file()); p.process(&p.bin("foo")) .with_stdout_data(str![[r#" i am foo "#]]) .run(); } #[cargo_test] fn missing_lib_and_bin() { let p = project().build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: no targets specified in the manifest either src/lib.rs, src/main.rs, a [lib] section, or [[bin]] section must be present "#]]) .run(); } #[cargo_test] fn lto_build() { let p = project() .file( "Cargo.toml", r#" [package] name = "test" version = "0.0.0" edition = "2015" authors = [] [profile.release] lto = true "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build -v --release") .with_stderr_data(str![[r#" [COMPILING] test v0.0.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name test --edition=2015 src/main.rs [..]--crate-type bin --emit=[..]link -C opt-level=3 -C lto [..]` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn explicit_examples() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" edition = "2015" authors = [] [lib] name = "foo" path = "src/lib.rs" [[example]] name = "hello" path = "examples/ex-hello.rs" [[example]] name = "goodbye" path = "examples/ex-goodbye.rs" "#, ) .file( "src/lib.rs", r#" pub fn get_hello() -> &'static str { "Hello" } pub fn get_goodbye() -> &'static str { "Goodbye" } pub fn get_world() -> &'static str { "World" } "#, ) .file( "examples/ex-hello.rs", r#" extern crate foo; fn main() { println!("{}, {}!", foo::get_hello(), foo::get_world()); } "#, ) .file( "examples/ex-goodbye.rs", r#" extern crate foo; fn main() { println!("{}, {}!", foo::get_goodbye(), foo::get_world()); } "#, ) .build(); p.cargo("build --examples").run(); p.process(&p.bin("examples/hello")) .with_stdout_data(str![[r#" Hello, World! "#]]) .run(); p.process(&p.bin("examples/goodbye")) .with_stdout_data(str![[r#" Goodbye, World! "#]]) .run(); } #[cargo_test] fn implicit_examples() { let p = project() .file( "src/lib.rs", r#" pub fn get_hello() -> &'static str { "Hello" } pub fn get_goodbye() -> &'static str { "Goodbye" } pub fn get_world() -> &'static str { "World" } "#, ) .file( "examples/hello.rs", r#" extern crate foo; fn main() { println!("{}, {}!", foo::get_hello(), foo::get_world()); } "#, ) .file( "examples/goodbye.rs", r#" extern crate foo; fn main() { println!("{}, {}!", foo::get_goodbye(), foo::get_world()); } "#, ) .build(); p.cargo("build --examples").run(); p.process(&p.bin("examples/hello")) .with_stdout_data(str![[r#" Hello, World! "#]]) .run(); p.process(&p.bin("examples/goodbye")) .with_stdout_data(str![[r#" Goodbye, World! "#]]) .run(); } #[cargo_test] fn standard_build_no_ndebug() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file( "src/foo.rs", r#" fn main() { if cfg!(debug_assertions) { println!("slow") } else { println!("fast") } } "#, ) .build(); p.cargo("build").run(); p.process(&p.bin("foo")) .with_stdout_data(str![[r#" slow "#]]) .run(); } #[cargo_test] fn release_build_ndebug() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file( "src/foo.rs", r#" fn main() { if cfg!(debug_assertions) { println!("slow") } else { println!("fast") } } "#, ) .build(); p.cargo("build --release").run(); p.process(&p.release_bin("foo")) .with_stdout_data(str![[r#" fast "#]]) .run(); } #[cargo_test] fn inferred_main_bin() { let p = project().file("src/main.rs", "fn main() {}").build(); p.cargo("build").run(); p.process(&p.bin("foo")).run(); } #[cargo_test] fn deletion_causes_failure() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "bar" "#, ) .file("src/main.rs", "extern crate bar; fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "") .build(); p.cargo("build").run(); p.change_file("Cargo.toml", &basic_manifest("foo", "0.0.1")); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) error[E0463]: can't find crate for `bar` ... [ERROR] could not compile `foo` (bin "foo") due to 1 previous error "#]]) .run(); } #[cargo_test] fn bad_cargo_toml_in_target_dir() { let p = project() .file("src/main.rs", "fn main() {}") .file("target/Cargo.toml", "bad-toml") .build(); p.cargo("build").run(); p.process(&p.bin("foo")).run(); } #[cargo_test] fn lib_with_standard_name() { let p = project() .file("Cargo.toml", &basic_manifest("syntax", "0.0.1")) .file("src/lib.rs", "pub fn foo() {}") .file( "src/main.rs", "extern crate syntax; fn main() { syntax::foo() }", ) .build(); p.cargo("build") .with_stderr_data(str![[r#" [COMPILING] syntax v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn simple_staticlib() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.1" edition = "2015" [lib] name = "foo" crate-type = ["staticlib"] "#, ) .file("src/lib.rs", "pub fn foo() {}") .build(); // env var is a test for #1381 p.cargo("build").env("CARGO_LOG", "nekoneko=trace").run(); } #[cargo_test] fn staticlib_rlib_and_bin() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.1" edition = "2015" [lib] name = "foo" crate-type = ["staticlib", "rlib"] "#, ) .file("src/lib.rs", "pub fn foo() {}") .file("src/main.rs", "extern crate foo; fn main() { foo::foo(); }") .build(); p.cargo("build -v").run(); } #[cargo_test] fn opt_out_of_bin() { let p = project() .file( "Cargo.toml", r#" bin = [] [package] name = "foo" authors = [] version = "0.0.1" edition = "2015" "#, ) .file("src/lib.rs", "") .file("src/main.rs", "bad syntax") .build(); p.cargo("build").run(); } #[cargo_test] fn single_lib() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.1" edition = "2015" [lib] name = "foo" path = "src/bar.rs" "#, ) .file("src/bar.rs", "") .build(); p.cargo("build").run(); } #[cargo_test] fn freshness_ignores_excluded() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] build = "build.rs" exclude = ["src/b*.rs"] "#, ) .file("build.rs", "fn main() {}") .file("src/lib.rs", "pub fn bar() -> i32 { 1 }") .build(); foo.root().move_into_the_past(); foo.cargo("build") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Smoke test to make sure it doesn't compile again println!("first pass"); foo.cargo("build") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Modify an ignored file and make sure we don't rebuild println!("second pass"); foo.change_file("src/bar.rs", ""); foo.cargo("build") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn rebuild_preserves_out_dir() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] build = 'build.rs' "#, ) .file( "build.rs", r#" use std::env; use std::fs::File; use std::path::Path; fn main() { let path = Path::new(&env::var("OUT_DIR").unwrap()).join("foo"); if env::var_os("FIRST").is_some() { File::create(&path).unwrap(); } else { File::create(&path).unwrap(); } } "#, ) .file("src/lib.rs", "pub fn bar() -> i32 { 1 }") .build(); foo.root().move_into_the_past(); foo.cargo("build") .env("FIRST", "1") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); foo.change_file("src/bar.rs", ""); foo.cargo("build") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn dep_no_libs() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [dependencies.bar] path = "bar" "#, ) .file("src/lib.rs", "pub fn bar() -> i32 { 1 }") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.0")) .file("bar/src/main.rs", "") .build(); foo.cargo("build").run(); } #[cargo_test] fn recompile_space_in_name() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [lib] name = "foo" path = "src/my lib.rs" "#, ) .file("src/my lib.rs", "") .build(); foo.cargo("build").run(); foo.root().move_into_the_past(); foo.cargo("build") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cfg(unix)] #[cargo_test] fn credentials_is_unreadable() { use cargo_test_support::paths::home; use std::os::unix::prelude::*; let p = project() .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("src/lib.rs", "") .build(); let credentials = home().join(".cargo/credentials.toml"); t!(fs::create_dir_all(credentials.parent().unwrap())); t!(fs::write( &credentials, r#" [registry] token = "api-token" "# )); let stat = fs::metadata(credentials.as_path()).unwrap(); let mut perms = stat.permissions(); perms.set_mode(0o000); fs::set_permissions(credentials, perms).unwrap(); p.cargo("build").run(); } #[cfg(unix)] #[cargo_test] fn ignore_bad_directories() { use std::os::unix::prelude::*; let foo = project() .file("Cargo.toml", &basic_manifest("foo", "0.0.0")) .file("src/lib.rs", "") .build(); let dir = foo.root().join("tmp"); fs::create_dir(&dir).unwrap(); let stat = fs::metadata(&dir).unwrap(); let mut perms = stat.permissions(); perms.set_mode(0o644); fs::set_permissions(&dir, perms.clone()).unwrap(); foo.cargo("build").run(); perms.set_mode(0o755); fs::set_permissions(&dir, perms).unwrap(); } #[cargo_test] fn bad_cargo_config() { let foo = project() .file("Cargo.toml", &basic_manifest("foo", "0.0.0")) .file("src/lib.rs", "") .file(".cargo/config.toml", "this is not valid toml") .build(); foo.cargo("build -v") .with_status(101) .with_stderr_data(str![[r#" [ERROR] could not load Cargo configuration Caused by: could not parse TOML configuration in `[ROOT]/foo/.cargo/config.toml` Caused by: TOML parse error at line 1, column 6 | 1 | this is not valid toml | ^ key with no value, expected `=` "#]]) .run(); } #[cargo_test] fn cargo_platform_specific_dependency() { let host = rustc_host(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] build = "build.rs" [target.{host}.dependencies] dep = {{ path = "dep" }} [target.{host}.build-dependencies] build = {{ path = "build" }} [target.{host}.dev-dependencies] dev = {{ path = "dev" }} "#, host = host ), ) .file("src/main.rs", "extern crate dep; fn main() { dep::dep() }") .file( "tests/foo.rs", "extern crate dev; #[test] fn foo() { dev::dev() }", ) .file( "build.rs", "extern crate build; fn main() { build::build(); }", ) .file("dep/Cargo.toml", &basic_manifest("dep", "0.5.0")) .file("dep/src/lib.rs", "pub fn dep() {}") .file("build/Cargo.toml", &basic_manifest("build", "0.5.0")) .file("build/src/lib.rs", "pub fn build() {}") .file("dev/Cargo.toml", &basic_manifest("dev", "0.5.0")) .file("dev/src/lib.rs", "pub fn dev() {}") .build(); p.cargo("build").run(); assert!(p.bin("foo").is_file()); p.cargo("test").run(); } #[cargo_test] fn bad_platform_specific_dependency() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [target.wrong-target.dependencies.bar] path = "bar" "#, ) .file("src/main.rs", &main_file(r#""{}", bar::gimme()"#, &["bar"])) .file("bar/Cargo.toml", &basic_manifest("bar", "0.5.0")) .file( "bar/src/lib.rs", r#"pub fn gimme() -> String { format!("") }"#, ) .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] foo v0.5.0 ([ROOT]/foo) error[E0463]: can't find crate for `bar` ... [ERROR] could not compile `foo` (bin "foo") due to 1 previous error "#]]) .run(); } #[cargo_test] fn cargo_platform_specific_dependency_wrong_platform() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [target.non-existing-triplet.dependencies.bar] path = "bar" "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.5.0")) .file( "bar/src/lib.rs", "invalid rust file, should not be compiled", ) .build(); p.cargo("build").run(); assert!(p.bin("foo").is_file()); p.process(&p.bin("foo")).run(); let lockfile = p.read_lockfile(); assert!(lockfile.contains("bar")); } #[cargo_test] fn example_as_lib() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [[example]] name = "ex" crate-type = ["lib"] "#, ) .file("src/lib.rs", "") .file("examples/ex.rs", "") .build(); p.cargo("build --example=ex").run(); assert!(p.example_lib("ex", "lib").is_file()); } #[cargo_test] fn example_as_rlib() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [[example]] name = "ex" crate-type = ["rlib"] "#, ) .file("src/lib.rs", "") .file("examples/ex.rs", "") .build(); p.cargo("build --example=ex").run(); assert!(p.example_lib("ex", "rlib").is_file()); } #[cargo_test] fn example_as_dylib() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [[example]] name = "ex" crate-type = ["dylib"] "#, ) .file("src/lib.rs", "") .file("examples/ex.rs", "") .build(); p.cargo("build --example=ex").run(); assert!(p.example_lib("ex", "dylib").is_file()); } #[cargo_test] fn example_as_proc_macro() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [[example]] name = "ex" crate-type = ["proc-macro"] "#, ) .file("src/lib.rs", "") .file( "examples/ex.rs", r#" extern crate proc_macro; use proc_macro::TokenStream; #[proc_macro] pub fn eat(_item: TokenStream) -> TokenStream { "".parse().unwrap() } "#, ) .build(); p.cargo("build --example=ex").run(); assert!(p.example_lib("ex", "proc-macro").is_file()); } #[cargo_test] fn example_bin_same_name() { let p = project() .file("src/main.rs", "fn main() {}") .file("examples/foo.rs", "fn main() {}") .build(); p.cargo("build --examples").run(); assert!(!p.bin("foo").is_file()); // We expect a file of the form bin/foo-{metadata_hash} assert!(p.bin("examples/foo").is_file()); p.cargo("build --examples").run(); assert!(!p.bin("foo").is_file()); // We expect a file of the form bin/foo-{metadata_hash} assert!(p.bin("examples/foo").is_file()); } #[cargo_test] fn compile_then_delete() { let p = project().file("src/main.rs", "fn main() {}").build(); p.cargo("run -v").run(); assert!(p.bin("foo").is_file()); if cfg!(windows) { // On windows unlinking immediately after running often fails, so sleep sleep_ms(100); } fs::remove_file(&p.bin("foo")).unwrap(); p.cargo("run -v").run(); } #[cargo_test] fn transitive_dependencies_not_available() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.aaaaa] path = "a" "#, ) .file( "src/main.rs", "extern crate bbbbb; extern crate aaaaa; fn main() {}", ) .file( "a/Cargo.toml", r#" [package] name = "aaaaa" version = "0.0.1" edition = "2015" authors = [] [dependencies.bbbbb] path = "../b" "#, ) .file("a/src/lib.rs", "extern crate bbbbb;") .file("b/Cargo.toml", &basic_manifest("bbbbb", "0.0.1")) .file("b/src/lib.rs", "") .build(); p.cargo("build -v") .with_status(101) .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] bbbbb v0.0.1 ([ROOT]/foo/b) [RUNNING] `rustc [..] [COMPILING] aaaaa v0.0.1 ([ROOT]/foo/a) [RUNNING] `rustc [..]` [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..]` error[E0463]: can't find crate for `bbbbb` ... [ERROR] could not compile `foo` (bin "foo") due to 1 previous error Caused by: process didn't exit successfully: `rustc [..]` ([EXIT_STATUS]: 1) "#]]) .run(); } #[cargo_test] fn cyclic_deps_rejected() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.a] path = "a" "#, ) .file("src/lib.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] [dependencies.foo] path = ".." "#, ) .file("a/src/lib.rs", "") .build(); p.cargo("build -v") .with_status(101) .with_stderr_data(str![[r#" [ERROR] cyclic package dependency: package `a v0.0.1 ([ROOT]/foo/a)` depends on itself. Cycle: package `a v0.0.1 ([ROOT]/foo/a)` ... which satisfies path dependency `a` of package `foo v0.0.1 ([ROOT]/foo)` ... which satisfies path dependency `foo` of package `a v0.0.1 ([ROOT]/foo/a)` "#]]) .run(); } #[cargo_test] fn predictable_filenames() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lib] name = "foo" crate-type = ["dylib", "rlib"] "#, ) .file("src/lib.rs", "") .build(); p.cargo("build -v").run(); assert!(p.root().join("target/debug/libfoo.rlib").is_file()); let dylib_name = format!("{}foo{}", env::consts::DLL_PREFIX, env::consts::DLL_SUFFIX); assert!(p.root().join("target/debug").join(dylib_name).is_file()); } #[cargo_test] fn dashes_to_underscores() { let p = project() .file("Cargo.toml", &basic_manifest("foo-bar", "0.0.1")) .file("src/lib.rs", "") .file("src/main.rs", "extern crate foo_bar; fn main() {}") .build(); p.cargo("build -v").run(); assert!(p.bin("foo-bar").is_file()); } #[cargo_test] fn dashes_in_crate_name_bad() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lib] name = "foo-bar" "#, ) .file("src/lib.rs", "") .file("src/main.rs", "extern crate foo_bar; fn main() {}") .build(); p.cargo("build -v") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: library target names cannot contain hyphens: foo-bar "#]]) .run(); } #[cargo_test] fn rustc_env_var() { let p = project().file("src/lib.rs", "").build(); p.cargo("build -v") .env("RUSTC", "rustc-that-does-not-exist") .with_status(101) .with_stderr_data(str![[r#" [ERROR] could not execute process `rustc-that-does-not-exist -vV` (never executed) Caused by: [..] "#]]) .run(); assert!(!p.bin("a").is_file()); } #[cargo_test] fn filtering() { let p = project() .file("src/lib.rs", "") .file("src/bin/a.rs", "fn main() {}") .file("src/bin/b.rs", "fn main() {}") .file("examples/a.rs", "fn main() {}") .file("examples/b.rs", "fn main() {}") .build(); p.cargo("build --lib").run(); assert!(!p.bin("a").is_file()); p.cargo("build --bin=a --example=a").run(); assert!(p.bin("a").is_file()); assert!(!p.bin("b").is_file()); assert!(p.bin("examples/a").is_file()); assert!(!p.bin("examples/b").is_file()); } #[cargo_test] fn filtering_implicit_bins() { let p = project() .file("src/lib.rs", "") .file("src/bin/a.rs", "fn main() {}") .file("src/bin/b.rs", "fn main() {}") .file("examples/a.rs", "fn main() {}") .file("examples/b.rs", "fn main() {}") .build(); p.cargo("build --bins").run(); assert!(p.bin("a").is_file()); assert!(p.bin("b").is_file()); assert!(!p.bin("examples/a").is_file()); assert!(!p.bin("examples/b").is_file()); } #[cargo_test] fn filtering_implicit_examples() { let p = project() .file("src/lib.rs", "") .file("src/bin/a.rs", "fn main() {}") .file("src/bin/b.rs", "fn main() {}") .file("examples/a.rs", "fn main() {}") .file("examples/b.rs", "fn main() {}") .build(); p.cargo("build --examples").run(); assert!(!p.bin("a").is_file()); assert!(!p.bin("b").is_file()); assert!(p.bin("examples/a").is_file()); assert!(p.bin("examples/b").is_file()); } #[cargo_test] fn ignore_dotfile() { let p = project() .file("src/bin/.a.rs", "") .file("src/bin/a.rs", "fn main() {}") .build(); p.cargo("build").run(); } #[cargo_test] fn ignore_dotdirs() { let p = project() .file("src/bin/a.rs", "fn main() {}") .file(".git/Cargo.toml", "") .file(".pc/dummy-fix.patch/Cargo.toml", "") .build(); p.cargo("build").run(); } #[cargo_test] fn dotdir_root() { let p = ProjectBuilder::new(root().join(".foo")) .file("src/bin/a.rs", "fn main() {}") .build(); p.cargo("build").run(); } #[cargo_test] fn custom_target_dir_env() { let p = project().file("src/main.rs", "fn main() {}").build(); let exe_name = format!("foo{}", env::consts::EXE_SUFFIX); p.cargo("build").env("CARGO_TARGET_DIR", "foo/target").run(); assert!(p.root().join("foo/target/debug").join(&exe_name).is_file()); assert!(!p.root().join("target/debug").join(&exe_name).is_file()); p.cargo("build").run(); assert!(p.root().join("foo/target/debug").join(&exe_name).is_file()); assert!(p.root().join("target/debug").join(&exe_name).is_file()); p.cargo("build") .env("CARGO_BUILD_TARGET_DIR", "foo2/target") .run(); assert!(p.root().join("foo2/target/debug").join(&exe_name).is_file()); p.change_file( ".cargo/config.toml", r#" [build] target-dir = "foo/target" "#, ); p.cargo("build").env("CARGO_TARGET_DIR", "bar/target").run(); assert!(p.root().join("bar/target/debug").join(&exe_name).is_file()); assert!(p.root().join("foo/target/debug").join(&exe_name).is_file()); assert!(p.root().join("target/debug").join(&exe_name).is_file()); } #[cargo_test] fn custom_target_dir_line_parameter() { let p = project().file("src/main.rs", "fn main() {}").build(); let exe_name = format!("foo{}", env::consts::EXE_SUFFIX); p.cargo("build --target-dir foo/target").run(); assert!(p.root().join("foo/target/debug").join(&exe_name).is_file()); assert!(!p.root().join("target/debug").join(&exe_name).is_file()); p.cargo("build").run(); assert!(p.root().join("foo/target/debug").join(&exe_name).is_file()); assert!(p.root().join("target/debug").join(&exe_name).is_file()); p.change_file( ".cargo/config.toml", r#" [build] target-dir = "foo/target" "#, ); p.cargo("build --target-dir bar/target").run(); assert!(p.root().join("bar/target/debug").join(&exe_name).is_file()); assert!(p.root().join("foo/target/debug").join(&exe_name).is_file()); assert!(p.root().join("target/debug").join(&exe_name).is_file()); p.cargo("build --target-dir foobar/target") .env("CARGO_TARGET_DIR", "bar/target") .run(); assert!( p.root() .join("foobar/target/debug") .join(&exe_name) .is_file() ); assert!(p.root().join("bar/target/debug").join(&exe_name).is_file()); assert!(p.root().join("foo/target/debug").join(&exe_name).is_file()); assert!(p.root().join("target/debug").join(&exe_name).is_file()); } #[cargo_test] fn build_multiple_packages() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.d1] path = "d1" [dependencies.d2] path = "d2" [[bin]] name = "foo" "#, ) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .file("d1/Cargo.toml", &basic_bin_manifest("d1")) .file("d1/src/lib.rs", "") .file("d1/src/main.rs", "fn main() { println!(\"d1\"); }") .file( "d2/Cargo.toml", r#" [package] name = "d2" version = "0.0.1" edition = "2015" authors = [] [[bin]] name = "d2" doctest = false "#, ) .file("d2/src/main.rs", "fn main() { println!(\"d2\"); }") .build(); p.cargo("build -p d1 -p d2 -p foo").run(); assert!(p.bin("foo").is_file()); p.process(&p.bin("foo")) .with_stdout_data(str![[r#" i am foo "#]]) .run(); let d1_path = &p .build_dir() .join("debug") .join(format!("d1{}", env::consts::EXE_SUFFIX)); let d2_path = &p .build_dir() .join("debug") .join(format!("d2{}", env::consts::EXE_SUFFIX)); assert!(d1_path.is_file()); p.process(d1_path) .with_stdout_data(str![[r#" d1 "#]]) .run(); assert!(d2_path.is_file()); p.process(d2_path) .with_stdout_data(str![[r#" d2 "#]]) .run(); } #[cargo_test] fn invalid_spec() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.d1] path = "d1" [[bin]] name = "foo" "#, ) .file("src/bin/foo.rs", &main_file(r#""i am foo""#, &[])) .file("d1/Cargo.toml", &basic_bin_manifest("d1")) .file("d1/src/lib.rs", "") .file("d1/src/main.rs", "fn main() { println!(\"d1\"); }") .build(); p.cargo("build -p notAValidDep") .with_status(101) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [ERROR] package ID specification `notAValidDep` did not match any packages "#]]) .run(); p.cargo("build -p d1 -p notAValidDep") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package ID specification `notAValidDep` did not match any packages "#]]) .run(); } #[cargo_test] fn manifest_with_bom_is_ok() { let p = project() .file( "Cargo.toml", "\u{FEFF} [package] name = \"foo\" version = \"0.0.1\" edition = \"2015\" authors = [] ", ) .file("src/lib.rs", "") .build(); p.cargo("build -v").run(); } #[cargo_test] fn panic_abort_compiles_with_panic_abort() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [profile.dev] panic = 'abort' "#, ) .file("src/lib.rs", "") .build(); p.cargo("build -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..] -C panic=abort [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn compiler_json_error_format() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.bar] path = "bar" "#, ) .file( "build.rs", "fn main() { println!(\"cargo::rustc-cfg=xyz\") }", ) .file("src/main.rs", "fn main() { let unused = 92; }") .file("bar/Cargo.toml", &basic_manifest("bar", "0.5.0")) .file("bar/src/lib.rs", r#"fn dead() {}"#) .build(); let output = |fresh| { r#" [ { "executable": null, "features": [], "fresh": $FRESH, "manifest_path": "[ROOT]/foo/Cargo.toml", "package_id": "path+[ROOTURL]/foo#0.5.0", "reason": "compiler-artifact", "target": { "kind": ["custom-build"], "...": "{...}" }, "...": "{...}" }, { "manifest_path": "[ROOT]/foo/bar/Cargo.toml", "package_id": "path+[ROOTURL]/foo/bar#0.5.0", "reason": "compiler-message", "target": { "kind": ["lib"], "...": "{...}" }, "...": "{...}" }, { "executable": null, "features": [], "fresh": $FRESH, "manifest_path": "[ROOT]/foo/bar/Cargo.toml", "package_id": "path+[ROOTURL]/foo/bar#0.5.0", "reason": "compiler-artifact", "target": { "kind": ["lib"], "...": "{...}" }, "...": "{...}" }, { "cfgs": [ "xyz" ], "env": [], "linked_libs": [], "linked_paths": [], "out_dir": "[ROOT]/foo/target/debug/build/foo-[HASH]/out", "package_id": "path+[ROOTURL]/foo#0.5.0", "reason": "build-script-executed" }, { "manifest_path": "[ROOT]/foo/Cargo.toml", "package_id": "path+[ROOTURL]/foo#0.5.0", "reason": "compiler-message", "target": { "kind": ["bin"], "...": "{...}" }, "...": "{...}" }, { "features": [], "fresh": $FRESH, "manifest_path": "[ROOT]/foo/Cargo.toml", "package_id": "path+[ROOTURL]/foo#0.5.0", "reason": "compiler-artifact", "target": { "kind": ["bin"], "...": "{...}" }, "...": "{...}" }, { "reason": "build-finished", "success": true }, "{...}" ] "# .replace("$FRESH", fresh) .is_json() .against_jsonlines() .unordered() }; // Use `jobs=1` to ensure that the order of messages is consistent. p.cargo("build -v --message-format=json --jobs=1") .with_stdout_data(output("false")) .run(); // With fresh build, we should repeat the artifacts, // and replay the cached compiler warnings. p.cargo("build -v --message-format=json --jobs=1") .with_stdout_data(output("true")) .run(); } #[cargo_test] fn wrong_message_format_option() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build --message-format XML") .with_status(1) .with_stderr_data(str![[r#" [ERROR] invalid value 'XML' for '--message-format ' [possible values: human, short, json, json-diagnostic-short, json-diagnostic-rendered-ansi, json-render-diagnostics] For more information, try '--help'. "#]]) .run(); } #[cargo_test] fn message_format_json_forward_stderr() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", "fn main() { let unused = 0; }") .build(); p.cargo("rustc --release --bin foo --message-format JSON") .with_stdout_data( str![[r#" [ { "manifest_path": "[ROOT]/foo/Cargo.toml", "message": "{...}", "package_id": "path+[ROOTURL]/foo#0.5.0", "reason": "compiler-message", "target": { "kind": ["bin"], "...": "{...}" }, "...": "{...}" }, { "features": [], "fresh": false, "manifest_path": "[ROOT]/foo/Cargo.toml", "package_id": "path+[ROOTURL]/foo#0.5.0", "reason": "compiler-artifact", "target": { "kind": ["bin"], "...": "{...}" }, "...": "{...}" }, { "reason": "build-finished", "success": true }, "{...}" ] "#]] .is_json() .against_jsonlines() .unordered(), ) .run(); } #[cargo_test] fn no_warn_about_package_metadata() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [package.metadata] foo = "bar" a = true b = 3 [package.metadata.another] bar = 3 "#, ) .file("src/lib.rs", "") .build(); p.cargo("build") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn no_warn_about_workspace_metadata() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo"] [workspace.metadata] something = "something_else" x = 1 y = 2 [workspace.metadata.another] bar = 12 "#, ) .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" "#, ) .file("foo/src/lib.rs", "") .build(); p.cargo("build") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn cargo_build_empty_target() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build --target") .arg("") .with_status(101) .with_stderr_data(str![[r#" [ERROR] target was empty "#]]) .run(); } #[cargo_test] fn cargo_build_with_unsupported_short_target_flag() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build -t") .arg("") .with_stderr_data(str![[r#" [ERROR] unexpected argument '-t' found tip: a similar argument exists: '--target' Usage: cargo[EXE] build [OPTIONS] For more information, try '--help'. "#]]) .with_status(1) .run(); } #[cargo_test] fn build_all_workspace() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { path = "bar" } [workspace] "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .build(); p.cargo("build --workspace") .with_stderr_data(str![[r#" [COMPILING] bar v0.1.0 ([ROOT]/foo/bar) [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn build_all_exclude() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [workspace] members = ["bar", "baz"] "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "pub fn baz() { break_the_build(); }") .build(); p.cargo("build --workspace --exclude baz") .with_stderr_data( str![[r#" [COMPILING] foo v0.1.0 ([ROOT]/foo) [COMPILING] bar v0.1.0 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn cargo_build_with_unsupported_short_exclude_flag() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [workspace] members = ["bar", "baz"] "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "pub fn baz() { break_the_build(); }") .build(); p.cargo("build --workspace -x baz") .with_stderr_data(str![[r#" [ERROR] unexpected argument '-x' found tip: a similar argument exists: '--exclude' Usage: cargo[EXE] build [OPTIONS] For more information, try '--help'. "#]]) .with_status(1) .run(); } #[cargo_test] fn build_all_exclude_not_found() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [workspace] members = ["bar"] "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .build(); p.cargo("build --workspace --exclude baz") .with_stderr_data( str![[r#" [WARNING] excluded package(s) `baz` not found in workspace `[ROOT]/foo` [COMPILING] foo v0.1.0 ([ROOT]/foo) [COMPILING] bar v0.1.0 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn build_all_exclude_glob() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [workspace] members = ["bar", "baz"] "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "pub fn baz() { break_the_build(); }") .build(); p.cargo("build --workspace --exclude '*z'") .with_stderr_data( str![[r#" [COMPILING] bar v0.1.0 ([ROOT]/foo/bar) [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn build_all_exclude_glob_not_found() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [workspace] members = ["bar"] "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .build(); p.cargo("build --workspace --exclude '*z'") .with_stderr_data( str![[r#" [WARNING] excluded package pattern(s) `*z` not found in workspace `[ROOT]/foo` [COMPILING] bar v0.1.0 ([ROOT]/foo/bar) [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn build_all_exclude_broken_glob() { let p = project().file("src/main.rs", "fn main() {}").build(); p.cargo("build --workspace --exclude '[*z'") .with_status(101) .with_stderr_data(str![[r#" [ERROR] cannot build glob pattern from `[*z` Caused by: ... "#]]) .run(); } #[cargo_test] fn build_all_workspace_implicit_examples() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { path = "bar" } [workspace] "#, ) .file("src/lib.rs", "") .file("src/bin/a.rs", "fn main() {}") .file("src/bin/b.rs", "fn main() {}") .file("examples/c.rs", "fn main() {}") .file("examples/d.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "") .file("bar/src/bin/e.rs", "fn main() {}") .file("bar/src/bin/f.rs", "fn main() {}") .file("bar/examples/g.rs", "fn main() {}") .file("bar/examples/h.rs", "fn main() {}") .build(); p.cargo("build --workspace --examples") .with_stderr_data(str![[r#" [COMPILING] bar v0.1.0 ([ROOT]/foo/bar) [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert!(!p.bin("a").is_file()); assert!(!p.bin("b").is_file()); assert!(p.bin("examples/c").is_file()); assert!(p.bin("examples/d").is_file()); assert!(!p.bin("e").is_file()); assert!(!p.bin("f").is_file()); assert!(p.bin("examples/g").is_file()); assert!(p.bin("examples/h").is_file()); } #[cargo_test] fn build_all_virtual_manifest() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "pub fn baz() {}") .build(); // The order in which bar and baz are built is not guaranteed p.cargo("build --workspace") .with_stderr_data( str![[r#" [COMPILING] bar v0.1.0 ([ROOT]/foo/bar) [COMPILING] baz v0.1.0 ([ROOT]/foo/baz) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn build_virtual_manifest_all_implied() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "pub fn baz() {}") .build(); // The order in which `bar` and `baz` are built is not guaranteed. p.cargo("build") .with_stderr_data( str![[r#" [COMPILING] bar v0.1.0 ([ROOT]/foo/bar) [COMPILING] baz v0.1.0 ([ROOT]/foo/baz) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn build_virtual_manifest_one_project() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "pub fn baz() { break_the_build(); }") .build(); p.cargo("build -p bar") .with_stderr_data(str![[r#" [COMPILING] bar v0.1.0 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn build_virtual_manifest_glob() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() { break_the_build(); }") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "pub fn baz() {}") .build(); p.cargo("build -p '*z'") .with_stderr_data(str![[r#" [COMPILING] baz v0.1.0 ([ROOT]/foo/baz) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn build_virtual_manifest_glob_not_found() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .build(); p.cargo("build -p bar -p '*z'") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package pattern(s) `*z` not found in workspace `[ROOT]/foo` "#]]) .run(); } #[cargo_test] fn build_virtual_manifest_broken_glob() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .build(); p.cargo("build -p '[*z'") .with_status(101) .with_stderr_data(str![[r#" [ERROR] cannot build glob pattern from `[*z` Caused by: ... "#]]) .run(); } #[cargo_test] fn build_all_virtual_manifest_implicit_examples() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "") .file("bar/src/bin/a.rs", "fn main() {}") .file("bar/src/bin/b.rs", "fn main() {}") .file("bar/examples/c.rs", "fn main() {}") .file("bar/examples/d.rs", "fn main() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "") .file("baz/src/bin/e.rs", "fn main() {}") .file("baz/src/bin/f.rs", "fn main() {}") .file("baz/examples/g.rs", "fn main() {}") .file("baz/examples/h.rs", "fn main() {}") .build(); // The order in which bar and baz are built is not guaranteed p.cargo("build --workspace --examples") .with_stderr_data( str![[r#" [COMPILING] baz v0.1.0 ([ROOT]/foo/baz) [COMPILING] bar v0.1.0 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); assert!(!p.bin("a").is_file()); assert!(!p.bin("b").is_file()); assert!(p.bin("examples/c").is_file()); assert!(p.bin("examples/d").is_file()); assert!(!p.bin("e").is_file()); assert!(!p.bin("f").is_file()); assert!(p.bin("examples/g").is_file()); assert!(p.bin("examples/h").is_file()); } #[cargo_test] fn build_all_member_dependency_same_name() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a"] "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" [dependencies] a = "0.1.0" "#, ) .file("a/src/lib.rs", "pub fn a() {}") .build(); Package::new("a", "0.1.0").publish(); p.cargo("build --workspace") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] a v0.1.0 (registry `dummy-registry`) [COMPILING] a v0.1.0 [COMPILING] a v0.1.0 ([ROOT]/foo/a) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn run_proper_binary() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.0" edition = "2015" [[bin]] name = "main" [[bin]] name = "other" "#, ) .file("src/lib.rs", "") .file( "src/bin/main.rs", r#"fn main() { panic!("This should never be run."); }"#, ) .file("src/bin/other.rs", "fn main() {}") .build(); p.cargo("run --bin other").run(); } #[cargo_test] fn run_proper_binary_main_rs() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/lib.rs", "") .file("src/bin/main.rs", "fn main() {}") .build(); p.cargo("run --bin foo").run(); } #[cargo_test] fn run_proper_alias_binary_from_src() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.0" edition = "2015" [[bin]] name = "foo" [[bin]] name = "bar" "#, ) .file("src/foo.rs", r#"fn main() { println!("foo"); }"#) .file("src/bar.rs", r#"fn main() { println!("bar"); }"#) .build(); p.cargo("build --workspace").run(); p.process(&p.bin("foo")) .with_stdout_data(str![[r#" foo "#]]) .run(); p.process(&p.bin("bar")) .with_stdout_data(str![[r#" bar "#]]) .run(); } #[cargo_test] fn run_proper_alias_binary_main_rs() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.0" edition = "2015" [[bin]] name = "foo" [[bin]] name = "bar" "#, ) .file("src/main.rs", r#"fn main() { println!("main"); }"#) .build(); p.cargo("build --workspace").run(); p.process(&p.bin("foo")) .with_stdout_data(str![[r#" main "#]]) .run(); p.process(&p.bin("bar")) .with_stdout_data(str![[r#" main "#]]) .run(); } #[cargo_test] fn run_proper_binary_main_rs_as_foo() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file( "src/foo.rs", r#" fn main() { panic!("This should never be run."); }"#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("run --bin foo").run(); } #[cargo_test] fn rustc_wrapper() { let p = project().file("src/lib.rs", "").build(); let wrapper = tools::echo_wrapper(); p.cargo("build -v") .env("RUSTC_WRAPPER", &wrapper) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[..]/rustc-echo-wrapper[EXE] rustc --crate-name foo [..]` WRAPPER CALLED: rustc --crate-name foo [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.build_dir().rm_rf(); p.cargo("build -v") .env("RUSTC_WORKSPACE_WRAPPER", &wrapper) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[..]/rustc-echo-wrapper[EXE] rustc --crate-name foo [..]` WRAPPER CALLED: rustc --crate-name foo [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } /// Checks what happens when both rust-wrapper and rustc-workspace-wrapper are set. #[cargo_test] fn rustc_wrapper_precendence() { let p = project().file("src/lib.rs", "").build(); let rustc_wrapper = tools::echo_wrapper(); let ws_wrapper = rustc_wrapper.with_file_name("rustc-ws-wrapper"); assert_ne!(rustc_wrapper, ws_wrapper); std::fs::hard_link(&rustc_wrapper, &ws_wrapper).unwrap(); p.cargo("build -v") .env("RUSTC_WRAPPER", &rustc_wrapper) .env("RUSTC_WORKSPACE_WRAPPER", &ws_wrapper) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[..]/rustc-echo-wrapper[EXE] [..]/rustc-ws-wrapper rustc --crate-name foo [..]` WRAPPER CALLED: [..]/rustc-ws-wrapper rustc --crate-name foo [..] WRAPPER CALLED: rustc --crate-name foo [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn rustc_wrapper_queries() { // Check that the invocations querying rustc for information are done with the wrapper. let p = project().file("src/lib.rs", "").build(); let wrapper = tools::echo_wrapper(); p.cargo("build") .env("CARGO_LOG", "cargo::util::rustc=debug") .env("RUSTC_WRAPPER", &wrapper) .with_stderr_contains("[..]running [..]rustc-echo-wrapper[EXE] rustc -vV[..]") .with_stderr_contains( "[..]running [..]rustc-echo-wrapper[EXE] rustc - --crate-name ___ --print[..]", ) .run(); p.build_dir().rm_rf(); p.cargo("build") .env("CARGO_LOG", "cargo::util::rustc=debug") .env("RUSTC_WORKSPACE_WRAPPER", &wrapper) .with_stderr_contains("[..]running [..]rustc-echo-wrapper[EXE] rustc -vV[..]") .with_stderr_contains( "[..]running [..]rustc-echo-wrapper[EXE] rustc - --crate-name ___ --print[..]", ) .run(); } #[cargo_test] fn rustc_wrapper_relative() { Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); let wrapper = tools::echo_wrapper(); let exe_name = wrapper.file_name().unwrap().to_str().unwrap(); let relative_path = format!("./{}", exe_name); fs::hard_link(&wrapper, p.root().join(exe_name)).unwrap(); p.cargo("build -v") .env("RUSTC_WRAPPER", &relative_path) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [COMPILING] bar v1.0.0 [RUNNING] `[ROOT]/foo/./rustc-echo-wrapper[EXE] rustc --crate-name bar [..]` WRAPPER CALLED: rustc --crate-name bar [..] [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `[ROOT]/foo/./rustc-echo-wrapper[EXE] rustc --crate-name foo [..]` WRAPPER CALLED: rustc --crate-name foo [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.build_dir().rm_rf(); p.cargo("build -v") .env("RUSTC_WORKSPACE_WRAPPER", &relative_path) .with_stderr_data(str![[r#" [COMPILING] bar v1.0.0 [RUNNING] `rustc --crate-name bar [..]` [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `[ROOT]/foo/./rustc-echo-wrapper[EXE] rustc --crate-name foo [..]` WRAPPER CALLED: rustc --crate-name foo [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.build_dir().rm_rf(); p.change_file( ".cargo/config.toml", &format!( r#" build.rustc-wrapper = "./{}" "#, exe_name ), ); p.cargo("build -v") .with_stderr_data(str![[r#" [COMPILING] bar v1.0.0 [RUNNING] `[ROOT]/foo/./rustc-echo-wrapper[EXE] rustc --crate-name bar [..]` WRAPPER CALLED: rustc --crate-name bar [..] [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `[ROOT]/foo/./rustc-echo-wrapper[EXE] rustc --crate-name foo [..]` WRAPPER CALLED: rustc --crate-name foo [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn rustc_wrapper_from_path() { let p = project().file("src/lib.rs", "").build(); p.cargo("build -v") .env("RUSTC_WRAPPER", "wannabe_sccache") .with_status(101) .with_stderr_data(str![[r#" [ERROR] could not execute process `wannabe_sccache rustc -vV` (never executed) Caused by: [..] "#]]) .run(); p.build_dir().rm_rf(); p.cargo("build -v") .env("RUSTC_WORKSPACE_WRAPPER", "wannabe_sccache") .with_status(101) .with_stderr_data(str![[r#" [ERROR] could not execute process `wannabe_sccache rustc -vV` (never executed) Caused by: [..] "#]]) .run(); } #[cargo_test] fn cdylib_not_lifted() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.1.0" edition = "2015" [lib] crate-type = ["cdylib"] "#, ) .file("src/lib.rs", "") .build(); p.cargo("build").run(); let files = if cfg!(windows) { if cfg!(target_env = "msvc") { vec!["foo.dll.lib", "foo.dll.exp", "foo.dll"] } else { vec!["libfoo.dll.a", "foo.dll"] } } else if cfg!(target_os = "macos") { vec!["libfoo.dylib"] } else { vec!["libfoo.so"] }; for file in files { println!("checking: {}", file); assert!(p.root().join("target/debug/deps").join(&file).is_file()); } } #[cargo_test] fn cdylib_final_outputs() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo-bar" authors = [] version = "0.1.0" edition = "2015" [lib] crate-type = ["cdylib"] "#, ) .file("src/lib.rs", "") .build(); p.cargo("build").run(); let files = if cfg!(windows) { if cfg!(target_env = "msvc") { vec!["foo_bar.dll.lib", "foo_bar.dll"] } else { vec!["foo_bar.dll", "libfoo_bar.dll.a"] } } else if cfg!(target_os = "macos") { vec!["libfoo_bar.dylib"] } else { vec!["libfoo_bar.so"] }; for file in files { println!("checking: {}", file); assert!(p.root().join("target/debug").join(&file).is_file()); } } #[cargo_test] // NOTE: Windows MSVC and wasm32-unknown-emscripten do not use metadata. Skip them. // See #[cfg(not(all(target_os = "windows", target_env = "msvc")))] fn no_dep_info_collision_when_cdylib_and_bin_coexist() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" edition = "2015" [lib] crate-type = ["cdylib"] "#, ) .file("src/main.rs", "fn main() {}") .file("src/lib.rs", "") .build(); p.cargo("build -v") .with_stderr_data( str![[r#" [COMPILING] foo v1.0.0 ([ROOT]/foo) [RUNNING] `rustc [..] --crate-type bin [..] -C metadata=[..]` [RUNNING] `rustc [..] --crate-type cdylib [..] -C metadata=[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); let deps_dir = p.target_debug_dir().join("deps"); assert!(deps_dir.join("foo.d").exists()); let dep_info_count = deps_dir .read_dir() .unwrap() .filter(|e| { let filename = e.as_ref().unwrap().file_name(); let filename = filename.to_str().unwrap(); filename.starts_with("foo") && filename.ends_with(".d") }) .count(); // cdylib -> foo.d // bin -> foo-.d assert_eq!(dep_info_count, 2); } #[cargo_test] fn deterministic_cfg_flags() { // This bug is non-deterministic. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] build = "build.rs" [features] default = ["f_a", "f_b", "f_c", "f_d"] f_a = [] f_b = [] f_c = [] f_d = [] "#, ) .file( "build.rs", r#" fn main() { println!("cargo::rustc-cfg=cfg_a"); println!("cargo::rustc-cfg=cfg_b"); println!("cargo::rustc-cfg=cfg_c"); println!("cargo::rustc-cfg=cfg_d"); println!("cargo::rustc-cfg=cfg_e"); } "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc [..]` [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc --crate-name foo [..] --cfg[..]default[..]--cfg[..]f_a[..]--cfg[..]f_b[..] --cfg[..]f_c[..]--cfg[..]f_d[..] --cfg cfg_a --cfg cfg_b --cfg cfg_c --cfg cfg_d --cfg cfg_e` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn explicit_bins_without_paths() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [[bin]] name = "foo" [[bin]] name = "bar" "#, ) .file("src/lib.rs", "") .file("src/main.rs", "fn main() {}") .file("src/bin/bar.rs", "fn main() {}") .build(); p.cargo("build").run(); } #[cargo_test] fn no_bin_in_src_with_lib() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/lib.rs", "") .file("src/foo.rs", "fn main() {}") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: can't find `foo` bin at `src/bin/foo.rs` or `src/bin/foo/main.rs`. Please specify bin.path if you want to use a non-default path. "#]]) .run(); } #[cargo_test] fn inferred_bins() { let p = project() .file("src/main.rs", "fn main() {}") .file("src/bin/bar.rs", "fn main() {}") .file("src/bin/baz/main.rs", "fn main() {}") .build(); p.cargo("build").run(); assert!(p.bin("foo").is_file()); assert!(p.bin("bar").is_file()); assert!(p.bin("baz").is_file()); } #[cargo_test] fn inferred_bins_duplicate_name() { // this should fail, because we have two binaries with the same name let p = project() .file("src/main.rs", "fn main() {}") .file("src/bin/bar.rs", "fn main() {}") .file("src/bin/bar/main.rs", "fn main() {}") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: found duplicate binary name bar, but all binary targets must have a unique name "#]]) .run(); } #[cargo_test] fn inferred_bin_path() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [[bin]] name = "bar" # Note, no `path` key! "#, ) .file("src/bin/bar/main.rs", "fn main() {}") .build(); p.cargo("build").run(); assert!(p.bin("bar").is_file()); } #[cargo_test] fn inferred_examples() { let p = project() .file("src/lib.rs", "fn main() {}") .file("examples/bar.rs", "fn main() {}") .file("examples/baz/main.rs", "fn main() {}") .build(); p.cargo("build --examples").run(); assert!(p.bin("examples/bar").is_file()); assert!(p.bin("examples/baz").is_file()); } #[cargo_test] fn inferred_tests() { let p = project() .file("src/lib.rs", "fn main() {}") .file("tests/bar.rs", "fn main() {}") .file("tests/baz/main.rs", "fn main() {}") .build(); p.cargo("test --test=bar --test=baz").run(); } #[cargo_test] fn inferred_benchmarks() { let p = project() .file("src/lib.rs", "fn main() {}") .file("benches/bar.rs", "fn main() {}") .file("benches/baz/main.rs", "fn main() {}") .build(); p.cargo("bench --bench=bar --bench=baz").run(); } #[cargo_test] fn no_infer_dirs() { let p = project() .file("src/lib.rs", "fn main() {}") .file("examples/dir.rs/dummy", "") .file("benches/dir.rs/dummy", "") .file("tests/dir.rs/dummy", "") .build(); p.cargo("build --examples --benches --tests").run(); // should not fail with "is a directory" } #[cargo_test] fn target_edition() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [lib] edition = "2018" "#, ) .file("src/lib.rs", "") .build(); p.cargo("build -v") .with_stderr_data(str![[r#" [WARNING] `edition` is set on library `foo` which is deprecated [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..]--edition=2018 [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn target_edition_override() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] edition = "2018" [lib] edition = "2015" "#, ) .file( "src/lib.rs", " pub fn async() {} pub fn try() {} pub fn await() {} ", ) .build(); p.cargo("build -v").run(); } #[cargo_test] fn same_metadata_different_directory() { // A top-level crate built in two different workspaces should have the // same metadata hash. let p = project() .at("foo1") .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); let output = t!(String::from_utf8(p.cargo("build -v").run().stderr,)); let metadata = output .split_whitespace() .find(|arg| arg.starts_with("metadata=")) .unwrap(); let p = project() .at("foo2") .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("build -v") .with_stderr_data(format!("...\n[..]{metadata}[..]\n...")) .run(); } #[cargo_test] fn building_a_dependent_crate_without_bin_should_fail() { Package::new("testless", "0.1.0") .file( "Cargo.toml", r#" [package] name = "testless" version = "0.1.0" edition = "2015" [[bin]] name = "a_bin" "#, ) .file("src/lib.rs", "") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] testless = "0.1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] testless v0.1.0 (registry `dummy-registry`) [ERROR] failed to download replaced source registry `crates-io` Caused by: failed to parse manifest at `[ROOT]/home/.cargo/registry/src/-[HASH]/testless-0.1.0/Cargo.toml` Caused by: can't find `a_bin` bin at `src/bin/a_bin.rs` or `src/bin/a_bin/main.rs`. Please specify bin.path if you want to use a non-default path. "#]]) .run(); } #[cargo_test] #[cfg(any(target_os = "macos", target_os = "ios"))] fn uplift_dsym_of_bin_on_mac() { let p = project() .file("src/main.rs", "fn main() { panic!(); }") .file("src/bin/b.rs", "fn main() { panic!(); }") .file("examples/c.rs", "fn main() { panic!(); }") .file("tests/d.rs", "fn main() { panic!(); }") .build(); p.cargo("build --bins --examples --tests") .enable_mac_dsym() .run(); assert!(p.target_debug_dir().join("foo.dSYM").is_dir()); assert!(p.target_debug_dir().join("b.dSYM").is_dir()); assert!(p.target_debug_dir().join("b.dSYM").is_symlink()); assert!(p.target_debug_dir().join("examples/c.dSYM").is_dir()); assert!(!p.target_debug_dir().join("c.dSYM").exists()); assert!(!p.target_debug_dir().join("d.dSYM").exists()); } #[cargo_test] #[cfg(any(target_os = "macos", target_os = "ios"))] fn uplift_dsym_of_bin_on_mac_when_broken_link_exists() { let p = project() .file("src/main.rs", "fn main() { panic!(); }") .build(); let dsym = p.target_debug_dir().join("foo.dSYM"); p.cargo("build").enable_mac_dsym().run(); assert!(dsym.is_dir()); // Simulate the situation where the underlying dSYM bundle goes missing // but the uplifted symlink to it remains. This would previously cause // builds to permanently fail until the bad symlink was manually removed. dsym.rm_rf(); p.symlink( p.target_debug_dir() .join("deps") .join("foo-baaaaaadbaaaaaad.dSYM"), &dsym, ); assert!(dsym.is_symlink()); assert!(!dsym.exists()); p.cargo("build").enable_mac_dsym().run(); assert!(dsym.is_dir()); } #[cargo_test] #[cfg(all(target_os = "windows", target_env = "msvc"))] fn uplift_pdb_of_bin_on_windows() { let p = project() .file("src/main.rs", "fn main() { panic!(); }") .file("src/bin/b.rs", "fn main() { panic!(); }") .file("src/bin/foo-bar.rs", "fn main() { panic!(); }") .file("examples/c.rs", "fn main() { panic!(); }") .file("tests/d.rs", "fn main() { panic!(); }") .build(); p.cargo("build --bins --examples --tests").run(); assert!(p.target_debug_dir().join("foo.pdb").is_file()); assert!(p.target_debug_dir().join("b.pdb").is_file()); assert!(p.target_debug_dir().join("examples/c.pdb").exists()); assert!(p.target_debug_dir().join("foo-bar.exe").is_file()); assert!(p.target_debug_dir().join("foo_bar.pdb").is_file()); assert!(!p.target_debug_dir().join("c.pdb").exists()); assert!(!p.target_debug_dir().join("d.pdb").exists()); } #[cargo_test] #[cfg(target_os = "linux")] fn uplift_dwp_of_bin_on_linux() { let p = project() .file("src/main.rs", "fn main() { panic!(); }") .file("src/bin/b.rs", "fn main() { panic!(); }") .file("src/bin/foo-bar.rs", "fn main() { panic!(); }") .file("examples/c.rs", "fn main() { panic!(); }") .file("tests/d.rs", "fn main() { panic!(); }") .build(); p.cargo("build --bins --examples --tests") .enable_split_debuginfo_packed() .run(); assert!(p.target_debug_dir().join("foo.dwp").is_file()); assert!(p.target_debug_dir().join("b.dwp").is_file()); assert!(p.target_debug_dir().join("examples/c.dwp").exists()); assert!(p.target_debug_dir().join("foo-bar").is_file()); assert!(p.target_debug_dir().join("foo-bar.dwp").is_file()); assert!(!p.target_debug_dir().join("c.dwp").exists()); assert!(!p.target_debug_dir().join("d.dwp").exists()); } // Ensure that `cargo build` chooses the correct profile for building // targets based on filters (assuming `--profile` is not specified). #[cargo_test] fn build_filter_infer_profile() { let p = project() .file("src/lib.rs", "") .file("src/main.rs", "fn main() {}") .file("tests/t1.rs", "") .file("benches/b1.rs", "") .file("examples/ex1.rs", "fn main() {}") .build(); p.cargo("build -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]link[..]` [RUNNING] `rustc --crate-name foo --edition=2015 src/main.rs [..]--crate-type bin --emit=[..]link[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]].unordered()) .run(); p.root().join("target").rm_rf(); p.cargo("build -v --test=t1") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]link[..] -C debuginfo=2 [..]` [RUNNING] `rustc --crate-name foo --edition=2015 src/main.rs [..]--crate-type bin --emit=[..]link[..] -C debuginfo=2 [..]` [RUNNING] `rustc --crate-name t1 --edition=2015 tests/t1.rs [..]--emit=[..]link[..] -C debuginfo=2 [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]].unordered()) .run(); p.root().join("target").rm_rf(); // Bench uses test profile without `--release`. p.cargo("build -v --bench=b1") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]link [..]-C debuginfo=2 [..]` [RUNNING] `rustc --crate-name b1 --edition=2015 benches/b1.rs [..]--emit=[..]link[..] -C embed-bitcode=no -C debuginfo=2 [..]--test [..]` [RUNNING] `rustc --crate-name foo --edition=2015 src/main.rs [..]--crate-type bin --emit=[..]link [..]-C debuginfo=2 [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]].unordered()) .run(); } #[cargo_test] fn targets_selected_default() { let p = project().file("src/main.rs", "fn main() {}").build(); p.cargo("build -v") // Binaries. .with_stderr_contains( "[RUNNING] `rustc --crate-name foo --edition=2015 src/main.rs [..]--crate-type bin \ --emit=[..]link[..]", ) // Benchmarks. .with_stderr_does_not_contain( "[RUNNING] `rustc --crate-name foo --edition=2015 src/main.rs [..]--emit=[..]link \ -C opt-level=3 --test [..]", ) // Unit tests. .with_stderr_does_not_contain( "[RUNNING] `rustc --crate-name foo --edition=2015 src/main.rs [..]--emit=[..]link[..]\ -C debuginfo=2 --test [..]", ) .run(); } #[cargo_test] fn targets_selected_all() { let p = project().file("src/main.rs", "fn main() {}").build(); // The first RUNNING is for unit tests // The second RUNNING is for binaries p.cargo("build -v --all-targets") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/main.rs [..]--emit=[..]link [..]-C debuginfo=2 -[..]-test[..]` [RUNNING] `rustc --crate-name foo --edition=2015 src/main.rs [..]--crate-type bin --emit=[..]link[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]].unordered()) .run(); } #[cargo_test] fn all_targets_no_lib() { let p = project().file("src/main.rs", "fn main() {}").build(); // The first RUNNING is for unit tests // The second RUNNING is for binaries p.cargo("build -v --all-targets") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/main.rs [..]--emit=[..]link[..] -C debuginfo=2 [..]--test [..]` [RUNNING] `rustc --crate-name foo --edition=2015 src/main.rs [..]--crate-type bin --emit=[..]link[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]].unordered()) .run(); } #[cargo_test] fn no_linkable_target() { // Issue 3169: this is currently not an error as per discussion in PR #4797. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [dependencies] the_lib = { path = "the_lib" } "#, ) .file("src/main.rs", "fn main() {}") .file( "the_lib/Cargo.toml", r#" [package] name = "the_lib" version = "0.1.0" edition = "2015" [lib] name = "the_lib" crate-type = ["staticlib"] "#, ) .file("the_lib/src/lib.rs", "pub fn foo() {}") .build(); p.cargo("build") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [WARNING] The package `the_lib` provides no linkable target. The compiler might raise an error while compiling `foo`. Consider adding 'dylib' or 'rlib' to key `crate-type` in `the_lib`'s Cargo.toml. This warning might turn into a hard error in the future. [COMPILING] the_lib v0.1.0 ([ROOT]/foo/the_lib) [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn avoid_dev_deps() { Package::new("foo", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] [dev-dependencies] baz = "1.0.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] no matching package named `baz` found location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `bar v0.1.0 ([ROOT]/foo)` "#]]) .run(); p.cargo("build -Zavoid-dev-deps") .masquerade_as_nightly_cargo(&["avoid-dev-deps"]) .run(); } #[cargo_test] fn default_cargo_config_jobs() { let p = project() .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [build] jobs = 1 "#, ) .build(); p.cargo("build -v").run(); } #[cargo_test] fn good_cargo_config_jobs() { let p = project() .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [build] jobs = 4 "#, ) .build(); p.cargo("build -v").run(); } #[cargo_test] fn good_jobs() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("build --jobs 1").run(); p.cargo("build --jobs -1").run(); p.cargo("build --jobs default").run(); } #[cargo_test] fn invalid_cargo_config_jobs() { let p = project() .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [build] jobs = 0 "#, ) .build(); p.cargo("build -v") .with_status(101) .with_stderr_data(str![[r#" [ERROR] jobs may not be 0 "#]]) .run(); } #[cargo_test] fn invalid_jobs() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("build --jobs 0") .with_status(101) .with_stderr_data(str![[r#" [ERROR] jobs may not be 0 "#]]) .run(); p.cargo("build --jobs over9000") .with_status(101) .with_stderr_data(str![[r#" [ERROR] could not parse `over9000`. Number of parallel jobs should be `default` or a number. "#]]) .run(); } #[cargo_test] fn target_filters_workspace() { let ws = project() .at("ws") .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] "#, ) .file("a/Cargo.toml", &basic_lib_manifest("a")) .file("a/src/lib.rs", "") .file("a/examples/ex1.rs", "fn main() {}") .file("b/Cargo.toml", &basic_bin_manifest("b")) .file("b/src/lib.rs", "") .file("b/src/main.rs", "fn main() {}") .build(); ws.cargo("build -v --example ex") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no example target named `ex` in default-run packages [HELP] a target with a similar name exists: `ex1` "#]]) .run(); ws.cargo("build -v --example 'ex??'") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no example target matches pattern `ex??` in default-run packages [HELP] a target with a similar name exists: `ex1` "#]]) .run(); ws.cargo("build -v --lib") .with_stderr_data( str![[r#" [COMPILING] a v0.5.0 ([ROOT]/ws/a) [COMPILING] b v0.5.0 ([ROOT]/ws/b) [RUNNING] `rustc [..]a/src/lib.rs[..]` [RUNNING] `rustc [..]b/src/lib.rs[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); ws.cargo("build -v --example ex1") .with_stderr_data(str![[r#" [COMPILING] a v0.5.0 ([ROOT]/ws/a) [RUNNING] `rustc [..]a/examples/ex1.rs[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn target_filters_workspace_not_found() { let ws = project() .at("ws") .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] "#, ) .file("a/Cargo.toml", &basic_bin_manifest("a")) .file("a/src/main.rs", "fn main() {}") .file("b/Cargo.toml", &basic_bin_manifest("b")) .file("b/src/main.rs", "fn main() {}") .build(); ws.cargo("build -v --lib") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no library targets found in packages: a, b "#]]) .run(); } #[cfg(unix)] #[cargo_test] fn signal_display() { // Cause the compiler to crash with a signal. let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] pm = { path = "pm" } "#, ) .file( "src/lib.rs", r#" #[macro_use] extern crate pm; #[derive(Foo)] pub struct S; "#, ) .file( "pm/Cargo.toml", r#" [package] name = "pm" version = "0.1.0" edition = "2015" [lib] proc-macro = true "#, ) .file( "pm/src/lib.rs", r#" extern crate proc_macro; use proc_macro::TokenStream; #[proc_macro_derive(Foo)] pub fn derive(_input: TokenStream) -> TokenStream { std::process::abort() } "#, ) .build(); foo.cargo("build") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] pm v0.1.0 ([ROOT]/foo/pm) [COMPILING] foo v0.1.0 ([ROOT]/foo) [ERROR] could not compile `foo` (lib) Caused by: process didn't exit successfully: `rustc [..]` (signal: 6, SIGABRT: process abort signal) "#]]) .with_status(101) .run(); } #[cargo_test] fn tricky_pipelining() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { path = "bar" } "#, ) .file("src/lib.rs", "extern crate bar;") .file("bar/Cargo.toml", &basic_lib_manifest("bar")) .file("bar/src/lib.rs", "") .build(); foo.cargo("build -p bar").run(); foo.cargo("build -p foo").run(); } #[cargo_test] fn pipelining_works() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { path = "bar" } "#, ) .file("src/lib.rs", "extern crate bar;") .file("bar/Cargo.toml", &basic_lib_manifest("bar")) .file("bar/src/lib.rs", "") .build(); foo.cargo("build") .with_stdout_data(str![]) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn pipelining_big_graph() { // Create a crate graph of the form {a,b}{0..29}, where {a,b}(n) depend on {a,b}(n+1) // Then have `foo`, a binary crate, depend on the whole thing. let mut project = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] a1 = { path = "a1" } b1 = { path = "b1" } "#, ) .file("src/main.rs", "fn main(){}"); for n in 0..30 { for x in &["a", "b"] { project = project .file( &format!("{x}{n}/Cargo.toml", x = x, n = n), &format!( r#" [package] name = "{x}{n}" version = "0.1.0" edition = "2015" [dependencies] a{np1} = {{ path = "../a{np1}" }} b{np1} = {{ path = "../b{np1}" }} "#, x = x, n = n, np1 = n + 1 ), ) .file(&format!("{x}{n}/src/lib.rs", x = x, n = n), ""); } } let foo = project .file("a30/Cargo.toml", &basic_lib_manifest("a30")) .file( "a30/src/lib.rs", r#"compile_error!("don't actually build me");"#, ) .file("b30/Cargo.toml", &basic_lib_manifest("b30")) .file("b30/src/lib.rs", "") .build(); foo.cargo("build -p foo") .with_status(101) .with_stderr_data( str![[r#" [COMPILING] a30 v0.5.0 ([ROOT]/foo/a30) [ERROR] don't actually build me ... [ERROR] could not compile `a30` (lib) due to 1 previous error ... "#]] .unordered(), ) .run(); } #[cargo_test] fn forward_rustc_output() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = '2018' [dependencies] bar = { path = "bar" } "#, ) .file("src/lib.rs", "bar::foo!();") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" [lib] proc-macro = true "#, ) .file( "bar/src/lib.rs", r#" extern crate proc_macro; use proc_macro::*; #[proc_macro] pub fn foo(input: TokenStream) -> TokenStream { println!("a"); println!("b"); println!("{{}}"); eprintln!("c"); eprintln!("d"); eprintln!("{{a"); // "malformed json" input } "#, ) .build(); foo.cargo("build") .with_stdout_data(str![[r#" a b {} "#]]) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.1.0 ([ROOT]/foo/bar) [COMPILING] foo v0.1.0 ([ROOT]/foo) c d {a [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn build_lib_only() { let p = project() .file("src/main.rs", "fn main() {}") .file("src/lib.rs", r#" "#) .build(); p.cargo("build --lib -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]link[..] -L dependency=[ROOT]/foo/target/debug/deps` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn build_with_no_lib() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build --lib") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no library targets found in package `foo` "#]]) .run(); } #[cargo_test] fn build_with_relative_cargo_home_path() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = ["wycats@example.com"] [dependencies] "test-dependency" = { path = "src/test_dependency" } "#, ) .file("src/main.rs", "fn main() {}") .file("src/test_dependency/src/lib.rs", r#" "#) .file( "src/test_dependency/Cargo.toml", &basic_manifest("test-dependency", "0.0.1"), ) .build(); p.cargo("build").env("CARGO_HOME", "./cargo_home/").run(); } #[cargo_test] fn user_specific_cfgs_are_filtered_out() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", r#"fn main() {}"#) .file( "build.rs", r#" fn main() { assert!(std::env::var_os("CARGO_CFG_PROC_MACRO").is_none()); assert!(std::env::var_os("CARGO_CFG_DEBUG_ASSERTIONS").is_none()); } "#, ) .build(); p.cargo("rustc -- --cfg debug_assertions --cfg proc_macro -Aunknown_lints -Aexplicit_builtin_cfgs_in_flags") .run(); p.process(&p.bin("foo")).run(); } #[cargo_test] fn close_output() { // What happens when stdout or stderr is closed during a build. // Server to know when rustc has spawned. let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); let addr = listener.local_addr().unwrap(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [lib] proc-macro = true [[bin]] name = "foobar" "#, ) .file( "src/lib.rs", &r#" use proc_macro::TokenStream; use std::io::Read; #[proc_macro] pub fn repro(_input: TokenStream) -> TokenStream { println!("hello stdout!"); eprintln!("hello stderr!"); // Tell the test we have started. let mut socket = std::net::TcpStream::connect("__ADDR__").unwrap(); // Wait for the test to tell us to start printing. let mut buf = [0]; drop(socket.read_exact(&mut buf)); let use_stderr = std::env::var("__CARGO_REPRO_STDERR").is_ok(); // Emit at least 1MB of data. // Linux pipes can buffer up to 64KB. // This test seems to be sensitive to having other threads // calling fork. My hypothesis is that the stdout/stderr // file descriptors are duplicated into the child process, // and during the short window between fork and exec, the // file descriptor is kept alive long enough for the // build to finish. It's a half-baked theory, but this // seems to prevent the spurious errors in CI. // An alternative solution is to run this test in // a single-threaded environment. for i in 0..100000 { if use_stderr { eprintln!("0123456789{}", i); } else { println!("0123456789{}", i); } } TokenStream::new() } "# .replace("__ADDR__", &addr.to_string()), ) .file( "src/bin/foobar.rs", r#" foo::repro!(); fn main() {} "#, ) .build(); // The `stderr` flag here indicates if this should forcefully close stderr or stdout. let spawn = |stderr: bool| { let mut cmd = p.cargo("build").build_command(); cmd.stdout(Stdio::piped()).stderr(Stdio::piped()); if stderr { cmd.env("__CARGO_REPRO_STDERR", "1"); } let mut child = cmd.spawn().unwrap(); // Wait for proc macro to start. let pm_conn = listener.accept().unwrap().0; // Close stderr or stdout. if stderr { drop(child.stderr.take()); } else { drop(child.stdout.take()); } // Tell the proc-macro to continue; drop(pm_conn); // Read the output from the other channel. let out: &mut dyn Read = if stderr { child.stdout.as_mut().unwrap() } else { child.stderr.as_mut().unwrap() }; let mut result = String::new(); out.read_to_string(&mut result).unwrap(); let status = child.wait().unwrap(); assert!(!status.success()); result }; let stderr = spawn(false); assert_e2e().eq( &stderr, str![[r#" [COMPILING] foo v0.1.0 ([ROOT]/foo) hello stderr! [ERROR] [BROKEN_PIPE] [WARNING] build failed, waiting for other jobs to finish... "#]] .unordered(), ); // Try again with stderr. p.build_dir().rm_rf(); let stdout = spawn(true); assert_eq!(stdout, "hello stdout!\n"); } #[cargo_test] fn close_output_during_drain() { // Test to close the output during the build phase (drain_the_queue). // There was a bug where it would hang. // Server to know when rustc has spawned. let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); let addr = listener.local_addr().unwrap(); // Create a wrapper so the test can know when compiling has started. let rustc_wrapper = { let p = project() .at("compiler") .file("Cargo.toml", &basic_manifest("compiler", "1.0.0")) .file( "src/main.rs", &r#" use std::process::Command; use std::env; use std::io::Read; fn main() { // Only wait on the first dependency. if matches!(env::var("CARGO_PKG_NAME").as_deref(), Ok("dep")) { let mut socket = std::net::TcpStream::connect("__ADDR__").unwrap(); // Wait for the test to tell us to start printing. let mut buf = [0]; drop(socket.read_exact(&mut buf)); } let mut cmd = Command::new("rustc"); for arg in env::args_os().skip(1) { cmd.arg(arg); } std::process::exit(cmd.status().unwrap().code().unwrap()); } "# .replace("__ADDR__", &addr.to_string()), ) .build(); p.cargo("build").run(); p.bin("compiler") }; Package::new("dep", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] dep = "1.0" "#, ) .file("src/lib.rs", "") .build(); // Spawn cargo, wait for the first rustc to start, and then close stderr. let mut cmd = process(&cargo_exe()) .arg("check") .cwd(p.root()) .env("RUSTC", rustc_wrapper) .build_command(); cmd.stdout(Stdio::piped()).stderr(Stdio::piped()); let mut child = cmd.spawn().expect("cargo should spawn"); // Wait for the rustc wrapper to start. let rustc_conn = listener.accept().unwrap().0; // Close stderr to force an error. drop(child.stderr.take()); // Tell the wrapper to continue. drop(rustc_conn); match child.wait() { Ok(status) => assert!(!status.success()), Err(e) => panic!("child wait failed: {}", e), } } use cargo_test_support::registry::Dependency; #[cargo_test] fn reduced_reproduction_8249() { // https://github.com/rust-lang/cargo/issues/8249 Package::new("a-src", "0.1.0").links("a").publish(); Package::new("a-src", "0.2.0").links("a").publish(); Package::new("b", "0.1.0") .add_dep(Dependency::new("a-src", "0.1").optional(true)) .publish(); Package::new("b", "0.2.0") .add_dep(Dependency::new("a-src", "0.2").optional(true)) .publish(); Package::new("c", "1.0.0") .add_dep(&Dependency::new("b", "0.1.0")) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] b = { version = "*", features = ["a-src"] } a-src = "*" "#, ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); cargo_util::paths::append(&p.root().join("Cargo.toml"), b"c = \"*\"").unwrap(); p.cargo("check").run(); p.cargo("check").run(); } #[cargo_test] fn target_directory_backup_exclusion() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); // Newly created target/ should have CACHEDIR.TAG inside... p.cargo("build").run(); let cachedir_tag = p.build_dir().join("CACHEDIR.TAG"); assert!(cachedir_tag.is_file()); assert!( fs::read_to_string(&cachedir_tag) .unwrap() .starts_with("Signature: 8a477f597d28d172789f06886806bc55") ); // ...but if target/ already exists CACHEDIR.TAG should not be created in it. fs::remove_file(&cachedir_tag).unwrap(); p.cargo("build").run(); assert!(!&cachedir_tag.is_file()); } #[cargo_test] fn simple_terminal_width() { let p = project() .file( "src/lib.rs", r#" pub fn foo() { let _: () = 42; } "#, ) .build(); p.cargo("build -v") .env("__CARGO_TEST_TTY_WIDTH_DO_NOT_USE_THIS", "20") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..]--diagnostic-width=20[..] error[E0308][..] ... [ERROR] could not compile `foo` (lib) due to 1 previous error Caused by: process didn't exit successfully: `rustc [..]` ([EXIT_STATUS]: 1) "#]]) .run(); p.cargo("doc -v") .env("__CARGO_TEST_TTY_WIDTH_DO_NOT_USE_THIS", "20") .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustdoc [..]--diagnostic-width=20[..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); } #[cargo_test] fn build_script_o0_default() { let p = project() .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .build(); p.cargo("build -v --release") .with_stderr_does_not_contain("[..]build_script_build[..]opt-level[..]") .run(); } #[cargo_test] fn build_script_o0_default_even_with_release() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [profile.release] opt-level = 1 "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .build(); p.cargo("build -v --release") .with_stderr_does_not_contain("[..]build_script_build[..]opt-level[..]") .run(); } #[cargo_test] fn primary_package_env_var() { // Test that CARGO_PRIMARY_PACKAGE is enabled only for "foo" and not for any dependency. let is_primary_package = r#" pub fn is_primary_package() -> bool {{ option_env!("CARGO_PRIMARY_PACKAGE").is_some() }} "#; Package::new("qux", "0.1.0") .file("src/lib.rs", is_primary_package) .publish(); let baz = git::new("baz", |project| { project .file("Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("src/lib.rs", is_primary_package) }); let foo = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = {{ path = "bar" }} baz = {{ git = '{}' }} qux = "0.1" "#, baz.url() ), ) .file( "src/lib.rs", &format!( r#" extern crate bar; extern crate baz; extern crate qux; {} #[test] fn verify_primary_package() {{ assert!(!bar::is_primary_package()); assert!(!baz::is_primary_package()); assert!(!qux::is_primary_package()); assert!(is_primary_package()); }} "#, is_primary_package ), ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", is_primary_package) .build(); foo.cargo("test").run(); } #[cargo_test] fn renamed_uplifted_artifact_remains_unmodified_after_rebuild() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.0" edition = "2015" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build").run(); let bin = p.bin("foo"); let renamed_bin = p.bin("foo-renamed"); fs::rename(&bin, &renamed_bin).unwrap(); p.change_file("src/main.rs", "fn main() { eprintln!(\"hello, world\"); }"); p.cargo("build").run(); let not_the_same = !same_file::is_same_file(bin, renamed_bin).unwrap(); assert!(not_the_same, "renamed uplifted artifact must be unmodified"); } #[cargo_test(nightly, reason = "-Zembed-metadata is nightly only")] fn embed_metadata() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" [dependencies.bar] path = "bar" "#, ) .file("src/main.rs", &main_file(r#""{}", bar::gimme()"#, &[])) .file("bar/Cargo.toml", &basic_lib_manifest("bar")) .file( "bar/src/bar.rs", r#" pub fn gimme() -> &'static str { "test passed" } "#, ) .build(); p.cargo("build -Z no-embed-metadata") .masquerade_as_nightly_cargo(&["-Z no-embed-metadata"]) .arg("-v") .with_stderr_contains("[RUNNING] `[..]-Z embed-metadata=no[..]`") .with_stderr_contains( "[RUNNING] `[..]--extern bar=[ROOT]/foo/target/debug/deps/libbar-[HASH].rmeta[..]`", ) .run(); } // Make sure that cargo passes --extern=.rmeta even if // is compiled as a dylib. #[cargo_test(nightly, reason = "-Zembed-metadata is nightly only")] fn embed_metadata_dylib_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" [dependencies.bar] path = "bar" "#, ) .file("src/main.rs", &main_file(r#""{}", bar::gimme()"#, &[])) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" [lib] crate-type = ["dylib"] "#, ) .file( "bar/src/lib.rs", r#" pub fn gimme() -> &'static str { "test passed" } "#, ) .build(); p.cargo("build -Z no-embed-metadata") .masquerade_as_nightly_cargo(&["-Z no-embed-metadata"]) .arg("-v") .with_stderr_contains("[RUNNING] `[..]-Z embed-metadata=no[..]`") .with_stderr_contains( "[RUNNING] `[..]--extern bar=[ROOT]/foo/target/debug/deps/libbar.rmeta[..]`", ) .run(); } cargo-0.91.0/tests/testsuite/build_dir.rs000064400000000000000000000635711046102023000165200ustar 00000000000000//! Tests for `build.build-dir` config property. //! //! The testing strategy for build-dir functionality is primarily checking if directories / files //! are in the expected locations. //! The rational is that other tests will verify each individual feature, while the tests in this //! file verify the files saved to disk are in the correct locations according to the `build-dir` //! configuration. //! //! Tests check if directories match some "layout" by using [`assert_build_dir_layout`] and //! [`assert_artifact_dir_layout`]. use std::path::PathBuf; use crate::prelude::*; use cargo_test_support::{Project, prelude::*}; use cargo_test_support::{paths, project, str}; use std::env::consts::{DLL_PREFIX, DLL_SUFFIX, EXE_SUFFIX}; #[cargo_test] fn verify_build_dir_is_disabled_by_feature_flag() { let p = project() .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) .file( ".cargo/config.toml", r#" [build] build-dir = "build-dir" "#, ) .build(); p.cargo("build") .masquerade_as_nightly_cargo(&["build-dir"]) .enable_mac_dsym() .run(); assert_build_dir_layout(p.root().join("target"), "debug"); assert_exists(&p.root().join(format!("target/debug/foo{EXE_SUFFIX}"))); assert_exists(&p.root().join("target/debug/foo.d")); assert_not_exists(&p.root().join("build-dir")); } #[cargo_test] fn binary_with_debug() { let p = project() .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) .file( ".cargo/config.toml", r#" [build] target-dir = "target-dir" build-dir = "build-dir" "#, ) .build(); p.cargo("build -Z build-dir") .masquerade_as_nightly_cargo(&["build-dir"]) .enable_mac_dsym() .run(); assert_build_dir_layout(p.root().join("build-dir"), "debug"); assert_artifact_dir_layout(p.root().join("target-dir"), "debug"); assert_exists_patterns_with_base_dir( &p.root(), &[ // Check the pre-uplifted binary in the build-dir &format!("build-dir/debug/deps/foo*{EXE_SUFFIX}"), "build-dir/debug/deps/foo*.d", // Verify the binary was copied to the target-dir &format!("target-dir/debug/foo{EXE_SUFFIX}"), "target-dir/debug/foo.d", ], ); assert_not_exists(&p.root().join("target")); } #[cargo_test] fn binary_with_release() { let p = project() .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) .file( ".cargo/config.toml", r#" [build] target-dir = "target-dir" build-dir = "build-dir" "#, ) .build(); p.cargo("build --release -Z build-dir") .masquerade_as_nightly_cargo(&["build-dir"]) .enable_mac_dsym() .run(); assert_build_dir_layout(p.root().join("build-dir"), "release"); assert_exists(&p.root().join(format!("target-dir/release/foo{EXE_SUFFIX}"))); assert_exists_patterns_with_base_dir( &p.root(), &[ // Check the pre-uplifted binary in the build-dir &format!("build-dir/release/deps/foo*{EXE_SUFFIX}"), "build-dir/release/deps/foo*.d", // Verify the binary was copied to the target-dir &format!("target-dir/release/foo{EXE_SUFFIX}"), "target-dir/release/foo.d", ], ); } #[cargo_test] fn libs() { // https://doc.rust-lang.org/reference/linkage.html#r-link.staticlib let (staticlib_prefix, staticlib_suffix) = if cfg!(target_os = "windows") && cfg!(target_env = "msvc") { ("", ".lib") } else { ("lib", ".a") }; // (crate-type, list of final artifacts) let lib_types = [ ("lib", ["libfoo.rlib", "libfoo.d"]), ( "dylib", [ &format!("{DLL_PREFIX}foo{DLL_SUFFIX}"), &format!("{DLL_PREFIX}foo.d"), ], ), ( "cdylib", [ &format!("{DLL_PREFIX}foo{DLL_SUFFIX}"), &format!("{DLL_PREFIX}foo.d"), ], ), ( "staticlib", [ &format!("{staticlib_prefix}foo{staticlib_suffix}"), &format!("{staticlib_prefix}foo.d"), ], ), ]; for (lib_type, expected_files) in lib_types { let p = project() .file("src/lib.rs", r#"fn foo() { println!("Hello, World!") }"#) .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" authors = [] edition = "2015" [lib] crate-type = ["{lib_type}"] "# ), ) .file( ".cargo/config.toml", r#" [build] target-dir = "target-dir" build-dir = "build-dir" "#, ) .build(); p.cargo("build -Z build-dir") .masquerade_as_nightly_cargo(&["build-dir"]) .enable_mac_dsym() .run(); assert_build_dir_layout(p.root().join("build-dir"), "debug"); // Verify lib artifacts were copied into the artifact dir assert_exists_patterns_with_base_dir(&p.root().join("target-dir/debug"), &expected_files); } } #[cargo_test] fn should_default_to_target() { let p = project() .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) .build(); p.cargo("build -Z build-dir") .masquerade_as_nightly_cargo(&["build-dir"]) .enable_mac_dsym() .run(); assert_build_dir_layout(p.root().join("target"), "debug"); assert_exists(&p.root().join(format!("target/debug/foo{EXE_SUFFIX}"))); } #[cargo_test] fn should_respect_env_var() { let p = project() .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) .build(); p.cargo("build -Z build-dir") .masquerade_as_nightly_cargo(&["build-dir"]) .env("CARGO_BUILD_BUILD_DIR", "build-dir") .enable_mac_dsym() .run(); assert_build_dir_layout(p.root().join("build-dir"), "debug"); assert_exists(&p.root().join(format!("target/debug/foo{EXE_SUFFIX}"))); } #[cargo_test] fn build_script_should_output_to_build_dir() { let p = project() .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) .file( "build.rs", r#" fn main() { std::fs::write( format!("{}/foo.txt", std::env::var("OUT_DIR").unwrap()), "Hello, world!", ) .unwrap(); } "#, ) .file( ".cargo/config.toml", r#" [build] target-dir = "target-dir" build-dir = "build-dir" "#, ) .build(); p.cargo("build -Z build-dir") .masquerade_as_nightly_cargo(&["build-dir"]) .enable_mac_dsym() .run(); assert_build_dir_layout(p.root().join("build-dir"), "debug"); assert_exists_patterns_with_base_dir( &p.root(), &[ &format!("build-dir/debug/build/foo-*/build-script-build{EXE_SUFFIX}"), "build-dir/debug/build/foo-*/out/foo.txt", // Verify OUT_DIR ], ); } #[cargo_test] fn cargo_tmpdir_should_output_to_build_dir() { let p = project() .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) .file( "tests/foo.rs", r#" #[test] fn test() { std::fs::write( format!("{}/foo.txt", env!("CARGO_TARGET_TMPDIR")), "Hello, world!", ) .unwrap(); } "#, ) .file( ".cargo/config.toml", r#" [build] target-dir = "target-dir" build-dir = "build-dir" "#, ) .build(); p.cargo("test -Z build-dir") .masquerade_as_nightly_cargo(&["build-dir"]) .enable_mac_dsym() .run(); assert_build_dir_layout(p.root().join("build-dir"), "debug"); assert_exists(&p.root().join(format!("build-dir/tmp/foo.txt"))); } #[cargo_test] fn examples_should_output_to_build_dir_and_uplift_to_target_dir() { let p = project() .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) .file("examples/foo.rs", r#"fn main() { }"#) .file( ".cargo/config.toml", r#" [build] target-dir = "target-dir" build-dir = "build-dir" "#, ) .build(); p.cargo("build --examples -Z build-dir") .masquerade_as_nightly_cargo(&["build-dir"]) .enable_mac_dsym() .run(); assert_build_dir_layout(p.root().join("build-dir"), "debug"); assert_exists_patterns_with_base_dir( &p.root(), &[ // uplifted (target-dir) &format!("target-dir/debug/examples/foo{EXE_SUFFIX}"), "target-dir/debug/examples/foo.d", // pre-uplifted (build-dir) &format!("build-dir/debug/examples/foo*{EXE_SUFFIX}"), "build-dir/debug/examples/foo*.d", ], ); } #[cargo_test] fn benches_should_output_to_build_dir() { let p = project() .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) .file("benches/foo.rs", r#"fn main() { }"#) .file( ".cargo/config.toml", r#" [build] target-dir = "target-dir" build-dir = "build-dir" "#, ) .build(); p.cargo("build --bench=foo -Z build-dir") .masquerade_as_nightly_cargo(&["build-dir"]) .enable_mac_dsym() .run(); assert_build_dir_layout(p.root().join("build-dir"), "debug"); assert_exists_patterns_with_base_dir( &p.root(), &[ &format!("build-dir/debug/deps/foo*{EXE_SUFFIX}"), "build-dir/debug/deps/foo*.d", ], ); } #[cargo_test] fn cargo_doc_should_output_to_target_dir() { let p = project() .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) .file( ".cargo/config.toml", r#" [build] target-dir = "target-dir" build-dir = "build-dir" "#, ) .build(); p.cargo("doc -Z build-dir") .masquerade_as_nightly_cargo(&["build-dir"]) .enable_mac_dsym() .run(); let docs_dir = p.root().join("target-dir/doc"); assert_exists(&docs_dir); assert_exists(&docs_dir.join("foo/index.html")); } #[cargo_test] fn cargo_package_should_build_in_build_dir_and_output_to_target_dir() { let p = project() .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) .file( ".cargo/config.toml", r#" [build] target-dir = "target-dir" build-dir = "build-dir" "#, ) .build(); p.cargo("package -Z build-dir") .masquerade_as_nightly_cargo(&["build-dir"]) .enable_mac_dsym() .run(); assert_build_dir_layout(p.root().join("build-dir"), "debug"); let package_artifact_dir = p.root().join("target-dir/package"); assert_exists(&package_artifact_dir); assert_exists(&package_artifact_dir.join("foo-0.0.1.crate")); assert!(package_artifact_dir.join("foo-0.0.1.crate").is_file()); let package_build_dir = p.root().join("build-dir/package"); assert_exists(&package_build_dir); assert_exists(&package_build_dir.join("foo-0.0.1")); assert!(package_build_dir.join("foo-0.0.1").is_dir()); } #[cargo_test] fn cargo_clean_should_clean_the_target_dir_and_build_dir() { let p = project() .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) .file( ".cargo/config.toml", r#" [build] target-dir = "target-dir" build-dir = "build-dir" "#, ) .build(); p.cargo("build -Z build-dir") .masquerade_as_nightly_cargo(&["build-dir"]) .enable_mac_dsym() .run(); assert_build_dir_layout(p.root().join("build-dir"), "debug"); p.cargo("clean -Z build-dir") .masquerade_as_nightly_cargo(&["build-dir"]) .enable_mac_dsym() .run(); assert_not_exists(&p.root().join("build-dir")); assert_not_exists(&p.root().join("target-dir")); } #[cargo_test] fn timings_report_should_output_to_target_dir() { let p = project() .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) .file( ".cargo/config.toml", r#" [build] target-dir = "target-dir" build-dir = "build-dir" "#, ) .build(); p.cargo("build --timings -Z build-dir") .masquerade_as_nightly_cargo(&["build-dir"]) .enable_mac_dsym() .run(); assert_exists(&p.root().join("target-dir/cargo-timings/cargo-timing.html")); } #[cargo_test( nightly, reason = "-Zfuture-incompat-test requires nightly (permanently)" )] fn future_incompat_should_output_to_build_dir() { let p = project() .file("src/main.rs", r#"fn main() { let x = 1; }"#) .file( ".cargo/config.toml", r#" [build] target-dir = "target-dir" build-dir = "build-dir" "#, ) .build(); p.cargo("build -Z build-dir") .masquerade_as_nightly_cargo(&["build-dir"]) .arg("--future-incompat-report") .env("RUSTFLAGS", "-Zfuture-incompat-test") .run(); assert_exists(&p.root().join("build-dir/.future-incompat-report.json")); } #[cargo_test] fn template_should_error_for_invalid_variables() { let p = project() .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) .file( ".cargo/config.toml", r#" [build] build-dir = "{fake}/build-dir" target-dir = "target-dir" "#, ) .build(); p.cargo("build -Z build-dir") .masquerade_as_nightly_cargo(&["build-dir"]) .enable_mac_dsym() .with_status(101) .with_stderr_data(str![[r#" [ERROR] unexpected variable `fake` in build.build-dir path `{fake}/build-dir` [HELP] available template variables are `{workspace-root}`, `{cargo-cache-home}`, `{workspace-path-hash}` "#]]) .run(); } #[cargo_test] fn template_should_suggest_nearest_variable() { let p = project() .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [build] build-dir = "{workspace-ro}/build-dir" "#, ) .build(); p.cargo("build -Z build-dir") .masquerade_as_nightly_cargo(&["build-dir"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] unexpected variable `workspace-ro` in build.build-dir path `{workspace-ro}/build-dir` [HELP] a template variable with a similar name exists: `workspace-root` "#]]) .run(); } #[cargo_test] fn template_workspace_root() { let p = project() .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) .file( ".cargo/config.toml", r#" [build] build-dir = "{workspace-root}/build-dir" target-dir = "target-dir" "#, ) .build(); p.cargo("build -Z build-dir") .masquerade_as_nightly_cargo(&["build-dir"]) .enable_mac_dsym() .run(); assert_build_dir_layout(p.root().join("build-dir"), "debug"); assert_artifact_dir_layout(p.root().join("target-dir"), "debug"); // Verify the binary was uplifted to the target-dir assert_exists(&p.root().join(&format!("target-dir/debug/foo{EXE_SUFFIX}"))); } #[cargo_test] fn template_cargo_cache_home() { let p = project() .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) .file( ".cargo/config.toml", r#" [build] build-dir = "{cargo-cache-home}/build-dir" target-dir = "target-dir" "#, ) .build(); p.cargo("build -Z build-dir") .masquerade_as_nightly_cargo(&["build-dir"]) .enable_mac_dsym() .run(); assert_build_dir_layout(paths::home().join(".cargo/build-dir"), "debug"); assert_artifact_dir_layout(p.root().join("target-dir"), "debug"); // Verify the binary was uplifted to the target-dir assert_exists(&p.root().join(&format!("target-dir/debug/foo{EXE_SUFFIX}"))); } #[cargo_test] fn template_workspace_path_hash() { let p = project() .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" authors = [] edition = "2015" "#, ) .file( ".cargo/config.toml", r#" [build] build-dir = "foo/{workspace-path-hash}/build-dir" target-dir = "target-dir" "#, ) .build(); p.cargo("build -Z build-dir") .masquerade_as_nightly_cargo(&["build-dir"]) .enable_mac_dsym() .run(); let foo_dir = p.root().join("foo"); assert_exists(&foo_dir); let hash_dir = parse_workspace_manifest_path_hash(&foo_dir); let build_dir = hash_dir.as_path().join("build-dir"); assert_build_dir_layout(build_dir, "debug"); assert_artifact_dir_layout(p.root().join("target-dir"), "debug"); // Verify the binary was uplifted to the target-dir assert_exists(&p.root().join(&format!("target-dir/debug/foo{EXE_SUFFIX}"))); } /// Verify that the {workspace-path-hash} does not changes if cargo is run from inside of /// a symlinked directory. /// The test approach is to build a project twice from the non-symlinked directory and a symlinked /// directory and then compare the build-dir paths. #[cargo_test] fn template_workspace_path_hash_should_handle_symlink() { #[cfg(unix)] use std::os::unix::fs::symlink; #[cfg(windows)] use std::os::windows::fs::symlink_dir as symlink; let p = project() .file("src/lib.rs", "") .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" authors = [] edition = "2015" "#, ) .file( ".cargo/config.toml", r#" [build] build-dir = "foo/{workspace-path-hash}/build-dir" "#, ) .build(); // Build from the non-symlinked directory p.cargo("check -Z build-dir") .masquerade_as_nightly_cargo(&["build-dir"]) .enable_mac_dsym() .run(); // Parse and verify the hash dir created from the non-symlinked dir let foo_dir = p.root().join("foo"); assert_exists(&foo_dir); let original_hash_dir = parse_workspace_manifest_path_hash(&foo_dir); verify_layouts(&p, &original_hash_dir); // Create a symlink of the project root. let mut symlinked_dir = p.root().clone(); symlinked_dir.pop(); symlinked_dir = symlinked_dir.join("symlink-dir"); symlink(p.root(), &symlinked_dir).unwrap(); // Remove the foo dir (which contains the build-dir) before we rebuild from a symlinked dir. foo_dir.rm_rf(); // Run cargo from the symlinked dir p.cargo("check -Z build-dir") .cwd(&symlinked_dir) .masquerade_as_nightly_cargo(&["build-dir"]) .enable_mac_dsym() .run(); // Parse and verify the hash created from the symlinked dir assert_exists(&foo_dir); let symlink_hash_dir = parse_workspace_manifest_path_hash(&foo_dir); verify_layouts(&p, &symlink_hash_dir); // Verify the hash dir created from the symlinked and non-symlinked dirs are the same. assert_eq!(original_hash_dir, symlink_hash_dir); fn verify_layouts(p: &Project, build_dir_parent: &PathBuf) { let build_dir = build_dir_parent.as_path().join("build-dir"); assert_build_dir_layout(build_dir, "debug"); assert_artifact_dir_layout(p.root().join("target"), "debug"); } } #[cargo_test] fn template_should_handle_reject_unmatched_brackets() { let p = project() .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [build] build-dir = "foo/{bar" "#, ) .build(); p.cargo("build -Z build-dir") .masquerade_as_nightly_cargo(&["build-dir"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] unexpected opening bracket `{` in build.build-dir path `foo/{bar` "#]]) .run(); let p = project() .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [build] build-dir = "foo/}bar" "#, ) .build(); p.cargo("build -Z build-dir") .masquerade_as_nightly_cargo(&["build-dir"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] unexpected closing bracket `}` in build.build-dir path `foo/}bar` "#]]) .run(); } fn parse_workspace_manifest_path_hash(hash_dir: &PathBuf) -> PathBuf { // Since the hash will change between test runs simply find the first directories and assume // that is the hash dir. The format is a 2 char directory followed by the remaining hash in the // inner directory (ie. `34/f9d02eb8411c05`) let mut dirs = std::fs::read_dir(hash_dir).unwrap().into_iter(); let outer_hash_dir = dirs.next().unwrap().unwrap(); // Validate there are no other directories in `hash_dir` assert!( dirs.next().is_none(), "Found multiple dir entries in {hash_dir:?}" ); // Validate the outer hash dir hash is a directory and has the correct hash length assert!( outer_hash_dir.path().is_dir(), "{outer_hash_dir:?} was not a directory" ); assert_eq!( outer_hash_dir.path().file_name().unwrap().len(), 2, "Path {:?} should have been 2 chars", outer_hash_dir.path().file_name() ); let mut dirs = std::fs::read_dir(outer_hash_dir.path()) .unwrap() .into_iter(); let inner_hash_dir = dirs.next().unwrap().unwrap(); // Validate there are no other directories in first hash dir assert!( dirs.next().is_none(), "Found multiple dir entries in {outer_hash_dir:?}" ); // Validate the outer hash dir hash is a directory and has the correct hash length assert!( inner_hash_dir.path().is_dir(), "{inner_hash_dir:?} was not a directory" ); assert_eq!( inner_hash_dir.path().file_name().unwrap().len(), 14, "Path {:?} should have been 2 chars", inner_hash_dir.path().file_name() ); return inner_hash_dir.path(); } #[track_caller] fn assert_build_dir_layout(path: PathBuf, profile: &str) { assert_dir_layout(path, profile, true); } #[allow(dead_code)] #[track_caller] fn assert_artifact_dir_layout(path: PathBuf, profile: &str) { assert_dir_layout(path, profile, false); } #[track_caller] fn assert_dir_layout(path: PathBuf, profile: &str, is_build_dir: bool) { println!("checking if {path:?} is a build directory ({is_build_dir})"); // For things that are in both `target` and the build directory we only check if they are // present if `is_build_dir` is true. if is_build_dir { assert_eq!( is_build_dir, path.join(profile).is_dir(), "Expected {:?} to exist and be a directory", path.join(profile) ); } let error_message = |dir: &str| { if is_build_dir { format!("`{dir}` dir was expected but not found") } else { format!("`{dir}` dir was not expected but was found") } }; if is_build_dir { assert_exists(&path.join(".rustc_info.json")); } else { assert_not_exists(&path.join(".rustc_info.json")); } assert_eq!( is_build_dir, path.join(profile).join("deps").is_dir(), "{}", error_message("deps") ); assert_eq!( is_build_dir, path.join(profile).join("build").is_dir(), "{}", error_message("build") ); assert_eq!( is_build_dir, path.join(profile).join("incremental").is_dir(), "{}", error_message("incremental") ); assert_eq!( is_build_dir, path.join(profile).join(".fingerprint").is_dir(), "{}", error_message(".fingerprint") ); } #[track_caller] fn assert_exists(path: &PathBuf) { assert!( path.exists(), "Expected `{}` to exist but was not found.", path.display() ); } #[track_caller] fn assert_not_exists(path: &PathBuf) { assert!( !path.exists(), "Expected `{}` to NOT exist but was found.", path.display() ); } #[track_caller] fn assert_exists_patterns_with_base_dir(base: &PathBuf, patterns: &[&str]) { let root = base.to_str().unwrap(); let p: Vec<_> = patterns.iter().map(|p| format!("{root}/{p}")).collect(); let p: Vec<&str> = p.iter().map(|v| v.as_str()).collect(); assert_exists_patterns(&p); } #[track_caller] fn assert_exists_patterns(patterns: &[&str]) { for p in patterns { assert_exists_pattern(p); } } #[track_caller] fn assert_exists_pattern(pattern: &str) { use glob::glob; let mut z = glob(pattern).unwrap(); assert!( z.next().is_some(), "Expected `{pattern}` to match existing file but was not found.", ) } cargo-0.91.0/tests/testsuite/build_plan.rs000064400000000000000000000124401046102023000166610ustar 00000000000000//! Tests for --build-plan feature. use crate::prelude::*; use cargo_test_support::registry::Package; use cargo_test_support::{basic_bin_manifest, basic_manifest, main_file, project, str}; #[cargo_test] fn cargo_build_plan_simple() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("build --build-plan -Zunstable-options") .masquerade_as_nightly_cargo(&["build-plan"]) .with_stdout_data( str![[r#" { "inputs": [ "[ROOT]/foo/Cargo.toml" ], "invocations": [ { "args": "{...}", "compile_mode": "build", "cwd": "[ROOT]/foo", "deps": [], "env": "{...}", "kind": null, "links": "{...}", "outputs": "{...}", "package_name": "foo", "package_version": "0.5.0", "program": "rustc", "target_kind": [ "bin" ] } ] } "#]] .is_json(), ) .run(); assert!(!p.bin("foo").is_file()); } #[cargo_test] fn cargo_build_plan_single_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.5.0" [dependencies] bar = { path = "bar" } "#, ) .file( "src/lib.rs", r#" extern crate bar; pub fn foo() { bar::bar(); } #[test] fn test() { foo(); } "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "pub fn bar() {}") .build(); p.cargo("build --build-plan -Zunstable-options") .masquerade_as_nightly_cargo(&["build-plan"]) .with_stdout_data( str![[r#" { "inputs": [ "[ROOT]/foo/Cargo.toml", "[ROOT]/foo/bar/Cargo.toml" ], "invocations": [ { "args": "{...}", "compile_mode": "build", "cwd": "[ROOT]/foo", "deps": [], "env": "{...}", "kind": null, "links": {}, "outputs": [ "[ROOT]/foo/target/debug/deps/libbar-[HASH].rlib", "[ROOT]/foo/target/debug/deps/libbar-[HASH].rmeta" ], "package_name": "bar", "package_version": "0.0.1", "program": "rustc", "target_kind": [ "lib" ] }, { "args": "{...}", "compile_mode": "build", "cwd": "[ROOT]/foo", "deps": [ 0 ], "env": "{...}", "kind": null, "links": "{...}", "outputs": [ "[ROOT]/foo/target/debug/deps/libfoo-[HASH].rlib", "[ROOT]/foo/target/debug/deps/libfoo-[HASH].rmeta" ], "package_name": "foo", "package_version": "0.5.0", "program": "rustc", "target_kind": [ "lib" ] } ] } "#]] .is_json(), ) .run(); } #[cargo_test] fn cargo_build_plan_build_script() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" authors = ["wycats@example.com"] build = "build.rs" "#, ) .file("src/main.rs", r#"fn main() {}"#) .file("build.rs", r#"fn main() {}"#) .build(); p.cargo("build --build-plan -Zunstable-options") .masquerade_as_nightly_cargo(&["build-plan"]) .with_stdout_data( str![[r#" { "inputs": [ "[ROOT]/foo/Cargo.toml" ], "invocations": [ { "args": "{...}", "compile_mode": "build", "cwd": "[ROOT]/foo", "deps": [], "env": "{...}", "kind": null, "links": "{...}", "outputs": "{...}", "package_name": "foo", "package_version": "0.5.0", "program": "rustc", "target_kind": [ "custom-build" ] }, { "args": "{...}", "compile_mode": "run-custom-build", "cwd": "[ROOT]/foo", "deps": [ 0 ], "env": "{...}", "kind": null, "links": {}, "outputs": [], "package_name": "foo", "package_version": "0.5.0", "program": "[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build", "target_kind": [ "custom-build" ] }, { "args": "{...}", "compile_mode": "build", "cwd": "[ROOT]/foo", "deps": [ 1 ], "env": "{...}", "kind": null, "links": "{...}", "outputs": "{...}", "package_name": "foo", "package_version": "0.5.0", "program": "rustc", "target_kind": [ "bin" ] } ] } "#]] .is_json(), ) .run(); } #[cargo_test] fn build_plan_with_dev_dep() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" authors = [] [dev-dependencies] bar = "*" "#, ) .file("src/lib.rs", "") .build(); p.cargo("build --build-plan -Zunstable-options") .masquerade_as_nightly_cargo(&["build-plan"]) .run(); } cargo-0.91.0/tests/testsuite/build_script.rs000064400000000000000000005120671046102023000172450ustar 00000000000000//! Tests for build.rs scripts. use std::env; use std::fs; use std::io; use std::thread; use crate::prelude::*; use crate::utils::cargo_exe; use crate::utils::cross_compile::{ can_run_on_host as cross_compile_can_run_on_host, disabled as cross_compile_disabled, }; use crate::utils::tools; use cargo_test_support::compare::assert_e2e; use cargo_test_support::paths::cargo_home; use cargo_test_support::registry::Package; use cargo_test_support::str; use cargo_test_support::{basic_manifest, cross_compile, is_coarse_mtime, project, project_in}; use cargo_test_support::{git, rustc_host, sleep_ms, slow_cpu_multiplier, symlink_supported}; use cargo_util::paths::{self, remove_dir_all}; #[cargo_test] fn custom_build_script_failed() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] build = "build.rs" "#, ) .file("src/main.rs", "fn main() {}") .file("build.rs", "fn main() { std::process::exit(101); }") .build(); p.cargo("build -v") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name build_script_build --edition=2015 build.rs [..]--crate-type bin [..]` [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [ERROR] failed to run custom build command for `foo v0.5.0 ([ROOT]/foo)` Caused by: process didn't exit successfully: `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` ([EXIT_STATUS]: 101) "#]]) .run(); } #[cargo_test] fn custom_build_script_failed_backtraces_message() { // In this situation (no dependency sharing), debuginfo is turned off in // `dev.build-override`. However, if an error occurs running e.g. a build // script, and backtraces are opted into: a message explaining how to // improve backtraces is also displayed. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] build = "build.rs" "#, ) .file("src/main.rs", "fn main() {}") .file("build.rs", "fn main() { std::process::exit(101); }") .build(); p.cargo("build -v") .env("RUST_BACKTRACE", "1") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name build_script_build --edition=2015 build.rs [..]--crate-type bin [..]` [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [ERROR] failed to run custom build command for `foo v0.5.0 ([ROOT]/foo)` [NOTE] To improve backtraces for build dependencies, set the CARGO_PROFILE_DEV_BUILD_OVERRIDE_DEBUG=true environment variable to enable debug information generation. Caused by: process didn't exit successfully: `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` ([EXIT_STATUS]: 101) "#]]) .run(); p.cargo("check -v") .env("RUST_BACKTRACE", "1") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [ERROR] failed to run custom build command for `foo v0.5.0 ([ROOT]/foo)` [NOTE] To improve backtraces for build dependencies, set the CARGO_PROFILE_DEV_BUILD_OVERRIDE_DEBUG=true environment variable to enable debug information generation. Caused by: process didn't exit successfully: `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` ([EXIT_STATUS]: 101) "#]]) .run(); } #[cargo_test] fn custom_build_script_failed_backtraces_message_with_debuginfo() { // This is the same test as `custom_build_script_failed_backtraces_message` above, this time // ensuring that the message dedicated to improving backtraces by requesting debuginfo is not // shown when debuginfo is already turned on. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] build = "build.rs" "#, ) .file("src/main.rs", "fn main() {}") .file("build.rs", "fn main() { std::process::exit(101); }") .build(); p.cargo("build -v") .env("RUST_BACKTRACE", "1") .env("CARGO_PROFILE_DEV_BUILD_OVERRIDE_DEBUG", "true") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name build_script_build --edition=2015 build.rs [..]--crate-type bin [..]` [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [ERROR] failed to run custom build command for `foo v0.5.0 ([ROOT]/foo)` Caused by: process didn't exit successfully: `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` ([EXIT_STATUS]: 101) "#]]) .run(); } #[cargo_test] fn custom_build_env_vars() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [features] bar_feat = ["bar/foo", "bar/other-feature"] [dependencies.bar] path = "bar" "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] build = "build.rs" [features] foo = [] other-feature = [] "#, ) .file("bar/src/lib.rs", "pub fn hello() {}"); let cargo = cargo_exe(); let cargo = cargo.to_str().unwrap(); let rustc = paths::resolve_executable("rustc".as_ref()).unwrap(); let rustc = rustc.to_str().unwrap(); let file_content = format!( r##" use std::env; use std::path::Path; fn main() {{ let _target = env::var("TARGET").unwrap(); let _ncpus = env::var("NUM_JOBS").unwrap(); let _dir = env::var("CARGO_MANIFEST_DIR").unwrap(); let opt = env::var("OPT_LEVEL").unwrap(); assert_eq!(opt, "0"); let opt = env::var("PROFILE").unwrap(); assert_eq!(opt, "debug"); let debug = env::var("DEBUG").unwrap(); assert_eq!(debug, "true"); let out = env::var("OUT_DIR").unwrap(); assert!(out.starts_with(r"{0}")); assert!(Path::new(&out).is_dir()); let _host = env::var("HOST").unwrap(); let _feat = env::var("CARGO_FEATURE_FOO").unwrap(); let feat = env::var("CARGO_CFG_FEATURE").unwrap(); assert_eq!(feat, "foo,other-feature"); let cargo = env::var("CARGO").unwrap(); if env::var_os("CHECK_CARGO_IS_RUSTC").is_some() {{ assert_eq!(cargo, r#"{rustc}"#); }} else {{ assert_eq!(cargo, r#"{cargo}"#); }} let rustc = env::var("RUSTC").unwrap(); assert_eq!(rustc, "rustc"); let rustdoc = env::var("RUSTDOC").unwrap(); assert_eq!(rustdoc, "rustdoc"); assert!(env::var("RUSTC_WRAPPER").is_err()); assert!(env::var("RUSTC_WORKSPACE_WRAPPER").is_err()); assert!(env::var("RUSTC_LINKER").is_err()); assert!(env::var("RUSTFLAGS").is_err()); let rustflags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap(); assert_eq!(rustflags, ""); }} "##, p.root() .join("target") .join("debug") .join("build") .display(), ); let p = p.file("bar/build.rs", &file_content).build(); p.cargo("build --features bar_feat").run(); p.cargo("build --features bar_feat") // we use rustc since $CARGO is only used if it points to a path that exists .env("CHECK_CARGO_IS_RUSTC", "1") .env(cargo::CARGO_ENV, rustc) .run(); } #[cargo_test] fn custom_build_env_var_rustflags() { let rustflags = "--cfg=special"; let rustflags_alt = "--cfg=notspecial"; let p = project() .file( ".cargo/config.toml", &format!( r#" [build] rustflags = ["{}"] "#, rustflags ), ) .file( "build.rs", &format!( r#" use std::env; fn main() {{ // Static assertion that exactly one of the cfg paths is always taken. assert!(env::var("RUSTFLAGS").is_err()); let x; #[cfg(special)] {{ assert_eq!(env::var("CARGO_ENCODED_RUSTFLAGS").unwrap(), "{}"); x = String::new(); }} #[cfg(notspecial)] {{ assert_eq!(env::var("CARGO_ENCODED_RUSTFLAGS").unwrap(), "{}"); x = String::new(); }} let _ = x; }} "#, rustflags, rustflags_alt, ), ) .file("src/lib.rs", "") .build(); p.cargo("check").run(); // RUSTFLAGS overrides build.rustflags, so --cfg=special shouldn't be passed p.cargo("check").env("RUSTFLAGS", rustflags_alt).run(); } #[cargo_test] fn custom_build_env_var_encoded_rustflags() { // NOTE: We use "-Clink-arg=-B nope" here rather than, say, "-A missing_docs", since for the // latter it won't matter if the whitespace accidentally gets split, as rustc will do the right // thing either way. let p = project() .file( ".cargo/config.toml", r#" [build] rustflags = ["-Clink-arg=-B nope", "--cfg=foo"] "#, ) .file( "build.rs", r#" use std::env; fn main() {{ assert_eq!(env::var("CARGO_ENCODED_RUSTFLAGS").unwrap(), "-Clink-arg=-B nope\x1f--cfg=foo"); }} "#, ) .file("src/lib.rs", "") .build(); p.cargo("check").run(); } #[cargo_test] fn custom_build_env_var_rustc_wrapper() { let wrapper = tools::echo_wrapper(); let p = project() .file( "build.rs", r#" use std::env; fn main() {{ assert_eq!( env::var("RUSTC_WRAPPER").unwrap(), env::var("CARGO_RUSTC_WRAPPER_CHECK").unwrap() ); }} "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .env("CARGO_BUILD_RUSTC_WRAPPER", &wrapper) .env("CARGO_RUSTC_WRAPPER_CHECK", &wrapper) .run(); } #[cargo_test] fn custom_build_env_var_rustc_workspace_wrapper() { let wrapper = tools::echo_wrapper(); // Workspace wrapper should be set for any crate we're operating directly on. let p = project() .file( "build.rs", r#" use std::env; fn main() {{ assert_eq!( env::var("RUSTC_WORKSPACE_WRAPPER").unwrap(), env::var("CARGO_RUSTC_WORKSPACE_WRAPPER_CHECK").unwrap() ); }} "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .env("CARGO_BUILD_RUSTC_WORKSPACE_WRAPPER", &wrapper) .env("CARGO_RUSTC_WORKSPACE_WRAPPER_CHECK", &wrapper) .run(); // But should not be set for a crate from the registry, as then it's not in a workspace. Package::new("bar", "0.1.0") .file( "Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" links = "a" "#, ) .file( "build.rs", r#" use std::env; fn main() {{ assert!(env::var("RUSTC_WORKSPACE_WRAPPER").is_err()); }} "#, ) .file("src/lib.rs", "") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "0.1" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .env("CARGO_BUILD_RUSTC_WORKSPACE_WRAPPER", &wrapper) .run(); } #[cargo_test] fn custom_build_env_var_rustc_linker() { if cross_compile_disabled() { return; } let target = cross_compile::alternate(); let p = project() .file( ".cargo/config.toml", &format!( r#" [target.{}] linker = "/path/to/linker" "#, target ), ) .file( "build.rs", r#" use std::env; fn main() { assert!(env::var("RUSTC_LINKER").unwrap().ends_with("/path/to/linker")); } "#, ) .file("src/lib.rs", "") .build(); // no crate type set => linker never called => build succeeds if and // only if build.rs succeeds, despite linker binary not existing. p.cargo("build --target").arg(&target).run(); } // Only run this test on linux, since it's difficult to construct // a case suitable for all platforms. // See:https://github.com/rust-lang/cargo/pull/12535#discussion_r1306618264 #[cargo_test] #[cfg(target_os = "linux")] fn custom_build_env_var_rustc_linker_with_target_cfg() { if cross_compile_disabled() { return; } let target = cross_compile::alternate(); let p = project() .file( ".cargo/config.toml", r#" [target.'cfg(target_pointer_width = "32")'] linker = "/path/to/linker" "#, ) .file( "build.rs", r#" use std::env; fn main() { assert!(env::var("RUSTC_LINKER").unwrap().ends_with("/path/to/linker")); } "#, ) .file("src/lib.rs", "") .build(); // no crate type set => linker never called => build succeeds if and // only if build.rs succeeds, despite linker binary not existing. p.cargo("build --target").arg(&target).run(); } #[cargo_test] fn custom_build_env_var_rustc_linker_bad_host_target() { let target = rustc_host(); let p = project() .file( ".cargo/config.toml", &format!( r#" [target.{}] linker = "/path/to/linker" "#, target ), ) .file("build.rs", "fn main() {}") .file("src/lib.rs", "") .build(); // build.rs should fail since host == target when no target is set p.cargo("build --verbose") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name build_script_build --edition=2015 build.rs [..]--crate-type bin [..]-C linker=[..]/path/to/linker [..]` [ERROR] linker `[..]/path/to/linker` not found ... "#]]) .run(); } #[cargo_test] fn custom_build_env_var_rustc_linker_host_target() { let target = rustc_host(); let p = project() .file( ".cargo/config.toml", &format!( r#" target-applies-to-host = false [target.{}] linker = "/path/to/linker" "#, target ), ) .file( "build.rs", r#" use std::env; fn main() { assert!(env::var("RUSTC_LINKER").unwrap().ends_with("/path/to/linker")); } "#, ) .file("src/lib.rs", "") .build(); // no crate type set => linker never called => build succeeds if and // only if build.rs succeeds, despite linker binary not existing. p.cargo("build -Z target-applies-to-host --target") .arg(&target) .masquerade_as_nightly_cargo(&["target-applies-to-host"]) .run(); } #[cargo_test] fn custom_build_env_var_rustc_linker_host_target_env() { let target = rustc_host(); let p = project() .file( ".cargo/config.toml", &format!( r#" [target.{}] linker = "/path/to/linker" "#, target ), ) .file( "build.rs", r#" use std::env; fn main() { assert!(env::var("RUSTC_LINKER").unwrap().ends_with("/path/to/linker")); } "#, ) .file("src/lib.rs", "") .build(); // no crate type set => linker never called => build succeeds if and // only if build.rs succeeds, despite linker binary not existing. p.cargo("build -Z target-applies-to-host --target") .env("CARGO_TARGET_APPLIES_TO_HOST", "false") .arg(&target) .masquerade_as_nightly_cargo(&["target-applies-to-host"]) .run(); } #[cargo_test] fn custom_build_invalid_host_config_feature_flag() { let target = rustc_host(); let p = project() .file( ".cargo/config.toml", &format!( r#" [target.{}] linker = "/path/to/linker" "#, target ), ) .file("build.rs", "fn main() {}") .file("src/lib.rs", "") .build(); // build.rs should fail due to -Zhost-config being set without -Ztarget-applies-to-host p.cargo("build -Z host-config --target") .arg(&target) .masquerade_as_nightly_cargo(&["host-config"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] the -Zhost-config flag requires the -Ztarget-applies-to-host flag to be set "#]]) .run(); } #[cargo_test] fn custom_build_linker_host_target_with_bad_host_config() { let target = rustc_host(); let p = project() .file( ".cargo/config.toml", &format!( r#" [host] linker = "/path/to/host/linker" [target.{}] linker = "/path/to/target/linker" "#, target ), ) .file("build.rs", "fn main() {}") .file("src/lib.rs", "") .build(); // build.rs should fail due to bad host linker being set p.cargo("build -Z target-applies-to-host -Z host-config --verbose --target") .arg(&target) .masquerade_as_nightly_cargo(&["target-applies-to-host", "host-config"]) .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name build_script_build --edition=2015 build.rs [..]--crate-type bin [..]-C linker=[..]/path/to/host/linker [..]` [ERROR] linker `[..]/path/to/host/linker` not found ... "#]]) .run(); } #[cargo_test] fn custom_build_linker_bad_host() { let target = rustc_host(); let p = project() .file( ".cargo/config.toml", &format!( r#" [host] linker = "/path/to/host/linker" [target.{}] linker = "/path/to/target/linker" "#, target ), ) .file("build.rs", "fn main() {}") .file("src/lib.rs", "") .build(); // build.rs should fail due to bad host linker being set p.cargo("build -Z target-applies-to-host -Z host-config --verbose --target") .arg(&target) .masquerade_as_nightly_cargo(&["target-applies-to-host", "host-config"]) .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name build_script_build --edition=2015 build.rs [..]--crate-type bin [..]-C linker=[..]/path/to/host/linker [..]` [ERROR] linker `[..]/path/to/host/linker` not found ... "#]]) .run(); } #[cargo_test] fn custom_build_linker_bad_host_with_arch() { let target = rustc_host(); let p = project() .file( ".cargo/config.toml", &format!( r#" [host] linker = "/path/to/host/linker" [host.{}] linker = "/path/to/host/arch/linker" [target.{}] linker = "/path/to/target/linker" "#, target, target ), ) .file("build.rs", "fn main() {}") .file("src/lib.rs", "") .build(); // build.rs should fail due to bad host linker being set p.cargo("build -Z target-applies-to-host -Z host-config --verbose --target") .arg(&target) .masquerade_as_nightly_cargo(&["target-applies-to-host", "host-config"]) .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name build_script_build --edition=2015 build.rs [..]--crate-type bin [..]-C linker=[..]/path/to/host/arch/linker [..]` [ERROR] linker `[..]/path/to/host/arch/linker` not found ... "#]]) .run(); } #[cargo_test] fn custom_build_env_var_rustc_linker_cross_arch_host() { let target = rustc_host(); let cross_target = cross_compile::alternate(); let p = project() .file( ".cargo/config.toml", &format!( r#" [host.{}] linker = "/path/to/host/arch/linker" [target.{}] linker = "/path/to/target/linker" "#, cross_target, target ), ) .file( "build.rs", r#" use std::env; fn main() { assert!(env::var("RUSTC_LINKER").unwrap().ends_with("/path/to/target/linker")); } "#, ) .file("src/lib.rs", "") .build(); // build.rs should be built fine since cross target != host target. // assertion should succeed since it's still passed the target linker p.cargo("build -Z target-applies-to-host -Z host-config --verbose --target") .arg(&target) .masquerade_as_nightly_cargo(&["target-applies-to-host", "host-config"]) .run(); } #[cargo_test] fn custom_build_linker_bad_cross_arch_host() { let target = rustc_host(); let cross_target = cross_compile::alternate(); let p = project() .file( ".cargo/config.toml", &format!( r#" [host] linker = "/path/to/host/linker" [host.{}] linker = "/path/to/host/arch/linker" [target.{}] linker = "/path/to/target/linker" "#, cross_target, target ), ) .file("build.rs", "fn main() {}") .file("src/lib.rs", "") .build(); // build.rs should fail due to bad host linker being set p.cargo("build -Z target-applies-to-host -Z host-config --verbose --target") .arg(&target) .masquerade_as_nightly_cargo(&["target-applies-to-host", "host-config"]) .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name build_script_build --edition=2015 build.rs [..]--crate-type bin [..] -C linker=[..]/path/to/host/linker [..]` [ERROR] linker `[..]/path/to/host/linker` not found ... "#]]) .run(); } #[cargo_test] fn custom_build_script_wrong_rustc_flags() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] build = "build.rs" "#, ) .file("src/main.rs", "fn main() {}") .file( "build.rs", r#"fn main() { println!("cargo::rustc-flags=-aaa -bbb"); }"#, ) .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [ERROR] Only `-l` and `-L` flags are allowed in build script of `foo v0.5.0 ([ROOT]/foo)`: `-aaa -bbb` "#]]) .run(); } #[cargo_test] fn custom_build_script_rustc_flags() { let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.foo] path = "foo" "#, ) .file("src/main.rs", "fn main() {}") .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] build = "build.rs" "#, ) .file("foo/src/lib.rs", "") .file( "foo/build.rs", r#" fn main() { let root = std::env::current_dir().unwrap(); let root = root.parent().unwrap(); println!("cargo::rustc-flags=-l nonexistinglib \ -L {R}/dummy-path1 -L {R}/dummy-path2", R=root.display()); } "#, ) .build(); p.root().join("dummy-path1").mkdir_p(); p.root().join("dummy-path2").mkdir_p(); p.cargo("build --verbose") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] foo v0.5.0 ([ROOT]/foo/foo) [RUNNING] `rustc --crate-name build_script_build --edition=2015 foo/build.rs [..]` [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc --crate-name foo --edition=2015 foo/src/lib.rs [..]-L dependency=[ROOT]/foo/target/debug/deps -L [ROOT]/foo/dummy-path1 -L [ROOT]/foo/dummy-path2 -l nonexistinglib` [COMPILING] bar v0.5.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name bar --edition=2015 src/main.rs [..]-L dependency=[ROOT]/foo/target/debug/deps --extern foo=[ROOT]/foo/target/debug/deps/libfoo-[HASH].rlib -L [ROOT]/foo/dummy-path1 -L [ROOT]/foo/dummy-path2` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn custom_build_script_rustc_flags_no_space() { let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.foo] path = "foo" "#, ) .file("src/main.rs", "fn main() {}") .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] build = "build.rs" "#, ) .file("foo/src/lib.rs", "") .file( "foo/build.rs", r#" fn main() { let root = std::env::current_dir().unwrap(); let root = root.parent().unwrap(); println!("cargo::rustc-flags=-lnonexistinglib \ -L {R}/dummy-path1 -L {R}/dummy-path2", R=root.display()); } "#, ) .build(); p.root().join("dummy-path1").mkdir_p(); p.root().join("dummy-path2").mkdir_p(); p.cargo("build --verbose").with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] foo v0.5.0 ([ROOT]/foo/foo) [RUNNING] `rustc --crate-name build_script_build --edition=2015 foo/build.rs [..]` [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc --crate-name foo --edition=2015 foo/src/lib.rs [..]-L dependency=[ROOT]/foo/target/debug/deps -L [ROOT]/foo/dummy-path1 -L [ROOT]/foo/dummy-path2 -l nonexistinglib` [COMPILING] bar v0.5.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name bar --edition=2015 src/main.rs [..]-L dependency=[ROOT]/foo/target/debug/deps --extern foo=[ROOT]/foo/target/debug/deps/libfoo-[HASH].rlib -L [ROOT]/foo/dummy-path1 -L [ROOT]/foo/dummy-path2` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn links_no_build_cmd() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] links = "a" "#, ) .file("src/lib.rs", "") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: package specifies that it links to `a` but does not have a custom build script "#]]) .run(); } #[cargo_test] fn links_duplicates() { // this tests that the links_duplicates are caught at resolver time let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] links = "a" build = "build.rs" [dependencies.a-sys] path = "a-sys" "#, ) .file("src/lib.rs", "") .file("build.rs", "") .file( "a-sys/Cargo.toml", r#" [package] name = "a-sys" version = "0.5.0" edition = "2015" authors = [] links = "a" build = "build.rs" "#, ) .file("a-sys/src/lib.rs", "") .file("a-sys/build.rs", "") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to select a version for `a-sys`. ... required by package `foo v0.5.0 ([ROOT]/foo)` versions that meet the requirements `*` are: 0.5.0 package `a-sys` links to the native library `a`, but it conflicts with a previous package which links to `a` as well: package `foo v0.5.0 ([ROOT]/foo)` Only one package in the dependency graph may specify the same links value. This helps ensure that only one copy of a native library is linked in the final binary. Try to adjust your dependencies so that only one package uses the `links = "a"` value. For more information, see https://doc.rust-lang.org/cargo/reference/resolver.html#links. failed to select a version for `a-sys` which could resolve this conflict "#]]) .run(); } #[cargo_test] fn links_duplicates_old_registry() { // Test old links validator. See `validate_links`. Package::new("bar", "0.1.0") .file( "Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" links = "a" "#, ) .file("build.rs", "fn main() {}") .file("src/lib.rs", "") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" links = "a" [dependencies] bar = "0.1" "#, ) .file("build.rs", "fn main() {}") .file("src/lib.rs", "") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.1.0 (registry `dummy-registry`) [ERROR] multiple packages link to native library `a`, but a native library can be linked only once package `bar v0.1.0` ... which satisfies dependency `bar = "^0.1"` (locked to 0.1.0) of package `foo v0.1.0 ([ROOT]/foo)` links to native library `a` package `foo v0.1.0 ([ROOT]/foo)` also links to native library `a` "#]]) .run(); } #[cargo_test] fn links_duplicates_deep_dependency() { // this tests that the links_duplicates are caught at resolver time let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] links = "a" build = "build.rs" [dependencies.a] path = "a" "#, ) .file("src/lib.rs", "") .file("build.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" [dependencies.a-sys] path = "a-sys" "#, ) .file("a/src/lib.rs", "") .file("a/build.rs", "") .file( "a/a-sys/Cargo.toml", r#" [package] name = "a-sys" version = "0.5.0" edition = "2015" authors = [] links = "a" build = "build.rs" "#, ) .file("a/a-sys/src/lib.rs", "") .file("a/a-sys/build.rs", "") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to select a version for `a-sys`. ... required by package `a v0.5.0 ([ROOT]/foo/a)` ... which satisfies path dependency `a` of package `foo v0.5.0 ([ROOT]/foo)` versions that meet the requirements `*` are: 0.5.0 package `a-sys` links to the native library `a`, but it conflicts with a previous package which links to `a` as well: package `foo v0.5.0 ([ROOT]/foo)` Only one package in the dependency graph may specify the same links value. This helps ensure that only one copy of a native library is linked in the final binary. Try to adjust your dependencies so that only one package uses the `links = "a"` value. For more information, see https://doc.rust-lang.org/cargo/reference/resolver.html#links. failed to select a version for `a-sys` which could resolve this conflict "#]]) .run(); } #[cargo_test] fn overrides_and_links() { let target = rustc_host(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" [dependencies.a] path = "a" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" use std::env; fn main() { assert_eq!(env::var("DEP_FOO_FOO").ok().expect("FOO missing"), "bar"); assert_eq!(env::var("DEP_FOO_BAR").ok().expect("BAR missing"), "baz"); } "#, ) .file( ".cargo/config.toml", &format!( r#" [target.{}.foo] rustc-flags = "-L foo -L bar" foo = "bar" bar = "baz" "#, target ), ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.5.0" edition = "2015" authors = [] links = "foo" build = "build.rs" "#, ) .file("a/src/lib.rs", "") .file("a/build.rs", "not valid rust code") .build(); p.cargo("build -v") .with_stderr_data( str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] a v0.5.0 ([ROOT]/foo/a) [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name build_script_build [..]` [RUNNING] `rustc --crate-name a [..]` [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc --crate-name foo [..] -L foo -L bar` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn unused_overrides() { let target = rustc_host(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .file( ".cargo/config.toml", &format!( r#" [target.{}.foo] rustc-flags = "-L foo -L bar" foo = "bar" bar = "baz" "#, target ), ) .build(); p.cargo("build -v").run(); } #[cargo_test] fn links_passes_env_vars() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" [dependencies.a] path = "a" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" use std::env; fn main() { assert_eq!(env::var("DEP_FOO_FOO").unwrap(), "bar"); assert_eq!(env::var("DEP_FOO_BAR").unwrap(), "baz"); } "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.5.0" edition = "2015" authors = [] links = "foo" build = "build.rs" "#, ) .file("a/src/lib.rs", "") .file( "a/build.rs", r#" use std::env; fn main() { let lib = env::var("CARGO_MANIFEST_LINKS").unwrap(); assert_eq!(lib, "foo"); println!("cargo::metadata=foo=bar"); println!("cargo::metadata=bar=baz"); } "#, ) .build(); p.cargo("build -v").run(); } #[cargo_test] fn only_rerun_build_script() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .build(); p.cargo("build -v").run(); p.root().move_into_the_past(); p.change_file("some-new-file", ""); p.root().move_into_the_past(); p.cargo("build -v") .with_stderr_data(str![[r#" [DIRTY] foo v0.5.0 ([ROOT]/foo): the precalculated components changed [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn rebuild_continues_to_pass_env_vars() { let a = project() .at("a") .file( "Cargo.toml", r#" [package] name = "a" version = "0.5.0" edition = "2015" authors = [] links = "foo" build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" use std::time::Duration; fn main() { println!("cargo::metadata=foo=bar"); println!("cargo::metadata=bar=baz"); std::thread::sleep(Duration::from_millis(500)); } "#, ) .build(); a.root().move_into_the_past(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" [dependencies.a] path = '{}' "#, a.root().display() ), ) .file("src/lib.rs", "") .file( "build.rs", r#" use std::env; fn main() { assert_eq!(env::var("DEP_FOO_FOO").unwrap(), "bar"); assert_eq!(env::var("DEP_FOO_BAR").unwrap(), "baz"); } "#, ) .build(); p.cargo("build -v").run(); p.root().move_into_the_past(); p.change_file("some-new-file", ""); p.root().move_into_the_past(); p.cargo("build -v").run(); } #[cargo_test] fn testing_and_such() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .build(); println!("build"); p.cargo("build -v").run(); p.root().move_into_the_past(); p.change_file("src/lib.rs", ""); p.root().move_into_the_past(); println!("test"); p.cargo("test -vj1") .with_stderr_data(str![[r#" [DIRTY] foo v0.5.0 ([ROOT]/foo): the precalculated components changed [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc --crate-name foo [..]` [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/debug/deps/foo-[HASH][EXE]` [DOCTEST] foo [RUNNING] `rustdoc [..]--test [..]` "#]]) .with_stdout_data(str![[r#" running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); println!("doc"); p.cargo("doc -v") .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustdoc [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); p.change_file("src/main.rs", "fn main() {}"); println!("run"); p.cargo("run") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .run(); } #[cargo_test] fn propagation_of_l_flags() { let target = rustc_host(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies.a] path = "a" "#, ) .file("src/lib.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.5.0" edition = "2015" authors = [] links = "bar" build = "build.rs" [dependencies.b] path = "../b" "#, ) .file("a/src/lib.rs", "") .file( "a/build.rs", r#"fn main() { println!("cargo::rustc-flags=-L bar"); }"#, ) .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.5.0" edition = "2015" authors = [] links = "foo" build = "build.rs" "#, ) .file("b/src/lib.rs", "") .file("b/build.rs", "bad file") .file( ".cargo/config.toml", &format!( r#" [target.{}.foo] rustc-flags = "-L foo" "#, target ), ) .build(); p.cargo("build -v -j1") .with_stderr_data(str![[r#" ... [RUNNING] `rustc --crate-name a [..] -L bar -L foo` [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] -L bar -L foo` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn propagation_of_l_flags_new() { let target = rustc_host(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies.a] path = "a" "#, ) .file("src/lib.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.5.0" edition = "2015" authors = [] links = "bar" build = "build.rs" [dependencies.b] path = "../b" "#, ) .file("a/src/lib.rs", "") .file( "a/build.rs", r#" fn main() { println!("cargo::rustc-link-search=bar"); } "#, ) .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.5.0" edition = "2015" authors = [] links = "foo" build = "build.rs" "#, ) .file("b/src/lib.rs", "") .file("b/build.rs", "bad file") .file( ".cargo/config.toml", &format!( r#" [target.{}.foo] rustc-link-search = ["foo"] "#, target ), ) .build(); p.cargo("build -v -j1") .with_stderr_data(str![[r#" ... [RUNNING] `rustc --crate-name a [..] -L bar -L foo` [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] -L bar -L foo` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn build_deps_simple() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" [build-dependencies.a] path = "a" "#, ) .file("src/lib.rs", "") .file( "build.rs", " #[allow(unused_extern_crates)] extern crate a; fn main() {} ", ) .file("a/Cargo.toml", &basic_manifest("a", "0.5.0")) .file("a/src/lib.rs", "") .build(); p.cargo("build -v").with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] a v0.5.0 ([ROOT]/foo/a) [RUNNING] `rustc --crate-name a [..]` [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name build_script_build [..] build.rs [..] --extern a=[ROOT]/foo/target/debug/deps/liba-[HASH].rlib` [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn build_deps_not_for_normal() { let target = rustc_host(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" [build-dependencies.aaaaa] path = "a" "#, ) .file( "src/lib.rs", "#[allow(unused_extern_crates)] extern crate aaaaa;", ) .file( "build.rs", " #[allow(unused_extern_crates)] extern crate aaaaa; fn main() {} ", ) .file("a/Cargo.toml", &basic_manifest("aaaaa", "0.5.0")) .file("a/src/lib.rs", "") .build(); p.cargo("build -v --target") .arg(&target) .with_status(101) .with_stderr_data( str![[r#" ... error[E0463]: can't find crate for `aaaaa` [ERROR] could not compile `foo` (lib) due to 1 previous error Caused by: process didn't exit successfully: `rustc --crate-name foo[..]` ([EXIT_STATUS]: 1) "#]] .unordered(), ) .run(); } #[cargo_test] fn build_cmd_with_a_build_cmd() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" [build-dependencies.a] path = "a" "#, ) .file("src/lib.rs", "") .file( "build.rs", " #[allow(unused_extern_crates)] extern crate a; fn main() {} ", ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" [build-dependencies.b] path = "../b" "#, ) .file("a/src/lib.rs", "") .file( "a/build.rs", "#[allow(unused_extern_crates)] extern crate b; fn main() {}", ) .file("b/Cargo.toml", &basic_manifest("b", "0.5.0")) .file("b/src/lib.rs", "") .build(); p.cargo("build -v").with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] b v0.5.0 ([ROOT]/foo/b) [RUNNING] `rustc --crate-name b [..]` [COMPILING] a v0.5.0 ([ROOT]/foo/a) [RUNNING] `rustc --crate-name build_script_build [..] a/build.rs [..] --extern b=[ROOT]/foo/target/debug/deps/libb-[HASH].rlib` [RUNNING] `[ROOT]/foo/target/debug/build/a-[HASH]/build-script-build` [RUNNING] `rustc --crate-name a [..]a/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C metadata=[..] --out-dir [ROOT]/foo/target/debug/deps -L dependency=[ROOT]/foo/target/debug/deps` [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name build_script_build --edition=2015 build.rs [..]--crate-type bin --emit=[..]link[..]-C metadata=[..] --out-dir [ROOT]/foo/target/debug/build/foo-[HASH] -L dependency=[ROOT]/foo/target/debug/deps --extern a=[ROOT]/foo/target/debug/deps/liba-[HASH].rlib` [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc --crate-name foo [..]src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C debuginfo=2 [..]-C metadata=[..] --out-dir [ROOT]/foo/target/debug/deps -L dependency=[ROOT]/foo/target/debug/deps` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn out_dir_is_preserved() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" use std::env; use std::fs::File; use std::path::Path; fn main() { let out = env::var("OUT_DIR").unwrap(); File::create(Path::new(&out).join("foo")).unwrap(); } "#, ) .build(); // Make the file p.cargo("build -v").run(); // Change to asserting that it's there p.change_file( "build.rs", r#" use std::env; use std::fs::File; use std::path::Path; fn main() { let out = env::var("OUT_DIR").unwrap(); File::open(&Path::new(&out).join("foo")).unwrap(); } "#, ); p.cargo("build -v") .with_stderr_data(str![[r#" [DIRTY] foo v0.5.0 ([ROOT]/foo): the file `build.rs` has changed ([TIME_DIFF_AFTER_LAST_BUILD]) [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name build_script_build [..]` [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Run a fresh build where file should be preserved p.cargo("build -v") .with_stderr_data(str![[r#" [FRESH] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // One last time to make sure it's still there. p.change_file("foo", ""); p.cargo("build -v") .with_stderr_data(str![[r#" [DIRTY] foo v0.5.0 ([ROOT]/foo): the precalculated components changed [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn output_separate_lines() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { println!("cargo::rustc-flags=-L foo"); println!("cargo::rustc-flags=-l static=foo"); } "#, ) .build(); p.cargo("build -v") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name build_script_build [..]` [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc --crate-name foo [..] -L foo -l static=foo` [ERROR] could not find native static library `foo`, perhaps an -L flag is missing? ... "#]]) .run(); } #[cargo_test] fn output_separate_lines_new() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { println!("cargo::rustc-link-search=foo"); println!("cargo::rustc-link-lib=static=foo"); println!("cargo::rustc-link-lib=bar"); println!("cargo::rustc-link-search=bar"); } "#, ) .build(); // The order of the arguments passed to rustc is important. p.cargo("build -v") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name build_script_build [..]` [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc --crate-name foo [..] -L foo -L bar -l static=foo -l bar` [ERROR] could not find native static library `foo`, perhaps an -L flag is missing? ... "#]]) .run(); } #[cargo_test] fn code_generation() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file( "src/main.rs", r#" include!(concat!(env!("OUT_DIR"), "/hello.rs")); fn main() { println!("{}", message()); } "#, ) .file( "build.rs", r#" use std::env; use std::fs; use std::path::PathBuf; fn main() { let dst = PathBuf::from(env::var("OUT_DIR").unwrap()); fs::write(dst.join("hello.rs"), " pub fn message() -> &'static str { \"Hello, World!\" } ") .unwrap(); } "#, ) .build(); p.cargo("run") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .with_stdout_data(str![[r#" Hello, World! "#]]) .run(); p.cargo("test").run(); } #[cargo_test] fn release_with_build_script() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() {} "#, ) .build(); p.cargo("build -v --release").run(); } #[cargo_test] fn build_script_only() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file("build.rs", r#"fn main() {}"#) .build(); p.cargo("build -v") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: no targets specified in the manifest either src/lib.rs, src/main.rs, a [lib] section, or [[bin]] section must be present "#]]) .run(); } #[cargo_test] fn shared_dep_with_a_build_script() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" [dependencies.a] path = "a" [build-dependencies.b] path = "b" "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file("a/build.rs", "fn main() {}") .file("a/src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.5.0" edition = "2015" authors = [] [dependencies.a] path = "../a" "#, ) .file("b/src/lib.rs", "") .build(); p.cargo("build -v").run(); } #[cargo_test] fn test_a_lib_with_a_build_command() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file( "src/lib.rs", r#" include!(concat!(env!("OUT_DIR"), "/foo.rs")); /// ``` /// foo::bar(); /// ``` pub fn bar() { assert_eq!(foo(), 1); } "#, ) .file( "build.rs", r#" use std::env; use std::fs; use std::path::PathBuf; fn main() { let out = PathBuf::from(env::var("OUT_DIR").unwrap()); fs::write(out.join("foo.rs"), "fn foo() -> i32 { 1 }").unwrap(); } "#, ) .build(); p.cargo("test").run(); } #[cargo_test] fn test_dev_dep_build_script() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dev-dependencies.a] path = "a" "#, ) .file("src/lib.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file("a/build.rs", "fn main() {}") .file("a/src/lib.rs", "") .build(); p.cargo("test").run(); } #[cargo_test] fn build_script_with_dynamic_native_dependency() { let build = project() .at("builder") .file( "Cargo.toml", r#" [package] name = "builder" version = "0.0.1" edition = "2015" authors = [] [lib] name = "builder" crate-type = ["dylib"] "#, ) .file("src/lib.rs", r#"#[no_mangle] pub extern "C" fn foo() {}"#) .build(); let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" [build-dependencies.bar] path = "bar" "#, ) .file("build.rs", "extern crate bar; fn main() { bar::bar() }") .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" "#, ) .file( "bar/build.rs", r#" use std::env; use std::fs; use std::path::PathBuf; fn main() { let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); let root = PathBuf::from(env::var("BUILDER_ROOT").unwrap()); let file = format!("{}builder{}", env::consts::DLL_PREFIX, env::consts::DLL_SUFFIX); let src = root.join(&file); let dst = out_dir.join(&file); fs::copy(src, dst).unwrap(); if cfg!(target_env = "msvc") { fs::copy(root.join("builder.dll.lib"), out_dir.join("builder.dll.lib")).unwrap(); } println!("cargo::rustc-link-search=native={}", out_dir.display()); } "#, ) .file( "bar/src/lib.rs", r#" pub fn bar() { #[cfg_attr(not(target_env = "msvc"), link(name = "builder"))] #[cfg_attr(target_env = "msvc", link(name = "builder.dll"))] extern "C" { fn foo(); } unsafe { foo() } } "#, ) .build(); build .cargo("build -v") .env("CARGO_LOG", "cargo::ops::cargo_rustc") .run(); let root = build.root().join("target").join("debug"); foo.cargo("build -v") .env("BUILDER_ROOT", root) .env("CARGO_LOG", "cargo::ops::cargo_rustc") .run(); } #[cargo_test] fn profile_and_opt_level_set_correctly() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" use std::env; fn main() { assert_eq!(env::var("OPT_LEVEL").unwrap(), "3"); assert_eq!(env::var("PROFILE").unwrap(), "release"); assert_eq!(env::var("DEBUG").unwrap(), "false"); } "#, ) .build(); p.cargo("bench").run(); } #[cargo_test] fn profile_debug_0() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [profile.dev] debug = 0 "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" use std::env; fn main() { assert_eq!(env::var("OPT_LEVEL").unwrap(), "0"); assert_eq!(env::var("PROFILE").unwrap(), "debug"); assert_eq!(env::var("DEBUG").unwrap(), "false"); } "#, ) .build(); p.cargo("build").run(); } #[cargo_test] fn build_script_with_lto() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" [profile.dev] lto = true "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .build(); p.cargo("build").run(); } #[cargo_test] fn test_duplicate_deps() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] build = "build.rs" [dependencies.bar] path = "bar" [build-dependencies.bar] path = "bar" "#, ) .file( "src/main.rs", r#" extern crate bar; fn main() { bar::do_nothing() } "#, ) .file( "build.rs", r#" extern crate bar; fn main() { bar::do_nothing() } "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn do_nothing() {}") .build(); p.cargo("build").run(); } #[cargo_test] fn cfg_feedback() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" "#, ) .file("src/main.rs", "#[cfg(foo)] fn main() {}") .file( "build.rs", r#"fn main() { println!("cargo::rustc-cfg=foo"); }"#, ) .build(); p.cargo("build -v").run(); } #[cargo_test] fn cfg_override() { let target = rustc_host(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] links = "a" build = "build.rs" "#, ) .file("src/main.rs", "#[cfg(foo)] fn main() {}") .file("build.rs", "") .file( ".cargo/config.toml", &format!( r#" [target.{}.a] rustc-cfg = ["foo"] "#, target ), ) .build(); p.cargo("build -v").run(); } #[cargo_test] fn cfg_test() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" "#, ) .file( "build.rs", r#"fn main() { println!("cargo::rustc-cfg=foo"); println!("cargo::rustc-check-cfg=cfg(foo)"); }"#, ) .file( "src/lib.rs", r#" /// /// ``` /// extern crate foo; /// /// fn main() { /// foo::foo() /// } /// ``` /// #[cfg(foo)] pub fn foo() {} #[cfg(foo)] #[test] fn test_foo() { foo() } "#, ) .file("tests/test.rs", "#[cfg(foo)] #[test] fn test_bar() {}") .build(); p.cargo("test -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name build_script_build [..]` [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc --crate-name foo [..] --cfg foo[..]` [RUNNING] `rustc --crate-name foo [..] --cfg foo[..]` [RUNNING] `rustc --crate-name test [..] --cfg foo[..]` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/debug/deps/foo-[HASH][EXE]` [RUNNING] `[ROOT]/foo/target/debug/deps/test-[HASH][EXE]` [DOCTEST] foo [RUNNING] `rustdoc [..]--cfg foo[..]` "#]]) .with_stdout_data(str![[r#" running 1 test test test_foo ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test test_bar ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test src/lib.rs - foo (line 3) ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn cfg_doc() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" [dependencies.bar] path = "bar" "#, ) .file( "build.rs", r#"fn main() { println!("cargo::rustc-cfg=foo"); }"#, ) .file("src/lib.rs", "#[cfg(foo)] pub fn foo() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" "#, ) .file( "bar/build.rs", r#"fn main() { println!("cargo::rustc-cfg=bar"); }"#, ) .file("bar/src/lib.rs", "#[cfg(bar)] pub fn bar() {}") .build(); p.cargo("doc").run(); assert!(p.root().join("target/doc").is_dir()); assert!(p.root().join("target/doc/foo/fn.foo.html").is_file()); assert!(p.root().join("target/doc/bar/fn.bar.html").is_file()); } #[cargo_test] fn cfg_override_test() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" links = "a" [lints.rust] unexpected_cfgs = "allow" # bc of override, stable/nightly, tests "#, ) .file("build.rs", "") .file( ".cargo/config.toml", &format!( r#" [target.{}.a] rustc-cfg = ["foo"] "#, rustc_host() ), ) .file( "src/lib.rs", r#" /// /// ``` /// extern crate foo; /// /// fn main() { /// foo::foo() /// } /// ``` /// #[cfg(foo)] pub fn foo() {} #[cfg(foo)] #[test] fn test_foo() { foo() } "#, ) .file("tests/test.rs", "#[cfg(foo)] #[test] fn test_bar() {}") .build(); p.cargo("test -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo[..]` [RUNNING] `rustc --crate-name foo[..]` [RUNNING] `rustc --crate-name test[..]` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/debug/deps/foo-[HASH][EXE]` [RUNNING] `[ROOT]/foo/target/debug/deps/test-[HASH][EXE]` [DOCTEST] foo [RUNNING] `rustdoc [..] --cfg foo[..]` "#]]) .with_stdout_data(str![[r#" running 1 test test test_foo ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test test_bar ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test src/lib.rs - foo (line 3) ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn cfg_override_doc() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" links = "a" [dependencies.bar] path = "bar" "#, ) .file( ".cargo/config.toml", &format!( r#" [target.{target}.a] rustc-cfg = ["foo"] [target.{target}.b] rustc-cfg = ["bar"] "#, target = rustc_host() ), ) .file("build.rs", "") .file("src/lib.rs", "#[cfg(foo)] pub fn foo() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" links = "b" "#, ) .file("bar/build.rs", "") .file("bar/src/lib.rs", "#[cfg(bar)] pub fn bar() {}") .build(); p.cargo("doc").run(); assert!(p.root().join("target/doc").is_dir()); assert!(p.root().join("target/doc/foo/fn.foo.html").is_file()); assert!(p.root().join("target/doc/bar/fn.bar.html").is_file()); } #[cargo_test] fn env_build() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" "#, ) .file( "src/main.rs", r#" const FOO: &'static str = env!("FOO"); fn main() { println!("{}", FOO); } "#, ) .file( "build.rs", r#"fn main() { println!("cargo::rustc-env=FOO=foo"); }"#, ) .build(); p.cargo("build -v").run(); p.cargo("run -v") .with_stdout_data(str![[r#" foo "#]]) .run(); } #[cargo_test] fn env_test() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" "#, ) .file( "build.rs", r#"fn main() { println!("cargo::rustc-env=FOO=foo"); }"#, ) .file( "src/lib.rs", r#"pub const FOO: &'static str = env!("FOO"); "#, ) .file( "tests/test.rs", r#" extern crate foo; #[test] fn test_foo() { assert_eq!("foo", foo::FOO); } "#, ) .build(); p.cargo("test -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name build_script_build[..]` [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc --crate-name foo[..]` [RUNNING] `rustc --crate-name foo[..]` [RUNNING] `rustc --crate-name test[..]` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/debug/deps/foo-[HASH][EXE]` [RUNNING] `[ROOT]/foo/target/debug/deps/test-[HASH][EXE]` [DOCTEST] foo [RUNNING] `rustdoc --edition=2015 --crate-type lib --color auto --crate-name foo[..]` "#]]) .with_stdout_data(str![[r#" running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test test_foo ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn env_doc() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" "#, ) .file( "src/main.rs", r#" const FOO: &'static str = env!("FOO"); fn main() {} "#, ) .file( "build.rs", r#"fn main() { println!("cargo::rustc-env=FOO=foo"); }"#, ) .build(); p.cargo("doc -v").run(); } #[cargo_test] fn flags_go_into_tests() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies] b = { path = "b" } "#, ) .file("src/lib.rs", "") .file("tests/foo.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.5.0" edition = "2015" authors = [] [dependencies] a = { path = "../a" } "#, ) .file("b/src/lib.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file("a/src/lib.rs", "") .file( "a/build.rs", r#" fn main() { let path = std::env::current_dir().unwrap().parent().unwrap().join("link-dir"); println!("cargo::rustc-link-search={}", path.display()); } "#, ) .build(); p.root().join("link-dir").mkdir_p(); p.cargo("test -v --test=foo") .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] a v0.5.0 ([ROOT]/foo/a) [RUNNING] `rustc [..] a/build.rs [..]` [RUNNING] `[ROOT]/foo/target/debug/build/a-[HASH]/build-script-build` [RUNNING] `rustc [..] a/src/lib.rs [..] -L [ROOT]/foo/link-dir` [COMPILING] b v0.5.0 ([ROOT]/foo/b) [RUNNING] `rustc [..] b/src/lib.rs [..] -L [ROOT]/foo/link-dir` [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc [..] src/lib.rs [..] -L [ROOT]/foo/link-dir` [RUNNING] `rustc [..] tests/foo.rs [..] -L [ROOT]/foo/link-dir` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/debug/deps/foo-[HASH][EXE]` "#]]) .with_stdout_data(str![[r#" running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); p.cargo("test -v -pb --lib") .with_stderr_data(str![[r#" [FRESH] a v0.5.0 ([ROOT]/foo/a) [COMPILING] b v0.5.0 ([ROOT]/foo/b) [RUNNING] `rustc --crate-name b [..] -L [ROOT]/foo/link-dir` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/debug/deps/b-[HASH][EXE]` "#]]) .with_stdout_data(str![[r#" running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn diamond_passes_args_only_once() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies] a = { path = "a" } b = { path = "b" } "#, ) .file("src/lib.rs", "") .file("tests/foo.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.5.0" edition = "2015" authors = [] [dependencies] b = { path = "../b" } c = { path = "../c" } "#, ) .file("a/src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.5.0" edition = "2015" authors = [] [dependencies] c = { path = "../c" } "#, ) .file("b/src/lib.rs", "") .file( "c/Cargo.toml", r#" [package] name = "c" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file( "c/build.rs", r#" fn main() { println!("cargo::rustc-link-search=native=test"); } "#, ) .file("c/src/lib.rs", "") .build(); p.cargo("build -v") .with_stderr_data(str![[r#" [LOCKING] 3 packages to latest compatible versions [COMPILING] c v0.5.0 ([ROOT]/foo/c) [RUNNING] `rustc --crate-name build_script_build [..]` [RUNNING] `[ROOT]/foo/target/debug/build/c-[HASH]/build-script-build` [RUNNING] `rustc --crate-name c [..] -L native=test` [COMPILING] b v0.5.0 ([ROOT]/foo/b) [RUNNING] `rustc --crate-name b [..] -L native=test` [COMPILING] a v0.5.0 ([ROOT]/foo/a) [RUNNING] `rustc --crate-name a [..] -L native=test` [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] -L native=test` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn adding_an_override_invalidates() { let target = rustc_host(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] links = "foo" build = "build.rs" "#, ) .file("src/lib.rs", "") .file(".cargo/config.toml", "") .file( "build.rs", r#" fn main() { println!("cargo::rustc-link-search=native=foo"); } "#, ) .build(); p.cargo("build -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name build_script_build [..]` [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc --crate-name foo [..] -L native=foo` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.change_file( ".cargo/config.toml", &format!( " [target.{}.foo] rustc-link-search = [\"native=bar\"] ", target ), ); p.cargo("build -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] -L native=bar` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn changing_an_override_invalidates() { let target = rustc_host(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] links = "foo" build = "build.rs" "#, ) .file("src/lib.rs", "") .file( ".cargo/config.toml", &format!( " [target.{}.foo] rustc-link-search = [\"native=foo\"] ", target ), ) .file("build.rs", "") .build(); p.cargo("build -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] -L native=foo` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.change_file( ".cargo/config.toml", &format!( " [target.{}.foo] rustc-link-search = [\"native=bar\"] ", target ), ); p.cargo("build -v") .with_stderr_data(str![[r#" [DIRTY] foo v0.5.0 ([ROOT]/foo): the precalculated components changed [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] -L native=bar` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn fresh_builds_possible_with_link_libs() { // The bug is non-deterministic. Sometimes you can get a fresh build let target = rustc_host(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] links = "nativefoo" build = "build.rs" "#, ) .file("src/lib.rs", "") .file( ".cargo/config.toml", &format!( " [target.{}.nativefoo] rustc-link-lib = [\"a\"] rustc-link-search = [\"./b\"] rustc-flags = \"-l z -L ./\" ", target ), ) .file("build.rs", "") .build(); p.cargo("build -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc [..] src/lib.rs [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -v") .with_stderr_data(str![[r#" [FRESH] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn fresh_builds_possible_with_multiple_metadata_overrides() { // The bug is non-deterministic. Sometimes you can get a fresh build let target = rustc_host(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] links = "foo" build = "build.rs" "#, ) .file("src/lib.rs", "") .file( ".cargo/config.toml", &format!( " [target.{}.foo] a = \"\" b = \"\" c = \"\" d = \"\" e = \"\" ", target ), ) .file("build.rs", "") .build(); p.cargo("build -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc [..] src/lib.rs [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -v") .with_stderr_data(str![[r#" [FRESH] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn generate_good_d_files() { // this is here to stop regression on an issue where build.rs rerun-if-changed paths aren't // made absolute properly, which in turn interacts poorly with the dep-info-basedir setting, // and the dep-info files have other-crate-relative paths spat out in them let p = project() .file( "awoo/Cargo.toml", r#" [package] name = "awoo" version = "0.5.0" edition = "2015" build = "build.rs" "#, ) .file("awoo/src/lib.rs", "") .file( "awoo/build.rs", r#" fn main() { println!("cargo::rerun-if-changed=build.rs"); println!("cargo::rerun-if-changed=barkbarkbark"); } "#, ) .file( "Cargo.toml", r#" [package] name = "meow" version = "0.5.0" edition = "2015" [dependencies] awoo = { path = "awoo" } "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build -v").run(); let dot_d_path = p.bin("meow").with_extension("d"); println!("*meow at* {:?}", dot_d_path); let dot_d = fs::read_to_string(&dot_d_path).unwrap(); println!("*.d file content*: {}", &dot_d); assert_e2e().eq( &dot_d, str![[r#" [ROOT]/foo/target/debug/meow[EXE]: [ROOT]/foo/awoo/barkbarkbark [ROOT]/foo/awoo/build.rs [ROOT]/foo/awoo/src/lib.rs [ROOT]/foo/src/main.rs "#]], ); // paths relative to dependency roots should not be allowed assert!( !dot_d .split_whitespace() .any(|v| v == "barkbarkbark" || v == "build.rs") ); p.change_file( ".cargo/config.toml", r#" [build] dep-info-basedir="." "#, ); p.cargo("build -v").run(); let dot_d = fs::read_to_string(&dot_d_path).unwrap(); println!("*.d file content with dep-info-basedir*: {}", &dot_d); assert_e2e().eq( &dot_d, str![[r#" target/debug/meow[EXE]: awoo/barkbarkbark awoo/build.rs awoo/src/lib.rs src/main.rs "#]], ); // paths relative to dependency roots should not be allowed assert!( !dot_d .split_whitespace() .any(|v| v == "barkbarkbark" || v == "build.rs") ); } #[cargo_test] fn generate_good_d_files_for_external_tools() { // This tests having a relative paths going out of the // project root in config's dep-info-basedir let p = project_in("rust_things") .file( "awoo/Cargo.toml", r#" [package] name = "awoo" version = "0.5.0" edition = "2015" build = "build.rs" "#, ) .file("awoo/src/lib.rs", "") .file( "awoo/build.rs", r#" fn main() { println!("cargo::rerun-if-changed=build.rs"); println!("cargo::rerun-if-changed=barkbarkbark"); } "#, ) .file( "Cargo.toml", r#" [package] name = "meow" version = "0.5.0" edition = "2015" [dependencies] awoo = { path = "awoo" } "#, ) .file("src/main.rs", "fn main() {}") .file( ".cargo/config.toml", r#" [build] dep-info-basedir="../.." "#, ) .build(); p.cargo("build -v").run(); let dot_d_path = p.bin("meow").with_extension("d"); let dot_d = fs::read_to_string(&dot_d_path).unwrap(); println!("*.d file content with dep-info-basedir*: {}", &dot_d); assert_e2e().eq(&dot_d, str![[r#" rust_things/foo/target/debug/meow[EXE]: rust_things/foo/awoo/barkbarkbark rust_things/foo/awoo/build.rs rust_things/foo/awoo/src/lib.rs rust_things/foo/src/main.rs "#]]); } #[cargo_test] fn rebuild_only_on_explicit_paths() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { println!("cargo::rerun-if-changed=foo"); println!("cargo::rerun-if-changed=bar"); } "#, ) .build(); p.cargo("build -v").run(); // files don't exist, so should always rerun if they don't exist println!("run without"); p.cargo("build -v") .with_stderr_data(str![[r#" [DIRTY] foo v0.5.0 ([ROOT]/foo): the file `foo` is missing [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc [..] src/lib.rs [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); sleep_ms(1000); p.change_file("foo", ""); p.change_file("bar", ""); sleep_ms(1000); // make sure the to-be-created outfile has a timestamp distinct from the infiles // now the exist, so run once, catch the mtime, then shouldn't run again println!("run with"); p.cargo("build -v") .with_stderr_data(str![[r#" [DIRTY] foo v0.5.0 ([ROOT]/foo): the file `foo` has changed ([TIME_DIFF_AFTER_LAST_BUILD]) [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc [..] src/lib.rs [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); println!("run with2"); p.cargo("build -v") .with_stderr_data(str![[r#" [FRESH] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); sleep_ms(1000); // random other files do not affect freshness println!("run baz"); p.change_file("baz", "// modified"); p.cargo("build -v") .with_stderr_data(str![[r#" [FRESH] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // but changing dependent files does println!("run foo change"); p.change_file("foo", "// modified"); p.cargo("build -v") .with_stderr_data(str![[r#" [DIRTY] foo v0.5.0 ([ROOT]/foo): the file `foo` has changed ([TIME_DIFF_AFTER_LAST_BUILD]) [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc [..] src/lib.rs [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // .. as does deleting a file println!("run bar delete"); fs::remove_file(p.root().join("bar")).unwrap(); p.cargo("build -v") .with_stderr_data(str![[r#" [DIRTY] foo v0.5.0 ([ROOT]/foo): the file `bar` is missing [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc [..] src/lib.rs [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn doctest_receives_build_link_args() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies.a] path = "a" "#, ) .file("src/lib.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.5.0" edition = "2015" authors = [] links = "bar" build = "build.rs" "#, ) .file("a/src/lib.rs", "") .file( "a/build.rs", r#" fn main() { println!("cargo::rustc-link-search=native=bar"); } "#, ) .build(); p.cargo("test -v") .with_stderr_data(str![[r#" ... [RUNNING] `rustdoc [..]--crate-name foo --test [..]-L native=bar[..]` "#]]) .run(); } #[cargo_test] fn please_respect_the_dag() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" [dependencies] a = { path = 'a' } "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { println!("cargo::rustc-link-search=native=foo"); } "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.5.0" edition = "2015" authors = [] links = "bar" build = "build.rs" "#, ) .file("a/src/lib.rs", "") .file( "a/build.rs", r#" fn main() { println!("cargo::rustc-link-search=native=bar"); } "#, ) .build(); p.cargo("build -v") .with_stderr_data(str![[r#" ... [RUNNING] `rustc --crate-name foo [..] -L native=foo -L native=bar` ... "#]]) .run(); } #[cargo_test] fn non_utf8_output() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file( "build.rs", r#" use std::io::prelude::*; fn main() { let mut out = std::io::stdout(); // print something that's not utf8 out.write_all(b"\xff\xff\n").unwrap(); // now print some cargo metadata that's utf8 println!("cargo::rustc-cfg=foo"); // now print more non-utf8 out.write_all(b"\xff\xff\n").unwrap(); } "#, ) .file("src/main.rs", "#[cfg(foo)] fn main() {}") .build(); p.cargo("build -v").run(); } #[cargo_test] fn custom_target_dir() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies] a = { path = "a" } "#, ) .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [build] target-dir = 'test' "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file("a/build.rs", "fn main() {}") .file("a/src/lib.rs", "") .build(); p.cargo("build -v").run(); } #[cargo_test] fn panic_abort_with_build_scripts() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [profile.release] panic = 'abort' [dependencies] a = { path = "a" } "#, ) .file( "src/lib.rs", "#[allow(unused_extern_crates)] extern crate a;", ) .file("build.rs", "fn main() {}") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" [build-dependencies] b = { path = "../b" } "#, ) .file("a/src/lib.rs", "") .file( "a/build.rs", "#[allow(unused_extern_crates)] extern crate b; fn main() {}", ) .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.5.0" edition = "2015" authors = [] "#, ) .file("b/src/lib.rs", "") .build(); p.cargo("build -v --release").run(); p.root().join("target").rm_rf(); p.cargo("test --release -v") .with_stderr_does_not_contain("[..]panic=abort[..]") .run(); } #[cargo_test] fn warnings_emitted() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { println!("cargo::warning=foo"); println!("cargo::warning=bar"); } "#, ) .build(); p.cargo("build") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [WARNING] foo@0.5.0: foo [WARNING] foo@0.5.0: bar [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn errors_and_warnings_emitted_and_build_failed() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { println!("cargo::warning=foo"); println!("cargo::warning=bar"); println!("cargo::error=foo err"); println!("cargo::error=bar err"); } "#, ) .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [WARNING] foo@0.5.0: foo [WARNING] foo@0.5.0: bar [ERROR] foo@0.5.0: foo err [ERROR] foo@0.5.0: bar err [ERROR] build script logged errors "#]]) .run(); } #[cargo_test] fn warnings_emitted_from_path_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies] a = { path = "a" } "#, ) .file("src/lib.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file("a/src/lib.rs", "") .file( "a/build.rs", r#" fn main() { println!("cargo::warning=foo"); println!("cargo::warning=bar"); } "#, ) .build(); p.cargo("build") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] a v0.5.0 ([ROOT]/foo/a) [WARNING] a@0.5.0: foo [WARNING] a@0.5.0: bar [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn warnings_emitted_when_build_script_panics() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { println!("cargo::warning=foo"); println!("cargo::warning=bar"); panic!("our crate panicked"); } "#, ) .build(); p.cargo("build") .with_status(101) .with_stdout_data("") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [WARNING] foo@0.5.0: foo [WARNING] foo@0.5.0: bar [ERROR] failed to run custom build command for `foo v0.5.0 ([ROOT]/foo)` Caused by: process didn't exit successfully: `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` ([EXIT_STATUS]: 101) --- stdout cargo::warning=foo cargo::warning=bar --- stderr ... [..]our crate panicked[..] ... "#]]) .run(); } #[cargo_test] fn warnings_emitted_when_dependency_panics() { Package::new("published", "0.1.0") .file( "build.rs", r#" fn main() { println!("cargo::warning=foo"); println!("cargo::warning=bar"); panic!("dependency panicked"); } "#, ) .file( "Cargo.toml", r#" [package] name = "published" version = "0.1.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies] published = "*" "#, ) .file("src/lib.rs", "") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] published v0.1.0 (registry `dummy-registry`) [COMPILING] published v0.1.0 [WARNING] published@0.1.0: foo [WARNING] published@0.1.0: bar [ERROR] failed to run custom build command for `published v0.1.0` Caused by: process didn't exit successfully: `[ROOT]/foo/target/debug/build/published-[HASH]/build-script-build` ([EXIT_STATUS]: 101) --- stdout cargo::warning=foo cargo::warning=bar --- stderr ... [..]dependency panicked[..] ... "#]]) .run(); } #[cargo_test] fn log_messages_emitted_when_dependency_logs_errors() { Package::new("published", "0.1.0") .file( "build.rs", r#" fn main() { println!("cargo::warning=foo"); println!("cargo::warning=bar"); println!("cargo::error=foo err"); println!("cargo::error=bar err"); } "#, ) .file( "Cargo.toml", r#" [package] name = "published" version = "0.1.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies] published = "*" "#, ) .file("src/lib.rs", "") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] published v0.1.0 (registry `dummy-registry`) [COMPILING] published v0.1.0 [WARNING] published@0.1.0: foo [WARNING] published@0.1.0: bar [ERROR] published@0.1.0: foo err [ERROR] published@0.1.0: bar err [ERROR] build script logged errors "#]]) .run(); } #[cargo_test] fn warnings_hidden_for_upstream() { Package::new("bar", "0.1.0") .file( "build.rs", r#" fn main() { println!("cargo::warning=foo"); println!("cargo::warning=bar"); } "#, ) .file( "Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies] bar = "*" "#, ) .file("src/lib.rs", "") .build(); p.cargo("build -v") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.1.0 (registry `dummy-registry`) [COMPILING] bar v0.1.0 [RUNNING] `rustc --crate-name build_script_build [..]` [RUNNING] `[ROOT]/foo/target/debug/build/bar-[HASH]/build-script-build` [RUNNING] `rustc --crate-name bar [..]` [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn warnings_printed_on_vv() { Package::new("bar", "0.1.0") .file( "build.rs", r#" fn main() { println!("cargo::warning=foo"); println!("cargo::warning=bar"); } "#, ) .file( "Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies] bar = "*" "#, ) .file("src/lib.rs", "") .build(); p.cargo("build -vv") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.1.0 (registry `dummy-registry`) [COMPILING] bar v0.1.0 [RUNNING] `[..] rustc --crate-name build_script_build [..]` [RUNNING] `[..] [ROOT]/foo/target/debug/build/bar-[HASH]/build-script-build` [WARNING] bar@0.1.0: foo [WARNING] bar@0.1.0: bar [RUNNING] `[..] rustc --crate-name bar [..]` [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `[..] rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn output_shows_on_vv() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" use std::io::prelude::*; fn main() { std::io::stderr().write_all(b"stderr\n").unwrap(); std::io::stdout().write_all(b"stdout\n").unwrap(); } "#, ) .build(); p.cargo("build -vv") .with_stdout_data(str![[r#" [foo 0.5.0] stdout "#]]) .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `[..] rustc --crate-name build_script_build [..]` [RUNNING] `[..] [ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [foo 0.5.0] stderr [RUNNING] `[..] rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn links_with_dots() { let target = rustc_host(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" links = "a.b" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { println!("cargo::rustc-link-search=bar") } "#, ) .file( ".cargo/config.toml", &format!( r#" [target.{}.'a.b'] rustc-link-search = ["foo"] "#, target ), ) .build(); p.cargo("build -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] -L foo` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn rustc_and_rustdoc_set_correctly() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" use std::env; fn main() { assert_eq!(env::var("RUSTC").unwrap(), "rustc"); assert_eq!(env::var("RUSTDOC").unwrap(), "rustdoc"); } "#, ) .build(); p.cargo("bench").run(); } #[cargo_test] fn cfg_env_vars_available() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" use std::env; fn main() { let fam = env::var("CARGO_CFG_TARGET_FAMILY").unwrap(); if cfg!(unix) { assert_eq!(fam, "unix"); } else { assert_eq!(fam, "windows"); } } "#, ) .build(); p.cargo("bench").run(); } #[cargo_test] fn switch_features_rerun() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" [features] foo = [] "#, ) .file( "src/main.rs", r#" fn main() { println!(include_str!(concat!(env!("OUT_DIR"), "/output"))); } "#, ) .file( "build.rs", r#" use std::env; use std::fs; use std::path::Path; fn main() { let out_dir = env::var_os("OUT_DIR").unwrap(); let output = Path::new(&out_dir).join("output"); if env::var_os("CARGO_FEATURE_FOO").is_some() { fs::write(output, "foo").unwrap(); } else { fs::write(output, "bar").unwrap(); } } "#, ) .build(); p.cargo("build -v --features=foo").run(); p.rename_run("foo", "with_foo") .with_stdout_data(str![[r#" foo "#]]) .run(); p.cargo("build -v").run(); p.rename_run("foo", "without_foo") .with_stdout_data(str![[r#" bar "#]]) .run(); p.cargo("build -v --features=foo").run(); p.rename_run("foo", "with_foo2") .with_stdout_data(str![[r#" foo "#]]) .run(); } #[cargo_test] fn assume_build_script_when_build_rs_present() { let p = project() .file( "src/main.rs", r#" fn main() { if ! cfg!(foo) { panic!("the build script was not run"); } } "#, ) .file( "build.rs", r#" fn main() { println!("cargo::rustc-cfg=foo"); } "#, ) .build(); p.cargo("run -v").run(); } #[cargo_test] fn if_build_set_to_false_dont_treat_build_rs_as_build_script() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] build = false "#, ) .file( "src/main.rs", r#" fn main() { if cfg!(foo) { panic!("the build script was run"); } } "#, ) .file( "build.rs", r#" fn main() { println!("cargo::rustc-cfg=foo"); } "#, ) .build(); p.cargo("run -v").run(); } #[cargo_test] fn deterministic_rustc_dependency_flags() { // This bug is non-deterministic hence the large number of dependencies // in the hopes it will have a much higher chance of triggering it. Package::new("dep1", "0.1.0") .file( "Cargo.toml", r#" [package] name = "dep1" version = "0.1.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file( "build.rs", r#" fn main() { println!("cargo::rustc-flags=-L native=test1"); } "#, ) .file("src/lib.rs", "") .publish(); Package::new("dep2", "0.1.0") .file( "Cargo.toml", r#" [package] name = "dep2" version = "0.1.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file( "build.rs", r#" fn main() { println!("cargo::rustc-flags=-L native=test2"); } "#, ) .file("src/lib.rs", "") .publish(); Package::new("dep3", "0.1.0") .file( "Cargo.toml", r#" [package] name = "dep3" version = "0.1.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file( "build.rs", r#" fn main() { println!("cargo::rustc-flags=-L native=test3"); } "#, ) .file("src/lib.rs", "") .publish(); Package::new("dep4", "0.1.0") .file( "Cargo.toml", r#" [package] name = "dep4" version = "0.1.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file( "build.rs", r#" fn main() { println!("cargo::rustc-flags=-L native=test4"); } "#, ) .file("src/lib.rs", "") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [dependencies] dep1 = "*" dep2 = "*" dep3 = "*" dep4 = "*" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build -v").with_stderr_data(str![[r#" ... [RUNNING] `rustc --crate-name foo [..] -L native=test1 -L native=test2 -L native=test3 -L native=test4` ... "#]]).run(); } #[cargo_test] fn links_duplicates_with_cycle() { // this tests that the links_duplicates are caught at resolver time let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] links = "a" build = "build.rs" [dependencies.a] path = "a" [dev-dependencies] b = { path = "b" } "#, ) .file("src/lib.rs", "") .file("build.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.5.0" edition = "2015" authors = [] links = "a" build = "build.rs" "#, ) .file("a/src/lib.rs", "") .file("a/build.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.5.0" edition = "2015" authors = [] [dependencies] foo = { path = ".." } "#, ) .file("b/src/lib.rs", "") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to select a version for `a`. ... required by package `foo v0.5.0 ([ROOT]/foo)` versions that meet the requirements `*` are: 0.5.0 package `a` links to the native library `a`, but it conflicts with a previous package which links to `a` as well: package `foo v0.5.0 ([ROOT]/foo)` Only one package in the dependency graph may specify the same links value. This helps ensure that only one copy of a native library is linked in the final binary. Try to adjust your dependencies so that only one package uses the `links = "a"` value. For more information, see https://doc.rust-lang.org/cargo/reference/resolver.html#links. failed to select a version for `a` which could resolve this conflict "#]]) .run(); } #[cargo_test] fn rename_with_link_search_path() { _rename_with_link_search_path( false, str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/bar[EXE]` "#]], ); } #[cargo_test] #[cfg_attr( target_os = "macos", ignore = "don't have a cdylib cross target on macos" )] fn rename_with_link_search_path_cross() { if cross_compile_disabled() { return; } _rename_with_link_search_path( true, str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/[ALT_TARGET]/debug/bar[EXE]` "#]], ); } fn _rename_with_link_search_path(cross: bool, expected: impl IntoData) { let target_arg = if cross { format!(" --target={}", cross_compile::alternate()) } else { "".to_string() }; let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [lib] crate-type = ["cdylib"] "#, ) .file( "src/lib.rs", r#"#[no_mangle] pub extern "C" fn cargo_test_foo() {}"#, ); let p = p.build(); p.cargo(&format!("build{}", target_arg)).run(); let p2 = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) .file( "build.rs", r#" use std::env; use std::fs; use std::path::PathBuf; fn main() { // Move the `libfoo.so` from the root of our project into the // build directory. This way Cargo should automatically manage // `LD_LIBRARY_PATH` and such. let root = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); let file = format!("{}foo{}", env::consts::DLL_PREFIX, env::consts::DLL_SUFFIX); let src = root.join(&file); let dst_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); let dst = dst_dir.join(&file); fs::copy(&src, &dst).unwrap(); // handle windows, like below drop(fs::copy(root.join("foo.dll.lib"), dst_dir.join("foo.dll.lib"))); println!("cargo::rerun-if-changed=build.rs"); if cfg!(target_env = "msvc") { println!("cargo::rustc-link-lib=foo.dll"); } else { println!("cargo::rustc-link-lib=foo"); } println!("cargo::rustc-link-search=all={}", dst.parent().unwrap().display()); } "#, ) .file( "src/main.rs", r#" extern "C" { #[link_name = "cargo_test_foo"] fn foo(); } fn main() { unsafe { foo(); } } "#, ); let p2 = p2.build(); // Move the output `libfoo.so` into the directory of `p2`, and then delete // the `p` project. On macOS, the `libfoo.dylib` artifact references the // original path in `p` so we want to make sure that it can't find it (hence // the deletion). let root = if cross { p.root() .join("target") .join(cross_compile::alternate()) .join("debug") .join("deps") } else { p.root().join("target").join("debug").join("deps") }; let file = format!("{}foo{}", env::consts::DLL_PREFIX, env::consts::DLL_SUFFIX); let src = root.join(&file); let dst = p2.root().join(&file); fs::copy(&src, &dst).unwrap(); // copy the import library for windows, if it exists drop(fs::copy( &root.join("foo.dll.lib"), p2.root().join("foo.dll.lib"), )); remove_dir_all(p.root()).unwrap(); // Everything should work the first time p2.cargo(&format!("run{}", target_arg)).run(); // Now rename the root directory and rerun `cargo run`. Not only should we // not build anything but we also shouldn't crash. let mut new = p2.root(); new.pop(); new.push("bar2"); // For whatever reason on Windows right after we execute a binary it's very // unlikely that we're able to successfully delete or rename that binary. // It's not really clear why this is the case or if it's a bug in Cargo // holding a handle open too long. In an effort to reduce the flakiness of // this test though we throw this in a loop // // For some more information see #5481 and rust-lang/rust#48775 let mut i = 0; loop { let error = match fs::rename(p2.root(), &new) { Ok(()) => break, Err(e) => e, }; i += 1; if !cfg!(windows) || error.kind() != io::ErrorKind::PermissionDenied || i > 10 { panic!("failed to rename: {}", error); } println!("assuming {} is spurious, waiting to try again", error); thread::sleep(slow_cpu_multiplier(100)); } p2.cargo(&format!("run{}", target_arg)) .cwd(&new) .with_stderr_data(expected) .run(); } #[cargo_test] fn optional_build_script_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies] bar = { path = "bar", optional = true } [build-dependencies] bar = { path = "bar", optional = true } "#, ) .file( "build.rs", r#" #[cfg(feature = "bar")] extern crate bar; fn main() { #[cfg(feature = "bar")] { println!("cargo::rustc-env=FOO={}", bar::bar()); return } println!("cargo::rustc-env=FOO=0"); } "#, ) .file( "src/main.rs", r#" #[cfg(feature = "bar")] extern crate bar; fn main() { println!("{}", env!("FOO")); } "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.5.0")) .file("bar/src/lib.rs", "pub fn bar() -> u32 { 1 }"); let p = p.build(); p.cargo("run") .with_stdout_data(str![[r#" 0 "#]]) .run(); p.cargo("run --features bar") .with_stdout_data(str![[r#" 1 "#]]) .run(); } #[cargo_test] fn optional_build_dep_and_required_normal_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [dependencies] bar = { path = "./bar", optional = true } [build-dependencies] bar = { path = "./bar" } "#, ) .file("build.rs", "extern crate bar; fn main() { bar::bar(); }") .file( "src/main.rs", r#" #[cfg(feature = "bar")] extern crate bar; fn main() { #[cfg(feature = "bar")] { println!("{}", bar::bar()); } #[cfg(not(feature = "bar"))] { println!("0"); } } "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.5.0")) .file("bar/src/lib.rs", "pub fn bar() -> u32 { 1 }"); let p = p.build(); p.cargo("run") .with_stdout_data(str![[r#" 0 "#]]) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .run(); p.cargo("run --all-features") .with_stdout_data(str![[r#" 1 "#]]) .with_stderr_data(str![[r#" [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .run(); } #[cargo_test] fn using_rerun_if_changed_does_not_rebuild() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] "#, ) .file( "build.rs", r#" fn main() { println!("cargo::rerun-if-changed=build.rs"); } "#, ) .file("src/lib.rs", "") .build(); p.cargo("build").run(); p.cargo("build") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn links_interrupted_can_restart() { // Test for a `links` dependent build script getting canceled and then // restarted. Steps: // 1. Build to establish fingerprints. // 2. Change something (an env var in this case) that triggers the // dependent build script to run again. Kill the top-level build script // while it is running (such as hitting Ctrl-C). // 3. Run the build again, it should re-run the build script. let bar = project() .at("bar") .file( "Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = [] links = "foo" build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { println!("cargo::rerun-if-env-changed=SOMEVAR"); } "#, ) .build(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" [dependencies.bar] path = '{}' "#, bar.root().display() ), ) .file("src/lib.rs", "") .file( "build.rs", r#" use std::env; fn main() { println!("cargo::metadata=rebuild-if-changed=build.rs"); if std::path::Path::new("abort").exists() { panic!("Crash!"); } } "#, ) .build(); p.cargo("build").run(); // Simulate the user hitting Ctrl-C during a build. p.change_file("abort", ""); // Set SOMEVAR to trigger a rebuild. p.cargo("build") .env("SOMEVAR", "1") .with_stderr_data(str![[r#" ... Crash! ... "#]]) .with_status(101) .run(); fs::remove_file(p.root().join("abort")).unwrap(); // Try again without aborting the script. // ***This is currently broken, the script does not re-run. p.cargo("build -v") .env("SOMEVAR", "1") .with_stderr_data(str![[r#" ... [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` ... "#]]) .run(); } #[cargo_test] fn dev_dep_with_links() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] links = "x" [dev-dependencies] bar = { path = "./bar" } "#, ) .file("build.rs", "fn main() {}") .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] links = "y" [dependencies] foo = { path = ".." } "#, ) .file("bar/build.rs", "fn main() {}") .file("bar/src/lib.rs", "") .build(); p.cargo("check --tests").run(); } #[cargo_test] fn rerun_if_directory() { if !symlink_supported() { return; } // rerun-if-changed of a directory should rerun if any file in the directory changes. let p = project() .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { println!("cargo::rerun-if-changed=somedir"); } "#, ) .build(); let dirty = |expected| { p.cargo("check -v").with_stderr_data(expected).run(); }; let fresh = || { p.cargo("check") .with_stderr_data( "\ [FINISHED] [..] ", ) .run(); }; // Start with a missing directory. dirty(str![[r#" [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name build_script_build[..]` [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); // Because the directory doesn't exist, it will trigger a rebuild every time. // https://github.com/rust-lang/cargo/issues/6003 dirty(str![[r#" [DIRTY] foo v0.1.0 ([ROOT]/foo): the file `somedir` is missing [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); if is_coarse_mtime() { sleep_ms(1000); } // Empty directory. fs::create_dir(p.root().join("somedir")).unwrap(); dirty(str![[r#" [DIRTY] foo v0.1.0 ([ROOT]/foo): the file `somedir` has changed ([TIME_DIFF_AFTER_LAST_BUILD]) [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); fresh(); if is_coarse_mtime() { sleep_ms(1000); } // Add a file. p.change_file("somedir/foo", ""); p.change_file("somedir/bar", ""); dirty(str![[r#" [DIRTY] foo v0.1.0 ([ROOT]/foo): the file `somedir` has changed ([TIME_DIFF_AFTER_LAST_BUILD]) [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); fresh(); if is_coarse_mtime() { sleep_ms(1000); } // Add a symlink. p.symlink("foo", "somedir/link"); dirty(str![[r#" [DIRTY] foo v0.1.0 ([ROOT]/foo): the file `somedir` has changed ([TIME_DIFF_AFTER_LAST_BUILD]) [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); fresh(); if is_coarse_mtime() { sleep_ms(1000); } // Move the symlink. fs::remove_file(p.root().join("somedir/link")).unwrap(); p.symlink("bar", "somedir/link"); dirty(str![[r#" [DIRTY] foo v0.1.0 ([ROOT]/foo): the file `somedir` has changed ([TIME_DIFF_AFTER_LAST_BUILD]) [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); fresh(); if is_coarse_mtime() { sleep_ms(1000); } // Remove a file. fs::remove_file(p.root().join("somedir/foo")).unwrap(); dirty(str![[r#" [DIRTY] foo v0.1.0 ([ROOT]/foo): the file `somedir` has changed ([TIME_DIFF_AFTER_LAST_BUILD]) [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); fresh(); } #[cargo_test] fn rerun_if_published_directory() { // build script of a dependency contains a `rerun-if-changed` pointing to a directory Package::new("mylib-sys", "1.0.0") .file("mylib/balrog.c", "") .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { // Changing to mylib/balrog.c will not trigger a rebuild println!("cargo::rerun-if-changed=mylib"); } "#, ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] mylib-sys = "1.0.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check").run(); // Delete registry src to make directories being recreated with the latest timestamp. cargo_home().join("registry/src").rm_rf(); p.cargo("check --verbose") .with_stderr_data(str![[r#" [FRESH] mylib-sys v1.0.0 [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Upgrade of a package should still trigger a rebuild Package::new("mylib-sys", "1.0.1") .file("mylib/balrog.c", "") .file("mylib/balrog.h", "") .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { println!("cargo::rerun-if-changed=mylib"); } "#, ) .publish(); p.cargo("update").run(); p.cargo("fetch").run(); p.cargo("check -v") .with_stderr_data(str![[r#" [COMPILING] mylib-sys v1.0.1 [RUNNING] `rustc --crate-name build_script_build [..]` [RUNNING] `[ROOT]/foo/target/debug/build/mylib-sys-[HASH]/build-script-build` [RUNNING] `rustc --crate-name mylib_sys [..]` [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn test_with_dep_metadata() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { path = 'bar' } "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { assert_eq!(std::env::var("DEP_BAR_FOO").unwrap(), "bar"); } "#, ) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" links = 'bar' "#, ) .file("bar/src/lib.rs", "") .file( "bar/build.rs", r#" fn main() { println!("cargo::metadata=foo=bar"); } "#, ) .build(); p.cargo("test --lib").run(); } #[cargo_test] fn duplicate_script_with_extra_env() { // Test where a build script is run twice, that emits different rustc-env // and rustc-cfg values. In this case, one is run for host, the other for // target. if !cross_compile_can_run_on_host() { return; } let target = cross_compile::alternate(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo", "pm"] "#, ) .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] pm = { path = "../pm" } "#, ) .file( "foo/src/lib.rs", &r#" //! ```rust //! #[cfg(not(mycfg="{target}"))] //! compile_error!{"expected mycfg set"} //! assert_eq!(env!("CRATE_TARGET"), "{target}"); //! assert_eq!(std::env::var("CRATE_TARGET").unwrap(), "{target}"); //! ``` #[test] fn check_target() { #[cfg(not(mycfg="{target}"))] compile_error!{"expected mycfg set"} // Compile-time assertion. assert_eq!(env!("CRATE_TARGET"), "{target}"); // Run-time assertion. assert_eq!(std::env::var("CRATE_TARGET").unwrap(), "{target}"); } "# .replace("{target}", target), ) .file( "foo/build.rs", r#" fn main() { println!("cargo::rustc-env=CRATE_TARGET={}", std::env::var("TARGET").unwrap()); println!("cargo::rustc-cfg=mycfg=\"{}\"", std::env::var("TARGET").unwrap()); } "#, ) .file( "pm/Cargo.toml", r#" [package] name = "pm" version = "0.1.0" edition = "2015" [lib] proc-macro = true # This is just here to speed things up. doctest = false [dev-dependencies] foo = { path = "../foo" } "#, ) .file("pm/src/lib.rs", "") .build(); p.cargo("test --workspace --target") .arg(&target) .with_stdout_data(str![[r#" ... test check_target ... ok ... "#]]) .run(); p.cargo("test --workspace --doc --target") .arg(&target) .with_stdout_data(str![[r#" ... test foo/src/lib.rs - (line 2) ... ok ... "#]]) .run(); } #[cargo_test] fn wrong_output() { let p = project() .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { println!("cargo::example"); } "#, ) .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [ERROR] invalid output in build script of `foo v0.0.1 ([ROOT]/foo)`: `cargo::example` Expected a line with `cargo::KEY=VALUE` with an `=` character, but none was found. See https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script for more information about build script outputs. "#]]) .run(); } #[cargo_test] fn custom_build_closes_stdin() { // Ensure stdin is closed to prevent deadlock. // See https://github.com/rust-lang/cargo/issues/11196 let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" build = "build.rs" "#, ) .file("src/main.rs", "fn main() {}") .file( "build.rs", r#"fn main() { let mut line = String::new(); std::io::stdin().read_line(&mut line).unwrap(); }"#, ) .build(); p.cargo("build").run(); } #[cargo_test] fn test_old_syntax() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" "#, ) .file( "src/main.rs", r#" const FOO: &'static str = env!("FOO"); fn main() { println!("{}", FOO); } "#, ) .file( "build.rs", r#"fn main() { println!("cargo:rustc-env=FOO=foo"); println!("cargo:foo=foo"); }"#, ) .build(); p.cargo("build -v").run(); p.cargo("run -v") .with_stdout_data(str![[r#" foo "#]]) .run(); } #[cargo_test] fn test_invalid_old_syntax() { // Unexpected metadata value. let p = project() .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { println!("cargo:foo"); } "#, ) .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [ERROR] invalid output in build script of `foo v0.0.1 ([ROOT]/foo)`: `cargo:foo` Expected a line with `cargo:KEY=VALUE` with an `=` character, but none was found. See https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script for more information about build script outputs. "#]]) .run(); } #[cargo_test] fn test_invalid_new_syntax() { // Unexpected metadata value. let p = project() .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { println!("cargo::metadata=foo"); } "#, ) .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [ERROR] invalid output in build script of `foo v0.0.1 ([ROOT]/foo)`: `cargo::metadata=foo` Expected a line with `cargo::metadata=KEY=VALUE` with an `=` character, but none was found. See https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script for more information about build script outputs. "#]]) .run(); // `cargo::` can not be used with the unknown key. let p = project() .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { println!("cargo::foo=bar"); } "#, ) .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [ERROR] invalid output in build script of `foo v0.0.1 ([ROOT]/foo)`: `cargo::foo=bar` Unknown key: `foo`. See https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script for more information about build script outputs. "#]]) .run(); } #[cargo_test] fn test_new_syntax_with_old_msrv() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" rust-version = "1.60.0" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { println!("cargo::metadata=foo=bar"); } "#, ) .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [ERROR] the `cargo::` syntax for build script output instructions was added in Rust 1.77.0, but the minimum supported Rust version of `foo v0.5.0 ([ROOT]/foo)` is 1.60.0. Switch to the old `cargo:foo=bar` syntax instead of `cargo::metadata=foo=bar` (note the single colon). See https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script for more information about build script outputs. "#]]) .run(); } #[cargo_test] fn test_new_syntax_with_old_msrv_and_reserved_prefix() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" rust-version = "1.60.0" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { println!("cargo::rustc-check-cfg=cfg(foo)"); } "#, ) .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [ERROR] the `cargo::` syntax for build script output instructions was added in Rust 1.77.0, but the minimum supported Rust version of `foo v0.5.0 ([ROOT]/foo)` is 1.60.0. Switch to the old `cargo:rustc-check-cfg=cfg(foo)` syntax (note the single colon). See https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script for more information about build script outputs. "#]]) .run(); } #[cargo_test] fn test_new_syntax_with_old_msrv_and_unknown_prefix() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" rust-version = "1.60.0" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { println!("cargo::foo=bar"); } "#, ) .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [ERROR] the `cargo::` syntax for build script output instructions was added in Rust 1.77.0, but the minimum supported Rust version of `foo v0.5.0 ([ROOT]/foo)` is 1.60.0. See https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script for more information about build script outputs. "#]]) .run(); } #[cargo_test] fn test_new_syntax_with_compatible_partial_msrv() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" edition = "2015" build = "build.rs" rust-version = "1.77" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { println!("cargo::metadata=foo=bar"); } "#, ) .build(); p.cargo("check") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn test_old_syntax_with_old_msrv() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" rust-version = "1.60.0" "#, ) .file( "src/main.rs", r#" const FOO: &'static str = env!("FOO"); fn main() { println!("{}", FOO); } "#, ) .file( "build.rs", r#"fn main() { println!("cargo:rustc-env=FOO=foo"); println!("cargo:foo=foo"); }"#, ) .build(); p.cargo("build -v").run(); p.cargo("run -v") .with_stdout_data(str![[r#" foo "#]]) .run(); } #[cargo_test] fn build_script_rerun_when_target_rustflags_change() { let target = rustc_host(); let p = project() .file( "src/main.rs", r#" fn main() { #[cfg(enable)] println!("hello"); } "#, ) .file( "build.rs", r#" use std::env; fn main() { println!("cargo::rustc-check-cfg=cfg(enable)"); if let Ok(rustflags) = env::var("CARGO_ENCODED_RUSTFLAGS") { if !rustflags.is_empty() { println!("cargo::rustc-cfg=enable"); } } } "#, ) .build(); p.cargo("run --target") .arg(&target) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/[HOST_TARGET]/debug/foo[EXE]` "#]]) .run(); p.cargo("run --target") .arg(&target) .env("RUSTFLAGS", "-C opt-level=3") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/[HOST_TARGET]/debug/foo[EXE]` "#]]) .with_stdout_data(str![[r#" hello "#]]) .run(); } #[cargo_test] fn links_overrides_with_target_applies_to_host() { let p = project() .file( "Cargo.toml", r#" [package] name = "mylib-sys" edition = "2021" version = "0.0.1" authors = [] links = "mylib" "#, ) .file("src/lib.rs", "") .file("build.rs", "bad file") .build(); p.cargo("build -v") .masquerade_as_nightly_cargo(&["target-applies-to-host"]) .args(&[ "-Ztarget-applies-to-host", "--config", "target-applies-to-host=false", ]) .args(&[ "--config", &format!(r#"target.{}.mylib.rustc-link-search=["foo"]"#, rustc_host()), ]) .with_stderr_data(str![[r#" [COMPILING] mylib-sys v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name mylib_sys [..] -L foo` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn directory_with_leading_underscore() { let p: cargo_test_support::Project = git::new("foo", |p| { p.no_manifest() .file( "_foo/foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2021" build = "build.rs" "#, ) .file("_foo/foo/src/main.rs", "fn main() {}") .file("_foo/foo/build.rs", "fn main() { }") }); p.cargo("build --manifest-path=_foo/foo/Cargo.toml -v") .with_status(0) .run(); } #[cargo_test] fn linker_search_path_preference() { // This isn't strictly the exact scenario that causes the issue, but it's the shortest demonstration // of the issue. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2024" build = "build.rs" [dependencies] a = { path = "a" } b = { path = "b" } "#, ) .file( "build.rs", r#" fn main() { let out_dir = std::env::var("OUT_DIR").unwrap(); println!("cargo::rustc-link-search=/usr/lib"); println!("cargo::rustc-link-search={}/libs2", out_dir); println!("cargo::rustc-link-search=/lib"); println!("cargo::rustc-link-search={}/libs1", out_dir); } "#, ) .file("src/main.rs", "fn main() {}") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2024" build = "build.rs" "#, ) .file("a/src/lib.rs", "") .file( "a/build.rs", r#" fn main() { let out_dir = std::env::var("OUT_DIR").unwrap(); println!("cargo::rustc-link-search=/usr/lib3"); println!("cargo::rustc-link-search={}/libsA.2", out_dir); println!("cargo::rustc-link-search=/lib3"); println!("cargo::rustc-link-search={}/libsA.1", out_dir); } "#, ) .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" edition = "2024" build = "build.rs" "#, ) .file("b/src/lib.rs", "") .file( "b/build.rs", r#" fn main() { let out_dir = std::env::var("OUT_DIR").unwrap(); println!("cargo::rustc-link-search=/usr/lib2"); println!("cargo::rustc-link-search={}/libsB.1", out_dir); println!("cargo::rustc-link-search=/lib2"); println!("cargo::rustc-link-search={}/libsB.2", out_dir); } "#, ) .build(); p.cargo("build -v").with_stderr_data(str![[r#" ... [RUNNING] `rustc --crate-name foo [..] -L [ROOT]/foo/target/debug/build/foo-[HASH]/out/libs2 -L [ROOT]/foo/target/debug/build/foo-[HASH]/out/libs1 -L [ROOT]/foo/target/debug/build/a-[HASH]/out/libsA.2 -L [ROOT]/foo/target/debug/build/a-[HASH]/out/libsA.1 -L [ROOT]/foo/target/debug/build/b-[HASH]/out/libsB.1 -L [ROOT]/foo/target/debug/build/b-[HASH]/out/libsB.2 -L /usr/lib -L /lib -L /usr/lib3 -L /lib3 -L /usr/lib2 -L /lib2` ... "#]]).run(); } cargo-0.91.0/tests/testsuite/build_script_env.rs000064400000000000000000000317701046102023000201120ustar 00000000000000//! Tests for build.rs rerun-if-env-changed and rustc-env use crate::prelude::*; use cargo_test_support::basic_manifest; use cargo_test_support::project; use cargo_test_support::sleep_ms; use cargo_test_support::str; #[cargo_test] fn rerun_if_env_changes() { let p = project() .file("src/main.rs", "fn main() {}") .file( "build.rs", r#" fn main() { println!("cargo::rerun-if-env-changed=FOO"); } "#, ) .build(); p.cargo("check") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .env("FOO", "bar") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .env("FOO", "baz") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .env("FOO", "baz") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn rerun_if_env_or_file_changes() { let p = project() .file("src/main.rs", "fn main() {}") .file( "build.rs", r#" fn main() { println!("cargo::rerun-if-env-changed=FOO"); println!("cargo::rerun-if-changed=foo"); } "#, ) .file("foo", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .env("FOO", "bar") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .env("FOO", "bar") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); sleep_ms(1000); p.change_file("foo", "// modified"); p.cargo("check") .env("FOO", "bar") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn rustc_bootstrap() { let build_rs = r#" fn main() { println!("cargo::rustc-env=RUSTC_BOOTSTRAP=1"); } "#; let p = project() .file("Cargo.toml", &basic_manifest("has-dashes", "0.0.1")) .file( "src/lib.rs", "#![allow(internal_features)] #![feature(rustc_attrs)]", ) .file("build.rs", build_rs) .build(); // RUSTC_BOOTSTRAP unset on stable should error p.cargo("check") .with_stderr_data(str![[r#" [COMPILING] has-dashes v0.0.1 ([ROOT]/foo) [ERROR] Cannot set `RUSTC_BOOTSTRAP=1` from build script of `has-dashes v0.0.1 ([ROOT]/foo)`. [NOTE] Crates cannot set `RUSTC_BOOTSTRAP` themselves, as doing so would subvert the stability guarantees of Rust for your project. [HELP] If you're sure you want to do this in your project, set the environment variable `RUSTC_BOOTSTRAP=has_dashes` before running cargo instead. "#]]) .with_status(101) .run(); // nightly should warn whether or not RUSTC_BOOTSTRAP is set p.cargo("check") .masquerade_as_nightly_cargo(&["RUSTC_BOOTSTRAP"]) // NOTE: uses RUSTC_BOOTSTRAP so it will be propagated to rustc // (this matters when tests are being run with a beta or stable cargo) .env("RUSTC_BOOTSTRAP", "1") .with_stderr_data(str![[r#" [COMPILING] has-dashes v0.0.1 ([ROOT]/foo) [WARNING] has-dashes@0.0.1: Cannot set `RUSTC_BOOTSTRAP=1` from build script of `has-dashes v0.0.1 ([ROOT]/foo)`. [NOTE] Crates cannot set `RUSTC_BOOTSTRAP` themselves, as doing so would subvert the stability guarantees of Rust for your project. [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // RUSTC_BOOTSTRAP set to the name of the library should warn p.cargo("check") .env("RUSTC_BOOTSTRAP", "has_dashes") .with_stderr_data(str![[r#" [WARNING] has-dashes@0.0.1: Cannot set `RUSTC_BOOTSTRAP=1` from build script of `has-dashes v0.0.1 ([ROOT]/foo)`. [NOTE] Crates cannot set `RUSTC_BOOTSTRAP` themselves, as doing so would subvert the stability guarantees of Rust for your project. [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // RUSTC_BOOTSTRAP set to some random value should error p.cargo("check") .env("RUSTC_BOOTSTRAP", "bar") .with_stderr_data(str![[r#" [ERROR] Cannot set `RUSTC_BOOTSTRAP=1` from build script of `has-dashes v0.0.1 ([ROOT]/foo)`. [NOTE] Crates cannot set `RUSTC_BOOTSTRAP` themselves, as doing so would subvert the stability guarantees of Rust for your project. [HELP] If you're sure you want to do this in your project, set the environment variable `RUSTC_BOOTSTRAP=has_dashes` before running cargo instead. "#]]) .with_status(101) .run(); // Tests for binaries instead of libraries let p = project() .file("Cargo.toml", &basic_manifest("foo", "0.0.1")) .file( "src/main.rs", "#![allow(internal_features)] #![feature(rustc_attrs)] fn main() {}", ) .file("build.rs", build_rs) .build(); // nightly should warn when there's no library whether or not RUSTC_BOOTSTRAP is set p.cargo("check") .masquerade_as_nightly_cargo(&["RUSTC_BOOTSTRAP"]) // NOTE: uses RUSTC_BOOTSTRAP so it will be propagated to rustc // (this matters when tests are being run with a beta or stable cargo) .env("RUSTC_BOOTSTRAP", "1") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [WARNING] foo@0.0.1: Cannot set `RUSTC_BOOTSTRAP=1` from build script of `foo v0.0.1 ([ROOT]/foo)`. [NOTE] Crates cannot set `RUSTC_BOOTSTRAP` themselves, as doing so would subvert the stability guarantees of Rust for your project. [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // RUSTC_BOOTSTRAP conditionally set when there's no library should error (regardless of the value) p.cargo("check") .env("RUSTC_BOOTSTRAP", "foo") .with_stderr_data(str![[r#" [ERROR] Cannot set `RUSTC_BOOTSTRAP=1` from build script of `foo v0.0.1 ([ROOT]/foo)`. [NOTE] Crates cannot set `RUSTC_BOOTSTRAP` themselves, as doing so would subvert the stability guarantees of Rust for your project. [HELP] If you're sure you want to do this in your project, set the environment variable `RUSTC_BOOTSTRAP=1` before running cargo instead. "#]]) .with_status(101) .run(); } #[cargo_test] fn build_script_env_verbose() { let build_rs = r#" fn main() {} "#; let p = project() .file("Cargo.toml", &basic_manifest("verbose-build", "0.0.1")) .file("src/lib.rs", "") .file("build.rs", build_rs) .build(); p.cargo("check -vv") .with_stderr_data( "\ ... [RUNNING] `[..]CARGO=[..]build-script-build` ...", ) .run(); } #[cargo_test] #[cfg(target_arch = "x86_64")] fn build_script_sees_cfg_target_feature() { let build_rs = r#" fn main() { let cfg = std::env::var("CARGO_CFG_TARGET_FEATURE").unwrap(); eprintln!("CARGO_CFG_TARGET_FEATURE={cfg}"); } "#; let configs = [ r#" [build] rustflags = ["-Ctarget-feature=+sse4.1,+sse4.2"] "#, r#" [target.'cfg(target_arch = "x86_64")'] rustflags = ["-Ctarget-feature=+sse4.1,+sse4.2"] "#, ]; for config in configs { let p = project() .file(".cargo/config.toml", config) .file("src/lib.rs", r#""#) .file("build.rs", build_rs) .build(); p.cargo("check -vv") .with_stderr_data( "\ ... [foo 0.0.1] CARGO_CFG_TARGET_FEATURE=[..]sse4.2[..] ... [..]-Ctarget-feature=[..]+sse4.2[..] ...", ) .run(); } } /// In this test, the cfg is self-contradictory. There's no *right* answer as to /// what the value of `RUSTFLAGS` should be in this case. We chose to give a /// warning. However, no matter what we do, it's important that build scripts /// and rustc see a consistent picture #[cargo_test] fn cfg_paradox() { let build_rs = r#" fn main() { let cfg = std::env::var("CARGO_CFG_BERTRAND").is_ok(); eprintln!("cfg!(bertrand)={cfg}"); } "#; let config = r#" [target.'cfg(not(bertrand))'] rustflags = ["--cfg=bertrand"] "#; let p = project() .file(".cargo/config.toml", config) .file("src/lib.rs", r#""#) .file("build.rs", build_rs) .build(); p.cargo("check -vv") .with_stderr_data( "\ [WARNING] non-trivial mutual dependency between target-specific configuration and RUSTFLAGS ... [foo 0.0.1] cfg!(bertrand)=true ... [..]--cfg=bertrand[..] ...", ) .run(); } /// This test checks how Cargo handles rustc cfgs which are defined both with /// and without a value. The expected behavior is that the environment variable /// is going to contain all the values. /// /// For example, this configuration: /// ``` /// target_has_atomic /// target_has_atomic="16" /// target_has_atomic="32" /// target_has_atomic="64" /// target_has_atomic="8" /// target_has_atomic="ptr" /// ``` /// /// Should result in the following environment variable: /// /// ``` /// CARGO_CFG_TARGET_HAS_ATOMIC=16,32,64,8,ptr /// ``` /// /// On the other hand, configuration symbols without any value should result in /// an empty string. /// /// For example, this configuration: /// /// ``` /// target_thread_local /// ``` /// /// Should result in the following environment variable: /// /// ``` /// CARGO_CFG_TARGET_THREAD_LOCAL= /// ``` #[cargo_test(nightly, reason = "affected rustc cfg is unstable")] #[cfg(target_arch = "x86_64")] fn rustc_cfg_with_and_without_value() { let build_rs = r#" fn main() { let cfg = std::env::var("CARGO_CFG_TARGET_HAS_ATOMIC"); eprintln!("CARGO_CFG_TARGET_HAS_ATOMIC={cfg:?}"); let cfg = std::env::var("CARGO_CFG_WINDOWS"); eprintln!("CARGO_CFG_WINDOWS={cfg:?}"); let cfg = std::env::var("CARGO_CFG_UNIX"); eprintln!("CARGO_CFG_UNIX={cfg:?}"); } "#; let p = project() .file("src/lib.rs", r#""#) .file("build.rs", build_rs) .build(); let mut check = p.cargo("check -vv"); #[cfg(target_has_atomic = "64")] check.with_stderr_data( "\ ... [foo 0.0.1] CARGO_CFG_TARGET_HAS_ATOMIC=Ok(\"[..]64[..]\") ...", ); #[cfg(windows)] check.with_stderr_data( "\ ... [foo 0.0.1] CARGO_CFG_WINDOWS=Ok(\"\") ...", ); #[cfg(unix)] check.with_stderr_data( "\ ... [foo 0.0.1] CARGO_CFG_UNIX=Ok(\"\") ...", ); check.run(); } #[cargo_test] fn rerun_if_env_is_exsited_config() { let p = project() .file("src/main.rs", "fn main() {}") .file( "build.rs", r#" fn main() { println!("cargo::rerun-if-env-changed=FOO"); } "#, ) .file( ".cargo/config.toml", r#" [env] FOO = "foo" "#, ) .build(); p.cargo("check") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo(r#"check --config 'env.FOO="bar"'"#) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn rerun_if_env_newly_added_in_config() { let p = project() .file("src/main.rs", "fn main() {}") .file( "build.rs", r#" fn main() { println!("cargo::rerun-if-env-changed=FOO"); } "#, ) .build(); p.cargo("check") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo(r#"check --config 'env.FOO="foo"'"#) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } cargo-0.91.0/tests/testsuite/build_script_extra_link_arg.rs000064400000000000000000000320161046102023000223050ustar 00000000000000//! Tests for additional link arguments. // NOTE: Many of these tests use `without_status()` when passing bogus flags // because MSVC link.exe just gives a warning on unknown flags (how helpful!), // and other linkers will return an error. use crate::prelude::*; use cargo_test_support::registry::Package; use cargo_test_support::str; use cargo_test_support::{basic_bin_manifest, basic_lib_manifest, basic_manifest, project}; #[cargo_test] fn build_script_extra_link_arg_bin() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", "fn main() {}") .file( "build.rs", r#" fn main() { println!("cargo::rustc-link-arg-bins=--this-is-a-bogus-flag"); } "#, ) .build(); p.cargo("build -v") .without_status() .with_stderr_data( "\ ... [RUNNING] `rustc --crate-name foo [..]-C link-arg=--this-is-a-bogus-flag[..] ...", ) .run(); } #[cargo_test] fn build_script_extra_link_arg_bin_single() { let p = project() .file( "Cargo.toml", r#" [package] name = "foobar" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [[bin]] name = "foo" [[bin]] name = "bar" "#, ) .file("src/main.rs", "fn main() {}") .file( "build.rs", r#" fn main() { println!("cargo::rustc-link-arg-bins=--bogus-flag-all"); println!("cargo::rustc-link-arg-bin=foo=--bogus-flag-foo"); println!("cargo::rustc-link-arg-bin=bar=--bogus-flag-bar"); } "#, ) .build(); p.cargo("build -v") .without_status() .with_stderr_data( "\ ... [RUNNING] `rustc --crate-name foo [..]-C link-arg=--bogus-flag-all -C link-arg=--bogus-flag-foo[..] ...", ) .with_stderr_data( "\ ... [RUNNING] `rustc --crate-name bar [..]-C link-arg=--bogus-flag-all -C link-arg=--bogus-flag-bar[..] ...", ) .run(); } #[cargo_test] fn build_script_extra_link_arg() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", "fn main() {}") .file( "build.rs", r#" fn main() { println!("cargo::rustc-link-arg=--this-is-a-bogus-flag"); } "#, ) .build(); p.cargo("build -v") .without_status() .with_stderr_data( "\ ... [RUNNING] `rustc --crate-name foo [..]-C link-arg=--this-is-a-bogus-flag[..] ...", ) .run(); } #[cargo_test] fn link_arg_missing_target() { // Errors when a given target doesn't exist. let p = project() .file("src/lib.rs", "") .file( "build.rs", r#"fn main() { println!("cargo::rustc-link-arg-cdylib=--bogus"); }"#, ) .build(); // TODO: Uncomment this if cdylib restriction is re-added (see // cdylib_link_arg_transitive below). // p.cargo("check") // .with_status(101) // .with_stderr("\ // [COMPILING] foo [..] // error: invalid instruction `cargo::rustc-link-arg-cdylib` from build script of `foo v0.0.1 ([ROOT]/foo)` // The package foo v0.0.1 ([ROOT]/foo) does not have a cdylib target. // ") // .run(); p.change_file( "build.rs", r#"fn main() { println!("cargo::rustc-link-arg-bins=--bogus"); }"#, ); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [ERROR] invalid instruction `cargo::rustc-link-arg-bins` from build script of `foo v0.0.1 ([ROOT]/foo)` The package foo v0.0.1 ([ROOT]/foo) does not have a bin target. "#]]) .run(); p.change_file( "build.rs", r#"fn main() { println!("cargo::rustc-link-arg-bin=abc=--bogus"); }"#, ); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [ERROR] invalid instruction `cargo::rustc-link-arg-bin` from build script of `foo v0.0.1 ([ROOT]/foo)` The package foo v0.0.1 ([ROOT]/foo) does not have a bin target with the name `abc`. "#]]) .run(); p.change_file( "build.rs", r#"fn main() { println!("cargo::rustc-link-arg-bin=abc"); }"#, ); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [ERROR] invalid instruction `cargo::rustc-link-arg-bin=abc` from build script of `foo v0.0.1 ([ROOT]/foo)` The instruction should have the form cargo::rustc-link-arg-bin=BIN=ARG "#]]) .run(); } #[cargo_test] fn cdylib_link_arg_transitive() { // There was an unintended regression in 1.50 where rustc-link-arg-cdylib // arguments from dependencies were being applied in the parent package. // Previously it was silently ignored. // See https://github.com/rust-lang/cargo/issues/9562 let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [lib] crate-type = ["cdylib"] [dependencies] bar = {path="bar"} "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "1.0.0")) .file("bar/src/lib.rs", "") .file( "bar/build.rs", r#" fn main() { println!("cargo::rustc-link-arg-cdylib=--bogus"); } "#, ) .build(); p.cargo("build -v") .without_status() .with_stderr_data( "\ ... [COMPILING] bar v1.0.0 ([ROOT]/foo/bar) [RUNNING] `rustc --crate-name build_script_build --edition=2015 bar/build.rs [..] [RUNNING] `[ROOT]/foo/target/debug/build/bar-[HASH]/build-script-build` [WARNING] bar@1.0.0: cargo::rustc-link-arg-cdylib was specified in the build script of bar v1.0.0 \ ([ROOT]/foo/bar), but that package does not contain a cdylib target Allowing this was an unintended change in the 1.50 release, and may become an error in \ the future. For more information, see . [RUNNING] `rustc --crate-name bar --edition=2015 bar/src/lib.rs [..] [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]-C link-arg=--bogus[..]` ...", ) .run(); } #[cargo_test] fn link_arg_transitive_not_allowed() { // Verify that transitive dependencies don't pass link args. // // Note that rustc-link-arg doesn't have any errors or warnings when it is // unused. Perhaps that could be more aggressive, but it is difficult // since it could be used for test binaries. Package::new("bar", "1.0.0") .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { println!("cargo::rustc-link-arg=--bogus"); } "#, ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [lib] crate-type = ["cdylib"] [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("build -v") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [COMPILING] bar v1.0.0 [RUNNING] `rustc --crate-name build_script_build [..] [RUNNING] `[ROOT]/foo/target/debug/build/bar-[HASH]/build-script-build` [RUNNING] `rustc --crate-name bar [..] [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .with_stderr_does_not_contain("--bogus") .run(); } #[cargo_test] fn link_arg_with_doctest() { let p = project() .file( "src/lib.rs", r#" //! ``` //! let x = 5; //! assert_eq!(x, 5); //! ``` "#, ) .file( "build.rs", r#" fn main() { println!("cargo::rustc-link-arg=--this-is-a-bogus-flag"); } "#, ) .build(); p.cargo("test --doc -v") .without_status() .with_stderr_data( "\ ... [RUNNING] `rustdoc [..]--crate-name foo [..]-C link-arg=--this-is-a-bogus-flag[..] ...", ) .run(); } #[cargo_test] fn build_script_extra_link_arg_tests() { let p = project() .file("Cargo.toml", &basic_lib_manifest("foo")) .file("src/lib.rs", "") .file("tests/test_foo.rs", "") .file( "build.rs", r#" fn main() { println!("cargo::rustc-link-arg-tests=--this-is-a-bogus-flag"); } "#, ) .build(); p.cargo("test -v") .without_status() .with_stderr_data( "\ ... [RUNNING] `rustc --crate-name test_foo [..]-C link-arg=--this-is-a-bogus-flag[..] ...", ) .run(); } #[cargo_test] fn build_script_extra_link_arg_benches() { let p = project() .file("Cargo.toml", &basic_lib_manifest("foo")) .file("src/lib.rs", "") .file("benches/bench_foo.rs", "") .file( "build.rs", r#" fn main() { println!("cargo::rustc-link-arg-benches=--this-is-a-bogus-flag"); } "#, ) .build(); p.cargo("bench -v") .without_status() .with_stderr_data( "\ ... [RUNNING] `rustc --crate-name bench_foo [..]-C link-arg=--this-is-a-bogus-flag[..] ...", ) .run(); } #[cargo_test] fn build_script_extra_link_arg_examples() { let p = project() .file("Cargo.toml", &basic_lib_manifest("foo")) .file("src/lib.rs", "") .file("examples/example_foo.rs", "fn main() {}") .file( "build.rs", r#" fn main() { println!("cargo::rustc-link-arg-examples=--this-is-a-bogus-flag"); } "#, ) .build(); p.cargo("build -v --examples") .without_status() .with_stderr_data( "\ ... [RUNNING] `rustc --crate-name example_foo [..]-C link-arg=--this-is-a-bogus-flag[..] ...", ) .run(); } #[cargo_test] fn cdylib_both_forms() { // Cargo accepts two different forms for the cdylib link instruction, // which have the same meaning. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [lib] crate-type = ["cdylib"] "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { println!("cargo::rustc-cdylib-link-arg=--bogus-flag-one"); println!("cargo::rustc-link-arg-cdylib=--bogus-flag-two"); } "#, ) .build(); p.cargo("build -v") .without_status() .with_stderr_data(str![[r#" [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name build_script_build [..] [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc --crate-name foo [..]--crate-type cdylib [..]-C link-arg=--bogus-flag-one -C link-arg=--bogus-flag-two[..] ... "#]]) .run(); } // https://github.com/rust-lang/cargo/issues/12663 #[cargo_test] fn cdylib_extra_link_args_should_not_apply_to_unit_tests() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" [lib] crate-type = ["lib", "cdylib"] "#, ) .file( "src/lib.rs", r#" #[test] fn noop() {} "#, ) .file( "build.rs", r#" fn main() { // This would fail if cargo passed `-lhack` to building the test because `hack` doesn't exist. println!("cargo::rustc-link-arg-cdylib=-lhack"); } "#, ) .build(); p.cargo("test --lib").run(); } cargo-0.91.0/tests/testsuite/build_scripts_multiple.rs000064400000000000000000000444751046102023000213460ustar 00000000000000//! Tests for multiple build scripts feature. use crate::prelude::*; use cargo_test_support::compare::assert_e2e; use cargo_test_support::git; use cargo_test_support::publish::validate_crate_contents; use cargo_test_support::str; use cargo_test_support::{Project, project}; use std::fs::File; #[cargo_test] fn build_without_feature_enabled_aborts_with_error() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2024" build = ["build1.rs", "build2.rs"] "#, ) .file("src/main.rs", "fn main() {}") .file("build1.rs", "fn main() {}") .file("build2.rs", "fn main() {}") .build(); p.cargo("check") .masquerade_as_nightly_cargo(&["multiple-build-scripts"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: feature `multiple-build-scripts` is required The package requires the Cargo feature called `multiple-build-scripts`, but that feature is not stabilized in this version of Cargo ([..]). Consider adding `cargo-features = ["multiple-build-scripts"]` to the top of Cargo.toml (above the [package] table) to tell Cargo you are opting in to use this unstable feature. See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#multiple-build-scripts for more information about the status of this feature. "#]]) .run(); } fn basic_empty_project() -> Project { project() .file( "Cargo.toml", r#" cargo-features = ["multiple-build-scripts"] [package] name = "foo" version = "0.1.0" edition = "2024" build = ["build1.rs", "build2.rs"] "#, ) .file("src/main.rs", "fn main() {}") .file("build1.rs", "fn main() {}") .file("build2.rs", "fn main() {}") .build() } #[cargo_test] fn empty_multiple_build_script_project() { let p = basic_empty_project(); p.cargo("check") .masquerade_as_nightly_cargo(&["multiple-build-scripts"]) .with_status(0) .with_stderr_data(str![[r#" [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn multiple_build_scripts_metadata() { let p = basic_empty_project(); p.cargo("metadata --format-version=1") .masquerade_as_nightly_cargo(&["multiple-build-scripts"]) .with_status(0) .with_stderr_data("") .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2024", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo#0.1.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/Cargo.toml", "metadata": null, "name": "foo", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "bin" ], "doc": true, "doctest": false, "edition": "2024", "kind": [ "bin" ], "name": "foo", "src_path": "[ROOT]/foo/src/main.rs", "test": true }, { "crate_types": [ "bin" ], "doc": false, "doctest": false, "edition": "2024", "kind": [ "custom-build" ], "name": "build-script-build1", "src_path": "[ROOT]/foo/build1.rs", "test": false }, { "crate_types": [ "bin" ], "doc": false, "doctest": false, "edition": "2024", "kind": [ "custom-build" ], "name": "build-script-build2", "src_path": "[ROOT]/foo/build2.rs", "test": false } ], "version": "0.1.0" } ], "resolve": { "nodes": [ { "dependencies": [], "deps": [], "features": [], "id": "path+[ROOTURL]/foo#0.1.0" } ], "root": "path+[ROOTURL]/foo#0.1.0" }, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo#0.1.0" ], "workspace_members": [ "path+[ROOTURL]/foo#0.1.0" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); } #[cargo_test] fn verify_package_multiple_build_scripts() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["multiple-build-scripts"] [package] name = "foo" version = "0.1.0" edition = "2024" license = "MIT" description = "foo" documentation = "docs.rs/foo" authors = [] build = ["build1.rs", "build2.rs"] include = [ "src/main.rs", "build1.rs" ] "#, ) .file("src/main.rs", "fn main() {}") .file("build1.rs", "fn main() {}") .file("build2.rs", "fn main() {}") .build(); p.cargo("package") .masquerade_as_nightly_cargo(&["multiple-build-scripts"]) .with_status(0) .with_stderr_data(str![[r#" [PACKAGING] foo v0.1.0 ([ROOT]/foo) [WARNING] ignoring `package.build` entry `build2.rs` as it is not included in the published package [PACKAGED] 5 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.1.0 ([ROOT]/foo) [COMPILING] foo v0.1.0 ([ROOT]/foo/target/package/foo-0.1.0) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let f = File::open(&p.root().join("target/package/foo-0.1.0.crate")).unwrap(); validate_crate_contents( f, "foo-0.1.0.crate", &[ "Cargo.toml", "Cargo.toml.orig", "src/main.rs", "build1.rs", "Cargo.lock", ], [( "Cargo.toml", str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. cargo-features = ["multiple-build-scripts"] [package] edition = "2024" name = "foo" version = "0.1.0" authors = [] build = "build1.rs" include = [ "src/main.rs", "build1.rs", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "docs.rs/foo" readme = false license = "MIT" [[bin]] name = "foo" path = "src/main.rs" "##]], )], ); } fn add_git_vendor_config(p: &Project, git_project: &Project) { p.change_file( ".cargo/config.toml", &format!( r#" [source."git+{url}"] git = "{url}" replace-with = 'vendor' [source.vendor] directory = 'vendor' "#, url = git_project.url() ), ); } #[cargo_test] fn verify_vendor_multiple_build_scripts() { let git_project = git::new("dep", |project| { project .file( "Cargo.toml", r#" cargo-features = ["multiple-build-scripts"] [package] name = "dep" version = "0.1.0" edition = "2024" license = "MIT" description = "dependency of foo" documentation = "docs.rs/dep" authors = [] build = ["build1.rs", "build2.rs"] include = [ "src/main.rs", "build1.rs" ] "#, ) .file("src/main.rs", "fn main() {}") .file("build1.rs", "fn main() {}") .file("build2.rs", "fn main() {}") }); let p = project() .file( "Cargo.toml", &format!( r#" cargo-features = ["multiple-build-scripts"] [package] name = "foo" version = "0.1.0" edition = "2024" [dependencies.dep] git = '{}' "#, git_project.url() ), ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("vendor --respect-source-config") .masquerade_as_nightly_cargo(&["multiple-build-scripts"]) .with_status(0) .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/dep` [LOCKING] 1 package to latest [..] compatible version Vendoring dep v0.1.0 ([ROOTURL]/dep#[..]) ([ROOT]/home/.cargo/git/checkouts/dep-[HASH]/[..]) to vendor/dep [WARNING] ignoring `package.build` entry `build2.rs` as it is not included in the published package To use vendored sources, add this to your .cargo/config.toml for this project: "#]]) .run(); add_git_vendor_config(&p, &git_project); assert_e2e().eq( p.read_file("vendor/dep/Cargo.toml"), str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. cargo-features = ["multiple-build-scripts"] [package] edition = "2024" name = "dep" version = "0.1.0" authors = [] build = "build1.rs" include = [ "src/main.rs", "build1.rs", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "dependency of foo" documentation = "docs.rs/dep" readme = false license = "MIT" [[bin]] name = "dep" path = "src/main.rs" "##]], ); p.cargo("check") .masquerade_as_nightly_cargo(&["multiple-build-scripts"]) .run(); } #[cargo_test] fn custom_build_script_first_index_script_failed() { // In this, the script that is at first index in the build script array fails let p = project() .file( "Cargo.toml", r#" cargo-features = ["multiple-build-scripts"] [package] name = "foo" version = "0.1.0" edition = "2024" build = ["build1.rs", "build2.rs"] "#, ) .file("src/main.rs", "fn main() {}") .file("build1.rs", "fn main() { std::process::exit(101); }") .file("build2.rs", "fn main() {}") .build(); p.cargo("check -v") .masquerade_as_nightly_cargo(&["multiple-build-scripts"]) .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.1.0 ([ROOT]/foo) ... [ERROR] failed to run custom build command for `foo v0.1.0 ([ROOT]/foo)` Caused by: process didn't exit successfully: `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build1` ([EXIT_STATUS]: 101) ... "#]]) .run(); } #[cargo_test] fn custom_build_script_second_index_script_failed() { // In this, the script that is at second index in the build script array fails // This test was necessary because earlier, the program failed only if first script failed. let p = project() .file( "Cargo.toml", r#" cargo-features = ["multiple-build-scripts"] [package] name = "foo" version = "0.1.0" edition = "2024" build = ["build1.rs", "build2.rs"] "#, ) .file("src/main.rs", "fn main() {}") .file("build1.rs", "fn main() {}") .file("build2.rs", "fn main() { std::process::exit(101); }") .build(); p.cargo("check -v") .masquerade_as_nightly_cargo(&["multiple-build-scripts"]) .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.1.0 ([ROOT]/foo) ... [ERROR] failed to run custom build command for `foo v0.1.0 ([ROOT]/foo)` Caused by: process didn't exit successfully: `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build2` ([EXIT_STATUS]: 101) ... "#]]) .run(); } #[cargo_test] fn build_script_with_conflicting_environment_variables() { // In this, multiple scripts set different values to same environment variables let p = project() .file( "Cargo.toml", r#" cargo-features = ["multiple-build-scripts"] [package] name = "foo" version = "0.1.0" edition = "2024" build = ["build1.rs", "build2.rs"] "#, ) .file( "src/main.rs", r#" const FOO: &'static str = env!("FOO"); fn main() { println!("{}", FOO); } "#, ) .file( "build1.rs", r#"fn main() { println!("cargo::rustc-env=FOO=bar1"); }"#, ) .file( "build2.rs", r#"fn main() { println!("cargo::rustc-env=FOO=bar2"); }"#, ) .build(); p.cargo("run -v") .masquerade_as_nightly_cargo(&["multiple-build-scripts"]) .with_status(0) .with_stdout_data(str![[r#" bar2 "#]]) .run(); } #[cargo_test] fn build_script_with_conflicting_out_dirs() { // In this, multiple scripts create file with same name in their respective OUT_DIR. let p = project() .file( "Cargo.toml", r#" cargo-features = ["multiple-build-scripts"] [package] name = "foo" version = "0.1.0" edition = "2024" build = ["build1.rs", "build2.rs"] "#, ) // OUT_DIR is set to the lexicographically largest build script's OUT_DIR by default .file( "src/main.rs", r#" include!(concat!(env!("OUT_DIR"), "/foo.rs")); fn main() { println!("{}", message()); } "#, ) .file( "build1.rs", r#" use std::env; use std::fs; use std::path::Path; fn main() { let out_dir = env::var_os("OUT_DIR").unwrap(); let dest_path = Path::new(&out_dir).join("foo.rs"); fs::write( &dest_path, "pub fn message() -> &'static str { \"Hello, from Build Script 1!\" } " ).unwrap(); }"#, ) .file( "build2.rs", r#" use std::env; use std::fs; use std::path::Path; fn main() { let out_dir = env::var_os("OUT_DIR").unwrap(); let dest_path = Path::new(&out_dir).join("foo.rs"); fs::write( &dest_path, "pub fn message() -> &'static str { \"Hello, from Build Script 2!\" } " ).unwrap(); }"#, ) .build(); p.cargo("run -v") .masquerade_as_nightly_cargo(&["multiple-build-scripts"]) .with_status(0) .with_stdout_data(str![[r#" Hello, from Build Script 2! "#]]) .run(); } #[cargo_test] fn rerun_untracks_other_files() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["multiple-build-scripts"] [package] name = "foo" version = "0.1.0" edition = "2024" build = ["build1.rs", "build2.rs"] "#, ) .file("src/main.rs", "fn main() {}") .file( "build1.rs", r#" fn main() { foo(); } fn foo() { let _path = "assets/foo.txt"; } "#, ) .file( "build2.rs", r#" fn main() { bar(); } fn bar() { let path = "assets/bar.txt"; println!("cargo::rerun-if-changed={path}"); }"#, ) .file("assets/foo.txt", "foo") .file("assets/bar.txt", "bar") .build(); p.cargo("check") .masquerade_as_nightly_cargo(&["multiple-build-scripts"]) .run(); // Editing foo.txt will also recompile now since they are separate build scripts p.change_file("assets/foo.txt", "foo updated"); p.cargo("check -v") .masquerade_as_nightly_cargo(&["multiple-build-scripts"]) .with_stderr_data(str![[r#" [DIRTY] foo v0.1.0 ([ROOT]/foo): the [..] [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build1` [RUNNING] `rustc --crate-name foo --edition=2024 src/main.rs [..] --crate-type bin [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Editing bar.txt will also recompile now since they are separate build scripts p.change_file("assets/bar.txt", "bar updated"); p.cargo("check -v") .masquerade_as_nightly_cargo(&["multiple-build-scripts"]) .with_stderr_data(str![[r#" [DIRTY] foo v0.1.0 ([ROOT]/foo): the [..] [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build[..]` [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build[..]` [RUNNING] `rustc --crate-name foo --edition=2024 src/main.rs [..] --crate-type bin [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } cargo-0.91.0/tests/testsuite/cache_lock.rs000064400000000000000000000253411046102023000166270ustar 00000000000000//! Tests for `CacheLock`. use std::thread::JoinHandle; use crate::prelude::*; use cargo::util::cache_lock::{CacheLockMode, CacheLocker}; use cargo_test_support::paths; use cargo_test_support::{retry, thread_wait_timeout, threaded_timeout}; use crate::config::GlobalContextBuilder; /// Helper to verify that it is OK to acquire the given lock (it shouldn't block). fn verify_lock_is_ok(mode: CacheLockMode) { let root = paths::root(); threaded_timeout(10, move || { let gctx = GlobalContextBuilder::new().root(root).build(); let locker = CacheLocker::new(); // This would block if it is held. let _lock = locker.lock(&gctx, mode).unwrap(); assert!(locker.is_locked(mode)); }); } /// Helper to acquire two locks from the same locker. fn a_b_nested(a: CacheLockMode, b: CacheLockMode) { let gctx = GlobalContextBuilder::new().build(); let locker = CacheLocker::new(); let lock1 = locker.lock(&gctx, a).unwrap(); assert!(locker.is_locked(a)); let lock2 = locker.lock(&gctx, b).unwrap(); assert!(locker.is_locked(b)); drop(lock2); drop(lock1); // Verify locks were unlocked. verify_lock_is_ok(CacheLockMode::Shared); verify_lock_is_ok(CacheLockMode::DownloadExclusive); verify_lock_is_ok(CacheLockMode::MutateExclusive); } /// Helper to acquire two locks from separate lockers, verifying that they /// don't block each other. fn a_then_b_separate_not_blocked(a: CacheLockMode, b: CacheLockMode, verify: CacheLockMode) { let gctx = GlobalContextBuilder::new().build(); let locker1 = CacheLocker::new(); let lock1 = locker1.lock(&gctx, a).unwrap(); assert!(locker1.is_locked(a)); let locker2 = CacheLocker::new(); let lock2 = locker2.lock(&gctx, b).unwrap(); assert!(locker2.is_locked(b)); let thread = verify_lock_would_block(verify); // Unblock the thread. drop(lock1); drop(lock2); // Verify the thread is unblocked. thread_wait_timeout::<()>(100, thread); } /// Helper to acquire two locks from separate lockers, verifying that the /// second one blocks. fn a_then_b_separate_blocked(a: CacheLockMode, b: CacheLockMode) { let gctx = GlobalContextBuilder::new().build(); let locker = CacheLocker::new(); let lock = locker.lock(&gctx, a).unwrap(); assert!(locker.is_locked(a)); let thread = verify_lock_would_block(b); // Unblock the thread. drop(lock); // Verify the thread is unblocked. thread_wait_timeout::<()>(100, thread); } /// Helper to verify that acquiring the given mode would block. /// /// Always call `thread_wait_timeout` on the result. #[must_use] fn verify_lock_would_block(mode: CacheLockMode) -> JoinHandle<()> { let root = paths::root(); // Spawn a thread that will block on the lock. let thread = std::thread::spawn(move || { let gctx = GlobalContextBuilder::new().root(root).build(); let locker2 = CacheLocker::new(); let lock2 = locker2.lock(&gctx, mode).unwrap(); assert!(locker2.is_locked(mode)); drop(lock2); }); // Verify that it blocked. retry(100, || { if let Ok(s) = std::fs::read_to_string(paths::root().join("shell.out")) { if s.trim().starts_with("Blocking waiting for file lock on") { return Some(()); } else { eprintln!("unexpected output: {s}"); // Try again, it might have been partially written. } } None }); thread } #[test] fn new_is_unlocked() { let locker = CacheLocker::new(); assert!(!locker.is_locked(CacheLockMode::Shared)); assert!(!locker.is_locked(CacheLockMode::DownloadExclusive)); assert!(!locker.is_locked(CacheLockMode::MutateExclusive)); } #[cargo_test] fn multiple_shared() { // Test that two nested shared locks from the same locker are safe to acquire. a_b_nested(CacheLockMode::Shared, CacheLockMode::Shared); } #[cfg_attr( target_os = "aix", ignore = "Test fails on AIX due to unsupported flock behaviour" )] #[cargo_test] fn multiple_shared_separate() { // Test that two independent shared locks are safe to acquire at the same time. a_then_b_separate_not_blocked( CacheLockMode::Shared, CacheLockMode::Shared, CacheLockMode::MutateExclusive, ); } #[cargo_test] fn multiple_download() { // That that two nested download locks from the same locker are safe to acquire. a_b_nested( CacheLockMode::DownloadExclusive, CacheLockMode::DownloadExclusive, ); } #[cargo_test] fn multiple_mutate() { // That that two nested mutate locks from the same locker are safe to acquire. a_b_nested( CacheLockMode::MutateExclusive, CacheLockMode::MutateExclusive, ); } #[cargo_test] #[should_panic(expected = "lock is not allowed")] fn download_then_shared() { // This sequence is not supported. a_b_nested(CacheLockMode::DownloadExclusive, CacheLockMode::Shared); } #[cargo_test] #[should_panic(expected = "lock upgrade from shared to exclusive not supported")] fn shared_then_mutate() { // This sequence is not supported. a_b_nested(CacheLockMode::Shared, CacheLockMode::MutateExclusive); } #[cargo_test] fn shared_then_download() { a_b_nested(CacheLockMode::Shared, CacheLockMode::DownloadExclusive); // Verify drop actually unlocked. verify_lock_is_ok(CacheLockMode::DownloadExclusive); verify_lock_is_ok(CacheLockMode::MutateExclusive); } #[cargo_test] fn mutate_then_shared() { a_b_nested(CacheLockMode::MutateExclusive, CacheLockMode::Shared); // Verify drop actually unlocked. verify_lock_is_ok(CacheLockMode::MutateExclusive); } #[cargo_test] fn download_then_mutate() { a_b_nested( CacheLockMode::DownloadExclusive, CacheLockMode::MutateExclusive, ); // Verify drop actually unlocked. verify_lock_is_ok(CacheLockMode::DownloadExclusive); verify_lock_is_ok(CacheLockMode::MutateExclusive); } #[cargo_test] fn mutate_then_download() { a_b_nested( CacheLockMode::MutateExclusive, CacheLockMode::DownloadExclusive, ); // Verify drop actually unlocked. verify_lock_is_ok(CacheLockMode::MutateExclusive); verify_lock_is_ok(CacheLockMode::DownloadExclusive); } #[cargo_test] fn readonly() { // In a permission denied situation, it should still allow a lock. It just // silently behaves as-if it was locked. let cargo_home = paths::home().join(".cargo"); std::fs::create_dir_all(&cargo_home).unwrap(); let mut perms = std::fs::metadata(&cargo_home).unwrap().permissions(); perms.set_readonly(true); std::fs::set_permissions(&cargo_home, perms).unwrap(); let gctx = GlobalContextBuilder::new().build(); let locker = CacheLocker::new(); for mode in [ CacheLockMode::Shared, CacheLockMode::DownloadExclusive, CacheLockMode::MutateExclusive, ] { let _lock1 = locker.lock(&gctx, mode).unwrap(); // Make sure it can recursively acquire the lock, too. let _lock2 = locker.lock(&gctx, mode).unwrap(); } } #[cfg_attr( target_os = "aix", ignore = "Test fails on AIX due to unsupported flock behaviour" )] #[cargo_test] fn download_then_shared_separate() { a_then_b_separate_not_blocked( CacheLockMode::DownloadExclusive, CacheLockMode::Shared, CacheLockMode::MutateExclusive, ); } #[cfg_attr( target_os = "aix", ignore = "Test fails on AIX due to unsupported flock behaviour" )] #[cargo_test] fn shared_then_download_separate() { a_then_b_separate_not_blocked( CacheLockMode::Shared, CacheLockMode::DownloadExclusive, CacheLockMode::MutateExclusive, ); } #[cfg_attr( target_os = "aix", ignore = "Test fails on AIX due to unsupported flock behaviour" )] #[cargo_test] fn multiple_download_separate() { // Test that with two independent download locks, the second blocks until // the first is released. a_then_b_separate_blocked( CacheLockMode::DownloadExclusive, CacheLockMode::DownloadExclusive, ); } #[cfg_attr( target_os = "aix", ignore = "Test fails on AIX due to unsupported flock behaviour" )] #[cargo_test] fn multiple_mutate_separate() { // Test that with two independent mutate locks, the second blocks until // the first is released. a_then_b_separate_blocked( CacheLockMode::MutateExclusive, CacheLockMode::MutateExclusive, ); } #[cfg_attr( target_os = "aix", ignore = "Test fails on AIX due to unsupported flock behaviour" )] #[cargo_test] fn shared_then_mutate_separate() { a_then_b_separate_blocked(CacheLockMode::Shared, CacheLockMode::MutateExclusive); } #[cfg_attr( target_os = "aix", ignore = "Test fails on AIX due to unsupported flock behaviour" )] #[cargo_test] fn download_then_mutate_separate() { a_then_b_separate_blocked( CacheLockMode::DownloadExclusive, CacheLockMode::MutateExclusive, ); } #[cfg_attr( target_os = "aix", ignore = "Test fails on AIX due to unsupported flock behaviour" )] #[cargo_test] fn mutate_then_download_separate() { a_then_b_separate_blocked( CacheLockMode::MutateExclusive, CacheLockMode::DownloadExclusive, ); } #[cfg_attr( target_os = "aix", ignore = "Test fails on AIX due to unsupported flock behaviour" )] #[cargo_test] fn mutate_then_shared_separate() { a_then_b_separate_blocked(CacheLockMode::MutateExclusive, CacheLockMode::Shared); } #[cargo_test(ignore_windows = "no method to prevent creating or locking a file")] fn mutate_err_is_atomic() { // Verifies that when getting a mutate lock, that if the first lock // succeeds, but the second one fails, that the first lock is released. let gctx = GlobalContextBuilder::new().build(); let locker = CacheLocker::new(); let cargo_home = gctx.home().as_path_unlocked(); let cache_path = cargo_home.join(".package-cache"); // This is a hacky way to force an error acquiring the download lock. By // making it a directory, it is unable to open it. // TODO: Unfortunately this doesn't work on Windows. I don't have any // ideas on how to simulate an error on Windows. cache_path.mkdir_p(); match locker.lock(&gctx, CacheLockMode::MutateExclusive) { Ok(_) => panic!("did not expect lock to succeed"), Err(e) => { let msg = format!("{e:?}"); assert!(msg.contains("failed to open:"), "{msg}"); } } assert!(!locker.is_locked(CacheLockMode::MutateExclusive)); assert!(!locker.is_locked(CacheLockMode::DownloadExclusive)); assert!(!locker.is_locked(CacheLockMode::Shared)); cache_path.rm_rf(); verify_lock_is_ok(CacheLockMode::DownloadExclusive); verify_lock_is_ok(CacheLockMode::Shared); verify_lock_is_ok(CacheLockMode::MutateExclusive); } cargo-0.91.0/tests/testsuite/cache_messages.rs000064400000000000000000000353411046102023000175070ustar 00000000000000//! Tests for caching compiler diagnostics. use crate::prelude::*; use crate::utils::tools; use cargo_test_support::str; use cargo_test_support::{basic_manifest, is_coarse_mtime, project, registry::Package, sleep_ms}; use super::messages::raw_rustc_output; fn as_str(bytes: &[u8]) -> &str { std::str::from_utf8(bytes).expect("valid utf-8") } #[cargo_test] fn simple() { // A simple example that generates two warnings (unused functions). let p = project() .file( "src/lib.rs", " fn a() {} fn b() {} ", ) .build(); // Capture what rustc actually emits. This is done to avoid relying on the // exact message formatting in rustc. let rustc_output = raw_rustc_output(&p, "src/lib.rs", &[]); // -q so the output is the same as rustc (no "Compiling" or "Finished"). let cargo_output1 = p.cargo("check -q --color=never").run(); assert_eq!(rustc_output, as_str(&cargo_output1.stderr)); assert!(cargo_output1.stdout.is_empty()); // Check that the cached version is exactly the same. let cargo_output2 = p.cargo("check -q").run(); assert_eq!(rustc_output, as_str(&cargo_output2.stderr)); assert!(cargo_output2.stdout.is_empty()); } // same as `simple`, except everything is using the short format #[cargo_test] fn simple_short() { let p = project() .file( "src/lib.rs", " fn a() {} fn b() {} ", ) .build(); let rustc_output = raw_rustc_output(&p, "src/lib.rs", &["--error-format=short"]); let cargo_output1 = p .cargo("check -q --color=never --message-format=short") .run(); assert_eq!(rustc_output, as_str(&cargo_output1.stderr)); // assert!(cargo_output1.stdout.is_empty()); let cargo_output2 = p.cargo("check -q --message-format=short").run(); println!("{}", String::from_utf8_lossy(&cargo_output2.stdout)); assert_eq!(rustc_output, as_str(&cargo_output2.stderr)); assert!(cargo_output2.stdout.is_empty()); } #[cargo_test] fn color() { // Check enabling/disabling color. let p = project().file("src/lib.rs", "fn a() {}").build(); // Hack for issue in fwdansi 1.1. It is squashing multiple resets // into a single reset. // https://github.com/kennytm/fwdansi/issues/2 fn normalize(s: &str) -> String { #[cfg(windows)] return s.replace("\x1b[0m\x1b[0m", "\x1b[0m"); #[cfg(not(windows))] return s.to_string(); } let compare = |a, b| { assert_eq!(normalize(a), normalize(b)); }; // Capture the original color output. let rustc_color = raw_rustc_output(&p, "src/lib.rs", &["--color=always"]); assert!(rustc_color.contains("\x1b[")); // Capture the original non-color output. let rustc_nocolor = raw_rustc_output(&p, "src/lib.rs", &[]); assert!(!rustc_nocolor.contains("\x1b[")); // First pass, non-cached, with color, should be the same. let cargo_output1 = p.cargo("check -q --color=always").run(); compare(&rustc_color, as_str(&cargo_output1.stderr)); // Replay cached, with color. let cargo_output2 = p.cargo("check -q --color=always").run(); compare(&rustc_color, as_str(&cargo_output2.stderr)); // Replay cached, no color. let cargo_output_nocolor = p.cargo("check -q --color=never").run(); compare(&rustc_nocolor, as_str(&cargo_output_nocolor.stderr)); } #[cargo_test] fn cached_as_json() { // Check that cached JSON output is the same. let p = project().file("src/lib.rs", "fn a() {}").build(); // Grab the non-cached output, feature disabled. // NOTE: When stabilizing, this will need to be redone. let cargo_output = p.cargo("check --message-format=json").run(); let orig_cargo_out = as_str(&cargo_output.stdout); assert!(orig_cargo_out.contains("compiler-message")); p.cargo("clean").run(); // Check JSON output, not fresh. let cargo_output1 = p.cargo("check --message-format=json").run(); assert_eq!(as_str(&cargo_output1.stdout), orig_cargo_out); // Check JSON output, fresh. let cargo_output2 = p.cargo("check --message-format=json").run(); // The only difference should be this field. let fix_fresh = as_str(&cargo_output2.stdout).replace("\"fresh\":true", "\"fresh\":false"); assert_eq!(fix_fresh, orig_cargo_out); } #[cargo_test] fn clears_cache_after_fix() { // Make sure the cache is invalidated when there is no output. let p = project().file("src/lib.rs", "fn asdf() {}").build(); // Fill the cache. p.cargo("check") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [WARNING] function `asdf` is never used ... [WARNING] `foo` (lib) generated 1 warning [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let cpath = p .glob("target/debug/.fingerprint/foo-*/output-*") .next() .unwrap() .unwrap(); assert!(std::fs::read_to_string(cpath).unwrap().contains("asdf")); // Fix it. if is_coarse_mtime() { sleep_ms(1000); } p.change_file("src/lib.rs", ""); p.cargo("check") .with_stdout_data("") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert_eq!( p.glob("target/debug/.fingerprint/foo-*/output-*").count(), 0 ); // And again, check the cache is correct. p.cargo("check") .with_stdout_data("") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn rustdoc() { // Create a warning in rustdoc. let p = project() .file( "src/lib.rs", " #![warn(missing_docs)] pub fn f() {} ", ) .build(); let rustdoc_output = p.cargo("doc -q --color=always").run(); let rustdoc_stderr = as_str(&rustdoc_output.stderr); assert!(rustdoc_stderr.contains("missing")); assert!(rustdoc_stderr.contains("\x1b[")); assert_eq!( p.glob("target/debug/.fingerprint/foo-*/output-*").count(), 1 ); // Check the cached output. let rustdoc_output = p.cargo("doc -q --color=always").run(); assert_eq!(as_str(&rustdoc_output.stderr), rustdoc_stderr); } #[cargo_test] fn fix() { // Make sure `fix` is not broken by caching. let p = project().file("src/lib.rs", "pub fn try() {}").build(); p.cargo("fix --edition --allow-no-vcs").run(); assert_eq!(p.read_file("src/lib.rs"), "pub fn r#try() {}"); } #[cargo_test] fn very_verbose() { // Handle cap-lints in dependencies. Package::new("bar", "1.0.0") .file("src/lib.rs", "fn not_used() {}") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check -vv") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [CHECKING] bar v1.0.0 [RUNNING] [..] [WARNING] function `not_used` is never used ... [CHECKING] foo v0.1.0 ([ROOT]/foo) [RUNNING] [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check -vv") .with_stderr_data(str![[r#" [FRESH] bar v1.0.0 [WARNING] function `not_used` is never used ... [WARNING] `bar` (lib) generated 1 warning [FRESH] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn doesnt_create_extra_files() { // Ensure it doesn't create `output` files when not needed. Package::new("dep", "1.0.0") .file("src/lib.rs", "fn unused() {}") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] dep = "1.0" "#, ) .file("src/lib.rs", "") .file("src/main.rs", "fn main() {}") .build(); p.cargo("check").run(); assert_eq!( p.glob("target/debug/.fingerprint/foo-*/output-*").count(), 0 ); assert_eq!( p.glob("target/debug/.fingerprint/dep-*/output-*").count(), 0 ); if is_coarse_mtime() { sleep_ms(1000); } p.change_file("src/lib.rs", "fn unused() {}"); p.cargo("check").run(); assert_eq!( p.glob("target/debug/.fingerprint/foo-*/output-*").count(), 1 ); } #[cargo_test] fn replay_non_json() { // Handles non-json output. let rustc = project() .at("rustc") .file("Cargo.toml", &basic_manifest("rustc_alt", "1.0.0")) .file( "src/main.rs", r#" fn main() { eprintln!("line 1"); eprintln!("line 2"); let r = std::process::Command::new("rustc") .args(std::env::args_os().skip(1)) .status(); std::process::exit(r.unwrap().code().unwrap_or(2)); } "#, ) .build(); rustc.cargo("build").run(); let p = project().file("src/lib.rs", "").build(); p.cargo("check") .env("RUSTC", rustc.bin("rustc_alt")) .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) line 1 line 2 [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .env("RUSTC", rustc.bin("rustc_alt")) .with_stderr_data(str![[r#" line 1 line 2 [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn caching_large_output() { // Handles large number of messages. // This is an arbitrary amount that is greater than the 100 used in // job_queue. This is here to check for deadlocks or any other problems. const COUNT: usize = 250; let rustc = project() .at("rustc") .file("Cargo.toml", &basic_manifest("rustc_alt", "1.0.0")) .file( "src/main.rs", &format!( r#" fn main() {{ for i in 0..{} {{ eprintln!("{{{{\"message\": \"test message {{}}\", \"level\": \"warning\", \ \"spans\": [], \"children\": [], \"rendered\": \"test message {{}}\"}}}}", i, i); }} let r = std::process::Command::new("rustc") .args(std::env::args_os().skip(1)) .status(); std::process::exit(r.unwrap().code().unwrap_or(2)); }} "#, COUNT ), ) .build(); let mut expected = String::new(); for i in 0..COUNT { expected.push_str(&format!("test message {}\n", i)); } rustc.cargo("build").run(); let p = project().file("src/lib.rs", "").build(); p.cargo("check") .env("RUSTC", rustc.bin("rustc_alt")) .with_stderr_data(&format!( "\ [CHECKING] foo v0.0.1 ([ROOT]/foo) {}[WARNING] `foo` (lib) generated 250 warnings [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s ", expected )) .run(); p.cargo("check") .env("RUSTC", rustc.bin("rustc_alt")) .with_stderr_data(&format!( "\ {}[WARNING] `foo` (lib) generated 250 warnings [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s ", expected )) .run(); } #[cargo_test] fn rustc_workspace_wrapper() { let p = project() .file( "src/lib.rs", "pub fn f() { assert!(true); }\n\ fn unused_func() {}", ) .build(); p.cargo("check -v") .env("RUSTC_WORKSPACE_WRAPPER", tools::echo_wrapper()) .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] [..]/rustc-echo-wrapper[EXE] rustc --crate-name foo [..] WRAPPER CALLED: rustc --crate-name foo --edition=2015 src/lib.rs [..] [WARNING] function `unused_func` is never used ... [WARNING] `foo` (lib) generated 1 warning [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Check without a wrapper should rebuild p.cargo("check -v") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc[..]` [WARNING] function `unused_func` is never used ... [WARNING] `foo` (lib) generated 1 warning [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .with_stdout_data("") .run(); // Again, reading from the cache. p.cargo("check -v") .env("RUSTC_WORKSPACE_WRAPPER", tools::echo_wrapper()) .with_stderr_data(str![[r#" [FRESH] foo v0.0.1 ([ROOT]/foo) WRAPPER CALLED: rustc [..] ... [WARNING] `foo` (lib) generated 1 warning [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .with_stdout_data("") .run(); // And `check` should also be fresh, reading from cache. p.cargo("check -v") .with_stderr_data(str![[r#" [FRESH] foo v0.0.1 ([ROOT]/foo) [WARNING] function `unused_func` is never used ... [WARNING] `foo` (lib) generated 1 warning [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .with_stdout_data("") .run(); } #[cargo_test] fn wacky_hashless_fingerprint() { // On Windows, executables don't have hashes. This checks for a bad // assumption that caused bad caching. let p = project() .file("src/bin/a.rs", "fn main() { let unused = 1; }") .file("src/bin/b.rs", "fn main() {}") .build(); p.cargo("check --bin b") .with_stderr_does_not_contain("[..]unused[..]") .run(); p.cargo("check --bin a") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [WARNING] unused variable: `unused` ... [WARNING] `foo` (bin "a") generated 1 warning [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // This should not pick up the cache from `a`. p.cargo("check --bin b") .with_stderr_does_not_contain("[..]unused[..]") .run(); } cargo-0.91.0/tests/testsuite/cargo/help/mod.rs000064400000000000000000000004341046102023000173520ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo/help/stdout.term.svg000064400000000000000000000203171046102023000212400ustar 00000000000000 Rust's package manager Usage: cargo [..][OPTIONS] [COMMAND] cargo [..][OPTIONS] -Zscript <MANIFEST_RS> [ARGS]... Options: -V, --version Print version info and exit --list List installed commands --explain <CODE> Provide a detailed explanation of a rustc error message -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] -C <DIRECTORY> Change to DIRECTORY before doing anything (nightly-only) --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Commands: build, b Compile the current package check, c Analyze the current package and report errors, but don't build object files clean Remove the target directory doc, d Build this package's and its dependencies' documentation new Create a new cargo package init Create a new cargo package in an existing directory add Add dependencies to a manifest file remove Remove dependencies from a manifest file run, r Run a binary or example of the local package test, t Run the tests bench Run the benchmarks update Update dependencies listed in Cargo.lock search Search registry for crates publish Package and upload this package to the registry install Install a Rust binary uninstall Uninstall a Rust binary ... See all commands with --list See 'cargo help <command>' for more information on a specific command. cargo-0.91.0/tests/testsuite/cargo/mod.rs000064400000000000000000000000261046102023000164170ustar 00000000000000mod help; mod z_help; cargo-0.91.0/tests/testsuite/cargo/z_help/mod.rs000064400000000000000000000005261046102023000177050ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .masquerade_as_nightly_cargo(&["-Z help"]) .args(["-Z", "help"]) .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo/z_help/stdout.term.svg000064400000000000000000000154751046102023000216020ustar 00000000000000 Available unstable (nightly-only) flags: -Z allow-features Allow *only* the listed unstable features -Z asymmetric-token Allows authenticating with asymmetric tokens -Z avoid-dev-deps Avoid installing dev-dependencies if possible -Z binary-dep-depinfo Track changes to dependency artifacts -Z bindeps Allow Cargo packages to depend on bin, cdylib, and staticlib crates, and use the artifacts built by those crates -Z build-dir Enable the `build.build-dir` option in .cargo/config.toml file -Z build-std Enable Cargo to compile the standard library itself as part of a crate graph compilation -Z build-std-features Configure features enabled for the standard library itself when building the standard library -Z cargo-lints Enable the `[lints.cargo]` table -Z checksum-freshness Use a checksum to determine if output is fresh rather than filesystem mtime -Z codegen-backend Enable the `codegen-backend` option in profiles in .cargo/config.toml file -Z config-include Enable the `include` key in config files -Z direct-minimal-versions Resolve minimal dependency versions instead of maximum (direct dependencies only) -Z dual-proc-macros Build proc-macros for both the host and the target -Z feature-unification Enable new feature unification modes in workspaces -Z fix-edition Permanently unstable edition migration helper -Z gc Track cache usage and "garbage collect" unused files -Z git Enable support for shallow git fetch operations -Z gitoxide Use gitoxide for the given git interactions, or all of them if no argument is given -Z host-config Enable the `[host]` section in the .cargo/config.toml file -Z minimal-versions Resolve minimal dependency versions instead of maximum -Z msrv-policy Enable rust-version aware policy within cargo -Z mtime-on-use Configure Cargo to update the mtime of used files -Z no-embed-metadata Avoid embedding metadata in library artifacts -Z no-index-update Do not update the registry index even if the cache is outdated -Z panic-abort-tests Enable support to run tests with -Cpanic=abort -Z profile-hint-mostly-unused Enable the `hint-mostly-unused` setting in profiles to mark a crate as mostly unused. -Z profile-rustflags Enable the `rustflags` option in profiles in .cargo/config.toml file -Z public-dependency Respect a dependency's `public` field in Cargo.toml to control public/private dependencies -Z publish-timeout Enable the `publish.timeout` key in .cargo/config.toml file -Z root-dir Set the root directory relative to which paths are printed (defaults to workspace root) -Z rustdoc-depinfo Use dep-info files in rustdoc rebuild detection -Z rustdoc-map Allow passing external documentation mappings to rustdoc -Z rustdoc-scrape-examples Allows Rustdoc to scrape code examples from reverse-dependencies -Z sbom Enable the `sbom` option in build config in .cargo/config.toml file -Z script Enable support for single-file, `.rs` packages -Z target-applies-to-host Enable the `target-applies-to-host` key in the .cargo/config.toml file -Z trim-paths Enable the `trim-paths` option in profiles -Z unstable-options Allow the usage of unstable options -Z warnings Allow use of the build.warnings config key Run with `cargo -Z [FLAG] [COMMAND]` See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html for more information about these flags. cargo-0.91.0/tests/testsuite/cargo_add/add_basic/mod.rs000064400000000000000000000020131046102023000211160ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/add_basic/stderr.term.svg000064400000000000000000000017641046102023000227770ustar 00000000000000 Updating `dummy-registry` index Adding my-package v99999.0.0 to dependencies Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/add_multiple/mod.rs000064400000000000000000000021631046102023000216760ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for name in ["my-package1", "my-package2"] { for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new(name, ver).publish(); } } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package1 my-package2") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/add_multiple/stderr.term.svg000064400000000000000000000022071046102023000235420ustar 00000000000000 Updating `dummy-registry` index Adding my-package1 v99999.0.0 to dependencies Adding my-package2 v99999.0.0 to dependencies Locking 2 packages to latest compatible versions cargo-0.91.0/tests/testsuite/cargo_add/add_no_vendored_package_with_alter_registry/mod.rs000064400000000000000000000021211046102023000301640ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::alt_init(); cargo_test_support::registry::Package::new("linked-hash-map", "0.5.4") .feature("clippy", &[]) .feature("heapsize", &[]) .feature("heapsize_impl", &[]) .feature("nightly", &[]) .feature("serde", &[]) .feature("serde_impl", &[]) .feature("serde_test", &[]) .alternative(true) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("linked_hash_map --registry alternative") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/add_no_vendored_package_with_alter_registry/stderr.term.svg000064400000000000000000000042661046102023000320450ustar 00000000000000 Updating `alternative` index warning: translating `linked_hash_map` to `linked-hash-map` Adding linked-hash-map v0.5.4 to dependencies Features: - clippy - heapsize - heapsize_impl - nightly - serde - serde_impl - serde_test Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/add_no_vendored_package_with_vendor/mod.rs000064400000000000000000000013031046102023000264230ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("cbindgen") .current_dir(cwd) .assert() .failure() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/add_no_vendored_package_with_vendor/stderr.term.svg000064400000000000000000000014171046102023000302760ustar 00000000000000 error: the crate `cbindgen` could not be found in registry index. cargo-0.91.0/tests/testsuite/cargo_add/add_toolchain/mod.rs000064400000000000000000000013031046102023000220160ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("+nightly") .current_dir(cwd) .assert() .failure() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/add_toolchain/stderr.term.svg000064400000000000000000000016101046102023000236640ustar 00000000000000 error: invalid character `+` in dependency name: `+nightly` Use `cargo +nightly add` if you meant to use the `nightly` toolchain. cargo-0.91.0/tests/testsuite/cargo_add/build/mod.rs000064400000000000000000000022231046102023000203270ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for name in ["my-build-package1", "my-build-package2"] { for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new(name, ver).publish(); } } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("--build my-build-package1 my-build-package2") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/build/stderr.term.svg000064400000000000000000000022371046102023000222010ustar 00000000000000 Updating `dummy-registry` index Adding my-build-package1 v99999.0.0 to build-dependencies Adding my-build-package2 v99999.0.0 to build-dependencies Locking 2 packages to latest compatible versions cargo-0.91.0/tests/testsuite/cargo_add/build_prefer_existing_version/mod.rs000064400000000000000000000021631046102023000253540ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::alt_init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", ver) .alternative(true) .publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("cargo-list-test-fixture-dependency --build") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/build_prefer_existing_version/stderr.term.svg000064400000000000000000000023621046102023000272220ustar 00000000000000 Adding cargo-list-test-fixture-dependency (local) to build-dependencies Features: - one - two Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/change_rename_target/mod.rs000064400000000000000000000021751046102023000233600ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for name in ["my-package1", "my-package2"] { for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new(name, ver).publish(); } } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package2 --rename some-package") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/change_rename_target/stderr.term.svg000064400000000000000000000021761046102023000252260ustar 00000000000000 Updating `dummy-registry` index Adding my-package2 v99999.0.0 to optional dependencies Adding feature `some-package` Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/cyclic_features/mod.rs000064400000000000000000000017251046102023000224020ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("test_cyclic_features", "0.1.1") .feature("default", &["feature-one", "feature-two"]) .feature("feature-one", &["feature-two"]) .feature("feature-two", &["feature-one"]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("test_cyclic_features") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/cyclic_features/stderr.term.svg000064400000000000000000000025221046102023000242430ustar 00000000000000 Updating `dummy-registry` index Adding test_cyclic_features v0.1.1 to dependencies Features: + feature-one + feature-two Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/default_features/mod.rs000064400000000000000000000022141046102023000225520ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for name in ["my-package1", "my-package2"] { for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new(name, ver).publish(); } } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package1 my-package2@0.4.1 --default-features") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/default_features/stderr.term.svg000064400000000000000000000026211046102023000244210ustar 00000000000000 Updating `dummy-registry` index Adding my-package1 v99999.0.0 to dependencies Adding my-package2 v0.4.1 to dependencies Locking 2 packages to latest compatible versions Adding my-package2 v0.4.1+my-package (available: v99999.0.0+my-package) cargo-0.91.0/tests/testsuite/cargo_add/deprecated_default_features/mod.rs000064400000000000000000000020141046102023000247300ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package") .current_dir(&cwd) .assert() .failure() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/deprecated_default_features/stderr.term.svg000064400000000000000000000014621046102023000266030ustar 00000000000000 error: Use of `default_features` in `my-package` is unsupported, please switch to `default-features` cargo-0.91.0/tests/testsuite/cargo_add/deprecated_section/mod.rs000064400000000000000000000020141046102023000230520ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package") .current_dir(&cwd) .assert() .failure() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/deprecated_section/stderr.term.svg000064400000000000000000000014511046102023000247230ustar 00000000000000 error: Deprecated dependency sections are unsupported: dev_dependencies, build_dependencies cargo-0.91.0/tests/testsuite/cargo_add/detect_workspace_inherit/mod.rs000064400000000000000000000013121046102023000242760ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .args(["foo", "-p", "bar"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/detect_workspace_inherit/stderr.term.svg000064400000000000000000000013421046102023000261460ustar 00000000000000 Adding foo (workspace) to dependencies cargo-0.91.0/tests/testsuite/cargo_add/detect_workspace_inherit_features/mod.rs000064400000000000000000000013401046102023000261750ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .args(["foo", "-p", "bar", "--features", "test"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/detect_workspace_inherit_features/stderr.term.svg000064400000000000000000000035731046102023000300540ustar 00000000000000 Adding foo (workspace) to dependencies Features as of v0.0.0: + default-base + default-merge-base + default-test-base + merge + merge-base + test + test-base - unrelated cargo-0.91.0/tests/testsuite/cargo_add/detect_workspace_inherit_optional/mod.rs000064400000000000000000000013301046102023000262030ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .args(["foo", "-p", "bar", "--optional"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/detect_workspace_inherit_optional/stderr.term.svg000064400000000000000000000015401046102023000300530ustar 00000000000000 Adding foo (workspace) to optional dependencies Adding feature `foo` cargo-0.91.0/tests/testsuite/cargo_add/detect_workspace_inherit_path_base/mod.rs000064400000000000000000000013771046102023000263170ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .args(["foo", "-p", "bar"]) .current_dir(cwd) .masquerade_as_nightly_cargo(&["path-base"]) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/detect_workspace_inherit_path_base/stderr.term.svg000064400000000000000000000013421046102023000301540ustar 00000000000000 Adding foo (workspace) to dependencies cargo-0.91.0/tests/testsuite/cargo_add/detect_workspace_inherit_public/mod.rs000064400000000000000000000014231046102023000256370ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .args(["foo", "-p", "bar", "--public"]) .masquerade_as_nightly_cargo(&["public-dependency"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/detect_workspace_inherit_public/stderr.term.svg000064400000000000000000000013511046102023000275040ustar 00000000000000 Adding foo (workspace) to public dependencies cargo-0.91.0/tests/testsuite/cargo_add/dev/mod.rs000064400000000000000000000022111046102023000200030ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for name in ["my-dev-package1", "my-dev-package2"] { for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new(name, ver).publish(); } } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("--dev my-dev-package1 my-dev-package2") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/dev/stderr.term.svg000064400000000000000000000022271046102023000216570ustar 00000000000000 Updating `dummy-registry` index Adding my-dev-package1 v99999.0.0 to dev-dependencies Adding my-dev-package2 v99999.0.0 to dev-dependencies Locking 2 packages to latest compatible versions cargo-0.91.0/tests/testsuite/cargo_add/dev_build_conflict/mod.rs000064400000000000000000000020271046102023000230500ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package --dev --build") .current_dir(cwd) .assert() .code(1) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/dev_build_conflict/stderr.term.svg000064400000000000000000000041141046102023000247140ustar 00000000000000 error: the argument '--dev' cannot be used with '--build' Usage: cargo add [OPTIONS] <DEP>[@<VERSION>] ... cargo add [OPTIONS] --path <PATH> ... cargo add [OPTIONS] --git <URL> ... For more information, try '--help'. cargo-0.91.0/tests/testsuite/cargo_add/dev_existing_path_base/in/.cargo/config.toml000064400000000000000000000000331046102023000265170ustar 00000000000000[path-bases] my_base = "." cargo-0.91.0/tests/testsuite/cargo_add/dev_existing_path_base/mod.rs000064400000000000000000000014501046102023000237270ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("cargo-list-test-fixture-dependency --dev") .current_dir(&cwd) .masquerade_as_nightly_cargo(&["path-base"]) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/dev_existing_path_base/stderr.term.svg000064400000000000000000000016171046102023000256010ustar 00000000000000 Adding cargo-list-test-fixture-dependency (local) to dev-dependencies Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/dev_prefer_existing_version/mod.rs000064400000000000000000000021611046102023000250310ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::alt_init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", ver) .alternative(true) .publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("cargo-list-test-fixture-dependency --dev") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/dev_prefer_existing_version/stderr.term.svg000064400000000000000000000023751046102023000267050ustar 00000000000000 Adding cargo-list-test-fixture-dependency (local) to dev-dependencies Features as of v0.0.0: - one - two Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/dry_run/mod.rs000064400000000000000000000020251046102023000207120ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package --dry-run") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/dry_run/stderr.term.svg000064400000000000000000000020431046102023000225570ustar 00000000000000 Updating `dummy-registry` index Adding my-package v99999.0.0 to dependencies warning: aborting add due to dry run cargo-0.91.0/tests/testsuite/cargo_add/empty_dep_name/mod.rs000064400000000000000000000013011046102023000222120ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("@1.2.3") .current_dir(cwd) .assert() .failure() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/empty_dep_name/stderr.term.svg000064400000000000000000000013611046102023000240650ustar 00000000000000 error: package name cannot be empty cargo-0.91.0/tests/testsuite/cargo_add/feature_suggestion_multiple/mod.rs000064400000000000000000000012471046102023000250520ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::current_dir; use cargo_test_support::file; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; cargo_test_support::registry::Package::new("my-package", "0.1.0+my-package") .feature("bar", &[]) .feature("foo", &[]) .publish(); snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package --features baz --features feo") .current_dir(cwd) .assert() .failure() .stderr_eq(file!["stderr.term.svg"]); } cargo-0.91.0/tests/testsuite/cargo_add/feature_suggestion_multiple/stderr.term.svg000064400000000000000000000025211046102023000267130ustar 00000000000000 Updating `dummy-registry` index Adding my-package v0.1.0 to dependencies error: unrecognized features for crate my-package: baz, feo help: a feature with a similar name exists: `bar` help: a feature with a similar name exists: `foo` cargo-0.91.0/tests/testsuite/cargo_add/feature_suggestion_none/mod.rs000064400000000000000000000012421046102023000241510ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::current_dir; use cargo_test_support::file; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; cargo_test_support::registry::Package::new("my-package", "0.1.0+my-package") .feature("bar", &[]) .feature("foo", &[]) .publish(); snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package --features none_existent") .current_dir(cwd) .assert() .failure() .stderr_eq(file!["stderr.term.svg"]); } cargo-0.91.0/tests/testsuite/cargo_add/feature_suggestion_none/stderr.term.svg000064400000000000000000000023511046102023000260200ustar 00000000000000 Updating `dummy-registry` index Adding my-package v0.1.0 to dependencies error: unrecognized feature for crate my-package: none_existent disabled features: bar, foo cargo-0.91.0/tests/testsuite/cargo_add/feature_suggestion_single/mod.rs000064400000000000000000000011731046102023000244760ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::current_dir; use cargo_test_support::file; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; cargo_test_support::registry::Package::new("my-package", "0.1.0+my-package") .feature("bar", &[]) .publish(); snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package --features baz") .current_dir(cwd) .assert() .failure() .stderr_eq(file!["stderr.term.svg"]); } cargo-0.91.0/tests/testsuite/cargo_add/feature_suggestion_single/stderr.term.svg000064400000000000000000000022731046102023000263450ustar 00000000000000 Updating `dummy-registry` index Adding my-package v0.1.0 to dependencies error: unrecognized feature for crate my-package: baz help: a feature with a similar name exists: `bar` cargo-0.91.0/tests/testsuite/cargo_add/features/mod.rs000064400000000000000000000016661046102023000210600ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") .feature("nose", &[]) .feature("mouth", &[]) .feature("eyes", &[]) .feature("ears", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("your-face --features eyes") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/features/stderr.term.svg000064400000000000000000000031221046102023000227120ustar 00000000000000 Updating `dummy-registry` index Adding your-face v99999.0.0 to dependencies Features: + eyes - ears - mouth - nose Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/features_activated_over_limit/mod.rs000064400000000000000000000023211046102023000253220ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; use itertools::Itertools; #[cargo_test] fn case() { const MANY_FEATURES_COUNT: usize = 200; const ACTIVATED_FEATURES_COUNT: usize = 100; cargo_test_support::registry::init(); let mut test_package = cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package"); for i in 0..MANY_FEATURES_COUNT { test_package.feature(format!("eyes{i:03}").as_str(), &[]); } test_package.publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; let features = (0..ACTIVATED_FEATURES_COUNT) .map(|i| format!("eyes{i:03}")) .join(","); snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line(format!("your-face --features {features}").as_str()) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/features_activated_over_limit/stderr.term.svg000064400000000000000000000023671046102023000272010ustar 00000000000000 Updating `dummy-registry` index Adding your-face v99999.0.0 to dependencies Features: 100 activated features 100 deactivated features Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/features_deactivated_over_limit/mod.rs000064400000000000000000000023201046102023000256320ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; use itertools::Itertools; #[cargo_test] fn case() { const MANY_FEATURES_COUNT: usize = 200; const ACTIVATED_FEATURES_COUNT: usize = 30; cargo_test_support::registry::init(); let mut test_package = cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package"); for i in 0..MANY_FEATURES_COUNT { test_package.feature(format!("eyes{i:03}").as_str(), &[]); } test_package.publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; let features = (0..ACTIVATED_FEATURES_COUNT) .map(|i| format!("eyes{i:03}")) .join(","); snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line(format!("your-face --features {features}").as_str()) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/features_deactivated_over_limit/stderr.term.svg000064400000000000000000000116731046102023000275120ustar 00000000000000 Updating `dummy-registry` index Adding your-face v99999.0.0 to dependencies Features: + eyes000 + eyes001 + eyes002 + eyes003 + eyes004 + eyes005 + eyes006 + eyes007 + eyes008 + eyes009 + eyes010 + eyes011 + eyes012 + eyes013 + eyes014 + eyes015 + eyes016 + eyes017 + eyes018 + eyes019 + eyes020 + eyes021 + eyes022 + eyes023 + eyes024 + eyes025 + eyes026 + eyes027 + eyes028 + eyes029 170 deactivated features Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/features_empty/mod.rs000064400000000000000000000016641046102023000222740ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") .feature("nose", &[]) .feature("mouth", &[]) .feature("eyes", &[]) .feature("ears", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("your-face --features ''") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/features_empty/stderr.term.svg000064400000000000000000000031201046102023000241260ustar 00000000000000 Updating `dummy-registry` index Adding your-face v99999.0.0 to dependencies Features: - ears - eyes - mouth - nose Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/features_error_activated_over_limit/mod.rs000064400000000000000000000020761046102023000265420ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { const MANY_FEATURES_COUNT: usize = 50; cargo_test_support::registry::init(); let mut test_package = cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package"); for i in 0..MANY_FEATURES_COUNT { test_package.feature(format!("eyes{i:03}").as_str(), &[]); } test_package.publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; let features = "eees100,eees101"; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line(format!("your-face --features {features}").as_str()) .current_dir(cwd) .assert() .failure() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/features_error_activated_over_limit/stderr.term.svg000064400000000000000000000025431046102023000304060ustar 00000000000000 Updating `dummy-registry` index Adding your-face v99999.0.0 to dependencies error: unrecognized features for crate your-face: eees100, eees101 help: a feature with a similar name exists: `eyes000` help: a feature with a similar name exists: `eyes001` cargo-0.91.0/tests/testsuite/cargo_add/features_error_deactivated_over_limit/mod.rs000064400000000000000000000020771046102023000270540ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { const MANY_FEATURES_COUNT: usize = 200; cargo_test_support::registry::init(); let mut test_package = cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package"); for i in 0..MANY_FEATURES_COUNT { test_package.feature(format!("eyes{i:03}").as_str(), &[]); } test_package.publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; let features = "eees100,eees101"; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line(format!("your-face --features {features}").as_str()) .current_dir(cwd) .assert() .failure() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/features_error_deactivated_over_limit/stderr.term.svg000064400000000000000000000025431046102023000307170ustar 00000000000000 Updating `dummy-registry` index Adding your-face v99999.0.0 to dependencies error: unrecognized features for crate your-face: eees100, eees101 help: a feature with a similar name exists: `eyes100` help: a feature with a similar name exists: `eyes101` cargo-0.91.0/tests/testsuite/cargo_add/features_multiple_occurrences/mod.rs000064400000000000000000000017061046102023000253610ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") .feature("nose", &[]) .feature("mouth", &[]) .feature("eyes", &[]) .feature("ears", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("your-face --features eyes --features nose") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/features_multiple_occurrences/stderr.term.svg000064400000000000000000000031241046102023000272220ustar 00000000000000 Updating `dummy-registry` index Adding your-face v99999.0.0 to dependencies Features: + eyes + nose - ears - mouth Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/features_preserve/mod.rs000064400000000000000000000016461046102023000227710ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") .feature("nose", &[]) .feature("mouth", &[]) .feature("eyes", &[]) .feature("ears", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("your-face") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/features_preserve/stderr.term.svg000064400000000000000000000031221046102023000246250ustar 00000000000000 Updating `dummy-registry` index Adding your-face v99999.0.0 to dependencies Features: + eyes - ears - mouth - nose Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/features_spaced_values/mod.rs000064400000000000000000000016731046102023000237540ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") .feature("nose", &[]) .feature("mouth", &[]) .feature("eyes", &[]) .feature("ears", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("your-face --features eyes,nose") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/features_spaced_values/stderr.term.svg000064400000000000000000000031241046102023000256120ustar 00000000000000 Updating `dummy-registry` index Adding your-face v99999.0.0 to dependencies Features: + eyes + nose - ears - mouth Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/features_unknown/mod.rs000064400000000000000000000016661046102023000226370ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") .feature("nose", &[]) .feature("mouth", &[]) .feature("eyes", &[]) .feature("ears", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("your-face --features noze") .current_dir(cwd) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/features_unknown/stderr.term.svg000064400000000000000000000022771046102023000245030ustar 00000000000000 Updating `dummy-registry` index Adding your-face v99999.0.0 to dependencies error: unrecognized feature for crate your-face: noze help: a feature with a similar name exists: `nose` cargo-0.91.0/tests/testsuite/cargo_add/features_unknown_no_features/mod.rs000064400000000000000000000020331046102023000252160ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package --features noze") .current_dir(cwd) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/features_unknown_no_features/stderr.term.svg000064400000000000000000000022711046102023000270670ustar 00000000000000 Updating `dummy-registry` index Adding my-package v99999.0.0 to dependencies error: unrecognized feature for crate my-package: noze no features available for crate my-package cargo-0.91.0/tests/testsuite/cargo_add/git/mod.rs000064400000000000000000000020431046102023000200130ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; let git_dep = cargo_test_support::git::new("git-package", |project| { project .file( "Cargo.toml", &cargo_test_support::basic_manifest("git-package", "0.3.0+git-package"), ) .file("src/lib.rs", "") }); let git_url = git_dep.url().to_string(); snapbox::cmd::Command::cargo_ui() .arg("add") .args(["git-package", "--git", &git_url]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/git/stderr.term.svg000064400000000000000000000022201046102023000216550ustar 00000000000000 Updating git repository `[ROOTURL]/git-package` Adding git-package (git) to dependencies Updating git repository `[ROOTURL]/git-package` Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/git_branch/mod.rs000064400000000000000000000023501046102023000213310ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; let (git_dep, git_repo) = cargo_test_support::git::new_repo("git-package", |project| { project .file( "Cargo.toml", &cargo_test_support::basic_manifest("git-package", "0.3.0+git-package"), ) .file("src/lib.rs", "") }); let branch = "dev"; let find_head = || (git_repo.head().unwrap().peel_to_commit().unwrap()); git_repo.branch(branch, &find_head(), false).unwrap(); let git_url = git_dep.url().to_string(); snapbox::cmd::Command::cargo_ui() .arg("add") .args(["git-package", "--git", &git_url, "--branch", branch]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/git_branch/stderr.term.svg000064400000000000000000000022201046102023000231720ustar 00000000000000 Updating git repository `[ROOTURL]/git-package` Adding git-package (git) to dependencies Updating git repository `[ROOTURL]/git-package` Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/git_conflicts_namever/mod.rs000064400000000000000000000021551046102023000236000ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .args([ "my-package@0.4.3", "--git", "https://github.com/dcjanus/invalid", ]) .current_dir(cwd) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/git_conflicts_namever/stderr.term.svg000064400000000000000000000014561046102023000254500ustar 00000000000000 error: cannot specify a git URL (`https://github.com/dcjanus/invalid`) with a version (`0.4.3`). cargo-0.91.0/tests/testsuite/cargo_add/git_dev/mod.rs000064400000000000000000000020541046102023000206530ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; let git_dep = cargo_test_support::git::new("git-package", |project| { project .file( "Cargo.toml", &cargo_test_support::basic_manifest("git-package", "0.3.0+git-package"), ) .file("src/lib.rs", "") }); let git_url = git_dep.url().to_string(); snapbox::cmd::Command::cargo_ui() .arg("add") .args(["git-package", "--git", &git_url, "--dev"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/git_dev/stderr.term.svg000064400000000000000000000022241046102023000225170ustar 00000000000000 Updating git repository `[ROOTURL]/git-package` Adding git-package (git) to dev-dependencies Updating git repository `[ROOTURL]/git-package` Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/git_inferred_name/mod.rs000064400000000000000000000020241046102023000226700ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; let git_dep = cargo_test_support::git::new("git-package", |project| { project .file( "Cargo.toml", &cargo_test_support::basic_manifest("git-package", "0.3.0+git-package"), ) .file("src/lib.rs", "") }); let git_url = git_dep.url().to_string(); snapbox::cmd::Command::cargo_ui() .arg("add") .args(["--git", &git_url]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/git_inferred_name/stderr.term.svg000064400000000000000000000024371046102023000245450ustar 00000000000000 Updating git repository `[ROOTURL]/git-package` Updating git repository `[ROOTURL]/git-package` Adding git-package (git) to dependencies Updating git repository `[ROOTURL]/git-package` Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/git_inferred_name_multiple/mod.rs000064400000000000000000000050521046102023000246070ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; let git_dep = cargo_test_support::git::new("git-package", |project| { project .file( "p1/Cargo.toml", &cargo_test_support::basic_manifest("my-package1", "0.3.0+my-package1"), ) .file("p1/src/lib.rs", "") .file( "p2/Cargo.toml", &cargo_test_support::basic_manifest("my-package2", "0.3.0+my-package2"), ) .file("p2/src/lib.rs", "") .file( "p3/Cargo.toml", &cargo_test_support::basic_manifest("my-package3", "0.3.0+my-package2"), ) .file("p3/src/lib.rs", "") .file( "p4/Cargo.toml", &cargo_test_support::basic_manifest("my-package4", "0.3.0+my-package2"), ) .file("p4/src/lib.rs", "") .file( "p5/Cargo.toml", &cargo_test_support::basic_manifest("my-package5", "0.3.0+my-package2"), ) .file("p5/src/lib.rs", "") .file( "p6/Cargo.toml", &cargo_test_support::basic_manifest("my-package6", "0.3.0+my-package2"), ) .file("p6/src/lib.rs", "") .file( "p7/Cargo.toml", &cargo_test_support::basic_manifest("my-package7", "0.3.0+my-package2"), ) .file("p7/src/lib.rs", "") .file( "p8/Cargo.toml", &cargo_test_support::basic_manifest("my-package8", "0.3.0+my-package2"), ) .file("p8/src/lib.rs", "") .file( "p9/Cargo.toml", &cargo_test_support::basic_manifest("my-package9", "0.3.0+my-package2"), ) .file("p9/src/lib.rs", "") }); let git_url = git_dep.url().to_string(); snapbox::cmd::Command::cargo_ui() .arg("add") .args(["--git", &git_url]) .current_dir(cwd) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/git_inferred_name_multiple/stderr.term.svg000064400000000000000000000024411046102023000264530ustar 00000000000000 Updating git repository `[ROOTURL]/git-package` error: multiple packages found at `[ROOTURL]/git-package`: my-package1, my-package2, my-package3, my-package4, my-package5, my-package6 my-package7, my-package8, my-package9 To disambiguate, run `cargo add --git [ROOTURL]/git-package <package>` cargo-0.91.0/tests/testsuite/cargo_add/git_multiple_names/mod.rs000064400000000000000000000032321046102023000231120ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for name in ["my-package1", "my-package2"] { for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new(name, ver).publish(); } } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; let git_dep = cargo_test_support::git::new("git-package", |project| { project .file( "p1/Cargo.toml", &cargo_test_support::basic_manifest("my-package1", "0.3.0+my-package1"), ) .file("p1/src/lib.rs", "") .file( "p2/Cargo.toml", &cargo_test_support::basic_manifest("my-package2", "0.3.0+my-package2"), ) .file("p2/src/lib.rs", "") }); let git_url = git_dep.url().to_string(); snapbox::cmd::Command::cargo_ui() .arg("add") .args(["my-package1", "my-package2", "--git", &git_url]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/git_multiple_names/stderr.term.svg000064400000000000000000000024341046102023000247620ustar 00000000000000 Updating git repository `[ROOTURL]/git-package` Adding my-package1 (git) to dependencies Adding my-package2 (git) to dependencies Updating git repository `[ROOTURL]/git-package` Locking 2 packages to latest compatible versions cargo-0.91.0/tests/testsuite/cargo_add/git_multiple_packages_features/mod.rs000064400000000000000000000034351046102023000254700ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let main_manifest = r#" [package] name = "main-package" version = "0.1.1+main-package" authors = [] [workspace] members = ["package-wo-feature", "package-with-feature"] "#; let manifest_feature = r#" [package] name = "package-with-feature" version = "0.1.3+package-with-feature" [features] target_feature = [] "#; let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; let git_dep = cargo_test_support::git::new("git-package", |project| { project .file("Cargo.toml", &main_manifest) .file( "package-wo-feature/Cargo.toml", &cargo_test_support::basic_manifest( "package-wo-feature", "0.1.1+package-wo-feature", ), ) .file("package-wo-feature/src/lib.rs", "") .file("package-with-feature/Cargo.toml", &manifest_feature) .file("package-with-feature/src/lib.rs", "") }); let git_url = git_dep.url().to_string(); snapbox::cmd::Command::cargo_ui() .arg("add") .args([ "--git", &git_url, "package-with-feature", "--features=target_feature", ]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/git_multiple_packages_features/stderr.term.svg000064400000000000000000000025561046102023000273400ustar 00000000000000 Updating git repository `[ROOTURL]/git-package` Adding package-with-feature (git) to dependencies Features: + target_feature Updating git repository `[ROOTURL]/git-package` Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/git_registry/mod.rs000064400000000000000000000030331046102023000217430ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::alt_init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("versioned-package", ver) .alternative(true) .publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; let git_dep = cargo_test_support::git::new("versioned-package", |project| { project .file( "Cargo.toml", &cargo_test_support::basic_manifest("versioned-package", "0.3.0+versioned-package"), ) .file("src/lib.rs", "") }); let git_url = git_dep.url().to_string(); snapbox::cmd::Command::cargo_ui() .arg("add") .args([ "versioned-package", "--git", &git_url, "--registry", "alternative", ]) .current_dir(cwd) .assert() .failure() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/git_registry/stderr.term.svg000064400000000000000000000025251046102023000236150ustar 00000000000000 Updating git repository `[ROOTURL]/versioned-package` Adding versioned-package (git) to dependencies error: failed to parse manifest at `[ROOT]/case/Cargo.toml` Caused by: dependency (versioned-package) specification is ambiguous. Only one of `git` or `registry` is allowed. cargo-0.91.0/tests/testsuite/cargo_add/git_rev/mod.rs000064400000000000000000000022761046102023000206770ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; let (git_dep, git_repo) = cargo_test_support::git::new_repo("git-package", |project| { project .file( "Cargo.toml", &cargo_test_support::basic_manifest("git-package", "0.3.0+git-package"), ) .file("src/lib.rs", "") }); let find_head = || (git_repo.head().unwrap().peel_to_commit().unwrap()); let head = find_head().id().to_string(); let git_url = git_dep.url().to_string(); snapbox::cmd::Command::cargo_ui() .arg("add") .args(["git-package", "--git", &git_url, "--rev", &head]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/git_rev/stderr.term.svg000064400000000000000000000022201046102023000225310ustar 00000000000000 Updating git repository `[ROOTURL]/git-package` Adding git-package (git) to dependencies Updating git repository `[ROOTURL]/git-package` Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/git_tag/mod.rs000064400000000000000000000022141046102023000206460ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; let (git_dep, git_repo) = cargo_test_support::git::new_repo("git-package", |project| { project .file( "Cargo.toml", &cargo_test_support::basic_manifest("git-package", "0.3.0+git-package"), ) .file("src/lib.rs", "") }); let tag = "v1.0.0"; cargo_test_support::git::tag(&git_repo, tag); let git_url = git_dep.url().to_string(); snapbox::cmd::Command::cargo_ui() .arg("add") .args(["git-package", "--git", &git_url, "--tag", tag]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/git_tag/stderr.term.svg000064400000000000000000000022201046102023000225100ustar 00000000000000 Updating git repository `[ROOTURL]/git-package` Adding git-package (git) to dependencies Updating git repository `[ROOTURL]/git-package` Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/help/mod.rs000064400000000000000000000004601046102023000201610ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("add") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_add/help/stdout.term.svg000064400000000000000000000360271046102023000220550ustar 00000000000000 Add dependencies to a Cargo.toml manifest file Usage: cargo add [OPTIONS] <DEP>[@<VERSION>] ... cargo add [OPTIONS] --path <PATH> ... cargo add [OPTIONS] --git <URL> ... Arguments: [DEP_ID]... Reference to a package to add as a dependency You can reference a package by: - `<name>`, like `cargo add serde` (latest version will be used) - `<name>@<version-req>`, like `cargo add serde@1` or `cargo add serde@=1.0.38` Options: --no-default-features Disable the default features --default-features Re-enable the default features -F, --features <FEATURES> Space or comma separated list of features to activate --optional Mark the dependency as optional The package name will be exposed as feature of your crate. --no-optional Mark the dependency as required The package will be removed from your features. --public Mark the dependency as public (unstable) The dependency can be referenced in your library's public API. --no-public Mark the dependency as private (unstable) While you can use the crate in your implementation, it cannot be referenced in your public API. --rename <NAME> Rename the dependency Example uses: - Depending on multiple versions of a crate - Depend on crates with the same name from different registries -n, --dry-run Don't actually write the manifest -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help (see a summary with '-h') Manifest Options: --manifest-path <PATH> Path to Cargo.toml --lockfile-path <PATH> Path to Cargo.lock (unstable) --ignore-rust-version Ignore `rust-version` specification in packages --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Package Selection: -p, --package [<SPEC>] Package to modify Source: --path <PATH> Filesystem path to local crate to add --base <BASE> The path base to use when adding from a local crate (unstable). --git <URI> Git repository location Without any other information, cargo will use latest commit on the main branch. --branch <BRANCH> Git branch to download the crate from --tag <TAG> Git tag to download the crate from --rev <REV> Git reference to download the crate from This is the catch all, handling hashes to named references in remote repositories. --registry <NAME> Package registry for this dependency Section: --dev Add as development dependency Dev-dependencies are not used when compiling a package for building, but are used for compiling tests, examples, and benchmarks. These dependencies are not propagated to other packages which depend on this package. --build Add as build dependency Build-dependencies are the only dependencies available for use by build scripts (`build.rs` files). --target <TARGET> Add as dependency to the given target platform Run `cargo help add` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_add/infer_prerelease/mod.rs000064400000000000000000000014511046102023000225440ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("prerelease_only", "0.2.0-alpha.1").publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("prerelease_only") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/infer_prerelease/stderr.term.svg000064400000000000000000000017751046102023000244220ustar 00000000000000 Updating `dummy-registry` index Adding prerelease_only v0.2.0-alpha.1 to dependencies Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/invalid_arg/mod.rs000064400000000000000000000020201046102023000215020ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package --flag") .current_dir(cwd) .assert() .code(1) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/invalid_arg/stderr.term.svg000064400000000000000000000043651046102023000233650ustar 00000000000000 error: unexpected argument '--flag' found tip: a similar argument exists: '--tag' Usage: cargo add [OPTIONS] <DEP>[@<VERSION>] ... cargo add [OPTIONS] --path <PATH> ... cargo add [OPTIONS] --git <URL> ... For more information, try '--help'. cargo-0.91.0/tests/testsuite/cargo_add/invalid_git_name/mod.rs000064400000000000000000000020421046102023000225200ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; let git_dep = cargo_test_support::git::new("git-package", |project| { project .file( "Cargo.toml", &cargo_test_support::basic_manifest("git-package", "0.3.0+git-package"), ) .file("src/lib.rs", "") }); let git_url = git_dep.url().to_string(); snapbox::cmd::Command::cargo_ui() .arg("add") .args(["not-in-git", "--git", &git_url]) .current_dir(cwd) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/invalid_git_name/stderr.term.svg000064400000000000000000000017361046102023000243760ustar 00000000000000 Updating git repository `[ROOTURL]/git-package` error: the crate `not-in-git@[ROOTURL]/git-package` could not be found at `[ROOTURL]/git-package` cargo-0.91.0/tests/testsuite/cargo_add/invalid_key_inherit_dependency/mod.rs000064400000000000000000000013371046102023000254530ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .args(["foo", "--default-features", "-p", "bar"]) .current_dir(cwd) .assert() .failure() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/invalid_key_inherit_dependency/stderr.term.svg000064400000000000000000000016201046102023000273130ustar 00000000000000 error: cannot override workspace dependency with `--default-features`, either change `workspace.dependencies.foo.default-features` or define the dependency exclusively in the package's manifest cargo-0.91.0/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/mod.rs000064400000000000000000000013371046102023000275610ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .args(["foo", "--default-features", "-p", "bar"]) .current_dir(cwd) .assert() .failure() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/stderr.term.svg000064400000000000000000000016201046102023000314210ustar 00000000000000 error: cannot override workspace dependency with `--default-features`, either change `workspace.dependencies.foo.default-features` or define the dependency exclusively in the package's manifest cargo-0.91.0/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/mod.rs000064400000000000000000000013401046102023000267740ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .args(["--rename", "foo", "foo-alt", "-p", "bar"]) .current_dir(cwd) .assert() .failure() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/stderr.term.svg000064400000000000000000000015751046102023000306530ustar 00000000000000 error: cannot override workspace dependency with `--rename`, either change `workspace.dependencies.foo.package` or define the dependency exclusively in the package's manifest cargo-0.91.0/tests/testsuite/cargo_add/invalid_manifest/mod.rs000064400000000000000000000020131046102023000225410ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package") .current_dir(cwd) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/invalid_manifest/stderr.term.svg000064400000000000000000000026411046102023000244150ustar 00000000000000 error: invalid float, expected `inf` --> Cargo.toml:9:7 | 9 | key = invalid-value | ^^^^^^^^^^^^^ | cargo-0.91.0/tests/testsuite/cargo_add/invalid_name_external/mod.rs000064400000000000000000000013461046102023000235650ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("lets_hope_nobody_ever_publishes_this_crate") .current_dir(cwd) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/invalid_name_external/stderr.term.svg000064400000000000000000000017171046102023000254340ustar 00000000000000 Updating `dummy-registry` index error: the crate `lets_hope_nobody_ever_publishes_this_crate` could not be found in registry index. cargo-0.91.0/tests/testsuite/cargo_add/invalid_path/mod.rs000064400000000000000000000013611046102023000216740ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("cargo-list-test-fixture --path ./tests/fixtures/local") .current_dir(cwd) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/invalid_path/stderr.term.svg000064400000000000000000000025511046102023000235430ustar 00000000000000 error: failed to load source for dependency `cargo-list-test-fixture` Caused by: Unable to update [ROOT]/case/tests/fixtures/local Caused by: failed to read `[ROOT]/case/tests/fixtures/local/Cargo.toml` Caused by: [..] cargo-0.91.0/tests/testsuite/cargo_add/invalid_path_name/mod.rs000064400000000000000000000021261046102023000226740ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", ver) .publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("not-at-path --path ../dependency") .current_dir(&cwd) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/invalid_path_name/stderr.term.svg000064400000000000000000000014631046102023000245440ustar 00000000000000 error: the crate `not-at-path@[ROOT]/case/dependency` could not be found at `[ROOT]/case/dependency` cargo-0.91.0/tests/testsuite/cargo_add/invalid_path_self/mod.rs000064400000000000000000000013341046102023000227050ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("cargo-list-test-fixture --path .") .current_dir(cwd) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/invalid_path_self/stderr.term.svg000064400000000000000000000017121046102023000245520ustar 00000000000000 Adding cargo-list-test-fixture (local) to dependencies error: cannot add `cargo-list-test-fixture` as a dependency to itself cargo-0.91.0/tests/testsuite/cargo_add/invalid_target_empty/mod.rs000064400000000000000000000020251046102023000234420ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package --target ''") .current_dir(cwd) .assert() .code(1) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/invalid_target_empty/stderr.term.svg000064400000000000000000000020501046102023000253050ustar 00000000000000 error: a value is required for '--target <TARGET>' but none was supplied For more information, try '--help'. cargo-0.91.0/tests/testsuite/cargo_add/invalid_vers/mod.rs000064400000000000000000000020421046102023000217140ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package@invalid-version-string") .current_dir(cwd) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/invalid_vers/stderr.term.svg000064400000000000000000000017451046102023000235720ustar 00000000000000 error: invalid version requirement `invalid-version-string` Caused by: unexpected character 'i' while parsing major version number cargo-0.91.0/tests/testsuite/cargo_add/list_features/mod.rs000064400000000000000000000016441046102023000221070ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") .feature("nose", &[]) .feature("mouth", &[]) .feature("eyes", &[]) .feature("ears", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .args(["your-face"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/list_features/stderr.term.svg000064400000000000000000000031201046102023000237430ustar 00000000000000 Updating `dummy-registry` index Adding your-face v99999.0.0 to dependencies Features: - ears - eyes - mouth - nose Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/list_features_path/mod.rs000064400000000000000000000024201046102023000231140ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") .feature("nose", &[]) .feature("mouth", &[]) .feature("eyes", &[]) .feature("ears", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("your-face --path ../dependency") .current_dir(&cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/list_features_path/stderr.term.svg000064400000000000000000000035551046102023000247730ustar 00000000000000 Adding your-face (local) to dependencies Features: + mouth + nose - eyes - optional-dependency Updating `dummy-registry` index Locking 1 package to latest compatible version Adding my-package v0.1.1+my-package (available: v99999.0.0+my-package) cargo-0.91.0/tests/testsuite/cargo_add/list_features_path_no_default/mod.rs000064400000000000000000000025501046102023000253200ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") .feature("nose", &[]) .feature("mouth", &[]) .feature("eyes", &[]) .feature("ears", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); snapbox::cmd::Command::cargo_ui() .arg("add") .args([ "your-face", "--path", "../dependency", "--no-default-features", ]) .current_dir(&cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/list_features_path_no_default/stderr.term.svg000064400000000000000000000035511046102023000271670ustar 00000000000000 Adding your-face (local) to dependencies Features: - eyes - mouth - nose - optional-dependency Updating `dummy-registry` index Locking 1 package to latest compatible version Adding my-package v0.1.1+my-package (available: v99999.0.0+my-package) cargo-0.91.0/tests/testsuite/cargo_add/locked_changed/mod.rs000064400000000000000000000020241046102023000221410ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package --locked") .current_dir(cwd) .assert() .failure() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/locked_changed/stderr.term.svg000064400000000000000000000021451046102023000240120ustar 00000000000000 Updating `dummy-registry` index Adding my-package v99999.0.0 to dependencies error: the manifest file [ROOT]/case/Cargo.toml needs to be updated but --locked was passed to prevent this cargo-0.91.0/tests/testsuite/cargo_add/locked_unchanged/mod.rs000064400000000000000000000020241046102023000225040ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package --locked") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/locked_unchanged/stderr.term.svg000064400000000000000000000015461046102023000243610ustar 00000000000000 Updating `dummy-registry` index Adding my-package v99999.0.0 to dependencies cargo-0.91.0/tests/testsuite/cargo_add/lockfile_updated/mod.rs000064400000000000000000000021521046102023000225270ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for name in ["my-package", "unrelateed-crate"] { for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new(name, ver).publish(); } } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/lockfile_updated/stderr.term.svg000064400000000000000000000022341046102023000243750ustar 00000000000000 Updating `dummy-registry` index Adding my-package v99999.0.0 to dependencies Locking 1 package to latest compatible version Adding my-package v99999.0.0+my-package cargo-0.91.0/tests/testsuite/cargo_add/manifest_path_package/mod.rs000064400000000000000000000023261046102023000235310ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", ver) .publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .args([ "--manifest-path", "Cargo.toml", "--package", "cargo-list-test-fixture", "cargo-list-test-fixture-dependency", ]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/manifest_path_package/stderr.term.svg000064400000000000000000000013751046102023000254010ustar 00000000000000 Adding cargo-list-test-fixture-dependency (local) to dependencies cargo-0.91.0/tests/testsuite/cargo_add/merge_activated_features/mod.rs000064400000000000000000000013111046102023000242460ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .args(["foo", "-p", "bar"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/merge_activated_features/stderr.term.svg000064400000000000000000000035731046102023000261270ustar 00000000000000 Adding foo (workspace) to dependencies Features as of v0.0.0: + default-base + default-merge-base + default-test-base + merge + merge-base + test + test-base - unrelated cargo-0.91.0/tests/testsuite/cargo_add/missing_at_in_crate_spec/mod.rs000064400000000000000000000012411046102023000242420ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package=1.0.0") .current_dir(cwd) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/missing_at_in_crate_spec/stderr.term.svg000064400000000000000000000020451046102023000261120ustar 00000000000000 error: invalid character `=` in package name: `my-package=1.0.0`, characters must be Unicode XID characters (numbers, `-`, `_`, or most letters) help: if this is meant to be a package name followed by a version, insert an `@` like `my-package@=1.0.0` cargo-0.91.0/tests/testsuite/cargo_add/mod.rs000064400000000000000000000102661046102023000172360ustar 00000000000000mod add_basic; mod add_multiple; mod add_no_vendored_package_with_alter_registry; mod add_no_vendored_package_with_vendor; mod add_toolchain; mod build; mod build_prefer_existing_version; mod change_rename_target; mod cyclic_features; mod default_features; mod deprecated_default_features; mod deprecated_section; mod detect_workspace_inherit; mod detect_workspace_inherit_features; mod detect_workspace_inherit_optional; mod detect_workspace_inherit_path_base; mod detect_workspace_inherit_public; mod dev; mod dev_build_conflict; mod dev_existing_path_base; mod dev_prefer_existing_version; mod dry_run; mod empty_dep_name; mod feature_suggestion_multiple; mod feature_suggestion_none; mod feature_suggestion_single; mod features; mod features_activated_over_limit; mod features_deactivated_over_limit; mod features_empty; mod features_error_activated_over_limit; mod features_error_deactivated_over_limit; mod features_multiple_occurrences; mod features_preserve; mod features_spaced_values; mod features_unknown; mod features_unknown_no_features; mod git; mod git_branch; mod git_conflicts_namever; mod git_dev; mod git_inferred_name; mod git_inferred_name_multiple; mod git_multiple_names; mod git_multiple_packages_features; mod git_registry; mod git_rev; mod git_tag; mod help; mod infer_prerelease; mod invalid_arg; mod invalid_git_name; mod invalid_key_inherit_dependency; mod invalid_key_overwrite_inherit_dependency; mod invalid_key_rename_inherit_dependency; mod invalid_manifest; mod invalid_name_external; mod invalid_path; mod invalid_path_name; mod invalid_path_self; mod invalid_target_empty; mod invalid_vers; mod list_features; mod list_features_path; mod list_features_path_no_default; mod locked_changed; mod locked_unchanged; mod lockfile_updated; mod manifest_path_package; mod merge_activated_features; mod missing_at_in_crate_spec; mod multiple_conflicts_with_features; mod multiple_conflicts_with_rename; mod namever; mod no_args; mod no_default_features; mod no_optional; mod no_public; mod normalize_name_git; mod normalize_name_path; mod normalize_name_path_existing; mod normalize_name_registry; mod normalize_name_registry_existing; mod normalize_name_registry_yanked; mod normalize_name_workspace_dep; mod offline_empty_cache; mod optional; mod overwrite_default_features; mod overwrite_default_features_with_no_default_features; mod overwrite_features; mod overwrite_git_with_path; mod overwrite_inherit_features_noop; mod overwrite_inherit_noop; mod overwrite_inherit_optional_noop; mod overwrite_inline_features; mod overwrite_name_dev_noop; mod overwrite_name_noop; mod overwrite_no_default_features; mod overwrite_no_default_features_with_default_features; mod overwrite_no_optional; mod overwrite_no_optional_with_optional; mod overwrite_no_public; mod overwrite_no_public_with_public; mod overwrite_optional; mod overwrite_optional_with_no_optional; mod overwrite_optional_with_optional; mod overwrite_path_base_with_version; mod overwrite_path_noop; mod overwrite_path_with_version; mod overwrite_preserves_inline_table; mod overwrite_public; mod overwrite_public_with_no_public; mod overwrite_rename_with_no_rename; mod overwrite_rename_with_rename; mod overwrite_rename_with_rename_noop; mod overwrite_version_with_git; mod overwrite_version_with_path; mod overwrite_with_rename; mod overwrite_workspace_dep; mod overwrite_workspace_dep_features; mod path; mod path_base; mod path_base_inferred_name; mod path_base_missing_base_path; mod path_base_unstable; mod path_dev; mod path_inferred_name; mod path_inferred_name_conflicts_full_feature; mod prefixed_v_in_version; mod preserve_dep_std_table; mod preserve_features_sorted; mod preserve_features_table; mod preserve_features_unsorted; mod preserve_sorted; mod preserve_unsorted; mod public; mod quiet; mod registry; mod rename; mod require_weak; mod rust_version_ignore; mod rust_version_incompatible; mod rust_version_latest; mod rust_version_older; mod rustc_ignore; mod rustc_incompatible; mod rustc_latest; mod rustc_older; mod script_bare; mod script_frontmatter; mod script_shebang; mod sorted_table_with_dotted_item; mod symlink; mod target; mod target_cfg; mod unknown_inherited_feature; mod vers; mod workspace_name; mod workspace_path; mod workspace_path_dev; mod yanked; cargo-0.91.0/tests/testsuite/cargo_add/multiple_conflicts_with_features/mod.rs000064400000000000000000000024101046102023000260560ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package1", ver).publish(); } cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") .feature("nose", &[]) .feature("mouth", &[]) .feature("eyes", &[]) .feature("ears", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package1 your-face --features nose") .current_dir(cwd) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/multiple_conflicts_with_features/stderr.term.svg000064400000000000000000000015141046102023000277270ustar 00000000000000 error: feature `nose` must be qualified by the dependency it's being activated for, like `my-package1/nose`, `your-face/nose` cargo-0.91.0/tests/testsuite/cargo_add/multiple_conflicts_with_rename/mod.rs000064400000000000000000000022041046102023000255100ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for name in ["my-package1", "my-package2"] { for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new(name, ver).publish(); } } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package1 my-package2 --rename renamed") .current_dir(cwd) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/multiple_conflicts_with_rename/stderr.term.svg000064400000000000000000000014031046102023000273550ustar 00000000000000 error: cannot specify multiple crates with `--rename` cargo-0.91.0/tests/testsuite/cargo_add/namever/mod.rs000064400000000000000000000022321046102023000206650ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for name in ["my-package", "my-package1", "my-package2"] { for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new(name, ver).publish(); } } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package1@>=0.1.1 my-package2@0.2.3 my-package") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/namever/stderr.term.svg000064400000000000000000000030371046102023000225360ustar 00000000000000 Updating `dummy-registry` index Adding my-package1 >=0.1.1 to dependencies Adding my-package2 v0.2.3 to dependencies Adding my-package v99999.0.0 to dependencies Locking 3 packages to latest compatible versions Adding my-package2 v0.2.3+my-package (available: v99999.0.0+my-package) cargo-0.91.0/tests/testsuite/cargo_add/no_args/mod.rs000064400000000000000000000012441046102023000206620ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .current_dir(cwd) .assert() .code(1) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/no_args/stderr.term.svg000064400000000000000000000041231046102023000225260ustar 00000000000000 error: the following required arguments were not provided: <DEP_ID|--path <PATH>|--git <URI>> Usage: cargo add [OPTIONS] <DEP>[@<VERSION>] ... cargo add [OPTIONS] --path <PATH> ... cargo add [OPTIONS] --git <URL> ... For more information, try '--help'. cargo-0.91.0/tests/testsuite/cargo_add/no_default_features/mod.rs000064400000000000000000000022171046102023000232510ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for name in ["my-package1", "my-package2"] { for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new(name, ver).publish(); } } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package1 my-package2@0.4.1 --no-default-features") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/no_default_features/stderr.term.svg000064400000000000000000000026211046102023000251150ustar 00000000000000 Updating `dummy-registry` index Adding my-package1 v99999.0.0 to dependencies Adding my-package2 v0.4.1 to dependencies Locking 2 packages to latest compatible versions Adding my-package2 v0.4.1+my-package (available: v99999.0.0+my-package) cargo-0.91.0/tests/testsuite/cargo_add/no_optional/mod.rs000064400000000000000000000014451046102023000215560ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("my-package", "0.1.0").publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package --no-optional") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/no_optional/stderr.term.svg000064400000000000000000000017601046102023000234230ustar 00000000000000 Updating `dummy-registry` index Adding my-package v0.1.0 to dependencies Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/no_public/mod.rs000064400000000000000000000015371046102023000212110ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("my-package", "0.1.0").publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package --no-public") .current_dir(cwd) .masquerade_as_nightly_cargo(&["public-dependency"]) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/no_public/stderr.term.svg000064400000000000000000000017601046102023000230540ustar 00000000000000 Updating `dummy-registry` index Adding my-package v0.1.0 to dependencies Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/normalize_name_git/mod.rs000064400000000000000000000021351046102023000230750ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; let git_dep = cargo_test_support::git::new("git-package", |project| { project .file( "Cargo.toml", &cargo_test_support::basic_manifest("git-package", "0.3.0+git-package"), ) .file("src/lib.rs", "") }); let git_url = git_dep.url().to_string(); snapbox::cmd::Command::cargo_ui() .arg("add") .args(["git_package", "--git", &git_url]) .current_dir(cwd) .assert() .failure() // Fuzzy searching for paths isn't supported at this time .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/normalize_name_git/stderr.term.svg000064400000000000000000000017371046102023000247510ustar 00000000000000 Updating git repository `[ROOTURL]/git-package` error: the crate `git_package@[ROOTURL]/git-package` could not be found at `[ROOTURL]/git-package` cargo-0.91.0/tests/testsuite/cargo_add/normalize_name_path/mod.rs000064400000000000000000000022471046102023000232520ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", ver) .publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("cargo_list_test_fixture_dependency --path ../dependency") .current_dir(&cwd) .assert() .failure() // Fuzzy searching for paths isn't supported at this time .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/normalize_name_path/stderr.term.svg000064400000000000000000000015121046102023000251110ustar 00000000000000 error: the crate `cargo_list_test_fixture_dependency@[ROOT]/case/dependency` could not be found at `[ROOT]/case/dependency` cargo-0.91.0/tests/testsuite/cargo_add/normalize_name_path_existing/mod.rs000064400000000000000000000013201046102023000251530ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .args(["fuzzy-name", "-p", "bar"]) .current_dir(cwd) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/normalize_name_path_existing/stderr.term.svg000064400000000000000000000016571046102023000270350ustar 00000000000000 Updating `dummy-registry` index error: the crate `fuzzy-name` could not be found in registry index. cargo-0.91.0/tests/testsuite/cargo_add/normalize_name_registry/mod.rs000064400000000000000000000025201046102023000241600ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("linked-hash-map", "0.5.4") .feature("clippy", &[]) .feature("heapsize", &[]) .feature("heapsize_impl", &[]) .feature("nightly", &[]) .feature("serde", &[]) .feature("serde_impl", &[]) .feature("serde_test", &[]) .publish(); cargo_test_support::registry::Package::new("inflector", "0.11.4") .feature("default", &["heavyweight", "lazy_static", "regex"]) .feature("heavyweight", &[]) .feature("lazy_static", &[]) .feature("regex", &[]) .feature("unstable", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("linked_hash_map Inflector") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/normalize_name_registry/stderr.term.svg000064400000000000000000000061031046102023000260260ustar 00000000000000 Updating `dummy-registry` index warning: translating `linked_hash_map` to `linked-hash-map` warning: translating `Inflector` to `inflector` Adding linked-hash-map v0.5.4 to dependencies Features: - clippy - heapsize - heapsize_impl - nightly - serde - serde_impl - serde_test Adding inflector v0.11.4 to dependencies Features: + heavyweight + lazy_static + regex - unstable Locking 2 packages to latest compatible versions cargo-0.91.0/tests/testsuite/cargo_add/normalize_name_registry_existing/mod.rs000064400000000000000000000016461046102023000261020ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("your_face", "99999.0.0+my-package") .feature("nose", &[]) .feature("mouth", &[]) .feature("eyes", &[]) .feature("ears", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("your-face") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/normalize_name_registry_existing/stderr.term.svg000064400000000000000000000034331046102023000277430ustar 00000000000000 warning: translating `your-face` to `your_face` Updating `dummy-registry` index Adding your_face v99999.0.0 to dependencies Features: + eyes - ears - mouth - nose Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/normalize_name_registry_yanked/mod.rs000064400000000000000000000017541046102023000255230ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("linked-hash-map", "0.5.0").publish(); cargo_test_support::registry::Package::new("linked-hash-map", "0.5.4").publish(); cargo_test_support::registry::Package::new("linked-hash-map", "0.6.0") .yanked(true) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("linked_hash_map") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/normalize_name_registry_yanked/stderr.term.svg000064400000000000000000000023131046102023000273600ustar 00000000000000 Updating `dummy-registry` index warning: translating `linked_hash_map` to `linked-hash-map` Adding linked-hash-map v0.5.4 to dependencies Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/normalize_name_workspace_dep/mod.rs000064400000000000000000000014741046102023000251450ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::registry::Package; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); Package::new("fuzzy_dependency", "1.0.0").publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .args(["fuzzy-dependency", "-p", "bar"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/normalize_name_workspace_dep/stderr.term.svg000064400000000000000000000023231046102023000270040ustar 00000000000000 warning: translating `fuzzy-dependency` to `fuzzy_dependency` Updating `dummy-registry` index Adding fuzzy_dependency (workspace) to dependencies Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/offline_empty_cache/mod.rs000064400000000000000000000020251046102023000232130ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("--offline my-package") .current_dir(cwd) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/offline_empty_cache/stderr.term.svg000064400000000000000000000014211046102023000250570ustar 00000000000000 error: the crate `my-package` could not be found in registry index. cargo-0.91.0/tests/testsuite/cargo_add/optional/mod.rs000064400000000000000000000014421046102023000210570ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("my-package", "0.1.0").publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package --optional") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/optional/stderr.term.svg000064400000000000000000000021671046102023000227310ustar 00000000000000 Updating `dummy-registry` index Adding my-package v0.1.0 to optional dependencies Adding feature `my-package` Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/overwrite_default_features/mod.rs000064400000000000000000000022141046102023000246600ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for name in ["my-package1", "my-package2"] { for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new(name, ver).publish(); } } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package1 my-package2@0.4.1 --default-features") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_default_features/stderr.term.svg000064400000000000000000000026211046102023000265270ustar 00000000000000 Updating `dummy-registry` index Adding my-package1 v99999.0.0 to dependencies Adding my-package2 v0.4.1 to dependencies Locking 2 packages to latest compatible versions Adding my-package2 v0.4.1+my-package (available: v99999.0.0+my-package) cargo-0.91.0/tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/mod.rs000064400000000000000000000022171046102023000320140ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for name in ["my-package1", "my-package2"] { for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new(name, ver).publish(); } } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package1 my-package2@0.4.1 --no-default-features") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } ././@LongLink00006440000000000000000000000153000000000000007772Lustar cargo-0.91.0/tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/stderr.term.svgcargo-0.91.0/tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/stderr.te000064400000000000000000000026211046102023000325230ustar 00000000000000 Updating `dummy-registry` index Adding my-package1 v99999.0.0 to dependencies Adding my-package2 v0.4.1 to dependencies Locking 2 packages to latest compatible versions Adding my-package2 v0.4.1+my-package (available: v99999.0.0+my-package) cargo-0.91.0/tests/testsuite/cargo_add/overwrite_features/mod.rs000064400000000000000000000016661046102023000231660ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") .feature("nose", &[]) .feature("mouth", &[]) .feature("eyes", &[]) .feature("ears", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("your-face --features nose") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_features/stderr.term.svg000064400000000000000000000031241046102023000250220ustar 00000000000000 Updating `dummy-registry` index Adding your-face v99999.0.0 to dependencies Features: + eyes + nose - ears - mouth Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/overwrite_git_with_path/mod.rs000064400000000000000000000021551046102023000241740ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", ver) .publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("cargo-list-test-fixture-dependency --path ../dependency") .current_dir(&cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_git_with_path/stderr.term.svg000064400000000000000000000020501046102023000260330ustar 00000000000000 Adding cargo-list-test-fixture-dependency (local) to optional dependencies Adding feature `cargo-list-test-fixture-dependency` Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/overwrite_inherit_features_noop/mod.rs000064400000000000000000000013121046102023000257270ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .args(["foo", "-p", "bar"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_inherit_features_noop/stderr.term.svg000064400000000000000000000016701046102023000276030ustar 00000000000000 Adding foo (workspace) to dependencies Features as of v0.0.0: + test cargo-0.91.0/tests/testsuite/cargo_add/overwrite_inherit_noop/mod.rs000064400000000000000000000013121046102023000240310ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .args(["foo", "-p", "bar"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_inherit_noop/stderr.term.svg000064400000000000000000000013421046102023000257010ustar 00000000000000 Adding foo (workspace) to dependencies cargo-0.91.0/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/mod.rs000064400000000000000000000013121046102023000257360ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .args(["foo", "-p", "bar"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/stderr.term.svg000064400000000000000000000015401046102023000276060ustar 00000000000000 Adding foo (workspace) to optional dependencies Adding feature `foo` cargo-0.91.0/tests/testsuite/cargo_add/overwrite_inline_features/mod.rs000064400000000000000000000025241046102023000245160ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("unrelateed-crate", ver).publish(); } cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") .feature("nose", &[]) .feature("mouth", &[]) .feature("eyes", &[]) .feature("ears", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line( "unrelateed-crate your-face --features your-face/nose,your-face/mouth -Fyour-face/ears", ) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_inline_features/stderr.term.svg000064400000000000000000000033201046102023000263560ustar 00000000000000 Updating `dummy-registry` index Adding unrelateed-crate v99999.0.0 to dependencies Adding your-face v99999.0.0 to dependencies Features: + ears + eyes + mouth + nose Locking 2 packages to latest compatible versions cargo-0.91.0/tests/testsuite/cargo_add/overwrite_name_dev_noop/mod.rs000064400000000000000000000017131046102023000241520ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::alt_init(); cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") .alternative(true) .feature("nose", &[]) .feature("mouth", &[]) .feature("eyes", &[]) .feature("ears", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("your-face --dev") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_name_dev_noop/stderr.term.svg000064400000000000000000000023001046102023000260100ustar 00000000000000 Adding your-face (local) to dev-dependencies Features: + mouth + nose Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/overwrite_name_noop/mod.rs000064400000000000000000000017051046102023000233150ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::alt_init(); cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") .alternative(true) .feature("nose", &[]) .feature("mouth", &[]) .feature("eyes", &[]) .feature("ears", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("your-face") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_name_noop/stderr.term.svg000064400000000000000000000025011046102023000251550ustar 00000000000000 Adding your-face (local) to optional dependencies Features: + mouth + nose Adding feature `your-face` Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/overwrite_no_default_features/mod.rs000064400000000000000000000022171046102023000253570ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for name in ["my-package1", "my-package2"] { for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new(name, ver).publish(); } } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package1 my-package2@0.4.1 --no-default-features") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_no_default_features/stderr.term.svg000064400000000000000000000026211046102023000272230ustar 00000000000000 Updating `dummy-registry` index Adding my-package1 v99999.0.0 to dependencies Adding my-package2 v0.4.1 to dependencies Locking 2 packages to latest compatible versions Adding my-package2 v0.4.1+my-package (available: v99999.0.0+my-package) cargo-0.91.0/tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/mod.rs000064400000000000000000000022141046102023000320110ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for name in ["my-package1", "my-package2"] { for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new(name, ver).publish(); } } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package1 my-package2@0.4.1 --default-features") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } ././@LongLink00006440000000000000000000000153000000000000007772Lustar cargo-0.91.0/tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/stderr.term.svgcargo-0.91.0/tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/stderr.te000064400000000000000000000026211046102023000325230ustar 00000000000000 Updating `dummy-registry` index Adding my-package1 v99999.0.0 to dependencies Adding my-package2 v0.4.1 to dependencies Locking 2 packages to latest compatible versions Adding my-package2 v0.4.1+my-package (available: v99999.0.0+my-package) cargo-0.91.0/tests/testsuite/cargo_add/overwrite_no_optional/mod.rs000064400000000000000000000014451046102023000236640ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("my-package", "0.1.0").publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package --no-optional") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_no_optional/stderr.term.svg000064400000000000000000000017601046102023000255310ustar 00000000000000 Updating `dummy-registry` index Adding my-package v0.1.0 to dependencies Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/mod.rs000064400000000000000000000014421046102023000266210ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("my-package", "0.1.0").publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package --optional") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/stderr.term.svg000064400000000000000000000021671046102023000304730ustar 00000000000000 Updating `dummy-registry` index Adding my-package v0.1.0 to optional dependencies Adding feature `my-package` Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/overwrite_no_public/mod.rs000064400000000000000000000015371046102023000233170ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("my-package", "0.1.0").publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package --no-public") .current_dir(cwd) .masquerade_as_nightly_cargo(&["public-dependency"]) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_no_public/stderr.term.svg000064400000000000000000000017601046102023000251620ustar 00000000000000 Updating `dummy-registry` index Adding my-package v0.1.0 to dependencies Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/overwrite_no_public_with_public/mod.rs000064400000000000000000000015341046102023000257050ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("my-package", "0.1.0").publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package --public") .current_dir(cwd) .masquerade_as_nightly_cargo(&["public-dependency"]) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_no_public_with_public/stderr.term.svg000064400000000000000000000017671046102023000275620ustar 00000000000000 Updating `dummy-registry` index Adding my-package v0.1.0 to public dependencies Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/overwrite_optional/mod.rs000064400000000000000000000014421046102023000231650ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("my-package", "0.1.0").publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package --optional") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_optional/stderr.term.svg000064400000000000000000000021671046102023000250370ustar 00000000000000 Updating `dummy-registry` index Adding my-package v0.1.0 to optional dependencies Adding feature `my-package` Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/mod.rs000064400000000000000000000016651046102023000266300ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") .feature("nose", &[]) .feature("mouth", &[]) .feature("eyes", &[]) .feature("ears", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("your-face --no-optional") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/stderr.term.svg000064400000000000000000000031201046102023000304610ustar 00000000000000 Updating `dummy-registry` index Adding your-face v99999.0.0 to dependencies Features: - ears - eyes - mouth - nose Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/overwrite_optional_with_optional/mod.rs000064400000000000000000000014501046102023000261240ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("my-package1", "99999.0.0").publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package1 --optional") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_optional_with_optional/stderr.term.svg000064400000000000000000000017761046102023000300040ustar 00000000000000 Updating `dummy-registry` index Adding my-package1 v99999.0.0 to optional dependencies Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/overwrite_path_base_with_version/in/.cargo/config.toml000064400000000000000000000000331046102023000306550ustar 00000000000000[path-bases] my_base = "." cargo-0.91.0/tests/testsuite/cargo_add/overwrite_path_base_with_version/mod.rs000064400000000000000000000016331046102023000260700ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", "20.0.0") .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("cargo-list-test-fixture-dependency@20.0") .current_dir(&cwd) .masquerade_as_nightly_cargo(&["path-base"]) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_path_base_with_version/stderr.term.svg000064400000000000000000000023461046102023000277370ustar 00000000000000 Updating `dummy-registry` index Adding cargo-list-test-fixture-dependency v20.0 to optional dependencies Adding feature `cargo-list-test-fixture-dependency` Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/overwrite_path_noop/mod.rs000064400000000000000000000017311046102023000233300ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::alt_init(); cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") .alternative(true) .feature("nose", &[]) .feature("mouth", &[]) .feature("eyes", &[]) .feature("ears", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("your-face --path ./dependency") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_path_noop/stderr.term.svg000064400000000000000000000025011046102023000251710ustar 00000000000000 Adding your-face (local) to optional dependencies Features: + mouth + nose Adding feature `your-face` Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/overwrite_path_with_version/mod.rs000064400000000000000000000021351046102023000250740ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", ver) .publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("cargo-list-test-fixture-dependency@20.0") .current_dir(&cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_path_with_version/stderr.term.svg000064400000000000000000000027141046102023000267440ustar 00000000000000 Updating `dummy-registry` index Adding cargo-list-test-fixture-dependency v20.0 to optional dependencies Adding feature `cargo-list-test-fixture-dependency` Locking 1 package to latest compatible version Adding cargo-list-test-fixture-dependency v20.0.0+my-package (available: v99999.0.0+my-package) cargo-0.91.0/tests/testsuite/cargo_add/overwrite_preserves_inline_table/mod.rs000064400000000000000000000016661046102023000260730ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") .feature("nose", &[]) .feature("mouth", &[]) .feature("eyes", &[]) .feature("ears", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("your-face --features nose") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_preserves_inline_table/stderr.term.svg000064400000000000000000000031241046102023000277270ustar 00000000000000 Updating `dummy-registry` index Adding your-face v99999.0.0 to dependencies Features: + eyes + nose - ears - mouth Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/overwrite_public/mod.rs000064400000000000000000000015341046102023000226200ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("my-package", "0.1.0").publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package --public") .current_dir(cwd) .masquerade_as_nightly_cargo(&["public-dependency"]) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_public/stderr.term.svg000064400000000000000000000017671046102023000244750ustar 00000000000000 Updating `dummy-registry` index Adding my-package v0.1.0 to public dependencies Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/overwrite_public_with_no_public/mod.rs000064400000000000000000000015371046102023000257100ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("my-package", "0.1.0").publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package --no-public") .current_dir(cwd) .masquerade_as_nightly_cargo(&["public-dependency"]) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_public_with_no_public/stderr.term.svg000064400000000000000000000017601046102023000275530ustar 00000000000000 Updating `dummy-registry` index Adding my-package v0.1.0 to dependencies Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/overwrite_rename_with_no_rename/mod.rs000064400000000000000000000020311046102023000256600ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("versioned-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("versioned-package") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_rename_with_no_rename/stderr.term.svg000064400000000000000000000024221046102023000275310ustar 00000000000000 Updating `dummy-registry` index Adding versioned-package v99999.0.0 to dependencies Locking 2 packages to latest compatible versions Adding versioned-package v0.1.1+my-package (available: v99999.0.0+my-package) cargo-0.91.0/tests/testsuite/cargo_add/overwrite_rename_with_rename/mod.rs000064400000000000000000000020451046102023000251710ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("versioned-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("versioned-package --rename a2") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_rename_with_rename/stderr.term.svg000064400000000000000000000024221046102023000270350ustar 00000000000000 Updating `dummy-registry` index Adding versioned-package v99999.0.0 to dependencies Locking 2 packages to latest compatible versions Adding versioned-package v0.1.1+my-package (available: v99999.0.0+my-package) cargo-0.91.0/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/mod.rs000064400000000000000000000020451046102023000262240ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("versioned-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("versioned-package --rename a1") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/stderr.term.svg000064400000000000000000000026121046102023000300710ustar 00000000000000 Updating `dummy-registry` index Adding versioned-package v0.1.1 to optional dependencies Adding feature `a1` Locking 1 package to latest compatible version Adding versioned-package v0.1.1+my-package (available: v99999.0.0+my-package) cargo-0.91.0/tests/testsuite/cargo_add/overwrite_version_with_git/mod.rs000064400000000000000000000026071046102023000247270ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("versioned-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; let git_dep = cargo_test_support::git::new("versioned-package", |project| { project .file( "Cargo.toml", &cargo_test_support::basic_manifest("versioned-package", "0.3.0+versioned-package"), ) .file("src/lib.rs", "") }); let git_url = git_dep.url().to_string(); snapbox::cmd::Command::cargo_ui() .arg("add") .args(["versioned-package", "--git", &git_url]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_version_with_git/stderr.term.svg000064400000000000000000000024571046102023000265770ustar 00000000000000 Updating git repository `[ROOTURL]/versioned-package` Adding versioned-package (git) to optional dependencies Adding feature `versioned-package` Updating git repository `[ROOTURL]/versioned-package` Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/overwrite_version_with_path/mod.rs000064400000000000000000000021551046102023000250760ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", ver) .publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("cargo-list-test-fixture-dependency --path ../dependency") .current_dir(&cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_version_with_path/stderr.term.svg000064400000000000000000000020501046102023000267350ustar 00000000000000 Adding cargo-list-test-fixture-dependency (local) to optional dependencies Adding feature `cargo-list-test-fixture-dependency` Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/overwrite_with_rename/mod.rs000064400000000000000000000020521046102023000236400ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("versioned-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("versioned-package --rename renamed") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_with_rename/stderr.term.svg000064400000000000000000000024221046102023000255060ustar 00000000000000 Updating `dummy-registry` index Adding versioned-package v99999.0.0 to dependencies Locking 2 packages to latest compatible versions Adding versioned-package v0.1.1+my-package (available: v99999.0.0+my-package) cargo-0.91.0/tests/testsuite/cargo_add/overwrite_workspace_dep/mod.rs000064400000000000000000000013441046102023000241670ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .args(["foo", "--path", "./dependency", "-p", "bar"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_workspace_dep/stderr.term.svg000064400000000000000000000013361046102023000260350ustar 00000000000000 Adding foo (local) to dependencies cargo-0.91.0/tests/testsuite/cargo_add/overwrite_workspace_dep_features/mod.rs000064400000000000000000000013441046102023000260650ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .args(["foo", "--path", "./dependency", "-p", "bar"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/overwrite_workspace_dep_features/stderr.term.svg000064400000000000000000000035461046102023000277400ustar 00000000000000 Adding foo (local) to dependencies Features: + default-base + default-merge-base + default-test-base + test + test-base - merge - merge-base - unrelated cargo-0.91.0/tests/testsuite/cargo_add/path/mod.rs000064400000000000000000000021551046102023000201700ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", ver) .publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("cargo-list-test-fixture-dependency --path ../dependency") .current_dir(&cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/path/stderr.term.svg000064400000000000000000000016131046102023000220330ustar 00000000000000 Adding cargo-list-test-fixture-dependency (local) to dependencies Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/path_base/in/.cargo/config.toml000064400000000000000000000000331046102023000237470ustar 00000000000000[path-bases] my_base = "." cargo-0.91.0/tests/testsuite/cargo_add/path_base/mod.rs000064400000000000000000000015061046102023000211610ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("cargo-list-test-fixture-dependency --path ../dependency --base my_base") .current_dir(&cwd) .masquerade_as_nightly_cargo(&["path-base"]) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/path_base/stderr.term.svg000064400000000000000000000016131046102023000230250ustar 00000000000000 Adding cargo-list-test-fixture-dependency (local) to dependencies Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/path_base_inferred_name/in/.cargo/config.toml000064400000000000000000000000331046102023000266250ustar 00000000000000[path-bases] my_base = "." cargo-0.91.0/tests/testsuite/cargo_add/path_base_inferred_name/mod.rs000064400000000000000000000014431046102023000240370ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("--path ../dependency --base my_base") .current_dir(&cwd) .masquerade_as_nightly_cargo(&["path-base"]) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/path_base_inferred_name/stderr.term.svg000064400000000000000000000016131046102023000257030ustar 00000000000000 Adding cargo-list-test-fixture-dependency (local) to dependencies Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/path_base_missing_base_path/mod.rs000064400000000000000000000014411046102023000247160ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("--path dependency --base bad_base") .current_dir(&cwd) .masquerade_as_nightly_cargo(&["path-base"]) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/path_base_missing_base_path/stderr.term.svg000064400000000000000000000015131046102023000265630ustar 00000000000000 error: path base `bad_base` is undefined. You must add an entry for `bad_base` in the Cargo configuration [path-bases] table. cargo-0.91.0/tests/testsuite/cargo_add/path_base_unstable/mod.rs000064400000000000000000000013521046102023000230550ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("--path dependency --base mybase") .current_dir(&cwd) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/path_base_unstable/stderr.term.svg000064400000000000000000000024741046102023000247300ustar 00000000000000 error: feature `path-bases` is required The package requires the Cargo feature called `path-bases`, but that feature is not stabilized in this version of Cargo ([..]). Consider trying a newer version of Cargo (this may require the nightly release). See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#path-bases for more information about the status of this feature. cargo-0.91.0/tests/testsuite/cargo_add/path_dev/mod.rs000064400000000000000000000021631046102023000210250ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", ver) .publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("cargo-list-test-fixture-dependency --path ../dependency --dev") .current_dir(&cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/path_dev/stderr.term.svg000064400000000000000000000016171046102023000226750ustar 00000000000000 Adding cargo-list-test-fixture-dependency (local) to dev-dependencies Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/path_inferred_name/mod.rs000064400000000000000000000021121046102023000230370ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", ver) .publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("--path ../dependency") .current_dir(&cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/path_inferred_name/stderr.term.svg000064400000000000000000000016131046102023000247110ustar 00000000000000 Adding cargo-list-test-fixture-dependency (local) to dependencies Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/mod.rs000064400000000000000000000013721046102023000276470ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("--path ../dependency --features your-face/nose") .current_dir(&cwd) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/stderr.term.svg000064400000000000000000000014361046102023000315150ustar 00000000000000 error: `your-face/nose` is unsupported when inferring the crate name, use `nose` cargo-0.91.0/tests/testsuite/cargo_add/prefixed_v_in_version/mod.rs000064400000000000000000000013061046102023000236170ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("foo@v0.0.1") .current_dir(cwd) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/prefixed_v_in_version/stderr.term.svg000064400000000000000000000021711046102023000254650ustar 00000000000000 error: the version provided, `v0.0.1` is not a valid SemVer requirement help: changing the package to `foo@0.0.1` Caused by: unexpected character 'v' while parsing major version number cargo-0.91.0/tests/testsuite/cargo_add/preserve_dep_std_table/mod.rs000064400000000000000000000016641046102023000237440ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") .feature("nose", &[]) .feature("mouth", &[]) .feature("eyes", &[]) .feature("ears", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("your-face --no-optional") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/preserve_dep_std_table/stderr.term.svg000064400000000000000000000031201046102023000255760ustar 00000000000000 Updating `dummy-registry` index Adding your-face v99999.0.0 to dependencies Features: - ears - eyes - mouth - nose Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/preserve_features_sorted/mod.rs000064400000000000000000000016731046102023000243510ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("my-package", "99999.0.0+my-package") .feature("a", &[]) .feature("b", &[]) .feature("c", &[]) .feature("d", &[]) .feature("e", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package -F d") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/preserve_features_sorted/stderr.term.svg000064400000000000000000000032511046102023000262100ustar 00000000000000 Updating `dummy-registry` index Adding my-package v99999.0.0 to dependencies Features: + a + b + c + d + e Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/preserve_features_table/mod.rs000064400000000000000000000016641046102023000241400ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") .feature("nose", &[]) .feature("mouth", &[]) .feature("eyes", &[]) .feature("ears", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("your-face --no-optional") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/preserve_features_table/stderr.term.svg000064400000000000000000000031201046102023000257720ustar 00000000000000 Updating `dummy-registry` index Adding your-face v99999.0.0 to dependencies Features: - ears - eyes - mouth - nose Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/preserve_features_unsorted/mod.rs000064400000000000000000000016731046102023000247140ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("my-package", "99999.0.0+my-package") .feature("a", &[]) .feature("b", &[]) .feature("c", &[]) .feature("d", &[]) .feature("e", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package -F e") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/preserve_features_unsorted/stderr.term.svg000064400000000000000000000032511046102023000265530ustar 00000000000000 Updating `dummy-registry` index Adding my-package v99999.0.0 to dependencies Features: + a + b + c + d + e Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/preserve_sorted/mod.rs000064400000000000000000000021551046102023000224470ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for name in ["my-package", "versioned-package", "toml"] { for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new(name, ver).publish(); } } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("toml") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/preserve_sorted/stderr.term.svg000064400000000000000000000027221046102023000243140ustar 00000000000000 Updating `dummy-registry` index Adding toml v99999.0.0 to dependencies Locking 3 packages to latest compatible versions Adding my-package v0.1.1+my-package (available: v99999.0.0+my-package) Adding versioned-package v0.1.1+my-package (available: v99999.0.0+my-package) cargo-0.91.0/tests/testsuite/cargo_add/preserve_unsorted/mod.rs000064400000000000000000000021551046102023000230120ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for name in ["my-package", "versioned-package", "toml"] { for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new(name, ver).publish(); } } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("toml") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/preserve_unsorted/stderr.term.svg000064400000000000000000000027221046102023000246570ustar 00000000000000 Updating `dummy-registry` index Adding toml v99999.0.0 to dependencies Locking 3 packages to latest compatible versions Adding my-package v0.1.1+my-package (available: v99999.0.0+my-package) Adding versioned-package v0.1.1+my-package (available: v99999.0.0+my-package) cargo-0.91.0/tests/testsuite/cargo_add/public/mod.rs000064400000000000000000000015341046102023000205120ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("my-package", "0.1.0").publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package --public") .current_dir(cwd) .masquerade_as_nightly_cargo(&["public-dependency"]) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/public/stderr.term.svg000064400000000000000000000017671046102023000223670ustar 00000000000000 Updating `dummy-registry` index Adding my-package v0.1.0 to public dependencies Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/quiet/mod.rs000064400000000000000000000016001046102023000203550ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") .feature("nose", &[]) .feature("mouth", &[]) .feature("eyes", &[]) .feature("ears", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("--quiet your-face") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(str![""]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/registry/mod.rs000064400000000000000000000023021046102023000210760ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::alt_init(); for name in ["my-package1", "my-package2"] { for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new(name, ver) .alternative(true) .publish(); } } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package1 my-package2 --registry alternative") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/registry/stderr.term.svg000064400000000000000000000022041046102023000227440ustar 00000000000000 Updating `alternative` index Adding my-package1 v99999.0.0 to dependencies Adding my-package2 v99999.0.0 to dependencies Locking 2 packages to latest compatible versions cargo-0.91.0/tests/testsuite/cargo_add/rename/mod.rs000064400000000000000000000020341046102023000204770ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package --rename renamed") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/rename/stderr.term.svg000064400000000000000000000017641046102023000223550ustar 00000000000000 Updating `dummy-registry` index Adding my-package v99999.0.0 to dependencies Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/require_weak/mod.rs000064400000000000000000000016641046102023000217230ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") .feature("nose", &[]) .feature("mouth", &[]) .feature("eyes", &[]) .feature("ears", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("your-face --no-optional") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/require_weak/stderr.term.svg000064400000000000000000000031201046102023000235550ustar 00000000000000 Updating `dummy-registry` index Adding your-face v99999.0.0 to dependencies Features: - ears - eyes - mouth - nose Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/rust_version_ignore/mod.rs000064400000000000000000000020651046102023000233410ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("rust-version-user", "0.1.0") .rust_version("1.66") .publish(); cargo_test_support::registry::Package::new("rust-version-user", "0.2.1") .rust_version("1.72") .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg("--ignore-rust-version") .arg_line("rust-version-user") .current_dir(cwd) .env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback") .assert() .code(0) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/rust_version_ignore/stderr.term.svg000064400000000000000000000017671046102023000252160ustar 00000000000000 Updating `dummy-registry` index Adding rust-version-user v0.2.1 to dependencies Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/rust_version_incompatible/mod.rs000064400000000000000000000022201046102023000245150ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("rust-version-user", "0.1.0") .rust_version("1.66") .publish(); cargo_test_support::registry::Package::new("rust-version-user", "0.1.1") .rust_version("1.66") .publish(); cargo_test_support::registry::Package::new("rust-version-user", "0.2.1") .rust_version("1.72") .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("rust-version-user") .current_dir(cwd) .env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback") .assert() .failure() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/rust_version_incompatible/stderr.term.svg000064400000000000000000000021521046102023000263660ustar 00000000000000 Updating `dummy-registry` index error: no version of crate `rust-version-user` can maintain cargo-list-test-fixture's rust-version of 1.56 help: pass `--ignore-rust-version` to select rust-version-user@0.2.1 which requires rustc 1.72 cargo-0.91.0/tests/testsuite/cargo_add/rust_version_latest/mod.rs000064400000000000000000000020211046102023000233420ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("rust-version-user", "0.1.0") .rust_version("1.66") .publish(); cargo_test_support::registry::Package::new("rust-version-user", "0.2.1") .rust_version("1.72") .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("rust-version-user") .current_dir(cwd) .env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback") .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/rust_version_latest/stderr.term.svg000064400000000000000000000020011046102023000252050ustar 00000000000000 Updating `dummy-registry` index Adding rust-version-user v0.2.1 to dependencies Locking 1 package to latest Rust 1.72 compatible version cargo-0.91.0/tests/testsuite/cargo_add/rust_version_older/mod.rs000064400000000000000000000020211046102023000231530ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("rust-version-user", "0.1.0") .rust_version("1.66") .publish(); cargo_test_support::registry::Package::new("rust-version-user", "0.2.1") .rust_version("1.72") .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("rust-version-user") .current_dir(cwd) .env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback") .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/rust_version_older/stderr.term.svg000064400000000000000000000027441046102023000250340ustar 00000000000000 Updating `dummy-registry` index warning: ignoring rust-version-user@0.2.1 (which requires rustc 1.72) to maintain cargo-list-test-fixture's rust-version of 1.70 Adding rust-version-user v0.1.0 to dependencies Locking 1 package to latest Rust 1.70 compatible version Adding rust-version-user v0.1.0 (available: v0.2.1, requires Rust 1.72) cargo-0.91.0/tests/testsuite/cargo_add/rustc_ignore/mod.rs000064400000000000000000000022661046102023000217420ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("rust-version-user", "0.1.0") .rust_version("1.30") .publish(); cargo_test_support::registry::Package::new("rust-version-user", "0.1.1") .rust_version("1.30") .publish(); cargo_test_support::registry::Package::new("rust-version-user", "0.2.1") .rust_version("1.2345") .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg("--ignore-rust-version") .arg_line("rust-version-user") .current_dir(cwd) .env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback") .assert() .code(0) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/rustc_ignore/stderr.term.svg000064400000000000000000000023571046102023000236100ustar 00000000000000 Updating `dummy-registry` index Adding rust-version-user v0.2.1 to dependencies Locking 1 package to latest compatible version Adding rust-version-user v0.2.1 (requires Rust 1.2345) cargo-0.91.0/tests/testsuite/cargo_add/rustc_incompatible/mod.rs000064400000000000000000000016241046102023000231220ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("rust-version-user", "0.2.1") .rust_version("1.2345") .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("rust-version-user") .current_dir(cwd) .env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback") .assert() .failure() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/rustc_incompatible/stderr.term.svg000064400000000000000000000021161046102023000247640ustar 00000000000000 Updating `dummy-registry` index error: no version of crate `rust-version-user` is compatible with rustc [..] help: pass `--ignore-rust-version` to select rust-version-user@0.2.1 which requires rustc 1.2345 cargo-0.91.0/tests/testsuite/cargo_add/rustc_latest/mod.rs000064400000000000000000000022221046102023000217430ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("rust-version-user", "0.1.0") .rust_version("1.30") .publish(); cargo_test_support::registry::Package::new("rust-version-user", "0.1.1") .rust_version("1.30") .publish(); cargo_test_support::registry::Package::new("rust-version-user", "0.2.1") .rust_version("1.2345") .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("rust-version-user") .current_dir(cwd) .env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback") .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/rustc_latest/stderr.term.svg000064400000000000000000000027231046102023000236160ustar 00000000000000 Updating `dummy-registry` index warning: ignoring rust-version-user@0.2.1 (which requires rustc 1.2345) as it is incompatible with rustc [..] Adding rust-version-user v0.1.1 to dependencies Locking 1 package to latest Rust [..] compatible version Adding rust-version-user v0.1.1 (available: v0.2.1, requires Rust 1.2345) cargo-0.91.0/tests/testsuite/cargo_add/rustc_older/mod.rs000064400000000000000000000022221046102023000215540ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("rust-version-user", "0.1.0") .rust_version("1.30") .publish(); cargo_test_support::registry::Package::new("rust-version-user", "0.1.1") .rust_version("1.30") .publish(); cargo_test_support::registry::Package::new("rust-version-user", "0.2.1") .rust_version("1.2345") .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("rust-version-user") .current_dir(cwd) .env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback") .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/rustc_older/stderr.term.svg000064400000000000000000000027231046102023000234270ustar 00000000000000 Updating `dummy-registry` index warning: ignoring rust-version-user@0.2.1 (which requires rustc 1.2345) as it is incompatible with rustc [..] Adding rust-version-user v0.1.1 to dependencies Locking 1 package to latest Rust [..] compatible version Adding rust-version-user v0.1.1 (available: v0.2.1, requires Rust 1.2345) cargo-0.91.0/tests/testsuite/cargo_add/script_bare/in/cargo-test-fixture.rs000064400000000000000000000000151046102023000251050ustar 00000000000000fn main() {} cargo-0.91.0/tests/testsuite/cargo_add/script_bare/mod.rs000064400000000000000000000021741046102023000215320ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .masquerade_as_nightly_cargo(&["script"]) .arg("-Zscript") .arg("add") .arg_line("--manifest-path cargo-test-fixture.rs my-package") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/script_bare/out/cargo-test-fixture.rs000064400000000000000000000000761046102023000253150ustar 00000000000000--- [dependencies] my-package = "99999.0.0" --- fn main() {} cargo-0.91.0/tests/testsuite/cargo_add/script_bare/stderr.term.svg000064400000000000000000000026121046102023000233740ustar 00000000000000 warning: `package.edition` is unspecified, defaulting to `[..]` Updating `dummy-registry` index Adding my-package v99999.0.0 to dependencies warning: `package.edition` is unspecified, defaulting to `[..]` Locking 1 package to latest [..]compatible version cargo-0.91.0/tests/testsuite/cargo_add/script_frontmatter/in/cargo-test-fixture.rs000064400000000000000000000000621046102023000265430ustar 00000000000000--- [package] edition = "2015" --- fn main() { } cargo-0.91.0/tests/testsuite/cargo_add/script_frontmatter/mod.rs000064400000000000000000000021741046102023000231660ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .masquerade_as_nightly_cargo(&["script"]) .arg("-Zscript") .arg("add") .arg_line("--manifest-path cargo-test-fixture.rs my-package") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/script_frontmatter/out/cargo-test-fixture.rs000064400000000000000000000001331046102023000267430ustar 00000000000000--- [package] edition = "2015" [dependencies] my-package = "99999.0.0" --- fn main() { } cargo-0.91.0/tests/testsuite/cargo_add/script_frontmatter/stderr.term.svg000064400000000000000000000017701046102023000250340ustar 00000000000000 Updating `dummy-registry` index Adding my-package v99999.0.0 to dependencies Locking 1 package to latest [..]compatible version cargo-0.91.0/tests/testsuite/cargo_add/script_shebang/in/cargo-test-fixture.rs000064400000000000000000000000431046102023000256040ustar 00000000000000#!/usr/bin/env cargo fn main() {} cargo-0.91.0/tests/testsuite/cargo_add/script_shebang/mod.rs000064400000000000000000000021741046102023000222300ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .masquerade_as_nightly_cargo(&["script"]) .arg("-Zscript") .arg("add") .arg_line("--manifest-path cargo-test-fixture.rs my-package") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/script_shebang/out/cargo-test-fixture.rs000064400000000000000000000001231046102023000260040ustar 00000000000000#!/usr/bin/env cargo --- [dependencies] my-package = "99999.0.0" --- fn main() {} cargo-0.91.0/tests/testsuite/cargo_add/script_shebang/stderr.term.svg000064400000000000000000000026121046102023000240720ustar 00000000000000 warning: `package.edition` is unspecified, defaulting to `[..]` Updating `dummy-registry` index Adding my-package v99999.0.0 to dependencies warning: `package.edition` is unspecified, defaulting to `[..]` Locking 1 package to latest [..]compatible version cargo-0.91.0/tests/testsuite/cargo_add/sorted_table_with_dotted_item/mod.rs000064400000000000000000000022731046102023000253200ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for name in [ "unrelateed-crate", "versioned-package", "toml", "my-build-package1", ] { for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new(name, ver).publish(); } } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("unrelateed-crate") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/sorted_table_with_dotted_item/stderr.term.svg000064400000000000000000000032541046102023000271650ustar 00000000000000 Updating `dummy-registry` index Adding unrelateed-crate v99999.0.0 to dependencies Locking 4 packages to latest compatible versions Adding my-build-package1 v0.1.1+my-package (available: v99999.0.0+my-package) Adding toml v0.1.1+my-package (available: v99999.0.0+my-package) Adding versioned-package v0.1.1+my-package (available: v99999.0.0+my-package) cargo-0.91.0/tests/testsuite/cargo_add/symlink.rs000064400000000000000000000024361046102023000201450ustar 00000000000000use crate::prelude::*; use cargo_test_support::project; use cargo_test_support::registry; use std::fs; #[cargo_test] fn symlink_case() { if !cargo_test_support::symlink_supported() { return; } registry::init(); registry::Package::new("test-dep", "1.0.0").publish(); let project = project().file("src/lib.rs", "").build(); let target_dir = project.root().join("target_dir"); fs::create_dir_all(&target_dir).unwrap(); fs::copy( project.root().join("Cargo.toml"), target_dir.join("Cargo.toml"), ) .unwrap(); fs::remove_file(project.root().join("Cargo.toml")).unwrap(); #[cfg(unix)] { use std::os::unix::fs::symlink; symlink( target_dir.join("Cargo.toml"), project.root().join("Cargo.toml"), ) .unwrap(); } #[cfg(windows)] { use std::os::windows::fs::symlink_file; symlink_file( target_dir.join("Cargo.toml"), project.root().join("Cargo.toml"), ) .unwrap(); } project.cargo("add test-dep").run(); assert!(project.root().join("Cargo.toml").is_symlink()); let target_content = fs::read_to_string(target_dir.join("Cargo.toml")).unwrap(); assert!(target_content.contains("test-dep")); } cargo-0.91.0/tests/testsuite/cargo_add/target/mod.rs000064400000000000000000000022231046102023000205160ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for name in ["my-package1", "my-package2"] { for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new(name, ver).publish(); } } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package1 my-package2 --target wasm32-unknown-unknown") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/target/stderr.term.svg000064400000000000000000000023171046102023000223670ustar 00000000000000 Updating `dummy-registry` index Adding my-package1 v99999.0.0 to dependencies for target `wasm32-unknown-unknown` Adding my-package2 v99999.0.0 to dependencies for target `wasm32-unknown-unknown` Locking 2 packages to latest compatible versions cargo-0.91.0/tests/testsuite/cargo_add/target_cfg/mod.rs000064400000000000000000000022271046102023000213410ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for name in ["my-package1", "my-package2"] { for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new(name, ver).publish(); } } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package1 my-package2 --target 'cfg(target_os=\"linux\")'") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/target_cfg/stderr.term.svg000064400000000000000000000023171046102023000232060ustar 00000000000000 Updating `dummy-registry` index Adding my-package1 v99999.0.0 to dependencies for target `cfg(target_os="linux")` Adding my-package2 v99999.0.0 to dependencies for target `cfg(target_os="linux")` Locking 2 packages to latest compatible versions cargo-0.91.0/tests/testsuite/cargo_add/unknown_inherited_feature/mod.rs000064400000000000000000000013111046102023000244720ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .args(["foo", "-p", "bar"]) .current_dir(cwd) .assert() .failure() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/unknown_inherited_feature/stderr.term.svg000064400000000000000000000027171046102023000263520ustar 00000000000000 Adding foo (workspace) to dependencies error: unrecognized feature for crate foo: not_recognized disabled features: merge, merge-base, unrelated enabled features: default-base, default-merge-base, default-test-base long-feature-name-because-of-formatting-reasons, test, test-base cargo-0.91.0/tests/testsuite/cargo_add/vers/mod.rs000064400000000000000000000020231046102023000202050ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("my-package@>=0.1.1") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/vers/stderr.term.svg000064400000000000000000000017641046102023000220650ustar 00000000000000 Updating `dummy-registry` index Adding my-package >=0.1.1 to dependencies Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_add/workspace_name/mod.rs000064400000000000000000000021301046102023000222230ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", ver) .publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("cargo-list-test-fixture-dependency") .current_dir(&cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/workspace_name/stderr.term.svg000064400000000000000000000013751046102023000241020ustar 00000000000000 Adding cargo-list-test-fixture-dependency (local) to dependencies cargo-0.91.0/tests/testsuite/cargo_add/workspace_path/mod.rs000064400000000000000000000021551046102023000222460ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", ver) .publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("cargo-list-test-fixture-dependency --path ../dependency") .current_dir(&cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/workspace_path/stderr.term.svg000064400000000000000000000013751046102023000241160ustar 00000000000000 Adding cargo-list-test-fixture-dependency (local) to dependencies cargo-0.91.0/tests/testsuite/cargo_add/workspace_path_dev/mod.rs000064400000000000000000000021631046102023000231030ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("cargo-list-test-fixture-dependency", ver) .publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("cargo-list-test-fixture-dependency --path ../dependency --dev") .current_dir(&cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/workspace_path_dev/stderr.term.svg000064400000000000000000000014011046102023000247420ustar 00000000000000 Adding cargo-list-test-fixture-dependency (local) to dev-dependencies cargo-0.91.0/tests/testsuite/cargo_add/yanked/mod.rs000064400000000000000000000017541046102023000205130ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("linked-hash-map", "0.5.0").publish(); cargo_test_support::registry::Package::new("linked-hash-map", "0.5.4").publish(); cargo_test_support::registry::Package::new("linked-hash-map", "0.6.0") .yanked(true) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("add") .arg_line("linked-hash-map") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_add/yanked/stderr.term.svg000064400000000000000000000017651046102023000223620ustar 00000000000000 Updating `dummy-registry` index Adding linked-hash-map v0.5.4 to dependencies Locking 1 package to latest compatible version cargo-0.91.0/tests/testsuite/cargo_alias_config.rs000064400000000000000000000314101046102023000203370ustar 00000000000000//! Tests for `[alias]` config command aliases. use std::env; use crate::prelude::*; use crate::utils::tools::echo_subcommand; use cargo_test_support::str; use cargo_test_support::{basic_bin_manifest, project}; #[cargo_test] fn alias_incorrect_config_type() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", "fn main() {}") .file( ".cargo/config.toml", r#" [alias] b-cargo-test = 5 "#, ) .build(); p.cargo("b-cargo-test -v") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid configuration for key `alias.b-cargo-test` expected a list, but found a integer for `alias.b-cargo-test` in [ROOT]/foo/.cargo/config.toml "#]]) .run(); } #[cargo_test] fn alias_malformed_config_string() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", "fn main() {}") .file( ".cargo/config.toml", r#" [alias] b-cargo-test = ` "#, ) .build(); p.cargo("b-cargo-test -v") .with_status(101) .with_stderr_data(str![[r#" [ERROR] could not load Cargo configuration Caused by: could not parse TOML configuration in `[ROOT]/foo/.cargo/config.toml` Caused by: TOML parse error at line 3, column 32 | 3 | b-cargo-test = ` | ^ string values must be quoted, expected literal string "#]]) .run(); } #[cargo_test] fn alias_malformed_config_list() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", "fn main() {}") .file( ".cargo/config.toml", r#" [alias] b-cargo-test = [1, 2] "#, ) .build(); p.cargo("b-cargo-test -v") .with_status(101) .with_stderr_data(str![[r#" [ERROR] could not load Cargo configuration Caused by: failed to load TOML configuration from `[ROOT]/foo/.cargo/config.toml` Caused by: failed to parse key `alias` Caused by: failed to parse key `b-cargo-test` Caused by: expected string but found integer in list "#]]) .run(); } #[cargo_test] fn alias_config() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", "fn main() {}") .file( ".cargo/config.toml", r#" [alias] b-cargo-test = "build" "#, ) .build(); p.cargo("b-cargo-test -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn dependent_alias() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", "fn main() {}") .file( ".cargo/config.toml", r#" [alias] b-cargo-test = "build" a-cargo-test = ["b-cargo-test", "-v"] "#, ) .build(); p.cargo("a-cargo-test") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn builtin_alias_shadowing_external_subcommand() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", "fn main() {}") .executable("cargo-t", "") .build(); let mut paths: Vec<_> = env::split_paths(&env::var_os("PATH").unwrap_or_default()).collect(); paths.push(p.root()); let path = env::join_paths(paths).unwrap(); p.cargo("t") .env("PATH", &path) .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/main.rs (target/debug/deps/foo-[HASH][EXE]) "#]]) .run(); } #[cargo_test] fn alias_shadowing_external_subcommand() { let echo = echo_subcommand(); let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", "fn main() {}") .file( ".cargo/config.toml", r#" [alias] echo = "build" "#, ) .build(); let mut paths: Vec<_> = env::split_paths(&env::var_os("PATH").unwrap_or_default()).collect(); paths.push(echo.target_debug_dir()); let path = env::join_paths(paths).unwrap(); p.cargo("echo") .env("PATH", &path) .with_stderr_data(str![[r#" [WARNING] user-defined alias `echo` is shadowing an external subcommand found at: `[ROOT]/cargo-echo/target/debug/cargo-echo[EXE]` This was previously accepted but is being phased out; it will become a hard error in a future release. For more information, see issue #10049 . [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn default_args_alias() { let echo = echo_subcommand(); let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", "fn main() {}") .file( ".cargo/config.toml", r#" [alias] echo = "echo --flag1 --flag2" test-1 = "echo" build = "build --verbose" "#, ) .build(); let mut paths: Vec<_> = env::split_paths(&env::var_os("PATH").unwrap_or_default()).collect(); paths.push(echo.target_debug_dir()); let path = env::join_paths(paths).unwrap(); p.cargo("echo") .env("PATH", &path) .with_status(101) .with_stderr_data(str![[r#" [WARNING] user-defined alias `echo` is shadowing an external subcommand found at: `[ROOT]/cargo-echo/target/debug/cargo-echo[EXE]` This was previously accepted but is being phased out; it will become a hard error in a future release. For more information, see issue #10049 . [ERROR] alias echo has unresolvable recursive definition: echo -> echo "#]]) .run(); p.cargo("test-1") .env("PATH", &path) .with_status(101) .with_stderr_data(str![[r#" [WARNING] user-defined alias `echo` is shadowing an external subcommand found at: `[ROOT]/cargo-echo/target/debug/cargo-echo[EXE]` This was previously accepted but is being phased out; it will become a hard error in a future release. For more information, see issue #10049 . [ERROR] alias test-1 has unresolvable recursive definition: test-1 -> echo -> echo "#]]) .run(); // Builtins are not expanded by rule p.cargo("build") .with_stderr_data(str![[r#" [WARNING] user-defined alias `build` is ignored, because it is shadowed by a built-in command [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn corecursive_alias() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", "fn main() {}") .file( ".cargo/config.toml", r#" [alias] test-1 = "test-2 --flag1" test-2 = "test-3 --flag2" test-3 = "test-1 --flag3" "#, ) .build(); p.cargo("test-1") .with_status(101) .with_stderr_data(str![[r#" [ERROR] alias test-1 has unresolvable recursive definition: test-1 -> test-2 -> test-3 -> test-1 "#]]) .run(); p.cargo("test-2") .with_status(101) .with_stderr_data(str![[r#" [ERROR] alias test-2 has unresolvable recursive definition: test-2 -> test-3 -> test-1 -> test-2 "#]]) .run(); } #[cargo_test] fn alias_list_test() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", "fn main() {}") .file( ".cargo/config.toml", r#" [alias] b-cargo-test = ["build", "--release"] "#, ) .build(); p.cargo("b-cargo-test -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn alias_with_flags_config() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", "fn main() {}") .file( ".cargo/config.toml", r#" [alias] b-cargo-test = "build --release" "#, ) .build(); p.cargo("b-cargo-test -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn alias_cannot_shadow_builtin_command() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", "fn main() {}") .file( ".cargo/config.toml", r#" [alias] build = "fetch" "#, ) .build(); p.cargo("build") .with_stderr_data(str![[r#" [WARNING] user-defined alias `build` is ignored, because it is shadowed by a built-in command [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn alias_override_builtin_alias() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", "fn main() {}") .file( ".cargo/config.toml", r#" [alias] b = "run" "#, ) .build(); p.cargo("b") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .run(); } #[cargo_test] fn builtin_alias_takes_options() { // #6381 let p = project() .file("src/lib.rs", "") .file( "examples/ex1.rs", r#"fn main() { println!("{}", std::env::args().skip(1).next().unwrap()) }"#, ) .build(); p.cargo("r --example ex1 -- asdf") .with_stdout_data(str![[r#" asdf "#]]) .run(); } #[cargo_test] fn global_options_with_alias() { // Check that global options are passed through. let p = project().file("src/lib.rs", "").build(); p.cargo("-v c") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn weird_check() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", "fn main() {}") .build(); p.cargo("-- check --invalid_argument -some-other-argument") .with_status(101) .with_stderr_data(str![[r#" [ERROR] trailing arguments after built-in command `check` are unsupported: `--invalid_argument -some-other-argument` To pass the arguments to the subcommand, remove `--` "#]]) .run(); } #[cargo_test] fn empty_alias() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", "fn main() {}") .file( ".cargo/config.toml", r#" [alias] string = "" array = [] "#, ) .build(); p.cargo("string") .with_status(101) .with_stderr_data(str![[r#" [ERROR] subcommand is required, but `alias.string` is empty "#]]) .run(); p.cargo("array") .with_status(101) .with_stderr_data(str![[r#" [ERROR] subcommand is required, but `alias.array` is empty "#]]) .run(); } #[cargo_test] fn alias_no_subcommand() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", "fn main() {}") .file( ".cargo/config.toml", r#" [alias] a = "--locked" "#, ) .build(); p.cargo("a") .with_status(101) .with_stderr_data(str![[r#" [ERROR] subcommand is required, add a subcommand to the command alias `alias.a` "#]]) .run(); } cargo-0.91.0/tests/testsuite/cargo_bench/help/mod.rs000064400000000000000000000004621046102023000205120ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("bench") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_bench/help/stdout.term.svg000064400000000000000000000261631046102023000224040ustar 00000000000000 Execute all benchmarks of a local package Usage: cargo[EXE] bench [OPTIONS] [BENCHNAME] [-- [ARGS]...] Arguments: [BENCHNAME] If specified, only run benches containing this string in their names [ARGS]... Arguments for the bench binary Options: --no-run Compile, but don't run benchmarks --no-fail-fast Run all benchmarks regardless of failure --message-format <FMT> Error format [possible values: human, short, json, json-diagnostic-short, json-diagnostic-rendered-ansi, json-render-diagnostics] -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Package Selection: -p, --package [<SPEC>] Package to run benchmarks for --workspace Benchmark all packages in the workspace --exclude <SPEC> Exclude packages from the benchmark --all Alias for --workspace (deprecated) Target Selection: --lib Benchmark only this package's library --bins Benchmark all binaries --bin [<NAME>] Benchmark only the specified binary --examples Benchmark all examples --example [<NAME>] Benchmark only the specified example --tests Benchmark all targets that have `test = true` set --test [<NAME>] Benchmark only the specified test target --benches Benchmark all targets that have `bench = true` set --bench [<NAME>] Benchmark only the specified bench target --all-targets Benchmark all targets Feature Selection: -F, --features <FEATURES> Space or comma separated list of features to activate --all-features Activate all available features --no-default-features Do not activate the `default` feature Compilation Options: -j, --jobs <N> Number of parallel jobs, defaults to # of CPUs. --profile <PROFILE-NAME> Build artifacts with the specified profile --target [<TRIPLE>] Build for the target triple --target-dir <DIRECTORY> Directory for all generated artifacts --unit-graph Output build graph in JSON (unstable) --timings[=<FMTS>] Timing output formats (unstable) (comma separated): html, json Manifest Options: --manifest-path <PATH> Path to Cargo.toml --lockfile-path <PATH> Path to Cargo.lock (unstable) --ignore-rust-version Ignore `rust-version` specification in packages --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help bench` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_bench/mod.rs000064400000000000000000000000351046102023000175560ustar 00000000000000mod help; mod no_keep_going; cargo-0.91.0/tests/testsuite/cargo_bench/no_keep_going/mod.rs000064400000000000000000000010401046102023000223560ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("bench") .arg("--keep-going") .current_dir(cwd) .assert() .code(1) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); } cargo-0.91.0/tests/testsuite/cargo_bench/no_keep_going/stderr.term.svg000064400000000000000000000033231046102023000242310ustar 00000000000000 error: unexpected argument '--keep-going' found tip: use `--no-fail-fast` to run as many tests as possible regardless of failure Usage: cargo[EXE] bench [OPTIONS] [BENCHNAME] [-- [ARGS]...] For more information, try '--help'. cargo-0.91.0/tests/testsuite/cargo_build/help/mod.rs000064400000000000000000000004621046102023000205320ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("build") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_build/help/stdout.term.svg000064400000000000000000000262121046102023000224170ustar 00000000000000 Compile a local package and all of its dependencies Usage: cargo[EXE] build [OPTIONS] Options: --future-incompat-report Outputs a future incompatibility report at the end of the build --message-format <FMT> Error format [possible values: human, short, json, json-diagnostic-short, json-diagnostic-rendered-ansi, json-render-diagnostics] -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Package Selection: -p, --package [<SPEC>] Package to build (see `cargo help pkgid`) --workspace Build all packages in the workspace --exclude <SPEC> Exclude packages from the build --all Alias for --workspace (deprecated) Target Selection: --lib Build only this package's library --bins Build all binaries --bin [<NAME>] Build only the specified binary --examples Build all examples --example [<NAME>] Build only the specified example --tests Build all targets that have `test = true` set --test [<NAME>] Build only the specified test target --benches Build all targets that have `bench = true` set --bench [<NAME>] Build only the specified bench target --all-targets Build all targets Feature Selection: -F, --features <FEATURES> Space or comma separated list of features to activate --all-features Activate all available features --no-default-features Do not activate the `default` feature Compilation Options: -r, --release Build artifacts in release mode, with optimizations --profile <PROFILE-NAME> Build artifacts with the specified profile -j, --jobs <N> Number of parallel jobs, defaults to # of CPUs. --keep-going Do not abort the build as soon as there is an error --target [<TRIPLE>] Build for the target triple --target-dir <DIRECTORY> Directory for all generated artifacts --artifact-dir <PATH> Copy final artifacts to this directory (unstable) --build-plan Output the build plan in JSON (unstable) --unit-graph Output build graph in JSON (unstable) --timings[=<FMTS>] Timing output formats (unstable) (comma separated): html, json Manifest Options: --manifest-path <PATH> Path to Cargo.toml --lockfile-path <PATH> Path to Cargo.lock (unstable) --ignore-rust-version Ignore `rust-version` specification in packages --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help build` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_build/mod.rs000064400000000000000000000000121046102023000175710ustar 00000000000000mod help; cargo-0.91.0/tests/testsuite/cargo_check/help/mod.rs000064400000000000000000000004621046102023000205100ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("check") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_check/help/stdout.term.svg000064400000000000000000000253121046102023000223750ustar 00000000000000 Check a local package and all of its dependencies for errors Usage: cargo[EXE] check [OPTIONS] Options: --future-incompat-report Outputs a future incompatibility report at the end of the build --message-format <FMT> Error format [possible values: human, short, json, json-diagnostic-short, json-diagnostic-rendered-ansi, json-render-diagnostics] -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Package Selection: -p, --package [<SPEC>] Package(s) to check --workspace Check all packages in the workspace --exclude <SPEC> Exclude packages from the check --all Alias for --workspace (deprecated) Target Selection: --lib Check only this package's library --bins Check all binaries --bin [<NAME>] Check only the specified binary --examples Check all examples --example [<NAME>] Check only the specified example --tests Check all targets that have `test = true` set --test [<NAME>] Check only the specified test target --benches Check all targets that have `bench = true` set --bench [<NAME>] Check only the specified bench target --all-targets Check all targets Feature Selection: -F, --features <FEATURES> Space or comma separated list of features to activate --all-features Activate all available features --no-default-features Do not activate the `default` feature Compilation Options: -j, --jobs <N> Number of parallel jobs, defaults to # of CPUs. --keep-going Do not abort the build as soon as there is an error -r, --release Check artifacts in release mode, with optimizations --profile <PROFILE-NAME> Check artifacts with the specified profile --target [<TRIPLE>] Check for the target triple --target-dir <DIRECTORY> Directory for all generated artifacts --unit-graph Output build graph in JSON (unstable) --timings[=<FMTS>] Timing output formats (unstable) (comma separated): html, json Manifest Options: --manifest-path <PATH> Path to Cargo.toml --lockfile-path <PATH> Path to Cargo.lock (unstable) --ignore-rust-version Ignore `rust-version` specification in packages --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help check` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_check/mod.rs000064400000000000000000000000121046102023000175470ustar 00000000000000mod help; cargo-0.91.0/tests/testsuite/cargo_clean/help/mod.rs000064400000000000000000000004621046102023000205150ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("clean") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_clean/help/stdout.term.svg000064400000000000000000000136001046102023000223770ustar 00000000000000 Remove artifacts that cargo has generated in the past Usage: cargo[EXE] clean [OPTIONS] Options: --doc Whether or not to clean just the documentation directory -n, --dry-run Display what would be deleted without deleting anything -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Package Selection: -p, --package [<SPEC>] Package to clean artifacts for Compilation Options: -r, --release Whether or not to clean release artifacts --profile <PROFILE-NAME> Clean artifacts of the specified profile --target [<TRIPLE>] Target triple to clean output for --target-dir <DIRECTORY> Directory for all generated artifacts Manifest Options: --manifest-path <PATH> Path to Cargo.toml --lockfile-path <PATH> Path to Cargo.lock (unstable) --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help clean` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_clean/mod.rs000064400000000000000000000000121046102023000175540ustar 00000000000000mod help; cargo-0.91.0/tests/testsuite/cargo_command.rs000064400000000000000000000426601046102023000173500ustar 00000000000000//! Tests for custom cargo commands and other global command features. use std::env; use std::fs; use std::io::Read; use std::path::{Path, PathBuf}; use std::process::Stdio; use std::str; use crate::prelude::*; use crate::utils::cargo_exe; use crate::utils::cargo_process; use crate::utils::tools::echo_subcommand; use cargo_test_support::basic_manifest; use cargo_test_support::registry::Package; use cargo_test_support::rustc_host; use cargo_test_support::str; use cargo_test_support::{basic_bin_manifest, paths, project, project_in_home}; use cargo_util::paths::join_paths; fn path() -> Vec { env::split_paths(&env::var_os("PATH").unwrap_or_default()).collect() } #[cargo_test] fn list_commands_with_descriptions() { let p = project().build(); p.cargo("--list") .with_stdout_data( "\ ... b alias: build ... build Compile a local package and all of its dependencies ... c alias: check ... r alias: run ... read-manifest DEPRECATED: Print a JSON representation of a Cargo.toml manifest. ... t alias: test ... ", ) .run(); } #[cargo_test] fn list_custom_aliases_with_descriptions() { let p = project_in_home("proj") .file( &paths::home().join(".cargo").join("config"), r#" [alias] myaliasstr = "foo --bar" myaliasvec = ["foo", "--bar"] "#, ) .build(); p.cargo("--list") .with_stdout_data(str![[r#" ... myaliasstr alias: foo --bar myaliasvec alias: foo --bar ... "#]]) .run(); } #[cargo_test] fn list_dedupe() { let p = project() .executable(Path::new("path-test-1").join("cargo-dupe"), "") .executable(Path::new("path-test-2").join("cargo-dupe"), "") .build(); let mut path = path(); path.push(p.root().join("path-test-1")); path.push(p.root().join("path-test-2")); let path = env::join_paths(path.iter()).unwrap(); p.cargo("--list") .env("PATH", &path) .with_stdout_data(str![[r#" ... dupe ... "#]]) .run(); } #[cargo_test] fn list_command_looks_at_path() { let proj = project() .executable(Path::new("path-test").join("cargo-1"), "") .build(); let mut path = path(); path.push(proj.root().join("path-test")); let path = env::join_paths(path.iter()).unwrap(); let output = cargo_process("-v --list").env("PATH", &path).run(); let output = str::from_utf8(&output.stdout).unwrap(); assert!( output.contains("\n 1 "), "missing 1: {}", output ); } #[cfg(windows)] #[cargo_test] fn list_command_looks_at_path_case_mismatch() { let proj = project() .executable(Path::new("path-test").join("cargo-1"), "") .build(); let mut path = path(); path.push(proj.root().join("path-test")); let path = env::join_paths(path.iter()).unwrap(); // See issue #11814: Environment variable names are case-insensitive on Windows. // We need to check that having "Path" instead of "PATH" is okay. let output = cargo_process("-v --list") .env("Path", &path) .env_remove("PATH") .run(); let output = str::from_utf8(&output.stdout).unwrap(); assert!( output.contains("\n 1 "), "missing 1: {}", output ); } #[cargo_test] fn list_command_handles_known_external_commands() { let p = project() .executable(Path::new("path-test").join("cargo-fmt"), "") .build(); let fmt_desc = " fmt Formats all bin and lib files of the current crate using rustfmt."; // Without path - fmt isn't there p.cargo("--list") .env("PATH", "") .with_stdout_does_not_contain(fmt_desc) .run(); // With path - fmt is there with known description let mut path = path(); path.push(p.root().join("path-test")); let path = env::join_paths(path.iter()).unwrap(); p.cargo("--list") .env("PATH", &path) .with_stdout_data(str![[r#" ... fmt Formats all bin and lib files of the current crate using rustfmt. ..."#]]) .run(); } #[cargo_test] fn list_command_resolves_symlinks() { let proj = project() .symlink(cargo_exe(), Path::new("path-test").join("cargo-2")) .build(); let mut path = path(); path.push(proj.root().join("path-test")); let path = env::join_paths(path.iter()).unwrap(); let output = cargo_process("-v --list").env("PATH", &path).run(); let output = str::from_utf8(&output.stdout).unwrap(); assert!( output.contains("\n 2 "), "missing 2: {}", output ); } #[cargo_test] fn find_closest_capital_c_to_c() { cargo_process("C") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no such command: `C` [HELP] a command with a similar name exists: `c` [HELP] view all installed commands with `cargo --list` [HELP] find a package to install `C` with `cargo search cargo-C` "#]]) .run(); } #[cargo_test] fn find_closest_capital_b_to_b() { cargo_process("B") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no such command: `B` [HELP] a command with a similar name exists: `b` [HELP] view all installed commands with `cargo --list` [HELP] find a package to install `B` with `cargo search cargo-B` "#]]) .run(); } #[cargo_test] fn find_closest_biuld_to_build() { cargo_process("biuld") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no such command: `biuld` [HELP] a command with a similar name exists: `build` [HELP] view all installed commands with `cargo --list` [HELP] find a package to install `biuld` with `cargo search cargo-biuld` "#]]) .run(); // But, if we actually have `biuld`, it must work! // https://github.com/rust-lang/cargo/issues/5201 Package::new("cargo-biuld", "1.0.0") .file( "src/main.rs", r#" fn main() { println!("Similar, but not identical to, build"); } "#, ) .publish(); cargo_process("install cargo-biuld").run(); cargo_process("biuld") .with_stdout_data(str![[r#" Similar, but not identical to, build "#]]) .run(); cargo_process("--list") .with_stdout_data(str![[r#" ... biuld ... build Compile a local package and all of its dependencies ..."#]]) .run(); } #[cargo_test] fn find_closest_alias() { let root = paths::root(); let my_home = root.join("my_home"); fs::create_dir(&my_home).unwrap(); fs::write( &my_home.join("config.toml"), r#" [alias] myalias = "build" "#, ) .unwrap(); cargo_process("myalais") .env("CARGO_HOME", &my_home) .with_status(101) .with_stderr_data(str![[r#" [ERROR] no such command: `myalais` [HELP] a command with a similar name exists: `myalias` [HELP] view all installed commands with `cargo --list` [HELP] find a package to install `myalais` with `cargo search cargo-myalais` "#]]) .run(); // But, if no alias is defined, it must not suggest one! cargo_process("myalais") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no such command: `myalais` [HELP] view all installed commands with `cargo --list` [HELP] find a package to install `myalais` with `cargo search cargo-myalais` "#]]) .run(); } // If a subcommand is more than an edit distance of 3 away, we don't make a suggestion. #[cargo_test] fn find_closest_dont_correct_nonsense() { cargo_process("there-is-no-way-that-there-is-a-command-close-to-this") .cwd(&paths::root()) .with_status(101) .with_stderr_data(str![[r#" [ERROR] no such command: `there-is-no-way-that-there-is-a-command-close-to-this` [HELP] view all installed commands with `cargo --list` [HELP] find a package to install `there-is-no-way-that-there-is-a-command-close-to-this` with `cargo search cargo-there-is-no-way-that-there-is-a-command-close-to-this` "#]]) .run(); } #[cargo_test] fn displays_subcommand_on_error() { cargo_process("invalid-command") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no such command: `invalid-command` [HELP] view all installed commands with `cargo --list` [HELP] find a package to install `invalid-command` with `cargo search cargo-invalid-command` "#]]) .run(); } #[cargo_test] fn override_cargo_home() { let root = paths::root(); let my_home = root.join("my_home"); fs::create_dir(&my_home).unwrap(); fs::write( &my_home.join("config"), r#" [cargo-new] vcs = "none" "#, ) .unwrap(); cargo_process("new foo").env("CARGO_HOME", &my_home).run(); assert!(!paths::root().join("foo/.git").is_dir()); cargo_process("new foo2").run(); assert!(paths::root().join("foo2/.git").is_dir()); } #[cargo_test] fn cargo_subcommand_env() { let src = format!( r#" use std::env; fn main() {{ println!("{{}}", env::var("{}").unwrap()); }} "#, cargo::CARGO_ENV ); let p = project() .at("cargo-envtest") .file("Cargo.toml", &basic_bin_manifest("cargo-envtest")) .file("src/main.rs", &src) .build(); let target_dir = p.target_debug_dir(); p.cargo("build").run(); assert!(p.bin("cargo-envtest").is_file()); let cargo = cargo_exe(); let mut path = path(); path.push(target_dir.clone()); let path = env::join_paths(path.iter()).unwrap(); cargo_process("envtest") .env("PATH", &path) .with_stdout_data(format!("{}\n", cargo.to_str().unwrap()).raw()) .run(); // Check that subcommands inherit an overridden $CARGO let envtest_bin = target_dir .join("cargo-envtest") .with_extension(std::env::consts::EXE_EXTENSION); let envtest_bin = envtest_bin.to_str().unwrap(); // Previously, `$CARGO` would be left at `envtest_bin`. However, with the // fix for #15099, `$CARGO` is now overwritten with the path to the current // exe when it is detected to be a cargo binary. cargo_process("envtest") .env("PATH", &path) .env(cargo::CARGO_ENV, &envtest_bin) .with_stdout_data(format!("{}\n", cargo.display()).raw()) .run(); } #[cargo_test] fn cargo_cmd_bins_vs_explicit_path() { // Set up `cargo-foo` binary in two places: inside `$HOME/.cargo/bin` and outside of it // // Return paths to both places fn set_up_cargo_foo() -> (PathBuf, PathBuf) { let p = project() .at("cargo-foo") .file("Cargo.toml", &basic_manifest("cargo-foo", "1.0.0")) .file( "src/bin/cargo-foo.rs", r#"fn main() { println!("INSIDE"); }"#, ) .file( "src/bin/cargo-foo2.rs", r#"fn main() { println!("OUTSIDE"); }"#, ) .build(); p.cargo("build").run(); let cargo_bin_dir = paths::home().join(".cargo/bin"); cargo_bin_dir.mkdir_p(); let root_bin_dir = paths::root().join("bin"); root_bin_dir.mkdir_p(); let exe_name = format!("cargo-foo{}", env::consts::EXE_SUFFIX); fs::rename(p.bin("cargo-foo"), cargo_bin_dir.join(&exe_name)).unwrap(); fs::rename(p.bin("cargo-foo2"), root_bin_dir.join(&exe_name)).unwrap(); (root_bin_dir, cargo_bin_dir) } let (outside_dir, inside_dir) = set_up_cargo_foo(); // If `$CARGO_HOME/bin` is not in a path, prefer it over anything in `$PATH`. // // This is the historical behavior we don't want to break. cargo_process("foo") .with_stdout_data(str![[r#" INSIDE "#]]) .run(); // When `$CARGO_HOME/bin` is in the `$PATH` // use only `$PATH` so the user-defined ordering is respected. { cargo_process("foo") .env( "PATH", join_paths(&[&inside_dir, &outside_dir], "PATH").unwrap(), ) .with_stdout_data(str![[r#" INSIDE "#]]) .run(); cargo_process("foo") // Note: trailing slash .env( "PATH", join_paths(&[inside_dir.join(""), outside_dir.join("")], "PATH").unwrap(), ) .with_stdout_data(str![[r#" INSIDE "#]]) .run(); cargo_process("foo") .env( "PATH", join_paths(&[&outside_dir, &inside_dir], "PATH").unwrap(), ) .with_stdout_data(str![[r#" OUTSIDE "#]]) .run(); cargo_process("foo") // Note: trailing slash .env( "PATH", join_paths(&[outside_dir.join(""), inside_dir.join("")], "PATH").unwrap(), ) .with_stdout_data(str![[r#" OUTSIDE "#]]) .run(); } } #[cargo_test] fn cargo_subcommand_args() { let p = echo_subcommand(); let cargo_foo_bin = p.bin("cargo-echo"); assert!(cargo_foo_bin.is_file()); let mut path = path(); path.push(p.target_debug_dir()); let path = env::join_paths(path.iter()).unwrap(); cargo_process("echo bar -v --help") .env("PATH", &path) .with_stdout_data(str![[r#" echo bar -v --help "#]]) .run(); } #[cargo_test] fn explain() { cargo_process("--explain E0001") .with_stdout_data(str![[r#" ... This error suggests that the expression arm corresponding to the noted pattern[..] ... "#]]) .run(); } #[cargo_test] fn closed_output_ok() { // Checks that closed output doesn't cause an error. let mut p = cargo_process("--list").build_command(); p.stdout(Stdio::piped()).stderr(Stdio::piped()); let mut child = p.spawn().unwrap(); // Close stdout drop(child.stdout.take()); // Read stderr let mut s = String::new(); child .stderr .as_mut() .unwrap() .read_to_string(&mut s) .unwrap(); let status = child.wait().unwrap(); assert!(status.success()); assert!(s.is_empty(), "{}", s); } #[cargo_test] fn subcommand_leading_plus_output_contains() { cargo_process("+nightly") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no such command: `+nightly` [HELP] invoke `cargo` through `rustup` to handle `+toolchain` directives "#]]) .run(); } #[cargo_test] fn full_did_you_mean() { cargo_process("bluid") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no such command: `bluid` [HELP] a command with a similar name exists: `build` [HELP] view all installed commands with `cargo --list` [HELP] find a package to install `bluid` with `cargo search cargo-bluid` "#]]) .run(); } #[cargo_test] fn overwrite_cargo_environment_variable() { let rustc_host = rustc_host(); // If passed arguments `arg1 arg2 ...`, this program runs them as a command. // If passed no arguments, this program simply prints `$CARGO`. let p = project() .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) .file( "src/main.rs", r#" fn main() { let mut args = std::env::args().skip(1); if let Some(arg1) = args.next() { let status = std::process::Command::new(arg1) .args(args) .status() .unwrap(); assert!(status.success()); } else { eprintln!("{}", std::env::var("CARGO").unwrap()); } } "#, ) .build(); // Create two other cargo binaries in the project root, one with the wrong // name and one with the right name. let cargo_exe = crate::utils::cargo_exe(); let wrong_name_path = p .root() .join(format!("wrong_name{}", env::consts::EXE_SUFFIX)); let other_cargo_path = p.root().join(cargo_exe.file_name().unwrap()); std::fs::hard_link(&cargo_exe, &wrong_name_path).unwrap(); std::fs::hard_link(&cargo_exe, &other_cargo_path).unwrap(); // The output of each of the following commands should be `path-to-cargo`: // ``` // cargo run // cargo run -- cargo run // cargo run -- wrong_name run // ``` let cargo = cargo_exe.display().to_string(); let wrong_name = wrong_name_path.display().to_string(); let stderr_cargo = format!( "{}[EXE]\n", cargo_exe .with_extension("") .to_str() .unwrap() .replace(rustc_host, "[HOST_TARGET]") ); for cmd in [ "run", &format!("run -- {cargo} run"), &format!("run -- {wrong_name} run"), ] { p.cargo(cmd).with_stderr_contains(&stderr_cargo).run(); } // The output of the following command should be `path-to-other-cargo`: // ``` // cargo run -- other_cargo run // ``` let other_cargo = other_cargo_path.display().to_string(); let stderr_other_cargo = format!( "{}[EXE]\n", other_cargo_path .with_extension("") .to_str() .unwrap() .replace(p.root().parent().unwrap().to_str().unwrap(), "[ROOT]") ); p.cargo(&format!("run -- {other_cargo} run")) .with_stderr_contains(stderr_other_cargo) .run(); } cargo-0.91.0/tests/testsuite/cargo_config/help/mod.rs000064400000000000000000000004631046102023000207010ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("config") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_config/help/stdout.term.svg000064400000000000000000000067661046102023000226010ustar 00000000000000 Inspect configuration values Usage: cargo[EXE] config [OPTIONS] <COMMAND> Commands: get Options: -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Manifest Options: --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline cargo-0.91.0/tests/testsuite/cargo_config/mod.rs000064400000000000000000000401411046102023000177460ustar 00000000000000//! Tests for the `cargo config` command. use super::config::write_config_at; use crate::prelude::*; use cargo_test_support::paths; use cargo_test_support::str; use std::fs; use std::path::PathBuf; mod help; fn cargo_process(s: &str) -> cargo_test_support::Execs { let mut p = crate::utils::cargo_process(s); // Clear out some of the environment added by the default cargo_process so // the tests don't need to deal with it. p.env_remove("CARGO_PROFILE_DEV_SPLIT_DEBUGINFO") .env_remove("CARGO_PROFILE_TEST_SPLIT_DEBUGINFO") .env_remove("CARGO_PROFILE_RELEASE_SPLIT_DEBUGINFO") .env_remove("CARGO_PROFILE_BENCH_SPLIT_DEBUGINFO") .env_remove("CARGO_INCREMENTAL"); p } #[cargo_test] fn gated() { cargo_process("config get") .masquerade_as_nightly_cargo(&["cargo-config"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] the `cargo config` command is unstable, pass `-Z unstable-options` to enable it See https://github.com/rust-lang/cargo/issues/9301 for more information about the `cargo config` command. "#]]) .run(); } fn common_setup() -> PathBuf { write_config_at( paths::home().join(".cargo/config.toml"), " [alias] foo = \"abc --xyz\" [build] jobs = 99 rustflags = [\"--flag-global\"] [profile.dev] opt-level = 3 [profile.dev.package.foo] opt-level = 1 [target.'cfg(target_os = \"linux\")'] runner = \"runme\" # How unknown keys are handled. [extra-table] somekey = \"somevalue\" ", ); let sub_folder = paths::root().join("foo/.cargo"); write_config_at( sub_folder.join("config.toml"), " [alias] sub-example = [\"sub\", \"example\"] [build] rustflags = [\"--flag-directory\"] ", ); sub_folder } #[cargo_test] fn get_toml() { // Notes: // - The "extra-table" is shown without a warning. I'm not sure how that // should be handled, since displaying warnings could cause problems // with ingesting the output. // - Environment variables aren't loaded. :( let sub_folder = common_setup(); cargo_process("config get -Zunstable-options") .cwd(&sub_folder.parent().unwrap()) .masquerade_as_nightly_cargo(&["cargo-config"]) .env("CARGO_ALIAS_BAR", "cat dog") .env("CARGO_BUILD_JOBS", "100") // The weird forward slash in the linux line is due to testsuite normalization. .with_stdout_data(str![[r#" alias.foo = "abc --xyz" alias.sub-example = ["sub", "example"] build.jobs = 99 build.rustflags = ["--flag-global", "--flag-directory"] extra-table.somekey = "somevalue" profile.dev.opt-level = 3 profile.dev.package.foo.opt-level = 1 target.'cfg(target_os = "linux")'.runner = "runme" # The following environment variables may affect the loaded values. # CARGO_ALIAS_BAR=[..]cat dog[..] # CARGO_BUILD_JOBS=100 # CARGO_HOME=[ROOT]/home/.cargo "#]]) .with_stderr_data(str![[r#""#]]) .run(); // Env keys work if they are specific. cargo_process("config get build.jobs -Zunstable-options") .cwd(&sub_folder.parent().unwrap()) .masquerade_as_nightly_cargo(&["cargo-config"]) .env("CARGO_BUILD_JOBS", "100") .with_stdout_data(str![[r#" build.jobs = 100 "#]]) .with_stderr_data(str![[r#""#]]) .run(); // Array value. cargo_process("config get build.rustflags -Zunstable-options") .cwd(&sub_folder.parent().unwrap()) .masquerade_as_nightly_cargo(&["cargo-config"]) .with_stdout_data(str![[r#" build.rustflags = ["--flag-global", "--flag-directory"] "#]]) .with_stderr_data(str![[r#""#]]) .run(); // Sub-table cargo_process("config get profile -Zunstable-options") .cwd(&sub_folder.parent().unwrap()) .masquerade_as_nightly_cargo(&["cargo-config"]) .with_stdout_data(str![[r#" profile.dev.opt-level = 3 profile.dev.package.foo.opt-level = 1 "#]]) .with_stderr_data(str![[r#""#]]) .run(); // Specific profile entry. cargo_process("config get profile.dev.opt-level -Zunstable-options") .cwd(&sub_folder.parent().unwrap()) .masquerade_as_nightly_cargo(&["cargo-config"]) .with_stdout_data(str![[r#" profile.dev.opt-level = 3 "#]]) .with_stderr_data(str![[r#""#]]) .run(); // A key that isn't set. cargo_process("config get build.rustc -Zunstable-options") .cwd(&sub_folder.parent().unwrap()) .masquerade_as_nightly_cargo(&["cargo-config"]) .with_status(101) .with_stdout_data(str![[r#""#]]) .with_stderr_data(str![[r#" [ERROR] config value `build.rustc` is not set "#]]) .run(); // A key that is not part of Cargo's config schema. cargo_process("config get not.set -Zunstable-options") .cwd(&sub_folder.parent().unwrap()) .masquerade_as_nightly_cargo(&["cargo-config"]) .with_status(101) .with_stdout_data(str![[r#""#]]) .with_stderr_data(str![[r#" [ERROR] config value `not.set` is not set "#]]) .run(); } #[cargo_test] fn get_json() { let sub_folder = common_setup(); cargo_process("config get --format=json -Zunstable-options") .cwd(&sub_folder.parent().unwrap()) .masquerade_as_nightly_cargo(&["cargo-config"]) .env("CARGO_ALIAS_BAR", "cat dog") .env("CARGO_BUILD_JOBS", "100") .with_stdout_data( r#" { "alias": { "foo": "abc --xyz", "sub-example": [ "sub", "example" ] }, "build": { "jobs": 99, "rustflags": [ "--flag-global", "--flag-directory" ] }, "extra-table": { "somekey": "somevalue" }, "profile": { "dev": { "opt-level": 3, "package": { "foo": { "opt-level": 1 } } } }, "target": { "cfg(target_os = \"linux\")": { "runner": "runme" } } } "# .is_json(), ) .with_stderr_data(str![[r#" [NOTE] The following environment variables may affect the loaded values. CARGO_ALIAS_BAR=[..]cat dog[..] CARGO_BUILD_JOBS=100 CARGO_HOME=[ROOT]/home/.cargo "#]]) .run(); // json-value is the same for the entire root table cargo_process("config get --format=json-value -Zunstable-options") .cwd(&sub_folder.parent().unwrap()) .masquerade_as_nightly_cargo(&["cargo-config"]) .with_stdout_data( r#" { "alias": { "foo": "abc --xyz", "sub-example": [ "sub", "example" ] }, "build": { "jobs": 99, "rustflags": [ "--flag-global", "--flag-directory" ] }, "extra-table": { "somekey": "somevalue" }, "profile": { "dev": { "opt-level": 3, "package": { "foo": { "opt-level": 1 } } } }, "target": { "cfg(target_os = \"linux\")": { "runner": "runme" } } } "# .is_json(), ) .with_stderr_data(str![[r#" [NOTE] The following environment variables may affect the loaded values. CARGO_HOME=[ROOT]/home/.cargo "#]]) .run(); cargo_process("config get --format=json build.jobs -Zunstable-options") .cwd(&sub_folder.parent().unwrap()) .masquerade_as_nightly_cargo(&["cargo-config"]) .with_stdout_data(str![[r#" {"build":{"jobs":99}} "#]]) .with_stderr_data(str![[r#""#]]) .run(); cargo_process("config get --format=json-value build.jobs -Zunstable-options") .cwd(&sub_folder.parent().unwrap()) .masquerade_as_nightly_cargo(&["cargo-config"]) .with_stdout_data(str![[r#" 99 "#]]) .with_stderr_data(str![[r#""#]]) .run(); } #[cargo_test] fn show_origin_toml() { let sub_folder = common_setup(); cargo_process("config get --show-origin -Zunstable-options") .cwd(&sub_folder.parent().unwrap()) .masquerade_as_nightly_cargo(&["cargo-config"]) .with_stdout_data(str![[r#" alias.foo = "abc --xyz" # [ROOT]/home/.cargo/config.toml alias.sub-example = [ "sub", # [ROOT]/foo/.cargo/config.toml "example", # [ROOT]/foo/.cargo/config.toml ] build.jobs = 99 # [ROOT]/home/.cargo/config.toml build.rustflags = [ "--flag-global", # [ROOT]/home/.cargo/config.toml "--flag-directory", # [ROOT]/foo/.cargo/config.toml ] extra-table.somekey = "somevalue" # [ROOT]/home/.cargo/config.toml profile.dev.opt-level = 3 # [ROOT]/home/.cargo/config.toml profile.dev.package.foo.opt-level = 1 # [ROOT]/home/.cargo/config.toml target.'cfg(target_os = "linux")'.runner = "runme" # [ROOT]/home/.cargo/config.toml # The following environment variables may affect the loaded values. # CARGO_HOME=[ROOT]/home/.cargo "#]]) .with_stderr_data(str![[r#""#]]) .run(); cargo_process("config get --show-origin build.rustflags -Zunstable-options") .cwd(&sub_folder.parent().unwrap()) .masquerade_as_nightly_cargo(&["cargo-config"]) .env("CARGO_BUILD_RUSTFLAGS", "env1 env2") .with_stdout_data(str![[r#" build.rustflags = [ "--flag-global", # [ROOT]/home/.cargo/config.toml "--flag-directory", # [ROOT]/foo/.cargo/config.toml "env1", # environment variable `CARGO_BUILD_RUSTFLAGS` "env2", # environment variable `CARGO_BUILD_RUSTFLAGS` ] "#]]) .with_stderr_data(str![[r#""#]]) .run(); } #[cargo_test] fn show_origin_toml_cli() { let sub_folder = common_setup(); cargo_process("config get --show-origin build.jobs -Zunstable-options --config build.jobs=123") .cwd(&sub_folder.parent().unwrap()) .masquerade_as_nightly_cargo(&["cargo-config"]) .env("CARGO_BUILD_JOBS", "1") .with_stdout_data(str![[r#" build.jobs = 123 # --config cli option "#]]) .with_stderr_data(str![[r#""#]]) .run(); cargo_process("config get --show-origin build.rustflags -Zunstable-options --config") .arg("build.rustflags=[\"cli1\",\"cli2\"]") .cwd(&sub_folder.parent().unwrap()) .masquerade_as_nightly_cargo(&["cargo-config"]) .env("CARGO_BUILD_RUSTFLAGS", "env1 env2") .with_stdout_data(str![[r#" build.rustflags = [ "--flag-global", # [ROOT]/home/.cargo/config.toml "--flag-directory", # [ROOT]/foo/.cargo/config.toml "env1", # environment variable `CARGO_BUILD_RUSTFLAGS` "env2", # environment variable `CARGO_BUILD_RUSTFLAGS` "cli1", # --config cli option "cli2", # --config cli option ] "#]]) .with_stderr_data(str![[r#""#]]) .run(); } #[cargo_test] fn show_origin_json() { let sub_folder = common_setup(); cargo_process("config get --show-origin --format=json -Zunstable-options") .cwd(&sub_folder.parent().unwrap()) .masquerade_as_nightly_cargo(&["cargo-config"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] the `json` format does not support --show-origin, try the `toml` format instead "#]]) .run(); } #[cargo_test] fn unmerged_toml() { let sub_folder = common_setup(); cargo_process("config get --merged=no -Zunstable-options") .cwd(&sub_folder.parent().unwrap()) .masquerade_as_nightly_cargo(&["cargo-config"]) .env("CARGO_ALIAS_BAR", "cat dog") .env("CARGO_BUILD_JOBS", "100") .with_stdout_data(str![[r#" # Environment variables # CARGO=[..] # CARGO_ALIAS_BAR=[..]cat dog[..] # CARGO_BUILD_JOBS=100 # CARGO_HOME=[ROOT]/home/.cargo # [ROOT]/foo/.cargo/config.toml alias.sub-example = ["sub", "example"] build.rustflags = ["--flag-directory"] # [ROOT]/home/.cargo/config.toml alias.foo = "abc --xyz" build.jobs = 99 build.rustflags = ["--flag-global"] extra-table.somekey = "somevalue" profile.dev.opt-level = 3 profile.dev.package.foo.opt-level = 1 target.'cfg(target_os = "linux")'.runner = "runme" "#]]) .with_stderr_data(str![[r#""#]]) .run(); cargo_process("config get --merged=no build.rustflags -Zunstable-options") .cwd(&sub_folder.parent().unwrap()) .masquerade_as_nightly_cargo(&["cargo-config"]) .env("CARGO_BUILD_RUSTFLAGS", "env1 env2") .with_stdout_data(str![[r#" # Environment variables # CARGO_BUILD_RUSTFLAGS=[..]env1 env2[..] # [ROOT]/foo/.cargo/config.toml build.rustflags = ["--flag-directory"] # [ROOT]/home/.cargo/config.toml build.rustflags = ["--flag-global"] "#]]) .with_stderr_data(str![[r#""#]]) .run(); cargo_process("config get --merged=no does.not.exist -Zunstable-options") .cwd(&sub_folder.parent().unwrap()) .masquerade_as_nightly_cargo(&["cargo-config"]) .with_stderr_data(str![[r#""#]]) .with_stderr_data(str![[r#""#]]) .run(); cargo_process("config get --merged=no build.rustflags.extra -Zunstable-options") .cwd(&sub_folder.parent().unwrap()) .masquerade_as_nightly_cargo(&["cargo-config"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] expected table for configuration key `build.rustflags`, but found array in [ROOT]/foo/.cargo/config.toml "#]]) .run(); } #[cargo_test] fn unmerged_toml_cli() { let sub_folder = common_setup(); cargo_process("config get --merged=no build.rustflags -Zunstable-options --config") .arg("build.rustflags=[\"cli1\",\"cli2\"]") .cwd(&sub_folder.parent().unwrap()) .masquerade_as_nightly_cargo(&["cargo-config"]) .env("CARGO_BUILD_RUSTFLAGS", "env1 env2") .with_stdout_data(str![[r#" # --config cli option build.rustflags = ["cli1", "cli2"] # Environment variables # CARGO_BUILD_RUSTFLAGS=[..]env1 env2[..] # [ROOT]/foo/.cargo/config.toml build.rustflags = ["--flag-directory"] # [ROOT]/home/.cargo/config.toml build.rustflags = ["--flag-global"] "#]]) .with_stderr_data(str![[r#""#]]) .run(); } #[cargo_test] fn unmerged_json() { let sub_folder = common_setup(); cargo_process("config get --merged=no --format=json -Zunstable-options") .cwd(&sub_folder.parent().unwrap()) .masquerade_as_nightly_cargo(&["cargo-config"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] the `json` format does not support --merged=no, try the `toml` format instead "#]]) .run(); } #[cargo_test] fn includes() { let sub_folder = common_setup(); fs::write( sub_folder.join("config.toml"), " include = 'other.toml' [build] rustflags = [\"--flag-directory\"] ", ) .unwrap(); fs::write( sub_folder.join("other.toml"), " [build] rustflags = [\"--flag-other\"] ", ) .unwrap(); cargo_process("config get build.rustflags -Zunstable-options -Zconfig-include") .cwd(&sub_folder.parent().unwrap()) .masquerade_as_nightly_cargo(&["cargo-config", "config-include"]) .with_stdout_data(str![[r#" build.rustflags = ["--flag-global", "--flag-other", "--flag-directory"] "#]]) .with_stderr_data(str![[r#""#]]) .run(); cargo_process("config get build.rustflags --show-origin -Zunstable-options -Zconfig-include") .cwd(&sub_folder.parent().unwrap()) .masquerade_as_nightly_cargo(&["cargo-config", "config-include"]) .with_stdout_data(str![[r#" build.rustflags = [ "--flag-global", # [ROOT]/home/.cargo/config.toml "--flag-other", # [ROOT]/foo/.cargo/other.toml "--flag-directory", # [ROOT]/foo/.cargo/config.toml ] "#]]) .with_stderr_data(str![[r#""#]]) .run(); cargo_process("config get --merged=no -Zunstable-options -Zconfig-include") .cwd(&sub_folder.parent().unwrap()) .masquerade_as_nightly_cargo(&["cargo-config", "config-include"]) .with_stdout_data(str![[r#" # Environment variables # CARGO=[..] # CARGO_HOME=[ROOT]/home/.cargo # [ROOT]/foo/.cargo/other.toml build.rustflags = ["--flag-other"] # [ROOT]/foo/.cargo/config.toml build.rustflags = ["--flag-directory"] include = "other.toml" # [ROOT]/home/.cargo/config.toml alias.foo = "abc --xyz" build.jobs = 99 build.rustflags = ["--flag-global"] extra-table.somekey = "somevalue" profile.dev.opt-level = 3 profile.dev.package.foo.opt-level = 1 target.'cfg(target_os = "linux")'.runner = "runme" "#]]) .with_stderr_data(str![[r#""#]]) .run(); } cargo-0.91.0/tests/testsuite/cargo_doc/help/mod.rs000064400000000000000000000004601046102023000201760ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("doc") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_doc/help/stdout.term.svg000064400000000000000000000237571046102023000221000ustar 00000000000000 Build a package's documentation Usage: cargo[EXE] doc [OPTIONS] Options: --open Opens the docs in a browser after the operation --no-deps Don't build documentation for dependencies --document-private-items Document private items --message-format <FMT> Error format [possible values: human, short, json, json-diagnostic-short, json-diagnostic-rendered-ansi, json-render-diagnostics] -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Package Selection: -p, --package [<SPEC>] Package to document --workspace Document all packages in the workspace --exclude <SPEC> Exclude packages from the build --all Alias for --workspace (deprecated) Feature Selection: -F, --features <FEATURES> Space or comma separated list of features to activate --all-features Activate all available features --no-default-features Do not activate the `default` feature Target Selection: --lib Document only this package's library --bins Document all binaries --bin [<NAME>] Document only the specified binary --examples Document all examples --example [<NAME>] Document only the specified example Compilation Options: -j, --jobs <N> Number of parallel jobs, defaults to # of CPUs. --keep-going Do not abort the build as soon as there is an error -r, --release Build artifacts in release mode, with optimizations --profile <PROFILE-NAME> Build artifacts with the specified profile --target [<TRIPLE>] Build for the target triple --target-dir <DIRECTORY> Directory for all generated artifacts --unit-graph Output build graph in JSON (unstable) --timings[=<FMTS>] Timing output formats (unstable) (comma separated): html, json Manifest Options: --manifest-path <PATH> Path to Cargo.toml --lockfile-path <PATH> Path to Cargo.lock (unstable) --ignore-rust-version Ignore `rust-version` specification in packages --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help doc` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_doc/mod.rs000064400000000000000000000000121046102023000172370ustar 00000000000000mod help; cargo-0.91.0/tests/testsuite/cargo_env_config.rs000064400000000000000000000260561046102023000200500ustar 00000000000000//! Tests for `[env]` config. use crate::prelude::*; use cargo_test_support::basic_manifest; use cargo_test_support::str; use cargo_test_support::{basic_bin_manifest, project}; #[cargo_test] fn env_basic() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file( "src/main.rs", r#" use std::env; fn main() { println!( "compile-time:{}", env!("ENV_TEST_1233") ); println!( "run-time:{}", env::var("ENV_TEST_1233").unwrap()); } "#, ) .file( ".cargo/config.toml", r#" [env] ENV_TEST_1233 = "Hello" "#, ) .build(); p.cargo("run") .with_stdout_data(str![[r#" compile-time:Hello run-time:Hello "#]]) .run(); } #[cargo_test] fn env_invalid() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file( "src/main.rs", r#" fn main() { } "#, ) .file( ".cargo/config.toml", r#" [env] ENV_TEST_BOOL = false "#, ) .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] error in [ROOT]/foo/.cargo/config.toml: could not load config key `env.ENV_TEST_BOOL` Caused by: error in [ROOT]/foo/.cargo/config.toml: could not load config key `env.ENV_TEST_BOOL` Caused by: invalid type: boolean `false`, expected a string or map "#]]) .run(); } #[cargo_test] fn env_no_disallowed() { // Checks for keys that are not allowed in the [env] table. let p = project() .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) .file("src/lib.rs", "") .build(); for disallowed in &["CARGO_HOME", "RUSTUP_HOME", "RUSTUP_TOOLCHAIN"] { p.change_file( ".cargo/config.toml", &format!( r#" [env] {disallowed} = "foo" "# ), ); p.cargo("check") .with_status(101) .with_stderr_data(format!( "\ [ERROR] setting the `{disallowed}` environment variable \ is not supported in the `[env]` configuration table " )) .run(); } } #[cargo_test] fn env_force() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file( "src/main.rs", r#" use std::env; fn main() { println!( "ENV_TEST_FORCED:{}", env!("ENV_TEST_FORCED") ); println!( "ENV_TEST_UNFORCED:{}", env!("ENV_TEST_UNFORCED") ); println!( "ENV_TEST_UNFORCED_DEFAULT:{}", env!("ENV_TEST_UNFORCED_DEFAULT") ); } "#, ) .file( ".cargo/config.toml", r#" [env] ENV_TEST_UNFORCED_DEFAULT = "from-config" ENV_TEST_UNFORCED = { value = "from-config", force = false } ENV_TEST_FORCED = { value = "from-config", force = true } "#, ) .build(); p.cargo("run") .env("ENV_TEST_FORCED", "from-env") .env("ENV_TEST_UNFORCED", "from-env") .env("ENV_TEST_UNFORCED_DEFAULT", "from-env") .with_stdout_data(str![[r#" ENV_TEST_FORCED:from-config ENV_TEST_UNFORCED:from-env ENV_TEST_UNFORCED_DEFAULT:from-env "#]]) .run(); } #[cargo_test] fn env_relative() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo2")) .file( "src/main.rs", r#" use std::env; use std::path::Path; fn main() { println!( "ENV_TEST_REGULAR:{}", env!("ENV_TEST_REGULAR") ); println!( "ENV_TEST_REGULAR_DEFAULT:{}", env!("ENV_TEST_REGULAR_DEFAULT") ); println!( "ENV_TEST_RELATIVE:{}", env!("ENV_TEST_RELATIVE") ); assert!( Path::new(env!("ENV_TEST_RELATIVE")).is_absolute() ); assert!( !Path::new(env!("ENV_TEST_REGULAR")).is_absolute() ); assert!( !Path::new(env!("ENV_TEST_REGULAR_DEFAULT")).is_absolute() ); } "#, ) .file( ".cargo/config.toml", r#" [env] ENV_TEST_REGULAR = { value = "Cargo.toml", relative = false } ENV_TEST_REGULAR_DEFAULT = "Cargo.toml" ENV_TEST_RELATIVE = { value = "Cargo.toml", relative = true } "#, ) .build(); p.cargo("run").run(); } #[cargo_test] fn env_no_override() { let p = project() .file("Cargo.toml", &basic_bin_manifest("unchanged")) .file( "src/main.rs", r#" use std::env; fn main() { println!( "CARGO_PKG_NAME:{}", env!("CARGO_PKG_NAME") ); } "#, ) .file( ".cargo/config.toml", r#" [env] CARGO_PKG_NAME = { value = "from-config", force = true } "#, ) .build(); p.cargo("run") .with_stdout_data(str![[r#" CARGO_PKG_NAME:unchanged "#]]) .run(); } #[cargo_test] fn env_applied_to_target_info_discovery_rustc() { let wrapper = project() .at("wrapper") .file("Cargo.toml", &basic_manifest("wrapper", "1.0.0")) .file( "src/main.rs", r#" fn main() { let mut cmd = std::env::args().skip(1).collect::>(); // This will be invoked twice (with `-vV` and with all the `--print`), // make sure the environment variable exists each time. let env_test = std::env::var("ENV_TEST").unwrap(); eprintln!("WRAPPER ENV_TEST:{env_test}"); let (prog, args) = cmd.split_first().unwrap(); let status = std::process::Command::new(prog) .args(args).status().unwrap(); std::process::exit(status.code().unwrap_or(1)); } "#, ) .build(); wrapper.cargo("build").run(); let wrapper = &wrapper.bin("wrapper"); let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file( "src/main.rs", r#" fn main() { eprintln!( "MAIN ENV_TEST:{}", std::env!("ENV_TEST") ); } "#, ) .file( ".cargo/config.toml", r#" [env] ENV_TEST = "from-config" "#, ) .build(); p.cargo("run") .env("RUSTC_WORKSPACE_WRAPPER", wrapper) .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) WRAPPER ENV_TEST:from-config [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` MAIN ENV_TEST:from-config "#]]) .run(); // Ensure wrapper also maintains the same overridden priority for envs. p.cargo("clean").run(); p.cargo("run") .env("ENV_TEST", "from-env") .env("RUSTC_WORKSPACE_WRAPPER", wrapper) .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) WRAPPER ENV_TEST:from-env [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` MAIN ENV_TEST:from-env "#]]) .run(); } #[cargo_test] fn env_changed_defined_in_config_toml() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file( "src/main.rs", r#" use std::env; fn main() { println!( "{}", env!("ENV_TEST") ); } "#, ) .file( ".cargo/config.toml", r#" [env] ENV_TEST = "from-config" "#, ) .build(); p.cargo("run") .with_stdout_data(str![[r#" from-config "#]]) .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .run(); p.cargo("run") .env("ENV_TEST", "from-env") .with_stdout_data(str![[r#" from-env "#]]) .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .run(); // This identical cargo invocation is to ensure no rebuild happen. p.cargo("run") .env("ENV_TEST", "from-env") .with_stdout_data(str![[r#" from-env "#]]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .run(); } #[cargo_test] fn forced_env_changed_defined_in_config_toml() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file( "src/main.rs", r#" use std::env; fn main() { println!( "{}", env!("ENV_TEST") ); } "#, ) .file( ".cargo/config.toml", r#" [env] ENV_TEST = {value = "from-config", force = true} "#, ) .build(); p.cargo("run") .with_stdout_data(str![[r#" from-config "#]]) .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .run(); p.cargo("run") .env("ENV_TEST", "from-env") .with_stdout_data(str![[r#" from-config "#]]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .run(); } #[cargo_test] fn env_changed_defined_in_config_args() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file( "src/main.rs", r#" use std::env; fn main() { println!( "{}", env!("ENV_TEST") ); } "#, ) .build(); p.cargo(r#"run --config 'env.ENV_TEST="one"'"#) .with_stdout_data(str![[r#" one "#]]) .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .run(); p.cargo(r#"run --config 'env.ENV_TEST="two"'"#) .with_stdout_data(str![[r#" two "#]]) .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .run(); // This identical cargo invocation is to ensure no rebuild happen. p.cargo(r#"run --config 'env.ENV_TEST="two"'"#) .with_stdout_data(str![[r#" two "#]]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .run(); } cargo-0.91.0/tests/testsuite/cargo_features.rs000064400000000000000000000471121046102023000175450ustar 00000000000000//! Tests for `cargo-features` definitions. use crate::prelude::*; use cargo_test_support::registry::Package; use cargo_test_support::str; use cargo_test_support::{project, registry}; #[cargo_test] fn feature_required() { let p = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] im-a-teapot = true "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .masquerade_as_nightly_cargo(&["test-dummy-unstable"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: the `im-a-teapot` manifest key is unstable and may not work properly in England Caused by: feature `test-dummy-unstable` is required The package requires the Cargo feature called `test-dummy-unstable`, but that feature is not stabilized in this version of Cargo (1.[..]). Consider adding `cargo-features = ["test-dummy-unstable"]` to the top of Cargo.toml (above the [package] table) to tell Cargo you are opting in to use this unstable feature. See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html for more information about the status of this feature. "#]]) .run(); // Same, but stable. p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: the `im-a-teapot` manifest key is unstable and may not work properly in England Caused by: feature `test-dummy-unstable` is required The package requires the Cargo feature called `test-dummy-unstable`, but that feature is not stabilized in this version of Cargo (1.[..]). Consider trying a newer version of Cargo (this may require the nightly release). See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html for more information about the status of this feature. "#]]) .run(); } #[cargo_test] fn feature_required_dependency() { // The feature has been stabilized by a future version of Cargo, and // someone published something uses it, but this version of Cargo has not // yet stabilized it. Don't suggest editing Cargo.toml, since published // packages shouldn't be edited. Package::new("bar", "1.0.0") .file( "Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" im-a-teapot = true "#, ) .file("src/lib.rs", "") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .masquerade_as_nightly_cargo(&["test-dummy-unstable"]) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [ERROR] failed to download replaced source registry `crates-io` Caused by: failed to parse manifest at `[ROOT]/home/.cargo/registry/src/-[HASH]/bar-1.0.0/Cargo.toml` Caused by: the `im-a-teapot` manifest key is unstable and may not work properly in England Caused by: feature `test-dummy-unstable` is required The package requires the Cargo feature called `test-dummy-unstable`, but that feature is not stabilized in this version of Cargo (1.[..]). Consider trying a more recent nightly release. See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html for more information about the status of this feature. "#]]) .run(); // Same, but stable. p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to download `bar v1.0.0` Caused by: unable to get packages from source Caused by: failed to download replaced source registry `crates-io` Caused by: failed to parse manifest at `[ROOT]/home/.cargo/registry/src/-[HASH]/bar-1.0.0/Cargo.toml` Caused by: the `im-a-teapot` manifest key is unstable and may not work properly in England Caused by: feature `test-dummy-unstable` is required The package requires the Cargo feature called `test-dummy-unstable`, but that feature is not stabilized in this version of Cargo (1.[..]). Consider trying a newer version of Cargo (this may require the nightly release). See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html for more information about the status of this feature. "#]]) .run(); } #[cargo_test] fn unknown_feature() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["foo"] [package] name = "a" version = "0.0.1" edition = "2015" authors = [] "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: unknown cargo feature `foo` "#]]) .run(); } #[cargo_test] fn stable_feature_warns() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["test-dummy-stable"] [package] name = "a" version = "0.0.1" edition = "2015" authors = [] "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [WARNING] the cargo feature `test-dummy-stable` has been stabilized in the 1.0 release and is no longer necessary to be listed in the manifest See https://doc.rust-lang.org/cargo/ for more information about using this feature. [CHECKING] a v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "-Zallow-features is unstable")] fn allow_features() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["test-dummy-unstable"] [package] name = "a" version = "0.0.1" edition = "2015" authors = [] im-a-teapot = true "#, ) .file("src/lib.rs", "") .build(); p.cargo("-Zallow-features=test-dummy-unstable check") .masquerade_as_nightly_cargo(&["allow-features", "test-dummy-unstable"]) .with_stderr_data(str![[r#" [CHECKING] a v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("-Zallow-features=test-dummy-unstable,print-im-a-teapot -Zprint-im-a-teapot check") .masquerade_as_nightly_cargo(&[ "allow-features", "test-dummy-unstable", "print-im-a-teapot", ]) .with_stdout_data(str![[r#" im-a-teapot = true "#]]) .run(); p.cargo("-Zallow-features=test-dummy-unstable -Zprint-im-a-teapot check") .masquerade_as_nightly_cargo(&[ "allow-features", "test-dummy-unstable", "print-im-a-teapot", ]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] the feature `print-im-a-teapot` is not in the list of allowed features: [test-dummy-unstable] "#]]) .run(); p.cargo("-Zallow-features= check") .masquerade_as_nightly_cargo(&["allow-features", "test-dummy-unstable"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: the feature `test-dummy-unstable` is not in the list of allowed features: [] "#]]) .run(); } #[cargo_test(nightly, reason = "-Zallow-features is unstable")] fn allow_features_to_rustc() { let p = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] "#, ) .file( "src/lib.rs", r#" #![allow(internal_features)] #![feature(rustc_attrs)] "#, ) .build(); p.cargo("-Zallow-features= check") .masquerade_as_nightly_cargo(&["allow-features"]) .with_status(101) .with_stderr_data(str![[r#" [CHECKING] a v0.0.1 ([ROOT]/foo) error[E0725]: the feature `rustc_attrs` is not in the list of allowed features ... "#]]) .run(); p.cargo("-Zallow-features=rustc_attrs check") .masquerade_as_nightly_cargo(&["allow-features"]) .with_stderr_data(str![[r#" [CHECKING] a v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "-Zallow-features is unstable")] fn allow_features_in_cfg() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["test-dummy-unstable"] [package] name = "a" version = "0.0.1" edition = "2015" authors = [] im-a-teapot = true "#, ) .file( ".cargo/config.toml", r#" [unstable] allow-features = ["test-dummy-unstable", "print-im-a-teapot"] "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .masquerade_as_nightly_cargo(&[ "allow-features", "test-dummy-unstable", "print-im-a-teapot", ]) .with_stderr_data(str![[r#" [CHECKING] a v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("-Zprint-im-a-teapot check") .masquerade_as_nightly_cargo(&[ "allow-features", "test-dummy-unstable", "print-im-a-teapot", ]) .with_stdout_data(str![[r#" im-a-teapot = true "#]]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("-Zunstable-options check") .masquerade_as_nightly_cargo(&["allow-features", "test-dummy-unstable", "print-im-a-teapot"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] the feature `unstable-options` is not in the list of allowed features: [print-im-a-teapot, test-dummy-unstable] "#]]) .run(); // -Zallow-features overrides .cargo/config.toml p.cargo("-Zallow-features=test-dummy-unstable -Zprint-im-a-teapot check") .masquerade_as_nightly_cargo(&[ "allow-features", "test-dummy-unstable", "print-im-a-teapot", ]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] the feature `print-im-a-teapot` is not in the list of allowed features: [test-dummy-unstable] "#]]) .run(); p.cargo("-Zallow-features= check") .masquerade_as_nightly_cargo(&[ "allow-features", "test-dummy-unstable", "print-im-a-teapot", ]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: the feature `test-dummy-unstable` is not in the list of allowed features: [] "#]]) .run(); } #[cargo_test] fn nightly_feature_requires_nightly() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["test-dummy-unstable"] [package] name = "a" version = "0.0.1" edition = "2015" authors = [] im-a-teapot = true "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .masquerade_as_nightly_cargo(&["test-dummy-unstable"]) .with_stderr_data(str![[r#" [CHECKING] a v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: the cargo feature `test-dummy-unstable` requires a nightly version of Cargo, but this is the `stable` channel ... "#]]) .run(); } #[cargo_test] fn nightly_feature_requires_nightly_in_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "b" version = "0.0.1" edition = "2015" authors = [] [dependencies] a = { path = "a" } "#, ) .file("src/lib.rs", "") .file( "a/Cargo.toml", r#" cargo-features = ["test-dummy-unstable"] [package] name = "a" version = "0.0.1" edition = "2015" authors = [] im-a-teapot = true "#, ) .file("a/src/lib.rs", "") .build(); p.cargo("check") .masquerade_as_nightly_cargo(&["test-dummy-unstable"]) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [CHECKING] a v0.0.1 ([ROOT]/foo/a) [CHECKING] b v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to get `a` as a dependency of package `b v0.0.1 ([ROOT]/foo)` Caused by: failed to load source for dependency `a` Caused by: Unable to update [ROOT]/foo/a Caused by: failed to parse manifest at `[ROOT]/foo/a/Cargo.toml` Caused by: the cargo feature `test-dummy-unstable` requires a nightly version of Cargo, but this is the `stable` channel ... "#]]) .run(); } #[cargo_test] fn cant_publish() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["test-dummy-unstable"] [package] name = "a" version = "0.0.1" edition = "2015" authors = [] im-a-teapot = true "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .masquerade_as_nightly_cargo(&["test-dummy-unstable"]) .with_stderr_data(str![[r#" [CHECKING] a v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: the cargo feature `test-dummy-unstable` requires a nightly version of Cargo, but this is the `stable` channel ... "#]]) .run(); } #[cargo_test] fn z_flags_rejected() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["test-dummy-unstable"] [package] name = "a" version = "0.0.1" edition = "2015" authors = [] im-a-teapot = true "#, ) .file("src/lib.rs", "") .build(); p.cargo("check -Zprint-im-a-teapot") .with_status(101) .with_stderr_data(str![[r#" [ERROR] the `-Z` flag is only accepted on the nightly channel of Cargo, but this is the `stable` channel See [..] "#]]) .run(); p.cargo("check -Zarg") .masquerade_as_nightly_cargo(&["test-dummy-unstable"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] unknown `-Z` flag specified: arg For available unstable features, see https://doc.rust-lang.org/nightly/cargo/reference/unstable.html If you intended to use an unstable rustc feature, try setting `RUSTFLAGS="-Zarg"` "#]]) .run(); p.cargo("check -Zprint-im-a-teapot") .masquerade_as_nightly_cargo(&["test-dummy-unstable"]) .with_stdout_data(str![[r#" im-a-teapot = true "#]]) .with_stderr_data(str![[r#" [CHECKING] a v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn publish_allowed() { let registry = registry::RegistryBuilder::new() .http_api() .http_index() .build(); let p = project() .file( "Cargo.toml", r#" cargo-features = ["test-dummy-unstable"] [package] name = "a" version = "0.0.1" edition = "2015" authors = [] "#, ) .file("src/lib.rs", "") .build(); p.cargo("publish") .replace_crates_io(registry.index_url()) .masquerade_as_nightly_cargo(&["test-dummy-unstable"]) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no description, license, license-file, documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] a v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] a v0.0.1 ([ROOT]/foo) [COMPILING] a v0.0.1 ([ROOT]/foo/target/package/a-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] a v0.0.1 ([ROOT]/foo) [UPLOADED] a v0.0.1 to registry `crates-io` [NOTE] waiting for a v0.0.1 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] a v0.0.1 at registry `crates-io` "#]]) .run(); } #[cargo_test] fn wrong_position() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" cargo-features = ["test-dummy-unstable"] "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .masquerade_as_nightly_cargo(&["test-dummy-unstable"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] the field `cargo-features` should be set at the top of Cargo.toml before any tables --> Cargo.toml:6:34 | 6 | cargo-features = ["test-dummy-unstable"] | ^^^^^^^^^^^^^^^^^^^^^^^ | "#]]) .run(); } #[cargo_test] fn z_stabilized() { let p = project().file("src/lib.rs", "").build(); p.cargo("check -Z cache-messages") .masquerade_as_nightly_cargo(&["always_nightly"]) .with_stderr_data(str![[r#" [WARNING] flag `-Z cache-messages` has been stabilized in the 1.40 release, and is no longer necessary Message caching is now always enabled. [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check -Z offline") .masquerade_as_nightly_cargo(&["always_nightly"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] flag `-Z offline` has been stabilized in the 1.36 release Offline mode is now available via the --offline CLI option "#]]) .run(); } cargo-0.91.0/tests/testsuite/cargo_fetch/help/mod.rs000064400000000000000000000004621046102023000205240ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("fetch") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_fetch/help/stdout.term.svg000064400000000000000000000104671046102023000224160ustar 00000000000000 Fetch dependencies of a package from the network Usage: cargo[EXE] fetch [OPTIONS] Options: -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Compilation Options: --target [<TRIPLE>] Fetch dependencies for the target triple Manifest Options: --manifest-path <PATH> Path to Cargo.toml --lockfile-path <PATH> Path to Cargo.lock (unstable) --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help fetch` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_fetch/mod.rs000064400000000000000000000000121046102023000175630ustar 00000000000000mod help; cargo-0.91.0/tests/testsuite/cargo_fix/help/mod.rs000064400000000000000000000004601046102023000202170ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("fix") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_fix/help/stdout.term.svg000064400000000000000000000267741046102023000221230ustar 00000000000000 Automatically fix lint warnings reported by rustc Usage: cargo[EXE] fix [OPTIONS] Options: --edition Fix in preparation for the next edition --edition-idioms Fix warnings to migrate to the idioms of an edition --broken-code Fix code even if it already has compiler errors --allow-no-vcs Fix code even if a VCS was not detected --allow-dirty Fix code even if the working directory is dirty or has staged changes --allow-staged Fix code even if the working directory has staged changes --message-format <FMT> Error format [possible values: human, short, json, json-diagnostic-short, json-diagnostic-rendered-ansi, json-render-diagnostics] -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Package Selection: -p, --package [<SPEC>] Package(s) to fix --workspace Fix all packages in the workspace --exclude <SPEC> Exclude packages from the fixes --all Alias for --workspace (deprecated) Target Selection: --lib Fix only this package's library --bins Fix all binaries --bin [<NAME>] Fix only the specified binary --examples Fix all examples --example [<NAME>] Fix only the specified example --tests Fix all targets that have `test = true` set --test [<NAME>] Fix only the specified test target --benches Fix all targets that have `bench = true` set --bench [<NAME>] Fix only the specified bench target --all-targets Fix all targets (default) Feature Selection: -F, --features <FEATURES> Space or comma separated list of features to activate --all-features Activate all available features --no-default-features Do not activate the `default` feature Compilation Options: -j, --jobs <N> Number of parallel jobs, defaults to # of CPUs. --keep-going Do not abort the build as soon as there is an error -r, --release Fix artifacts in release mode, with optimizations --profile <PROFILE-NAME> Build artifacts with the specified profile --target [<TRIPLE>] Fix for the target triple --target-dir <DIRECTORY> Directory for all generated artifacts --timings[=<FMTS>] Timing output formats (unstable) (comma separated): html, json Manifest Options: --manifest-path <PATH> Path to Cargo.toml --lockfile-path <PATH> Path to Cargo.lock (unstable) --ignore-rust-version Ignore `rust-version` specification in packages --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help fix` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_fix/mod.rs000064400000000000000000000000121046102023000172600ustar 00000000000000mod help; cargo-0.91.0/tests/testsuite/cargo_generate_lockfile/help/mod.rs000064400000000000000000000004761046102023000231020ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("generate-lockfile") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_generate_lockfile/help/stdout.term.svg000064400000000000000000000101401046102023000247530ustar 00000000000000 Generate the lockfile for a package Usage: cargo[EXE] generate-lockfile [OPTIONS] Options: -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Manifest Options: --manifest-path <PATH> Path to Cargo.toml --lockfile-path <PATH> Path to Cargo.lock (unstable) --ignore-rust-version Ignore `rust-version` specification in packages --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help generate-lockfile` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_generate_lockfile/mod.rs000064400000000000000000000000121046102023000221340ustar 00000000000000mod help; cargo-0.91.0/tests/testsuite/cargo_git_checkout/help/mod.rs000064400000000000000000000004711046102023000221030ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("git-checkout") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_git_checkout/help/stdout.term.svg000064400000000000000000000011741046102023000237700ustar 00000000000000 The `git-checkout` command has been removed. cargo-0.91.0/tests/testsuite/cargo_git_checkout/mod.rs000064400000000000000000000000121046102023000211420ustar 00000000000000mod help; cargo-0.91.0/tests/testsuite/cargo_help/help/mod.rs000064400000000000000000000004611046102023000203620ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("help") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_help/help/stdout.term.svg000064400000000000000000000067701046102023000222570ustar 00000000000000 Displays help for a cargo subcommand Usage: cargo[EXE] help [OPTIONS] [COMMAND] Arguments: [COMMAND] Options: -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Manifest Options: --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline cargo-0.91.0/tests/testsuite/cargo_help/mod.rs000064400000000000000000000000121046102023000174220ustar 00000000000000mod help; cargo-0.91.0/tests/testsuite/cargo_info/basic/mod.rs000064400000000000000000000026041046102023000205170ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use super::init_registry_without_token; #[cargo_test] fn case() { init_registry_without_token(); cargo_test_support::registry::Package::new("my-package", "0.1.0") .file( "Cargo.toml", r#" [package] name = "my-package" version = "0.1.0" description = "A package for testing" repository = "https://github.com/hi-rustin/cargo-infromation" documentation = "https://docs.rs/my-package/0.1.0" license = "MIT" edition = "2018" rust-version = "1.50.0" keywords = ["foo", "bar", "baz"] [features] default = ["feature1"] feature1 = [] feature2 = [] [dependencies] foo = "0.1.0" bar = "0.2.0" baz = { version = "0.3.0", optional = true } [[bin]] name = "my_bin" [lib] name = "my_lib" "#, ) .file("src/bin/my_bin.rs", "") .file("src/lib.rs", "") .publish(); snapbox::cmd::Command::cargo_ui() .arg("info") .arg("my-package") .arg("--registry=dummy-registry") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(file!["stderr.term.svg"]); } cargo-0.91.0/tests/testsuite/cargo_info/basic/stderr.term.svg000064400000000000000000000017401046102023000223640ustar 00000000000000 Updating `dummy-registry` index Downloading crates ... Downloaded my-package v0.1.0 (registry `dummy-registry`) cargo-0.91.0/tests/testsuite/cargo_info/basic/stdout.term.svg000064400000000000000000000041331046102023000224020ustar 00000000000000 my-package #foo #bar #baz A package for testing version: 0.1.0 (from registry `dummy-registry`) license: MIT rust-version: 1.50.0 documentation: https://docs.rs/my-package/0.1.0 repository: https://github.com/hi-rustin/cargo-infromation features: +default = [feature1] feature1 = [] baz = [dep:baz] feature2 = [] cargo-0.91.0/tests/testsuite/cargo_info/features/mod.rs000064400000000000000000000011641046102023000212540ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use super::init_registry_without_token; #[cargo_test] fn case() { init_registry_without_token(); cargo_test_support::registry::Package::new("my-package", "0.1.1") .feature("default", &["feature1", "feature2"]) .feature("feature1", &[]) .feature("feature2", &[]) .publish(); snapbox::cmd::Command::cargo_ui() .arg("info") .arg("my-package") .arg("--registry=dummy-registry") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(file!["stderr.term.svg"]); } cargo-0.91.0/tests/testsuite/cargo_info/features/stderr.term.svg000064400000000000000000000017401046102023000231210ustar 00000000000000 Updating `dummy-registry` index Downloading crates ... Downloaded my-package v0.1.1 (registry `dummy-registry`) cargo-0.91.0/tests/testsuite/cargo_info/features/stdout.term.svg000064400000000000000000000031211046102023000231330ustar 00000000000000 my-package version: 0.1.1 (from registry `dummy-registry`) license: unknown rust-version: unknown features: +default = [feature1, feature2] feature1 = [] feature2 = [] cargo-0.91.0/tests/testsuite/cargo_info/features_activated_over_limit/mod.rs000064400000000000000000000020351046102023000255270ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use super::init_registry_without_token; #[cargo_test] fn case() { const MANY_FEATURES_COUNT: usize = 200; const DEFAULT_FEATURES_COUNT: usize = 100; init_registry_without_token(); let mut test_package = cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package"); let features = (0..MANY_FEATURES_COUNT) .map(|i| format!("eyes{i:03}")) .collect::>(); for name in &features { test_package.feature(name.as_str(), &[]); } let default_features = features .iter() .take(DEFAULT_FEATURES_COUNT) .map(|s| s.as_str()) .collect::>(); test_package.feature("default", &default_features); test_package.publish(); snapbox::cmd::Command::cargo_ui() .arg("info") .arg("your-face") .arg("--registry=dummy-registry") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(file!["stderr.term.svg"]); } cargo-0.91.0/tests/testsuite/cargo_info/features_activated_over_limit/stderr.term.svg000064400000000000000000000017561046102023000274050ustar 00000000000000 Updating `dummy-registry` index Downloading crates ... Downloaded your-face v99999.0.0+my-package (registry `dummy-registry`) cargo-0.91.0/tests/testsuite/cargo_info/features_activated_over_limit/stdout.term.svg000064400000000000000000000031071046102023000274140ustar 00000000000000 your-face version: 99999.0.0+my-package (from registry `dummy-registry`) license: unknown rust-version: unknown features: 101 activated features 100 deactivated features cargo-0.91.0/tests/testsuite/cargo_info/features_activated_over_limit_verbose/mod.rs000064400000000000000000000020671046102023000272610ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use super::init_registry_without_token; #[cargo_test] fn case() { const MANY_FEATURES_COUNT: usize = 200; const DEFAULT_FEATURES_COUNT: usize = 100; init_registry_without_token(); let mut test_package = cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package"); let features = (0..MANY_FEATURES_COUNT) .map(|i| format!("eyes{i:03}")) .collect::>(); for name in &features { test_package.feature(name.as_str(), &[]); } let default_features = features .iter() .take(DEFAULT_FEATURES_COUNT) .map(|s| s.as_str()) .collect::>(); test_package.feature("default", &default_features); test_package.publish(); snapbox::cmd::Command::cargo_ui() .arg("info") .arg("your-face") .arg("--verbose") .arg("--registry=dummy-registry") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(file!["stderr.term.svg"]); } cargo-0.91.0/tests/testsuite/cargo_info/features_activated_over_limit_verbose/stderr.term.svg000064400000000000000000000017561046102023000311320ustar 00000000000000 Updating `dummy-registry` index Downloading crates ... Downloaded your-face v99999.0.0+my-package (registry `dummy-registry`) cargo-0.91.0/tests/testsuite/cargo_info/features_activated_over_limit_verbose/stdout.term.svg000064400000000000000000000505671046102023000311550ustar 00000000000000 your-face version: 99999.0.0+my-package (from registry `dummy-registry`) license: unknown rust-version: unknown features: +default = [eyes000, eyes001, eyes002, eyes003, eyes004, eyes005, eyes006, eyes007, eyes008, eyes009, eyes010, eyes011, eyes012, eyes013, eyes014, eyes015, eyes016, eyes017, eyes018, eyes019, eyes020, eyes021, eyes022, eyes023, eyes024, eyes025, eyes026, eyes027, eyes028, eyes029, eyes030, eyes031, eyes032, eyes033, eyes034, eyes035, eyes036, eyes037, eyes038, eyes039, eyes040, eyes041, eyes042, eyes043, eyes044, eyes045, eyes046, eyes047, eyes048, eyes049, eyes050, eyes051, eyes052, eyes053, eyes054, eyes055, eyes056, eyes057, eyes058, eyes059, eyes060, eyes061, eyes062, eyes063, eyes064, eyes065, eyes066, eyes067, eyes068, eyes069, eyes070, eyes071, eyes072, eyes073, eyes074, eyes075, eyes076, eyes077, eyes078, eyes079, eyes080, eyes081, eyes082, eyes083, eyes084, eyes085, eyes086, eyes087, eyes088, eyes089, eyes090, eyes091, eyes092, eyes093, eyes094, eyes095, eyes096, eyes097, eyes098, eyes099] eyes000 = [] eyes001 = [] eyes002 = [] eyes003 = [] eyes004 = [] eyes005 = [] eyes006 = [] eyes007 = [] eyes008 = [] eyes009 = [] eyes010 = [] eyes011 = [] eyes012 = [] eyes013 = [] eyes014 = [] eyes015 = [] eyes016 = [] eyes017 = [] eyes018 = [] eyes019 = [] eyes020 = [] eyes021 = [] eyes022 = [] eyes023 = [] eyes024 = [] eyes025 = [] eyes026 = [] eyes027 = [] eyes028 = [] eyes029 = [] eyes030 = [] eyes031 = [] eyes032 = [] eyes033 = [] eyes034 = [] eyes035 = [] eyes036 = [] eyes037 = [] eyes038 = [] eyes039 = [] eyes040 = [] eyes041 = [] eyes042 = [] eyes043 = [] eyes044 = [] eyes045 = [] eyes046 = [] eyes047 = [] eyes048 = [] eyes049 = [] eyes050 = [] eyes051 = [] eyes052 = [] eyes053 = [] eyes054 = [] eyes055 = [] eyes056 = [] eyes057 = [] eyes058 = [] eyes059 = [] eyes060 = [] eyes061 = [] eyes062 = [] eyes063 = [] eyes064 = [] eyes065 = [] eyes066 = [] eyes067 = [] eyes068 = [] eyes069 = [] eyes070 = [] eyes071 = [] eyes072 = [] eyes073 = [] eyes074 = [] eyes075 = [] eyes076 = [] eyes077 = [] eyes078 = [] eyes079 = [] eyes080 = [] eyes081 = [] eyes082 = [] eyes083 = [] eyes084 = [] eyes085 = [] eyes086 = [] eyes087 = [] eyes088 = [] eyes089 = [] eyes090 = [] eyes091 = [] eyes092 = [] eyes093 = [] eyes094 = [] eyes095 = [] eyes096 = [] eyes097 = [] eyes098 = [] eyes099 = [] eyes100 = [] eyes101 = [] eyes102 = [] eyes103 = [] eyes104 = [] eyes105 = [] eyes106 = [] eyes107 = [] eyes108 = [] eyes109 = [] eyes110 = [] eyes111 = [] eyes112 = [] eyes113 = [] eyes114 = [] eyes115 = [] eyes116 = [] eyes117 = [] eyes118 = [] eyes119 = [] eyes120 = [] eyes121 = [] eyes122 = [] eyes123 = [] eyes124 = [] eyes125 = [] eyes126 = [] eyes127 = [] eyes128 = [] eyes129 = [] eyes130 = [] eyes131 = [] eyes132 = [] eyes133 = [] eyes134 = [] eyes135 = [] eyes136 = [] eyes137 = [] eyes138 = [] eyes139 = [] eyes140 = [] eyes141 = [] eyes142 = [] eyes143 = [] eyes144 = [] eyes145 = [] eyes146 = [] eyes147 = [] eyes148 = [] eyes149 = [] eyes150 = [] eyes151 = [] eyes152 = [] eyes153 = [] eyes154 = [] eyes155 = [] eyes156 = [] eyes157 = [] eyes158 = [] eyes159 = [] eyes160 = [] eyes161 = [] eyes162 = [] eyes163 = [] eyes164 = [] eyes165 = [] eyes166 = [] eyes167 = [] eyes168 = [] eyes169 = [] eyes170 = [] eyes171 = [] eyes172 = [] eyes173 = [] eyes174 = [] eyes175 = [] eyes176 = [] eyes177 = [] eyes178 = [] eyes179 = [] eyes180 = [] eyes181 = [] eyes182 = [] eyes183 = [] eyes184 = [] eyes185 = [] eyes186 = [] eyes187 = [] eyes188 = [] eyes189 = [] eyes190 = [] eyes191 = [] eyes192 = [] eyes193 = [] eyes194 = [] eyes195 = [] eyes196 = [] eyes197 = [] eyes198 = [] eyes199 = [] cargo-0.91.0/tests/testsuite/cargo_info/features_deactivated_over_limit/mod.rs000064400000000000000000000020341046102023000260370ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use super::init_registry_without_token; #[cargo_test] fn case() { const MANY_FEATURES_COUNT: usize = 200; const DEFAULT_FEATURES_COUNT: usize = 20; init_registry_without_token(); let mut test_package = cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package"); let features = (0..MANY_FEATURES_COUNT) .map(|i| format!("eyes{i:03}")) .collect::>(); for name in &features { test_package.feature(name.as_str(), &[]); } let default_features = features .iter() .take(DEFAULT_FEATURES_COUNT) .map(|s| s.as_str()) .collect::>(); test_package.feature("default", &default_features); test_package.publish(); snapbox::cmd::Command::cargo_ui() .arg("info") .arg("your-face") .arg("--registry=dummy-registry") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(file!["stderr.term.svg"]); } cargo-0.91.0/tests/testsuite/cargo_info/features_deactivated_over_limit/stderr.term.svg000064400000000000000000000017561046102023000277160ustar 00000000000000 Updating `dummy-registry` index Downloading crates ... Downloaded your-face v99999.0.0+my-package (registry `dummy-registry`) cargo-0.91.0/tests/testsuite/cargo_info/features_deactivated_over_limit/stdout.term.svg000064400000000000000000000061521046102023000277300ustar 00000000000000 your-face version: 99999.0.0+my-package (from registry `dummy-registry`) license: unknown rust-version: unknown features: +default = [eyes000, eyes001, eyes002, eyes003, eyes004, eyes005, eyes006, eyes007, eyes008, eyes009, eyes010, eyes011, eyes012, eyes013, eyes014, eyes015, eyes016, eyes017, eyes018, eyes019] eyes000 = [] eyes001 = [] eyes002 = [] eyes003 = [] eyes004 = [] eyes005 = [] eyes006 = [] eyes007 = [] eyes008 = [] eyes009 = [] eyes010 = [] eyes011 = [] eyes012 = [] eyes013 = [] eyes014 = [] eyes015 = [] eyes016 = [] eyes017 = [] eyes018 = [] eyes019 = [] 180 deactivated features cargo-0.91.0/tests/testsuite/cargo_info/git_dependency/mod.rs000064400000000000000000000020421046102023000224130ustar 00000000000000use crate::prelude::*; use cargo_test_support::{basic_manifest, file, git, project}; use super::init_registry_without_token; #[cargo_test] fn case() { init_registry_without_token(); let baz = git::new("baz", |project| { project .file("Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("src/lib.rs", "") }); let foo = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [dependencies] baz = {{ git = '{}' }} "#, baz.url() ), ) .file("src/lib.rs", "") .build(); let project_root = foo.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("info") .arg_line("--verbose foo") .current_dir(cwd) .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(""); } cargo-0.91.0/tests/testsuite/cargo_info/git_dependency/stdout.term.svg000064400000000000000000000026371046102023000243110ustar 00000000000000 foo version: 0.1.0 (from ./) license: unknown rust-version: unknown dependencies: +baz ([ROOTURL]/baz) cargo-0.91.0/tests/testsuite/cargo_info/help/mod.rs000064400000000000000000000004161046102023000203650ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("info") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(""); } cargo-0.91.0/tests/testsuite/cargo_info/help/stdout.term.svg000064400000000000000000000103161046102023000222510ustar 00000000000000 Display information about a package Usage: cargo[EXE] info [OPTIONS] <SPEC> Options: --index <INDEX> Registry index URL to search packages in --registry <REGISTRY> Registry to search packages in -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Package Selection: <SPEC> Package to inspect Manifest Options: --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help info` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_info/mod.rs000064400000000000000000000023571046102023000174430ustar 00000000000000mod basic; mod features; mod features_activated_over_limit; mod features_activated_over_limit_verbose; mod features_deactivated_over_limit; mod git_dependency; mod help; mod not_found; mod path_dependency; mod pick_msrv_compatible_package; mod pick_msrv_compatible_package_within_ws; mod pick_msrv_compatible_package_within_ws_and_use_msrv_from_ws; mod specify_empty_version_with_url; mod specify_version_outside_ws; mod specify_version_with_url_but_registry_is_not_matched; mod specify_version_within_ws_and_conflict_with_lockfile; mod specify_version_within_ws_and_match_with_lockfile; mod transitive_dependency_within_ws; mod verbose; mod with_frozen_outside_ws; mod with_frozen_within_ws; mod with_locked_outside_ws; mod with_locked_within_ws; mod with_locked_within_ws_and_pick_the_package; mod with_offline; mod with_quiet; mod within_ws; mod within_ws_and_pick_ws_package; mod within_ws_with_alternative_registry; mod within_ws_without_lockfile; mod without_requiring_registry_auth; // Initialize the registry without a token. // Otherwise, it will try to list owners of the crate and fail. pub(crate) fn init_registry_without_token() { let _reg = cargo_test_support::registry::RegistryBuilder::new() .no_configure_token() .build(); } cargo-0.91.0/tests/testsuite/cargo_info/not_found/mod.rs000064400000000000000000000012411046102023000214250ustar 00000000000000use crate::prelude::*; use cargo_test_support::{Project, compare::assert_ui, current_dir, file}; use super::init_registry_without_token; #[cargo_test] fn case() { init_registry_without_token(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("info") .arg("unknown") .arg("--registry=dummy-registry") .current_dir(cwd) .assert() .failure() .stdout_eq("") .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_info/not_found/stderr.term.svg000064400000000000000000000016541046102023000233020ustar 00000000000000 Updating `dummy-registry` index error: could not find `unknown` in registry `[ROOTURL]/registry` cargo-0.91.0/tests/testsuite/cargo_info/path_dependency/mod.rs000064400000000000000000000012031046102023000225620ustar 00000000000000use crate::prelude::*; use cargo_test_support::{Project, compare::assert_ui, current_dir, file}; use super::init_registry_without_token; #[cargo_test] fn case() { init_registry_without_token(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("info") .arg_line("--verbose foo") .current_dir(cwd) .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(""); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_info/path_dependency/stdout.term.svg000064400000000000000000000026441046102023000244600ustar 00000000000000 foo version: 0.0.0 (from ./) license: unknown rust-version: unknown dependencies: +crate1 (./crates/crate1) cargo-0.91.0/tests/testsuite/cargo_info/pick_msrv_compatible_package/mod.rs000064400000000000000000000012521046102023000253030ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use super::init_registry_without_token; #[cargo_test] fn case() { init_registry_without_token(); cargo_test_support::registry::Package::new("my-package", "0.1.1+my-package") .rust_version("1.0.0") .publish(); cargo_test_support::registry::Package::new("my-package", "0.2.0+my-package") .rust_version("1.9876.0") .publish(); snapbox::cmd::Command::cargo_ui() .arg("info") .arg("my-package") .arg("--registry=dummy-registry") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(file!["stderr.term.svg"]); } cargo-0.91.0/tests/testsuite/cargo_info/pick_msrv_compatible_package/stderr.term.svg000064400000000000000000000017531046102023000271560ustar 00000000000000 Updating `dummy-registry` index Downloading crates ... Downloaded my-package v0.1.1+my-package (registry `dummy-registry`) cargo-0.91.0/tests/testsuite/cargo_info/pick_msrv_compatible_package/stdout.term.svg000064400000000000000000000024521046102023000271720ustar 00000000000000 my-package version: 0.1.1+my-package (latest 0.2.0+my-package from registry `dummy-registry`) license: unknown rust-version: 1.0.0 cargo-0.91.0/tests/testsuite/cargo_info/pick_msrv_compatible_package_within_ws/mod.rs000064400000000000000000000020201046102023000273700ustar 00000000000000use crate::prelude::*; use cargo_test_support::{Project, compare::assert_ui, current_dir, file}; use super::init_registry_without_token; #[cargo_test] fn case() { init_registry_without_token(); cargo_test_support::registry::Package::new("my-package", "0.1.0").publish(); cargo_test_support::registry::Package::new("my-package", "0.2.0") .rust_version("1.0.0") .publish(); cargo_test_support::registry::Package::new("my-package", "0.2.1") .rust_version("1.9876.0") .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root.join("crate1"); snapbox::cmd::Command::cargo_ui() .arg("info") .arg("my-package") .arg("--registry=dummy-registry") .current_dir(cwd) .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_info/pick_msrv_compatible_package_within_ws/stderr.term.svg000064400000000000000000000017401046102023000312450ustar 00000000000000 Updating `dummy-registry` index Downloading crates ... Downloaded my-package v0.2.0 (registry `dummy-registry`) cargo-0.91.0/tests/testsuite/cargo_info/pick_msrv_compatible_package_within_ws/stdout.term.svg000064400000000000000000000024241046102023000312640ustar 00000000000000 my-package version: 0.2.0 (latest 0.2.1 from registry `dummy-registry`) license: unknown rust-version: 1.0.0 ././@LongLink00006440000000000000000000000153000000000000007772Lustar cargo-0.91.0/tests/testsuite/cargo_info/pick_msrv_compatible_package_within_ws_and_use_msrv_from_ws/mod.rscargo-0.91.0/tests/testsuite/cargo_info/pick_msrv_compatible_package_within_ws_and_use_msrv_from_ws/000064400000000000000000000020161046102023000325530ustar 00000000000000use crate::prelude::*; use cargo_test_support::{Project, compare::assert_ui, current_dir, file}; use super::init_registry_without_token; #[cargo_test] fn case() { init_registry_without_token(); cargo_test_support::registry::Package::new("my-package", "0.1.0").publish(); cargo_test_support::registry::Package::new("my-package", "0.2.0") .rust_version("1.0.0") .publish(); cargo_test_support::registry::Package::new("my-package", "0.2.1") .rust_version("1.70.0") .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root.join("crate1"); snapbox::cmd::Command::cargo_ui() .arg("info") .arg("my-package") .arg("--registry=dummy-registry") .current_dir(cwd) .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } ././@LongLink00006440000000000000000000000164000000000000007774Lustar cargo-0.91.0/tests/testsuite/cargo_info/pick_msrv_compatible_package_within_ws_and_use_msrv_from_ws/stderr.term.svgcargo-0.91.0/tests/testsuite/cargo_info/pick_msrv_compatible_package_within_ws_and_use_msrv_from_ws/000064400000000000000000000017401046102023000325560ustar 00000000000000 Updating `dummy-registry` index Downloading crates ... Downloaded my-package v0.2.0 (registry `dummy-registry`) ././@LongLink00006440000000000000000000000164000000000000007774Lustar cargo-0.91.0/tests/testsuite/cargo_info/pick_msrv_compatible_package_within_ws_and_use_msrv_from_ws/stdout.term.svgcargo-0.91.0/tests/testsuite/cargo_info/pick_msrv_compatible_package_within_ws_and_use_msrv_from_ws/000064400000000000000000000024241046102023000325560ustar 00000000000000 my-package version: 0.2.0 (latest 0.2.1 from registry `dummy-registry`) license: unknown rust-version: 1.0.0 cargo-0.91.0/tests/testsuite/cargo_info/specify_empty_version_with_url/mod.rs000064400000000000000000000012401046102023000257730ustar 00000000000000use crate::prelude::*; use cargo_test_support::{file, registry::RegistryBuilder}; use super::init_registry_without_token; #[cargo_test] fn case() { init_registry_without_token(); let _ = RegistryBuilder::new() .alternative() .no_configure_token() .build(); cargo_test_support::registry::Package::new("my-package", "99999.0.0-alpha.1+my-package") .alternative(true) .publish(); snapbox::cmd::Command::cargo_ui() .arg("info") .arg("https://crates.io") .arg("--registry=alternative") .assert() .failure() .stdout_eq("") .stderr_eq(file!["stderr.term.svg"]); } cargo-0.91.0/tests/testsuite/cargo_info/specify_empty_version_with_url/stderr.term.svg000064400000000000000000000017071046102023000276500ustar 00000000000000 error: invalid package ID specification: `https://crates.io` Caused by: package name cannot be empty cargo-0.91.0/tests/testsuite/cargo_info/specify_version_outside_ws/mod.rs000064400000000000000000000011071046102023000251070ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use super::init_registry_without_token; #[cargo_test] fn case() { init_registry_without_token(); for ver in ["0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package"] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } snapbox::cmd::Command::cargo_ui() .arg("info") .arg("my-package@0.2") .arg("--registry=dummy-registry") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(file!["stderr.term.svg"]); } cargo-0.91.0/tests/testsuite/cargo_info/specify_version_outside_ws/stderr.term.svg000064400000000000000000000017531046102023000267630ustar 00000000000000 Updating `dummy-registry` index Downloading crates ... Downloaded my-package v0.2.3+my-package (registry `dummy-registry`) cargo-0.91.0/tests/testsuite/cargo_info/specify_version_outside_ws/stdout.term.svg000064400000000000000000000023561046102023000270020ustar 00000000000000 my-package version: 0.2.3+my-package (from registry `dummy-registry`) license: unknown rust-version: unknown cargo-0.91.0/tests/testsuite/cargo_info/specify_version_with_url_but_registry_is_not_matched/mod.rs000064400000000000000000000012531046102023000324230ustar 00000000000000use crate::prelude::*; use cargo_test_support::{file, registry::RegistryBuilder}; use super::init_registry_without_token; #[cargo_test] fn case() { init_registry_without_token(); let _ = RegistryBuilder::new() .alternative() .no_configure_token() .build(); cargo_test_support::registry::Package::new("my-package", "99999.0.0-alpha.1+my-package") .alternative(true) .publish(); snapbox::cmd::Command::cargo_ui() .arg("info") .arg("https://crates.io/my-package") .arg("--registry=alternative") .assert() .failure() .stdout_eq("") .stderr_eq(file!["stderr.term.svg"]); } ././@LongLink00006440000000000000000000000155000000000000007774Lustar cargo-0.91.0/tests/testsuite/cargo_info/specify_version_with_url_but_registry_is_not_matched/stderr.term.svgcargo-0.91.0/tests/testsuite/cargo_info/specify_version_with_url_but_registry_is_not_matched/stderr.000064400000000000000000000017131046102023000326030ustar 00000000000000 Updating `alternative` index error: could not find `https://crates.io/my-package` in registry `[ROOTURL]/alternative-registry` cargo-0.91.0/tests/testsuite/cargo_info/specify_version_within_ws_and_conflict_with_lockfile/mod.rs000064400000000000000000000020041046102023000323400ustar 00000000000000use crate::prelude::*; use cargo_test_support::{Project, compare::assert_ui, current_dir, file}; use super::init_registry_without_token; #[cargo_test] fn case() { init_registry_without_token(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("info") .arg("my-package@0.4") .arg("--registry=dummy-registry") .current_dir(cwd) .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } ././@LongLink00006440000000000000000000000155000000000000007774Lustar cargo-0.91.0/tests/testsuite/cargo_info/specify_version_within_ws_and_conflict_with_lockfile/stderr.term.svgcargo-0.91.0/tests/testsuite/cargo_info/specify_version_within_ws_and_conflict_with_lockfile/stderr.000064400000000000000000000017531046102023000325310ustar 00000000000000 Updating `dummy-registry` index Downloading crates ... Downloaded my-package v0.4.1+my-package (registry `dummy-registry`) ././@LongLink00006440000000000000000000000155000000000000007774Lustar cargo-0.91.0/tests/testsuite/cargo_info/specify_version_within_ws_and_conflict_with_lockfile/stdout.term.svgcargo-0.91.0/tests/testsuite/cargo_info/specify_version_within_ws_and_conflict_with_lockfile/stdout.000064400000000000000000000025261046102023000325470ustar 00000000000000 my-package version: 0.4.1+my-package (latest 99999.0.0+my-package from registry `dummy-registry`) license: unknown rust-version: unknown cargo-0.91.0/tests/testsuite/cargo_info/specify_version_within_ws_and_match_with_lockfile/mod.rs000064400000000000000000000020041046102023000316330ustar 00000000000000use crate::prelude::*; use cargo_test_support::{Project, compare::assert_ui, current_dir, file}; use super::init_registry_without_token; #[cargo_test] fn case() { init_registry_without_token(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("info") .arg("my-package@0.1") .arg("--registry=dummy-registry") .current_dir(cwd) .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } ././@LongLink00006440000000000000000000000152000000000000007771Lustar cargo-0.91.0/tests/testsuite/cargo_info/specify_version_within_ws_and_match_with_lockfile/stderr.term.svgcargo-0.91.0/tests/testsuite/cargo_info/specify_version_within_ws_and_match_with_lockfile/stderr.ter000064400000000000000000000024371046102023000325370ustar 00000000000000 Updating `dummy-registry` index Downloading crates ... Downloaded my-package v0.1.1+my-package (registry `dummy-registry`) note: to see how you depend on my-package, run `cargo tree --invert --package my-package@0.1.1+my-package` ././@LongLink00006440000000000000000000000152000000000000007771Lustar cargo-0.91.0/tests/testsuite/cargo_info/specify_version_within_ws_and_match_with_lockfile/stdout.term.svgcargo-0.91.0/tests/testsuite/cargo_info/specify_version_within_ws_and_match_with_lockfile/stdout.ter000064400000000000000000000027771046102023000325650ustar 00000000000000 my-package version: 0.1.1+my-package (latest 99999.0.0+my-package) license: unknown rust-version: unknown documentation: https://docs.rs/my-package/0.1.1+my-package crates.io: https://crates.io/crates/my-package/0.1.1+my-package cargo-0.91.0/tests/testsuite/cargo_info/transitive_dependency_within_ws/direct1-stderr.term.svg000064400000000000000000000022111046102023000313070ustar 00000000000000 Downloading crates ... Downloaded my-package v1.0.0 (registry `dummy-registry`) note: to see how you depend on my-package, run `cargo tree --invert --package my-package@1.0.0` cargo-0.91.0/tests/testsuite/cargo_info/transitive_dependency_within_ws/direct1-stdout.term.svg000064400000000000000000000027201046102023000313330ustar 00000000000000 my-package version: 1.0.0 (latest 99.0.0) license: unknown rust-version: unknown documentation: https://docs.rs/my-package/1.0.0 crates.io: https://crates.io/crates/my-package/1.0.0 cargo-0.91.0/tests/testsuite/cargo_info/transitive_dependency_within_ws/direct2-stderr.term.svg000064400000000000000000000015421046102023000313160ustar 00000000000000 note: to see how you depend on my-package, run `cargo tree --invert --package my-package@2.0.0` cargo-0.91.0/tests/testsuite/cargo_info/transitive_dependency_within_ws/direct2-stdout.term.svg000064400000000000000000000027201046102023000313340ustar 00000000000000 my-package version: 2.0.0 (latest 99.0.0) license: unknown rust-version: unknown documentation: https://docs.rs/my-package/2.0.0 crates.io: https://crates.io/crates/my-package/2.0.0 cargo-0.91.0/tests/testsuite/cargo_info/transitive_dependency_within_ws/mod.rs000064400000000000000000000056011046102023000261170ustar 00000000000000use crate::prelude::*; use cargo_test_support::{Project, compare::assert_ui, current_dir, file}; use super::init_registry_without_token; #[cargo_test] fn case() { init_registry_without_token(); // 99.0.0 is unused for ver in ["1.0.0", "2.0.0", "3.0.0", "99.0.0"] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } // Dep1 depends on 3.0.0, Dep2 depends on 2.0.0, Dep3 depends on 1.0.0 cargo_test_support::registry::Package::new("dep1", "1.0.0") .dep("my-package", "1.0.0") .publish(); cargo_test_support::registry::Package::new("dep2", "1.0.0") .dep("my-package", "2.0.0") .publish(); cargo_test_support::registry::Package::new("dep3", "1.0.0") .dep("my-package", "3.0.0") .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let transitive1_root = project_root.join("crates/transitive1"); let transitive2_root = project_root.join("crates/transitive2"); let direct1_root = project_root.join("crates/direct1"); let direct2_root = project_root.join("crates/direct2"); let ws_directory = &project_root; let transitive1_directory = &transitive1_root; let transitive2_directory = &transitive2_root; let direct1_directory = &direct1_root; let direct2_directory = &direct2_root; snapbox::cmd::Command::cargo_ui() .arg("info") .arg("my-package") .arg("--registry=dummy-registry") .current_dir(ws_directory) .assert() .stdout_eq(file!["ws-stdout.term.svg"]) .stderr_eq(file!["ws-stderr.term.svg"]); snapbox::cmd::Command::cargo_ui() .arg("info") .arg("my-package") .arg("--registry=dummy-registry") .current_dir(transitive1_directory) .assert() .stdout_eq(file!["transitive1-stdout.term.svg"]) .stderr_eq(file!["transitive1-stderr.term.svg"]); snapbox::cmd::Command::cargo_ui() .arg("info") .arg("my-package") .arg("--registry=dummy-registry") .current_dir(transitive2_directory) .assert() .stdout_eq(file!["transitive2-stdout.term.svg"]) .stderr_eq(file!["transitive2-stderr.term.svg"]); snapbox::cmd::Command::cargo_ui() .arg("info") .arg("my-package") .arg("--registry=dummy-registry") .current_dir(direct1_directory) .assert() .stdout_eq(file!["direct1-stdout.term.svg"]) .stderr_eq(file!["direct1-stderr.term.svg"]); snapbox::cmd::Command::cargo_ui() .arg("info") .arg("my-package") .arg("--registry=dummy-registry") .current_dir(direct2_directory) .assert() .stdout_eq(file!["direct2-stdout.term.svg"]) .stderr_eq(file!["direct2-stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_info/transitive_dependency_within_ws/transitive1-stderr.term.svg000064400000000000000000000015421046102023000322330ustar 00000000000000 note: to see how you depend on my-package, run `cargo tree --invert --package my-package@2.0.0` cargo-0.91.0/tests/testsuite/cargo_info/transitive_dependency_within_ws/transitive1-stdout.term.svg000064400000000000000000000027201046102023000322510ustar 00000000000000 my-package version: 2.0.0 (latest 99.0.0) license: unknown rust-version: unknown documentation: https://docs.rs/my-package/2.0.0 crates.io: https://crates.io/crates/my-package/2.0.0 cargo-0.91.0/tests/testsuite/cargo_info/transitive_dependency_within_ws/transitive2-stderr.term.svg000064400000000000000000000015421046102023000322340ustar 00000000000000 note: to see how you depend on my-package, run `cargo tree --invert --package my-package@2.0.0` cargo-0.91.0/tests/testsuite/cargo_info/transitive_dependency_within_ws/transitive2-stdout.term.svg000064400000000000000000000027201046102023000322520ustar 00000000000000 my-package version: 2.0.0 (latest 99.0.0) license: unknown rust-version: unknown documentation: https://docs.rs/my-package/2.0.0 crates.io: https://crates.io/crates/my-package/2.0.0 cargo-0.91.0/tests/testsuite/cargo_info/transitive_dependency_within_ws/ws-stderr.term.svg000064400000000000000000000024111046102023000304070ustar 00000000000000 Updating `dummy-registry` index Downloading crates ... Downloaded my-package v2.0.0 (registry `dummy-registry`) note: to see how you depend on my-package, run `cargo tree --invert --package my-package@2.0.0` cargo-0.91.0/tests/testsuite/cargo_info/transitive_dependency_within_ws/ws-stdout.term.svg000064400000000000000000000027201046102023000304310ustar 00000000000000 my-package version: 2.0.0 (latest 99.0.0) license: unknown rust-version: unknown documentation: https://docs.rs/my-package/2.0.0 crates.io: https://crates.io/crates/my-package/2.0.0 cargo-0.91.0/tests/testsuite/cargo_info/verbose/mod.rs000064400000000000000000000026351046102023000211070ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use super::init_registry_without_token; #[cargo_test] fn case() { init_registry_without_token(); cargo_test_support::registry::Package::new("my-package", "0.1.0") .file( "Cargo.toml", r#" [package] name = "my-package" version = "0.1.0" description = "A package for testing" repository = "https://github.com/hi-rustin/cargo-infromation" documentation = "https://docs.rs/my-package/0.1.0" license = "MIT" edition = "2018" rust-version = "1.50.0" keywords = ["foo", "bar", "baz"] [features] default = ["feature1"] feature1 = [] feature2 = [] [dependencies] foo = "0.1.0" bar = "0.2.0" baz = { version = "0.3.0", optional = true } [[bin]] name = "my_bin" [lib] name = "my_lib" "#, ) .file("src/bin/my_bin.rs", "") .file("src/lib.rs", "") .publish(); snapbox::cmd::Command::cargo_ui() .arg("info") .arg("my-package") .arg("--verbose") .arg("--registry=dummy-registry") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(file!["stderr.term.svg"]); } cargo-0.91.0/tests/testsuite/cargo_info/verbose/stderr.term.svg000064400000000000000000000017401046102023000227500ustar 00000000000000 Updating `dummy-registry` index Downloading crates ... Downloaded my-package v0.1.0 (registry `dummy-registry`) cargo-0.91.0/tests/testsuite/cargo_info/verbose/stdout.term.svg000064400000000000000000000050011046102023000227610ustar 00000000000000 my-package #foo #bar #baz A package for testing version: 0.1.0 (from registry `dummy-registry`) license: MIT rust-version: 1.50.0 documentation: https://docs.rs/my-package/0.1.0 repository: https://github.com/hi-rustin/cargo-infromation features: +default = [feature1] feature1 = [] baz = [dep:baz] feature2 = [] dependencies: +bar@0.2.0 +foo@0.1.0 baz@0.3.0 cargo-0.91.0/tests/testsuite/cargo_info/with_frozen_outside_ws/mod.rs000064400000000000000000000013471046102023000242440ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use super::init_registry_without_token; #[cargo_test] fn case() { init_registry_without_token(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } snapbox::cmd::Command::cargo_ui() .arg("info") .arg("my-package") .arg("--frozen") .arg("--registry=dummy-registry") .assert() .failure() .stdout_eq("") .stderr_eq(file!["stderr.term.svg"]); } cargo-0.91.0/tests/testsuite/cargo_info/with_frozen_outside_ws/stderr.term.svg000064400000000000000000000014161046102023000261060ustar 00000000000000 error: the option `--frozen` can only be used within a workspace cargo-0.91.0/tests/testsuite/cargo_info/with_frozen_within_ws/mod.rs000064400000000000000000000020001046102023000240550ustar 00000000000000use crate::prelude::*; use cargo_test_support::{Project, compare::assert_ui, current_dir, file}; use super::init_registry_without_token; #[cargo_test] fn case() { init_registry_without_token(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("info") .arg("unknown") .arg("--frozen") .arg("--registry=dummy-registry") .current_dir(cwd) .assert() .failure() .stdout_eq("") .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_info/with_frozen_within_ws/stderr.term.svg000064400000000000000000000014161046102023000257340ustar 00000000000000 error: could not find `unknown` in registry `[ROOTURL]/registry` cargo-0.91.0/tests/testsuite/cargo_info/with_locked_outside_ws/mod.rs000064400000000000000000000013471046102023000242020ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use super::init_registry_without_token; #[cargo_test] fn case() { init_registry_without_token(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } snapbox::cmd::Command::cargo_ui() .arg("info") .arg("my-package") .arg("--locked") .arg("--registry=dummy-registry") .assert() .failure() .stdout_eq("") .stderr_eq(file!["stderr.term.svg"]); } cargo-0.91.0/tests/testsuite/cargo_info/with_locked_outside_ws/stderr.term.svg000064400000000000000000000014161046102023000260440ustar 00000000000000 error: the option `--locked` can only be used within a workspace cargo-0.91.0/tests/testsuite/cargo_info/with_locked_within_ws/mod.rs000064400000000000000000000020001046102023000240130ustar 00000000000000use crate::prelude::*; use cargo_test_support::{Project, compare::assert_ui, current_dir, file}; use super::init_registry_without_token; #[cargo_test] fn case() { init_registry_without_token(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("info") .arg("unknown") .arg("--locked") .arg("--registry=dummy-registry") .current_dir(cwd) .assert() .failure() .stdout_eq("") .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_info/with_locked_within_ws/stderr.term.svg000064400000000000000000000016541046102023000256760ustar 00000000000000 Updating `dummy-registry` index error: could not find `unknown` in registry `[ROOTURL]/registry` cargo-0.91.0/tests/testsuite/cargo_info/with_locked_within_ws_and_pick_the_package/mod.rs000064400000000000000000000020311046102023000301620ustar 00000000000000use crate::prelude::*; use cargo_test_support::{Project, compare::assert_ui, current_dir, file}; use super::init_registry_without_token; #[cargo_test] fn case() { init_registry_without_token(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("info") .arg("my-package") .arg("--locked") .arg("--registry=dummy-registry") .current_dir(cwd) .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_info/with_locked_within_ws_and_pick_the_package/stderr.term.svg000064400000000000000000000017571046102023000320450ustar 00000000000000 Updating `dummy-registry` index Downloading crates ... Downloaded my-package v99999.0.0+my-package (registry `dummy-registry`) cargo-0.91.0/tests/testsuite/cargo_info/with_locked_within_ws_and_pick_the_package/stdout.term.svg000064400000000000000000000023621046102023000320550ustar 00000000000000 my-package version: 99999.0.0+my-package (from registry `dummy-registry`) license: unknown rust-version: unknown cargo-0.91.0/tests/testsuite/cargo_info/with_offline/mod.rs000064400000000000000000000013501046102023000221100ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use super::init_registry_without_token; #[cargo_test] fn case() { init_registry_without_token(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } snapbox::cmd::Command::cargo_ui() .arg("info") .arg("my-package") .arg("--offline") .arg("--registry=dummy-registry") .assert() .failure() .stdout_eq("") .stderr_eq(file!["stderr.term.svg"]); } cargo-0.91.0/tests/testsuite/cargo_info/with_offline/stderr.term.svg000064400000000000000000000014211046102023000237540ustar 00000000000000 error: could not find `my-package` in registry `[ROOTURL]/registry` cargo-0.91.0/tests/testsuite/cargo_info/with_quiet/mod.rs000064400000000000000000000013461046102023000216220ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use super::init_registry_without_token; #[cargo_test] fn case() { init_registry_without_token(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } snapbox::cmd::Command::cargo_ui() .arg("info") .arg("my-package") .arg("--quiet") .arg("--registry=dummy-registry") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(""); } cargo-0.91.0/tests/testsuite/cargo_info/with_quiet/stdout.term.svg000064400000000000000000000023621046102023000235050ustar 00000000000000 my-package version: 99999.0.0+my-package (from registry `dummy-registry`) license: unknown rust-version: unknown cargo-0.91.0/tests/testsuite/cargo_info/within_ws/mod.rs000064400000000000000000000020001046102023000214370ustar 00000000000000use crate::prelude::*; use cargo_test_support::{Project, compare::assert_ui, current_dir, file}; use super::init_registry_without_token; #[cargo_test] fn case() { init_registry_without_token(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", "20.0.0+my-package", "99999.0.0+my-package", "99999.0.0-alpha.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("info") .arg("my-package") .arg("--registry=dummy-registry") .current_dir(cwd) .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_info/within_ws/stderr.term.svg000064400000000000000000000024371046102023000233220ustar 00000000000000 Updating `dummy-registry` index Downloading crates ... Downloaded my-package v0.1.1+my-package (registry `dummy-registry`) note: to see how you depend on my-package, run `cargo tree --invert --package my-package@0.1.1+my-package` cargo-0.91.0/tests/testsuite/cargo_info/within_ws/stdout.term.svg000064400000000000000000000027771046102023000233500ustar 00000000000000 my-package version: 0.1.1+my-package (latest 99999.0.0+my-package) license: unknown rust-version: unknown documentation: https://docs.rs/my-package/0.1.1+my-package crates.io: https://crates.io/crates/my-package/0.1.1+my-package cargo-0.91.0/tests/testsuite/cargo_info/within_ws_and_pick_ws_package/mod.rs000064400000000000000000000015261046102023000254670ustar 00000000000000use crate::prelude::*; use cargo_test_support::{Project, compare::assert_ui, current_dir, file}; use super::init_registry_without_token; #[cargo_test] fn case() { init_registry_without_token(); cargo_test_support::registry::Package::new("my-package", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("cargo-list-test-fixture", "0.1.1+my-package") .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("info") .arg("cargo-list-test-fixture") .current_dir(cwd) .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(""); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_info/within_ws_and_pick_ws_package/stdout.term.svg000064400000000000000000000023311046102023000273460ustar 00000000000000 cargo-list-test-fixture version: 0.2.0 (from ./) license: unknown rust-version: unknown cargo-0.91.0/tests/testsuite/cargo_info/within_ws_with_alternative_registry/mod.rs000064400000000000000000000017431046102023000270350ustar 00000000000000use crate::prelude::*; use cargo_test_support::{Project, compare::assert_ui, registry::RegistryBuilder}; use cargo_test_support::{current_dir, file}; use super::init_registry_without_token; #[cargo_test] fn case() { init_registry_without_token(); let _ = RegistryBuilder::new() .alternative() .no_configure_token() .build(); cargo_test_support::registry::Package::new("my-package", "99999.0.0-alpha.1+my-package") .alternative(true) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("info") .arg("my-package") .arg("--registry=alternative") .current_dir(cwd) .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_info/within_ws_with_alternative_registry/stderr.term.svg000064400000000000000000000021611046102023000306750ustar 00000000000000 Updating `dummy-registry` index Updating `alternative` index Downloading crates ... Downloaded my-package v99999.0.0-alpha.1+my-package (registry `alternative`) cargo-0.91.0/tests/testsuite/cargo_info/within_ws_with_alternative_registry/stdout.term.svg000064400000000000000000000023671046102023000307240ustar 00000000000000 my-package version: 99999.0.0-alpha.1+my-package (from registry `alternative`) license: unknown rust-version: unknown cargo-0.91.0/tests/testsuite/cargo_info/within_ws_without_lockfile/mod.rs000064400000000000000000000016331046102023000251050ustar 00000000000000use crate::prelude::*; use cargo_test_support::{Project, compare::assert_ui, current_dir, file}; use super::init_registry_without_token; #[cargo_test] fn case() { init_registry_without_token(); for ver in [ "0.1.1+my-package", "0.2.0+my-package", "0.2.3+my-package", "0.4.1+my-package", ] { cargo_test_support::registry::Package::new("my-package", ver).publish(); } let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("info") .arg("my-package") .arg("--registry=dummy-registry") .current_dir(cwd) .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_info/within_ws_without_lockfile/stderr.term.svg000064400000000000000000000032301046102023000267450ustar 00000000000000 Updating `dummy-registry` index Locking 1 package to latest compatible version Adding my-package v0.2.3+my-package (available: v0.4.1+my-package) Downloading crates ... Downloaded my-package v0.2.3+my-package (registry `dummy-registry`) note: to see how you depend on my-package, run `cargo tree --invert --package my-package@0.2.3+my-package` cargo-0.91.0/tests/testsuite/cargo_info/within_ws_without_lockfile/stdout.term.svg000064400000000000000000000027731046102023000267770ustar 00000000000000 my-package version: 0.2.3+my-package (latest 0.4.1+my-package) license: unknown rust-version: unknown documentation: https://docs.rs/my-package/0.2.3+my-package crates.io: https://crates.io/crates/my-package/0.2.3+my-package cargo-0.91.0/tests/testsuite/cargo_info/without_requiring_registry_auth/mod.rs000064400000000000000000000014271046102023000262010ustar 00000000000000use crate::prelude::*; use cargo_test_support::{Project, compare::assert_ui, current_dir, file}; use super::init_registry_without_token; #[cargo_test] fn case() { init_registry_without_token(); cargo_test_support::registry::Package::new("my-package", "0.1.1+my-package").publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("info") .arg("my-package") .arg("--registry=dummy-registry") .current_dir(cwd) .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_info/without_requiring_registry_auth/stderr.term.svg000064400000000000000000000024371046102023000300500ustar 00000000000000 Updating `dummy-registry` index Downloading crates ... Downloaded my-package v0.1.1+my-package (registry `dummy-registry`) note: to see how you depend on my-package, run `cargo tree --invert --package my-package@0.1.1+my-package` cargo-0.91.0/tests/testsuite/cargo_info/without_requiring_registry_auth/stdout.term.svg000064400000000000000000000026731046102023000300710ustar 00000000000000 my-package version: 0.1.1+my-package license: unknown rust-version: unknown documentation: https://docs.rs/my-package/0.1.1+my-package crates.io: https://crates.io/crates/my-package/0.1.1+my-package cargo-0.91.0/tests/testsuite/cargo_init/auto_git/in/mod.rs000064400000000000000000000003021046102023000216600ustar 00000000000000use cargo_test_support::compare::assert_ui; use crate::prelude::*; use cargo_test_support::{command_is_available, paths, Project}; use std::fs; use std::process::Command; use crate::test_root; cargo-0.91.0/tests/testsuite/cargo_init/auto_git/mod.rs000064400000000000000000000012451046102023000212610ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --lib") .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); assert!(project_root.join(".git").is_dir()); } cargo-0.91.0/tests/testsuite/cargo_init/auto_git/stderr.term.svg000064400000000000000000000017301046102023000231250ustar 00000000000000 Creating library package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/bin_already_exists_explicit/in/src/main.rs000064400000000000000000000001061046102023000265540ustar 00000000000000fn main() { println!("Check that our file is not overwritten") } cargo-0.91.0/tests/testsuite/cargo_init/bin_already_exists_explicit/mod.rs000064400000000000000000000011761046102023000252220ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --bin --vcs none") .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); } cargo-0.91.0/tests/testsuite/cargo_init/bin_already_exists_explicit/stderr.term.svg000064400000000000000000000017451046102023000270710ustar 00000000000000 Creating binary (application) package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/bin_already_exists_explicit_nosrc/in/main.rs000064400000000000000000000001061046102023000271710ustar 00000000000000fn main() { println!("Check that our file is not overwritten") } cargo-0.91.0/tests/testsuite/cargo_init/bin_already_exists_explicit_nosrc/mod.rs000064400000000000000000000012571046102023000264260ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --bin --vcs none") .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); assert!(!project_root.join("src").is_dir()); } cargo-0.91.0/tests/testsuite/cargo_init/bin_already_exists_explicit_nosrc/stderr.term.svg000064400000000000000000000017451046102023000302750ustar 00000000000000 Creating binary (application) package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/bin_already_exists_implicit/in/src/main.rs000064400000000000000000000001061046102023000265450ustar 00000000000000fn main() { println!("Check that our file is not overwritten") } cargo-0.91.0/tests/testsuite/cargo_init/bin_already_exists_implicit/mod.rs000064400000000000000000000011701046102023000252050ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --vcs none") .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); } cargo-0.91.0/tests/testsuite/cargo_init/bin_already_exists_implicit/stderr.term.svg000064400000000000000000000017451046102023000270620ustar 00000000000000 Creating binary (application) package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/bin_already_exists_implicit_namenosrc/in/case.rs000064400000000000000000000001061046102023000300120ustar 00000000000000fn main() { println!("Check that our file is not overwritten") } cargo-0.91.0/tests/testsuite/cargo_init/bin_already_exists_implicit_namenosrc/mod.rs000064400000000000000000000012511046102023000272520ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --vcs none") .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); assert!(!project_root.join("src").is_dir()); } cargo-0.91.0/tests/testsuite/cargo_init/bin_already_exists_implicit_namenosrc/stderr.term.svg000064400000000000000000000017451046102023000311270ustar 00000000000000 Creating binary (application) package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/bin_already_exists_implicit_namesrc/in/src/case.rs000064400000000000000000000001061046102023000302440ustar 00000000000000fn main() { println!("Check that our file is not overwritten") } cargo-0.91.0/tests/testsuite/cargo_init/bin_already_exists_implicit_namesrc/mod.rs000064400000000000000000000012621046102023000267170ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --vcs none") .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); assert!(!project_root.join("src/main.rs").is_file()); } cargo-0.91.0/tests/testsuite/cargo_init/bin_already_exists_implicit_namesrc/stderr.term.svg000064400000000000000000000017451046102023000305720ustar 00000000000000 Creating binary (application) package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/bin_already_exists_implicit_nosrc/in/main.rs000064400000000000000000000001061046102023000271620ustar 00000000000000fn main() { println!("Check that our file is not overwritten") } cargo-0.91.0/tests/testsuite/cargo_init/bin_already_exists_implicit_nosrc/mod.rs000064400000000000000000000012511046102023000264110ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --vcs none") .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); assert!(!project_root.join("src").is_dir()); } cargo-0.91.0/tests/testsuite/cargo_init/bin_already_exists_implicit_nosrc/stderr.term.svg000064400000000000000000000017451046102023000302660ustar 00000000000000 Creating binary (application) package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/both_lib_and_bin/mod.rs000064400000000000000000000006641046102023000227060ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::paths; use cargo_test_support::str; #[cargo_test] fn case() { let cwd = paths::root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --lib --bin") .current_dir(&cwd) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert!(!cwd.join("Cargo.toml").is_file()); } cargo-0.91.0/tests/testsuite/cargo_init/both_lib_and_bin/stderr.term.svg000064400000000000000000000013761046102023000245540ustar 00000000000000 error: can't specify both lib and binary outputs cargo-0.91.0/tests/testsuite/cargo_init/cant_create_library_when_both_binlib_present/in/case.rs000064400000000000000000000000151046102023000313120ustar 00000000000000fn main() {} cargo-0.91.0/tests/testsuite/cargo_init/cant_create_library_when_both_binlib_present/in/lib.rs000064400000000000000000000000121046102023000311420ustar 00000000000000fn f() {} cargo-0.91.0/tests/testsuite/cargo_init/cant_create_library_when_both_binlib_present/mod.rs000064400000000000000000000007741046102023000305640ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --lib") .current_dir(project_root) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); } cargo-0.91.0/tests/testsuite/cargo_init/cant_create_library_when_both_binlib_present/stderr.term.svg000064400000000000000000000016741046102023000324310ustar 00000000000000 Creating library package error: cannot have a package with multiple libraries, found both `case.rs` and `lib.rs` cargo-0.91.0/tests/testsuite/cargo_init/confused_by_multiple_lib_files/in/lib.rs000064400000000000000000000000371046102023000262640ustar 00000000000000fn f() { println!("lib.rs"); } cargo-0.91.0/tests/testsuite/cargo_init/confused_by_multiple_lib_files/in/src/lib.rs000064400000000000000000000000431046102023000270500ustar 00000000000000fn f() { println!("src/lib.rs"); } cargo-0.91.0/tests/testsuite/cargo_init/confused_by_multiple_lib_files/mod.rs000064400000000000000000000012611046102023000256670ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --vcs none") .current_dir(project_root) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); assert!(!project_root.join("Cargo.toml").is_file()); } cargo-0.91.0/tests/testsuite/cargo_init/confused_by_multiple_lib_files/out/lib.rs000064400000000000000000000000371046102023000264650ustar 00000000000000fn f() { println!("lib.rs"); } cargo-0.91.0/tests/testsuite/cargo_init/confused_by_multiple_lib_files/out/src/lib.rs000064400000000000000000000000431046102023000272510ustar 00000000000000fn f() { println!("src/lib.rs"); } cargo-0.91.0/tests/testsuite/cargo_init/confused_by_multiple_lib_files/stderr.term.svg000064400000000000000000000014501046102023000275340ustar 00000000000000 error: cannot have a package with multiple libraries, found both `src/lib.rs` and `lib.rs` cargo-0.91.0/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/in/case.rs000064400000000000000000000000151046102023000303100ustar 00000000000000fn main() {} cargo-0.91.0/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/in/lib.rs000064400000000000000000000000121046102023000301400ustar 00000000000000fn f() {} cargo-0.91.0/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/mod.rs000064400000000000000000000011761046102023000275570ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --bin --vcs none") .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); } cargo-0.91.0/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/stderr.term.svg000064400000000000000000000017451046102023000314260ustar 00000000000000 Creating binary (application) package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/creates_binary_when_instructed_and_has_lib_file/in/case.rs000064400000000000000000000000121046102023000317600ustar 00000000000000fn f() {} cargo-0.91.0/tests/testsuite/cargo_init/creates_binary_when_instructed_and_has_lib_file/mod.rs000064400000000000000000000011761046102023000312320ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --bin --vcs none") .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); } ././@LongLink00006440000000000000000000000150000000000000007767Lustar cargo-0.91.0/tests/testsuite/cargo_init/creates_binary_when_instructed_and_has_lib_file/stderr.term.svgcargo-0.91.0/tests/testsuite/cargo_init/creates_binary_when_instructed_and_has_lib_file/stderr.term.000064400000000000000000000022601046102023000323520ustar 00000000000000 Creating binary (application) package warning: file `case.rs` seems to be a library file note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/creates_library_when_instructed_and_has_bin_file/in/case.rs000064400000000000000000000000151046102023000321450ustar 00000000000000fn main() {} cargo-0.91.0/tests/testsuite/cargo_init/creates_library_when_instructed_and_has_bin_file/mod.rs000064400000000000000000000011761046102023000314140ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --lib --vcs none") .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); } ././@LongLink00006440000000000000000000000151000000000000007770Lustar cargo-0.91.0/tests/testsuite/cargo_init/creates_library_when_instructed_and_has_bin_file/stderr.term.svgcargo-0.91.0/tests/testsuite/cargo_init/creates_library_when_instructed_and_has_bin_file/stderr.term000064400000000000000000000022601046102023000324560ustar 00000000000000 Creating library package warning: file `case.rs` seems to be a binary (application) file note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/empty_dir/mod.rs000064400000000000000000000003021046102023000214330ustar 00000000000000use cargo_test_support::compare::assert_ui; use crate::prelude::*; use cargo_test_support::{command_is_available, paths, Project}; use std::fs; use std::process::Command; use crate::test_root; cargo-0.91.0/tests/testsuite/cargo_init/explicit_bin_with_git/in/mod.rs000064400000000000000000000003021046102023000244140ustar 00000000000000use cargo_test_support::compare::assert_ui; use crate::prelude::*; use cargo_test_support::{command_is_available, paths, Project}; use std::fs; use std::process::Command; use crate::test_root; cargo-0.91.0/tests/testsuite/cargo_init/explicit_bin_with_git/mod.rs000064400000000000000000000011751046102023000240170ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --vcs git --bin") .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); } cargo-0.91.0/tests/testsuite/cargo_init/explicit_bin_with_git/stderr.term.svg000064400000000000000000000017451046102023000256670ustar 00000000000000 Creating binary (application) package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/formats_source/in/rustfmt.toml000064400000000000000000000000171046102023000243570ustar 00000000000000tab_spaces = 2 cargo-0.91.0/tests/testsuite/cargo_init/formats_source/mod.rs000064400000000000000000000021071046102023000224770ustar 00000000000000use crate::prelude::*; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; use cargo_test_support::{Project, process}; #[cargo_test] fn case() { // This cannot use `requires_rustfmt` because rustfmt is not available in // the rust-lang/rust environment. Additionally, if running cargo without // rustup (but with rustup installed), this test also fails due to HOME // preventing the proxy from choosing a toolchain. if let Err(e) = process("rustfmt").arg("-V").exec_with_output() { eprintln!("skipping test, rustfmt not available:\n{e:?}"); return; } let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --lib --vcs none") .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); } cargo-0.91.0/tests/testsuite/cargo_init/formats_source/stderr.term.svg000064400000000000000000000017301046102023000243450ustar 00000000000000 Creating library package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/fossil_autodetect/in/.fossil/.keep000064400000000000000000000000001046102023000247350ustar 00000000000000cargo-0.91.0/tests/testsuite/cargo_init/fossil_autodetect/mod.rs000064400000000000000000000012451046102023000231660ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --lib") .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); assert!(!project_root.join(".git").is_dir()); } cargo-0.91.0/tests/testsuite/cargo_init/fossil_autodetect/stderr.term.svg000064400000000000000000000017301046102023000250320ustar 00000000000000 Creating library package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/git_autodetect/mod.rs000064400000000000000000000014111046102023000224450ustar 00000000000000use crate::prelude::*; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::paths; use cargo_test_support::str; use std::fs; #[cargo_test] fn case() { let project_root = &paths::root().join("foo"); // Need to create `.git` dir manually because it cannot be tracked under a git repo fs::create_dir_all(project_root.join(".git")).unwrap(); snapbox::cmd::Command::cargo_ui() .arg_line("init --lib") .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); assert!(project_root.join(".git").is_dir()); } cargo-0.91.0/tests/testsuite/cargo_init/git_autodetect/stderr.term.svg000064400000000000000000000017301046102023000243160ustar 00000000000000 Creating library package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/git_ignore_exists_no_conflicting_entries/in/.gitignore000064400000000000000000000000141046102023000312410ustar 00000000000000**/some.filecargo-0.91.0/tests/testsuite/cargo_init/git_ignore_exists_no_conflicting_entries/mod.rs000064400000000000000000000012631046102023000277770ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --lib --edition 2015") .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); assert!(project_root.join(".git").is_dir()); } cargo-0.91.0/tests/testsuite/cargo_init/git_ignore_exists_no_conflicting_entries/stderr.term.svg000064400000000000000000000017301046102023000316430ustar 00000000000000 Creating library package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/help/mod.rs000064400000000000000000000004611046102023000203750ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("init") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_init/help/stdout.term.svg000064400000000000000000000126531046102023000222670ustar 00000000000000 Create a new cargo package in an existing directory Usage: cargo[EXE] init [OPTIONS] [PATH] Arguments: [PATH] [default: .] Options: --vcs <VCS> Initialize a new repository for the given version control system, overriding a global configuration. [possible values: git, hg, pijul, fossil, none] --bin Use a binary (application) template [default] --lib Use a library template --edition <YEAR> Edition to set for the crate generated [possible values: 2015, 2018, 2021, 2024] --name <NAME> Set the resulting package name, defaults to the directory name --registry <REGISTRY> Registry to use -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Manifest Options: --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help init` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_init/ignores_failure_to_format_source/in/rustfmt.toml000064400000000000000000000000171046102023000301330ustar 00000000000000tab_spaces = 2 cargo-0.91.0/tests/testsuite/cargo_init/ignores_failure_to_format_source/mod.rs000064400000000000000000000012741046102023000262570ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --lib --vcs none") .env("PATH", "") // pretend that `rustfmt` is missing .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); } cargo-0.91.0/tests/testsuite/cargo_init/ignores_failure_to_format_source/stderr.term.svg000064400000000000000000000017301046102023000301210ustar 00000000000000 Creating library package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/inferred_bin_with_git/in/main.rs000064400000000000000000000000151046102023000245370ustar 00000000000000fn main() {} cargo-0.91.0/tests/testsuite/cargo_init/inferred_bin_with_git/mod.rs000064400000000000000000000011671046102023000237750ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --vcs git") .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); } cargo-0.91.0/tests/testsuite/cargo_init/inferred_bin_with_git/stderr.term.svg000064400000000000000000000017451046102023000256440ustar 00000000000000 Creating binary (application) package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/inferred_lib_with_git/in/lib.rs000064400000000000000000000000121046102023000243540ustar 00000000000000fn f() {} cargo-0.91.0/tests/testsuite/cargo_init/inferred_lib_with_git/mod.rs000064400000000000000000000011671046102023000237730ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --vcs git") .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); } cargo-0.91.0/tests/testsuite/cargo_init/inferred_lib_with_git/stderr.term.svg000064400000000000000000000017451046102023000256420ustar 00000000000000 Creating binary (application) package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/inherit_workspace_package_table/mod.rs000064400000000000000000000012321046102023000260040ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("init") .args(["crates/foo"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_init/inherit_workspace_package_table/stderr.term.svg000064400000000000000000000017451046102023000276620ustar 00000000000000 Creating binary (application) package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/invalid_dir_name/mod.rs000064400000000000000000000007531046102023000227350ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::paths; use cargo_test_support::str; use std::fs; #[cargo_test] fn case() { let foo = &paths::root().join("foo.bar"); fs::create_dir_all(foo).unwrap(); snapbox::cmd::Command::cargo_ui() .arg_line("init") .current_dir(foo) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert!(!foo.join("Cargo.toml").is_file()); } cargo-0.91.0/tests/testsuite/cargo_init/invalid_dir_name/stderr.term.svg000064400000000000000000000033331046102023000245770ustar 00000000000000 Creating binary (application) package error: invalid character `.` in package name: `foo.bar`, characters must be Unicode XID characters (numbers, `-`, `_`, or most letters) If you need a package name to not match the directory name, consider using --name flag. If you need a binary with the name "foo.bar", use a valid package name, and set the binary name to be different from the package. This can be done by setting the binary filename to `src/bin/foo.bar.rs` or change the name in Cargo.toml with: [[bin]] name = "foo.bar" path = "src/main.rs" cargo-0.91.0/tests/testsuite/cargo_init/lib_already_exists_nosrc/in/lib.rs000064400000000000000000000000001046102023000251010ustar 00000000000000cargo-0.91.0/tests/testsuite/cargo_init/lib_already_exists_nosrc/mod.rs000064400000000000000000000012621046102023000245170ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --vcs none") .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); assert!(!project_root.join("src/main.rs").is_file()); } cargo-0.91.0/tests/testsuite/cargo_init/lib_already_exists_nosrc/stderr.term.svg000064400000000000000000000017451046102023000263720ustar 00000000000000 Creating binary (application) package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/lib_already_exists_src/in/src/lib.rs000064400000000000000000000000121046102023000253360ustar 00000000000000fn f() {} cargo-0.91.0/tests/testsuite/cargo_init/lib_already_exists_src/mod.rs000064400000000000000000000012621046102023000241620ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --vcs none") .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); assert!(!project_root.join("src/main.rs").is_file()); } cargo-0.91.0/tests/testsuite/cargo_init/lib_already_exists_src/stderr.term.svg000064400000000000000000000017451046102023000260350ustar 00000000000000 Creating binary (application) package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/mercurial_autodetect/in/.hg/.keep000064400000000000000000000000001046102023000245200ustar 00000000000000cargo-0.91.0/tests/testsuite/cargo_init/mercurial_autodetect/mod.rs000064400000000000000000000012451046102023000236520ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --lib") .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); assert!(!project_root.join(".git").is_dir()); } cargo-0.91.0/tests/testsuite/cargo_init/mercurial_autodetect/stderr.term.svg000064400000000000000000000017301046102023000255160ustar 00000000000000 Creating library package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/mod.rs000064400000000000000000000023601046102023000174450ustar 00000000000000//! Tests for the `cargo init` command. mod auto_git; mod bin_already_exists_explicit; mod bin_already_exists_explicit_nosrc; mod bin_already_exists_implicit; mod bin_already_exists_implicit_namenosrc; mod bin_already_exists_implicit_namesrc; mod bin_already_exists_implicit_nosrc; mod both_lib_and_bin; mod cant_create_library_when_both_binlib_present; mod confused_by_multiple_lib_files; mod creates_binary_when_both_binlib_present; mod creates_binary_when_instructed_and_has_lib_file; mod creates_library_when_instructed_and_has_bin_file; mod explicit_bin_with_git; mod formats_source; mod fossil_autodetect; mod git_autodetect; mod git_ignore_exists_no_conflicting_entries; mod help; mod ignores_failure_to_format_source; mod inferred_bin_with_git; mod inferred_lib_with_git; mod inherit_workspace_package_table; mod invalid_dir_name; mod lib_already_exists_nosrc; mod lib_already_exists_src; mod mercurial_autodetect; mod multibin_project_name_clash; #[cfg(not(windows))] mod no_filename; #[cfg(unix)] mod path_contains_separator; mod pijul_autodetect; mod reserved_name; mod simple_bin; mod simple_git; mod simple_git_ignore_exists; mod simple_hg; mod simple_hg_ignore_exists; mod simple_lib; mod unknown_flags; mod with_argument; mod workspace_add_member; cargo-0.91.0/tests/testsuite/cargo_init/multibin_project_name_clash/in/case.rs000064400000000000000000000000421046102023000257250ustar 00000000000000fn main() { println!("foo.rs"); } cargo-0.91.0/tests/testsuite/cargo_init/multibin_project_name_clash/in/main.rs000064400000000000000000000000431046102023000257370ustar 00000000000000fn main() { println!("main.rs"); } cargo-0.91.0/tests/testsuite/cargo_init/multibin_project_name_clash/mod.rs000064400000000000000000000012671046102023000251750ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --lib --vcs none") .current_dir(project_root) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); assert!(!project_root.join("Cargo.toml").is_file()); } cargo-0.91.0/tests/testsuite/cargo_init/multibin_project_name_clash/out/case.rs000064400000000000000000000000421046102023000261260ustar 00000000000000fn main() { println!("foo.rs"); } cargo-0.91.0/tests/testsuite/cargo_init/multibin_project_name_clash/out/main.rs000064400000000000000000000000431046102023000261400ustar 00000000000000fn main() { println!("main.rs"); } cargo-0.91.0/tests/testsuite/cargo_init/multibin_project_name_clash/stderr.term.svg000064400000000000000000000020001046102023000270240ustar 00000000000000 error: multiple possible binary sources found: main.rs case.rs cannot automatically generate Cargo.toml as the main target would be ambiguous cargo-0.91.0/tests/testsuite/cargo_init/no_filename/mod.rs000064400000000000000000000005711046102023000217230ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::paths; use cargo_test_support::str; #[cfg(not(windows))] #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg_line("init /") .current_dir(paths::root()) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); } cargo-0.91.0/tests/testsuite/cargo_init/no_filename/stderr.term.svg000064400000000000000000000014331046102023000235660ustar 00000000000000 error: cannot auto-detect package name from path "/" ; use --name to override cargo-0.91.0/tests/testsuite/cargo_init/path_contains_separator/in/.keep000064400000000000000000000000001046102023000245520ustar 00000000000000cargo-0.91.0/tests/testsuite/cargo_init/path_contains_separator/mod.rs000064400000000000000000000015021046102023000243540ustar 00000000000000use crate::prelude::*; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; use cargo_test_support::{Project, t}; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root().join("test:ing"); if !project_root.exists() { t!(std::fs::create_dir(&project_root)); } snapbox::cmd::Command::cargo_ui() .arg_line("init --bin --vcs none --edition 2015 --name testing") .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); assert!(!project_root.join(".gitignore").is_file()); } cargo-0.91.0/tests/testsuite/cargo_init/path_contains_separator/stderr.term.svg000064400000000000000000000025311046102023000262240ustar 00000000000000 Creating binary (application) package warning: the path `[ROOT]/case/test:ing/.` contains invalid PATH characters (usually `:`, `;`, or `"`) It is recommended to use a different name to avoid problems. note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/pijul_autodetect/in/.pijul/.keep000064400000000000000000000000001046102023000244050ustar 00000000000000cargo-0.91.0/tests/testsuite/cargo_init/pijul_autodetect/mod.rs000064400000000000000000000012451046102023000230120ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --lib") .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); assert!(!project_root.join(".git").is_dir()); } cargo-0.91.0/tests/testsuite/cargo_init/pijul_autodetect/stderr.term.svg000064400000000000000000000017301046102023000246560ustar 00000000000000 Creating library package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/reserved_name/mod.rs000064400000000000000000000010151046102023000222600ustar 00000000000000use std::fs; use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::paths; use cargo_test_support::str; #[cargo_test] fn case() { let project_root = &paths::root().join("test"); fs::create_dir_all(project_root).unwrap(); snapbox::cmd::Command::cargo_ui() .arg_line("init") .current_dir(project_root) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert!(!project_root.join("Cargo.toml").is_file()); } cargo-0.91.0/tests/testsuite/cargo_init/reserved_name/stderr.term.svg000064400000000000000000000032621046102023000241330ustar 00000000000000 Creating binary (application) package error: the name `test` cannot be used as a package name, it conflicts with Rust's built-in test library If you need a package name to not match the directory name, consider using --name flag. If you need a binary with the name "test", use a valid package name, and set the binary name to be different from the package. This can be done by setting the binary filename to `src/bin/test.rs` or change the name in Cargo.toml with: [[bin]] name = "test" path = "src/main.rs" cargo-0.91.0/tests/testsuite/cargo_init/simple_bin/in/mod.rs000064400000000000000000000003021046102023000221660ustar 00000000000000use cargo_test_support::compare::assert_ui; use crate::prelude::*; use cargo_test_support::{command_is_available, paths, Project}; use std::fs; use std::process::Command; use crate::test_root; cargo-0.91.0/tests/testsuite/cargo_init/simple_bin/mod.rs000064400000000000000000000015701046102023000215700ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --bin --vcs none --edition 2015") .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); assert!(!project_root.join(".gitignore").is_file()); snapbox::cmd::Command::cargo_ui() .current_dir(project_root) .arg("build") .assert() .success(); assert!(project.bin("case").is_file()); } cargo-0.91.0/tests/testsuite/cargo_init/simple_bin/stderr.term.svg000064400000000000000000000017451046102023000234410ustar 00000000000000 Creating binary (application) package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/simple_git/in/mod.rs000064400000000000000000000003021046102023000222010ustar 00000000000000use cargo_test_support::compare::assert_ui; use crate::prelude::*; use cargo_test_support::{command_is_available, paths, Project}; use std::fs; use std::process::Command; use crate::test_root; cargo-0.91.0/tests/testsuite/cargo_init/simple_git/mod.rs000064400000000000000000000012561046102023000216040ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --lib --vcs git") .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); assert!(project_root.join(".git").is_dir()); } cargo-0.91.0/tests/testsuite/cargo_init/simple_git/stderr.term.svg000064400000000000000000000017301046102023000234460ustar 00000000000000 Creating library package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/simple_git_ignore_exists/in/.gitignore000064400000000000000000000000241046102023000260070ustar 00000000000000/target **/some.filecargo-0.91.0/tests/testsuite/cargo_init/simple_git_ignore_exists/mod.rs000064400000000000000000000014711046102023000245450ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --lib --edition 2015") .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); assert!(project_root.join(".git").is_dir()); snapbox::cmd::Command::cargo_ui() .current_dir(project_root) .arg("build") .assert() .success(); } cargo-0.91.0/tests/testsuite/cargo_init/simple_git_ignore_exists/stderr.term.svg000064400000000000000000000017301046102023000264100ustar 00000000000000 Creating library package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/simple_hg/in/mod.rs000064400000000000000000000003021046102023000220140ustar 00000000000000use cargo_test_support::compare::assert_ui; use crate::prelude::*; use cargo_test_support::{command_is_available, paths, Project}; use std::fs; use std::process::Command; use crate::test_root; cargo-0.91.0/tests/testsuite/cargo_init/simple_hg/mod.rs000064400000000000000000000012771046102023000214220ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test(requires = "hg")] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --lib --vcs hg") .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); assert!(!project_root.join(".git").is_dir()); } cargo-0.91.0/tests/testsuite/cargo_init/simple_hg/stderr.term.svg000064400000000000000000000017301046102023000232610ustar 00000000000000 Creating library package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/simple_hg_ignore_exists/in/.hg/.keep000064400000000000000000000000001046102023000252250ustar 00000000000000cargo-0.91.0/tests/testsuite/cargo_init/simple_hg_ignore_exists/in/.hgignore000064400000000000000000000000121046102023000254320ustar 00000000000000^/somefilecargo-0.91.0/tests/testsuite/cargo_init/simple_hg_ignore_exists/mod.rs000064400000000000000000000012451046102023000243570ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --lib") .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); assert!(!project_root.join(".git").is_dir()); } cargo-0.91.0/tests/testsuite/cargo_init/simple_hg_ignore_exists/stderr.term.svg000064400000000000000000000017301046102023000262230ustar 00000000000000 Creating library package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/simple_lib/in/mod.rs000064400000000000000000000003021046102023000221640ustar 00000000000000use cargo_test_support::compare::assert_ui; use crate::prelude::*; use cargo_test_support::{command_is_available, paths, Project}; use std::fs; use std::process::Command; use crate::test_root; cargo-0.91.0/tests/testsuite/cargo_init/simple_lib/mod.rs000064400000000000000000000015701046102023000215660ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --lib --vcs none --edition 2015") .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); assert!(!project_root.join(".gitignore").is_file()); snapbox::cmd::Command::cargo_ui() .current_dir(project_root) .arg("build") .assert() .success(); assert!(!project.bin("foo").is_file()); } cargo-0.91.0/tests/testsuite/cargo_init/simple_lib/stderr.term.svg000064400000000000000000000017301046102023000234310ustar 00000000000000 Creating library package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/unknown_flags/mod.rs000064400000000000000000000005531046102023000223220ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::paths; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg_line("init foo --flag") .current_dir(paths::root()) .assert() .code(1) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); } cargo-0.91.0/tests/testsuite/cargo_init/unknown_flags/stderr.term.svg000064400000000000000000000031121046102023000241610ustar 00000000000000 error: unexpected argument '--flag' found tip: to pass '--flag' as a value, use '-- --flag' Usage: cargo[EXE] init <PATH> For more information, try '--help'. cargo-0.91.0/tests/testsuite/cargo_init/with_argument/in/foo/.keep000064400000000000000000000000001046102023000233000ustar 00000000000000cargo-0.91.0/tests/testsuite/cargo_init/with_argument/mod.rs000064400000000000000000000011741046102023000223240ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init foo --vcs none") .current_dir(project_root) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), project_root); } cargo-0.91.0/tests/testsuite/cargo_init/with_argument/stderr.term.svg000064400000000000000000000017451046102023000241750ustar 00000000000000 Creating binary (application) package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_init/workspace_add_member/mod.rs000064400000000000000000000012321046102023000235770ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = &project.root(); snapbox::cmd::Command::cargo_ui() .arg_line("init --bin --vcs none") .current_dir(project_root.join("crates").join("foo")) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_init/workspace_add_member/stderr.term.svg000064400000000000000000000021721046102023000254500ustar 00000000000000 Creating binary (application) package Adding `foo` as member of workspace at `[ROOT]/case` note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_install/help/mod.rs000064400000000000000000000004641046102023000211030ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("install") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_install/help/stdout.term.svg000064400000000000000000000264451046102023000227760ustar 00000000000000 Install a Rust binary Usage: cargo[EXE] install [OPTIONS] [CRATE[@<VER>]]... Arguments: [CRATE[@<VER>]]... Select the package from the given source Options: --version <VERSION> Specify a version to install --index <INDEX> Registry index to install from --registry <REGISTRY> Registry to use --git <URL> Git URL to install the specified crate from --branch <BRANCH> Branch to use when installing from git --tag <TAG> Tag to use when installing from git --rev <SHA> Specific commit to use when installing from git --path <PATH> Filesystem path to local crate to install from --root <DIR> Directory to install packages into -f, --force Force overwriting existing crates or binaries -n, --dry-run Perform all checks without installing (unstable) --no-track Do not save tracking information --list List all installed packages and their versions --message-format <FMT> Error format [possible values: human, short, json, json-diagnostic-short, json-diagnostic-rendered-ansi, json-render-diagnostics] --debug Build in debug mode (with the 'dev' profile) instead of release mode -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Manifest Options: --ignore-rust-version Ignore `rust-version` specification in packages --lockfile-path <PATH> Path to Cargo.lock (unstable) --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Target Selection: --bin [<NAME>] Install only the specified binary --bins Install all binaries --example [<NAME>] Install only the specified example --examples Install all examples Feature Selection: -F, --features <FEATURES> Space or comma separated list of features to activate --all-features Activate all available features --no-default-features Do not activate the `default` feature Compilation Options: -j, --jobs <N> Number of parallel jobs, defaults to # of CPUs. --keep-going Do not abort the build as soon as there is an error --profile <PROFILE-NAME> Install artifacts with the specified profile --target [<TRIPLE>] Build for the target triple --target-dir <DIRECTORY> Directory for all generated artifacts --timings[=<FMTS>] Timing output formats (unstable) (comma separated): html, json Run `cargo help install` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_install/mod.rs000064400000000000000000000000121046102023000201400ustar 00000000000000mod help; cargo-0.91.0/tests/testsuite/cargo_locate_project/help/mod.rs000064400000000000000000000004731046102023000224320ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("locate-project") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_locate_project/help/stdout.term.svg000064400000000000000000000102071046102023000243120ustar 00000000000000 Print a JSON representation of a Cargo.toml file's location Usage: cargo[EXE] locate-project [OPTIONS] Options: --workspace Locate Cargo.toml of the workspace root --message-format <FMT> Output representation [possible values: json, plain] -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Manifest Options: --manifest-path <PATH> Path to Cargo.toml --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help locate-project` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_locate_project/mod.rs000064400000000000000000000000121046102023000214670ustar 00000000000000mod help; cargo-0.91.0/tests/testsuite/cargo_login/help/mod.rs000064400000000000000000000004621046102023000205430ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("login") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_login/help/stdout.term.svg000064400000000000000000000100501046102023000224210ustar 00000000000000 Log in to a registry. Usage: cargo[EXE] login [OPTIONS] [-- [args]...] Arguments: [args]... Additional arguments for the credential provider Options: --registry <REGISTRY> Registry to use -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Manifest Options: --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help login` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_login/mod.rs000064400000000000000000000000121046102023000176020ustar 00000000000000mod help; cargo-0.91.0/tests/testsuite/cargo_logout/help/mod.rs000064400000000000000000000004631046102023000207450ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("logout") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_logout/help/stdout.term.svg000064400000000000000000000072151046102023000226330ustar 00000000000000 Remove an API token from the registry locally Usage: cargo[EXE] logout [OPTIONS] Options: --registry <REGISTRY> Registry to use -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Manifest Options: --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help logout` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_logout/mod.rs000064400000000000000000000000121046102023000200030ustar 00000000000000mod help; cargo-0.91.0/tests/testsuite/cargo_metadata/help/mod.rs000064400000000000000000000004651046102023000212160ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("metadata") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_metadata/help/stdout.term.svg000064400000000000000000000131571046102023000231040ustar 00000000000000 Output the resolved dependencies of a package, the concrete used versions including overrides, in machine-readable format Usage: cargo[EXE] metadata [OPTIONS] Options: --filter-platform <TRIPLE> Only include resolve dependencies matching the given target-triple --no-deps Output information only about the workspace members and don't fetch dependencies --format-version <VERSION> Format version [possible values: 1] -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Feature Selection: -F, --features <FEATURES> Space or comma separated list of features to activate --all-features Activate all available features --no-default-features Do not activate the `default` feature Manifest Options: --manifest-path <PATH> Path to Cargo.toml --lockfile-path <PATH> Path to Cargo.lock (unstable) --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help metadata` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_metadata/mod.rs000064400000000000000000000000121046102023000202520ustar 00000000000000mod help; cargo-0.91.0/tests/testsuite/cargo_new/add_members_to_non_workspace/mod.rs000064400000000000000000000012331046102023000251650ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("new") .args(["bar", "--lib"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_new/add_members_to_non_workspace/stderr.term.svg000064400000000000000000000017361046102023000270420ustar 00000000000000 Creating library `bar` package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/mod.rs000064400000000000000000000012311046102023000310160ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("new") .args(["crates/foo"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } ././@LongLink00006440000000000000000000000146000000000000007774Lustar cargo-0.91.0/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/stderr.term.svgcargo-0.91.0/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/stderr.term.sv000064400000000000000000000022001046102023000325110ustar 00000000000000 Creating binary (application) `foo` package Adding `foo` as member of workspace at `[ROOT]/case` note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/mod.rs000064400000000000000000000012311046102023000272410ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("new") .args(["crates/foo"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/stderr.term.svg000064400000000000000000000022001046102023000311030ustar 00000000000000 Creating binary (application) `foo` package Adding `foo` as member of workspace at `[ROOT]/case` note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/mod.rs000064400000000000000000000013201046102023000317300ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; let package_path = cwd.join("crates").join("foo"); snapbox::cmd::Command::cargo_ui() .arg("new") .args([package_path]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } ././@LongLink00006440000000000000000000000153000000000000007772Lustar cargo-0.91.0/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/stderr.term.svgcargo-0.91.0/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/stderr.te000064400000000000000000000022001046102023000324360ustar 00000000000000 Creating binary (application) `foo` package Adding `foo` as member of workspace at `[ROOT]/case` note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/mod.rs000064400000000000000000000012311046102023000302740ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("new") .args(["crates/foo"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/stderr.term.svg000064400000000000000000000022001046102023000321360ustar 00000000000000 Creating binary (application) `foo` package Adding `foo` as member of workspace at `[ROOT]/case` note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/mod.rs000064400000000000000000000012311046102023000301100ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("new") .args(["crates/foo"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/stderr.term.svg000064400000000000000000000017531046102023000317660ustar 00000000000000 Creating binary (application) `foo` package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/mod.rs000064400000000000000000000012311046102023000300610ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("new") .args(["crates/foo"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/stderr.term.svg000064400000000000000000000017531046102023000317370ustar 00000000000000 Creating binary (application) `foo` package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_new/add_members_to_workspace_without_members/mod.rs000064400000000000000000000012331046102023000276100ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("new") .args(["bar", "--lib"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_new/add_members_to_workspace_without_members/stderr.term.svg000064400000000000000000000021631046102023000314600ustar 00000000000000 Creating library `bar` package Adding `bar` as member of workspace at `[ROOT]/case` note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_new/empty_name/in/.keep000064400000000000000000000000001046102023000216240ustar 00000000000000cargo-0.91.0/tests/testsuite/cargo_new/empty_name/mod.rs000064400000000000000000000012401046102023000214250ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("new") .args(["foo", "--name", ""]) .current_dir(cwd) .assert() .failure() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_new/empty_name/out/.keep000064400000000000000000000000001046102023000220250ustar 00000000000000cargo-0.91.0/tests/testsuite/cargo_new/empty_name/stderr.term.svg000064400000000000000000000016301046102023000232750ustar 00000000000000 Creating binary (application) `` package error: package name cannot be empty cargo-0.91.0/tests/testsuite/cargo_new/help/mod.rs000064400000000000000000000004601046102023000202220ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("new") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_new/help/stdout.term.svg000064400000000000000000000126401046102023000221110ustar 00000000000000 Create a new cargo package at <path> Usage: cargo[EXE] new [OPTIONS] <PATH> Arguments: <PATH> Options: --vcs <VCS> Initialize a new repository for the given version control system, overriding a global configuration. [possible values: git, hg, pijul, fossil, none] --bin Use a binary (application) template [default] --lib Use a library template --edition <YEAR> Edition to set for the crate generated [possible values: 2015, 2018, 2021, 2024] --name <NAME> Set the resulting package name, defaults to the directory name --registry <REGISTRY> Registry to use -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Manifest Options: --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help new` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_new/ignore_current_dir_workspace/mod.rs000064400000000000000000000012751046102023000252400ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root.join("workspace"); snapbox::cmd::Command::cargo_ui() .arg("new") .args(["../out-of-workspace", "--lib"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_new/ignore_current_dir_workspace/stderr.term.svg000064400000000000000000000017531046102023000271060ustar 00000000000000 Creating library `out-of-workspace` package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_new/inherit_workspace_lints/mod.rs000064400000000000000000000012311046102023000242200ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("new") .args(["crates/foo"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_new/inherit_workspace_lints/stderr.term.svg000064400000000000000000000017531046102023000260760ustar 00000000000000 Creating binary (application) `foo` package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_new/inherit_workspace_package_table/mod.rs000064400000000000000000000012311046102023000256310ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("new") .args(["crates/foo"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_new/inherit_workspace_package_table/stderr.term.svg000064400000000000000000000017531046102023000275070ustar 00000000000000 Creating binary (application) `foo` package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_new/inherit_workspace_package_table_with_edition/mod.rs000064400000000000000000000012561046102023000304060ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("new") .args(["crates/foo", "--edition", "2021"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_new/inherit_workspace_package_table_with_edition/stderr.term.svg000064400000000000000000000017531046102023000322550ustar 00000000000000 Creating binary (application) `foo` package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_new/inherit_workspace_package_table_with_registry/mod.rs000064400000000000000000000012561046102023000306230ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("new") .args(["crates/foo", "--registry", "foo"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_new/inherit_workspace_package_table_with_registry/stderr.term.svg000064400000000000000000000017531046102023000324720ustar 00000000000000 Creating binary (application) `foo` package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_new/inherit_workspace_package_table_without_version/mod.rs000064400000000000000000000012311046102023000311610ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("new") .args(["crates/foo"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } ././@LongLink00006440000000000000000000000147000000000000007775Lustar cargo-0.91.0/tests/testsuite/cargo_new/inherit_workspace_package_table_without_version/stderr.term.svgcargo-0.91.0/tests/testsuite/cargo_new/inherit_workspace_package_table_without_version/stderr.term.s000064400000000000000000000017531046102023000325020ustar 00000000000000 Creating binary (application) `foo` package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_new/mod.rs000064400000000000000000000013121046102023000172670ustar 00000000000000mod add_members_to_non_workspace; mod add_members_to_workspace_format_previous_items; mod add_members_to_workspace_format_sorted; mod add_members_to_workspace_with_absolute_package_path; mod add_members_to_workspace_with_empty_members; mod add_members_to_workspace_with_exclude_list; mod add_members_to_workspace_with_members_glob; mod add_members_to_workspace_without_members; mod empty_name; mod help; mod ignore_current_dir_workspace; mod inherit_workspace_lints; mod inherit_workspace_package_table; mod inherit_workspace_package_table_with_edition; mod inherit_workspace_package_table_with_registry; mod inherit_workspace_package_table_without_version; mod not_inherit_workspace_package_table_if_not_members; cargo-0.91.0/tests/testsuite/cargo_new/not_inherit_workspace_package_table_if_not_members/mod.rs000064400000000000000000000012221046102023000315610ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("new") .args(["bar"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } ././@LongLink00006440000000000000000000000152000000000000007771Lustar cargo-0.91.0/tests/testsuite/cargo_new/not_inherit_workspace_package_table_if_not_members/stderr.term.svgcargo-0.91.0/tests/testsuite/cargo_new/not_inherit_workspace_package_table_if_not_members/stderr.ter000064400000000000000000000017531046102023000324640ustar 00000000000000 Creating binary (application) `bar` package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html cargo-0.91.0/tests/testsuite/cargo_owner/help/mod.rs000064400000000000000000000004621046102023000205650ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("owner") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_owner/help/stdout.term.svg000064400000000000000000000123261046102023000224530ustar 00000000000000 Manage the owners of a crate on the registry Usage: cargo[EXE] owner [OPTIONS] [CRATE] Arguments: [CRATE] Options: -a, --add <LOGIN> Name of a user or team to invite as an owner -r, --remove <LOGIN> Name of a user or team to remove as an owner -l, --list List owners of a crate --index <INDEX> Registry index URL to modify owners for --registry <REGISTRY> Registry to modify owners for --token <TOKEN> API token to use when authenticating -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Manifest Options: --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help owner` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_owner/mod.rs000064400000000000000000000000121046102023000176240ustar 00000000000000mod help; cargo-0.91.0/tests/testsuite/cargo_package/help/mod.rs000064400000000000000000000004641046102023000210300ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("package") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_package/help/stdout.term.svg000064400000000000000000000205011046102023000227060ustar 00000000000000 Assemble the local package into a distributable tarball Usage: cargo[EXE] package [OPTIONS] Options: --index <INDEX> Registry index URL to prepare the package for --registry <REGISTRY> Registry to prepare the package for -l, --list Print files included in a package without making one --no-verify Don't verify the contents by building them --no-metadata Ignore warnings about a lack of human-usable metadata --allow-dirty Allow dirty working directories to be packaged --exclude-lockfile Don't include the lock file when packaging --message-format <FMT> Output representation (unstable) [possible values: human, json] -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Package Selection: -p, --package [<SPEC>] Package(s) to assemble --workspace Assemble all packages in the workspace --exclude <SPEC> Don't assemble specified packages Feature Selection: -F, --features <FEATURES> Space or comma separated list of features to activate --all-features Activate all available features --no-default-features Do not activate the `default` feature Compilation Options: --target [<TRIPLE>] Build for the target triple --target-dir <DIRECTORY> Directory for all generated artifacts -j, --jobs <N> Number of parallel jobs, defaults to # of CPUs. --keep-going Do not abort the build as soon as there is an error Manifest Options: --manifest-path <PATH> Path to Cargo.toml --lockfile-path <PATH> Path to Cargo.lock (unstable) --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help package` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_package/mod.rs000064400000000000000000000000121046102023000200650ustar 00000000000000mod help; cargo-0.91.0/tests/testsuite/cargo_pkgid/help/mod.rs000064400000000000000000000004621046102023000205310ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("pkgid") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_pkgid/help/stdout.term.svg000064400000000000000000000112131046102023000224110ustar 00000000000000 Print a fully qualified package specification Usage: cargo[EXE] pkgid [OPTIONS] [SPEC] Arguments: [SPEC] Options: -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Package Selection: -p, --package [<SPEC>] Argument to get the package ID specifier for Manifest Options: --manifest-path <PATH> Path to Cargo.toml --lockfile-path <PATH> Path to Cargo.lock (unstable) --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help pkgid` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_pkgid/mod.rs000064400000000000000000000000121046102023000175700ustar 00000000000000mod help; cargo-0.91.0/tests/testsuite/cargo_publish/help/mod.rs000064400000000000000000000004641046102023000211030ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("publish") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_publish/help/stdout.term.svg000064400000000000000000000175721046102023000227770ustar 00000000000000 Upload a package to the registry Usage: cargo[EXE] publish [OPTIONS] Options: -n, --dry-run Perform all checks without uploading --index <INDEX> Registry index URL to upload the package to --registry <REGISTRY> Registry to upload the package to --token <TOKEN> Token to use when uploading --no-verify Don't verify the contents by building them --allow-dirty Allow dirty working directories to be packaged -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Package Selection: -p, --package [<SPEC>] Package(s) to publish --workspace Publish all packages in the workspace --exclude <SPEC> Don't publish specified packages Feature Selection: -F, --features <FEATURES> Space or comma separated list of features to activate --all-features Activate all available features --no-default-features Do not activate the `default` feature Compilation Options: -j, --jobs <N> Number of parallel jobs, defaults to # of CPUs. --keep-going Do not abort the build as soon as there is an error --target [<TRIPLE>] Build for the target triple --target-dir <DIRECTORY> Directory for all generated artifacts Manifest Options: --manifest-path <PATH> Path to Cargo.toml --lockfile-path <PATH> Path to Cargo.lock (unstable) --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help publish` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_publish/mod.rs000064400000000000000000000000121046102023000201400ustar 00000000000000mod help; cargo-0.91.0/tests/testsuite/cargo_read_manifest/help/mod.rs000064400000000000000000000004721046102023000222350ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("read-manifest") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_read_manifest/help/stdout.term.svg000064400000000000000000000073001046102023000241160ustar 00000000000000 DEPRECATED: Print a JSON representation of a Cargo.toml manifest. Use `cargo metadata --no-deps` instead. Usage: cargo[EXE] read-manifest [OPTIONS] Options: -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Manifest Options: --manifest-path <PATH> Path to Cargo.toml --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline cargo-0.91.0/tests/testsuite/cargo_read_manifest/mod.rs000064400000000000000000000000121046102023000212730ustar 00000000000000mod help; cargo-0.91.0/tests/testsuite/cargo_remove/avoid_empty_tables/mod.rs000064400000000000000000000025471046102023000236600ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["clippy"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/avoid_empty_tables/stderr.term.svg000064400000000000000000000013331046102023000255150ustar 00000000000000 Removing clippy from dependencies cargo-0.91.0/tests/testsuite/cargo_remove/build/mod.rs000064400000000000000000000025621046102023000211020ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["--build", "semver"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/build/stderr.term.svg000064400000000000000000000013411046102023000227410ustar 00000000000000 Removing semver from build-dependencies cargo-0.91.0/tests/testsuite/cargo_remove/dev/mod.rs000064400000000000000000000025571046102023000205650ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["--dev", "regex"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/dev/stderr.term.svg000064400000000000000000000013361046102023000224240ustar 00000000000000 Removing regex from dev-dependencies cargo-0.91.0/tests/testsuite/cargo_remove/dry_run/mod.rs000064400000000000000000000025641046102023000214670ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["semver", "--dry-run"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/dry_run/stderr.term.svg000064400000000000000000000016331046102023000233300ustar 00000000000000 Removing semver from dependencies warning: aborting remove due to dry run cargo-0.91.0/tests/testsuite/cargo_remove/gc_keep_used_patch/mod.rs000064400000000000000000000016171046102023000235770ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("serde", "1.0.0").publish(); cargo_test_support::registry::Package::new("serde_json", "1.0.0") .dep("serde", "1.0.0") .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); snapbox::cmd::Command::cargo_ui() .current_dir(&project_root) .arg("remove") .args(["--package", "serde", "serde_derive"]) .assert() .code(0) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/gc_keep_used_patch/stderr.term.svg000064400000000000000000000013411046102023000254360ustar 00000000000000 Removing serde_derive from dependencies cargo-0.91.0/tests/testsuite/cargo_remove/gc_patch/mod.rs000064400000000000000000000043641046102023000215550ustar 00000000000000use crate::prelude::*; use cargo_test_support::basic_manifest; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::git; use cargo_test_support::project; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); let git_project1 = git::new("bar1", |project| { project .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "") }) .url(); let git_project2 = git::new("bar2", |project| { project .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "") }) .url(); let git_project3 = git::new("bar3", |project| { project .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "") }) .url(); let in_project = project() .file( "Cargo.toml", &format!( "[workspace]\n\ members = [ \"my-member\" ]\n\ \n\ [package]\n\ name = \"my-project\"\n\ version = \"0.1.0\"\n\ edition = \"2015\"\n\ \n\ [dependencies]\n\ bar = {{ git = \"{git_project1}\" }}\n\ \n\ [patch.\"{git_project1}\"]\n\ bar = {{ git = \"{git_project3}\" }}\n\ \n\ [patch.crates-io]\n\ bar = {{ git = \"{git_project2}\" }}\n", ), ) .file("src/lib.rs", "") .file( "my-member/Cargo.toml", "[package]\n\ name = \"my-member\"\n\ version = \"0.1.0\"\n\ \n\ [dependencies]\n\ bar = \"0.1.0\"\n", ) .file("my-member/src/lib.rs", "") .build(); snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["bar"]) .current_dir(&in_project.root()) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &in_project.root()); } cargo-0.91.0/tests/testsuite/cargo_remove/gc_patch/stderr.term.svg000064400000000000000000000013301046102023000234100ustar 00000000000000 Removing bar from dependencies cargo-0.91.0/tests/testsuite/cargo_remove/gc_profile/mod.rs000064400000000000000000000026731046102023000221170ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.2.3+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["toml"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/gc_profile/stderr.term.svg000064400000000000000000000013311046102023000237520ustar 00000000000000 Removing toml from dependencies cargo-0.91.0/tests/testsuite/cargo_remove/gc_replace/mod.rs000064400000000000000000000027261046102023000220710ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.2.3+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["--package", "my-package", "toml"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/gc_replace/stderr.term.svg000064400000000000000000000013311046102023000237250ustar 00000000000000 Removing toml from dependencies cargo-0.91.0/tests/testsuite/cargo_remove/help/mod.rs000064400000000000000000000004631046102023000207310ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("remove") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_remove/help/stdout.term.svg000064400000000000000000000130571046102023000226200ustar 00000000000000 Remove dependencies from a Cargo.toml manifest file Usage: cargo[EXE] remove [OPTIONS] <DEP_ID>... Arguments: <DEP_ID>... Dependencies to be removed Options: -n, --dry-run Don't actually write the manifest -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Section: --dev Remove from dev-dependencies --build Remove from build-dependencies --target <TARGET> Remove from target-dependencies Package Selection: -p, --package [<SPEC>] Package to remove from Manifest Options: --manifest-path <PATH> Path to Cargo.toml --lockfile-path <PATH> Path to Cargo.lock (unstable) --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help remove` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_remove/invalid_arg/mod.rs000064400000000000000000000025541046102023000222630ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["foo", "--flag"]) .current_dir(cwd) .assert() .code(1) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/invalid_arg/stderr.term.svg000064400000000000000000000031211046102023000241170ustar 00000000000000 error: unexpected argument '--flag' found tip: to pass '--flag' as a value, use '-- --flag' Usage: cargo[EXE] remove <DEP_ID>... For more information, try '--help'. cargo-0.91.0/tests/testsuite/cargo_remove/invalid_dep/mod.rs000064400000000000000000000022041046102023000222520ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("invalid-dependency-name", "0.6.2+my-package") .publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["invalid_dependency_name"]) .current_dir(cwd) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/invalid_dep/stderr.term.svg000064400000000000000000000020011046102023000241120ustar 00000000000000 Removing invalid_dependency_name from dependencies error: the dependency `invalid_dependency_name` could not be found in `dependencies`; dependency `invalid-dependency-name` exists cargo-0.91.0/tests/testsuite/cargo_remove/invalid_package/mod.rs000064400000000000000000000030551046102023000231020ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("dbus", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("ncurses", "20.0.0+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["docopt", "--package", "dep-c"]) .current_dir(cwd) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/invalid_package/stderr.term.svg000064400000000000000000000014141046102023000247440ustar 00000000000000 error: package(s) `dep-c` not found in workspace `[ROOT]/case` cargo-0.91.0/tests/testsuite/cargo_remove/invalid_package_multiple/mod.rs000064400000000000000000000030271046102023000250140ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("dbus", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("ncurses", "20.0.0+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["docopt"]) .current_dir(cwd) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/invalid_package_multiple/stderr.term.svg000064400000000000000000000016301046102023000266570ustar 00000000000000 error: `cargo remove` could not determine which package to modify. Use the `--package` option to specify a package. available packages: dep-a, dep-b cargo-0.91.0/tests/testsuite/cargo_remove/invalid_section/mod.rs000064400000000000000000000025621046102023000231550ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["--build", "docopt"]) .current_dir(cwd) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/invalid_section/stderr.term.svg000064400000000000000000000017361046102023000250240ustar 00000000000000 Removing docopt from build-dependencies error: the dependency `docopt` could not be found in `build-dependencies`; it is present in `dependencies` cargo-0.91.0/tests/testsuite/cargo_remove/invalid_section_dep/mod.rs000064400000000000000000000025711046102023000240050ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["--dev", "semver", "regex"]) .current_dir(cwd) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/invalid_section_dep/stderr.term.svg000064400000000000000000000017321046102023000256500ustar 00000000000000 Removing semver from dev-dependencies error: the dependency `semver` could not be found in `dev-dependencies`; it is present in `dependencies` cargo-0.91.0/tests/testsuite/cargo_remove/invalid_target/mod.rs000064400000000000000000000030761046102023000230000ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("dbus", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("ncurses", "20.0.0+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["--target", "powerpc-unknown-linux-gnu", "dbus"]) .current_dir(cwd) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/invalid_target/stderr.term.svg000064400000000000000000000020651046102023000246420ustar 00000000000000 Removing dbus from dependencies for target `powerpc-unknown-linux-gnu` error: the dependency `dbus` could not be found in `target.powerpc-unknown-linux-gnu.dependencies`; it is present in `target.wasm32-unknown-unknown.dependencies` cargo-0.91.0/tests/testsuite/cargo_remove/invalid_target_dep/mod.rs000064400000000000000000000030731046102023000236250ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("dbus", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("ncurses", "20.0.0+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["--target", "wasm32-unknown-unknown", "toml"]) .current_dir(cwd) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/invalid_target_dep/stderr.term.svg000064400000000000000000000020211046102023000254620ustar 00000000000000 Removing toml from dependencies for target `wasm32-unknown-unknown` error: the dependency `toml` could not be found in `target.wasm32-unknown-unknown.dependencies`; it is present in `dependencies` cargo-0.91.0/tests/testsuite/cargo_remove/last_dep/mod.rs000064400000000000000000000014331046102023000215720ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["docopt"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/last_dep/stderr.term.svg000064400000000000000000000013331046102023000234360ustar 00000000000000 Removing docopt from dependencies cargo-0.91.0/tests/testsuite/cargo_remove/mod.rs000064400000000000000000000012661046102023000200030ustar 00000000000000mod avoid_empty_tables; mod build; mod dev; mod dry_run; mod gc_keep_used_patch; mod gc_patch; mod gc_profile; mod gc_replace; mod help; mod invalid_arg; mod invalid_dep; mod invalid_package; mod invalid_package_multiple; mod invalid_section; mod invalid_section_dep; mod invalid_target; mod invalid_target_dep; mod last_dep; mod multiple_deps; mod multiple_dev; mod no_arg; mod offline; mod optional_dep_feature; mod optional_dep_feature_formatting; mod optional_feature; mod package; mod remove_basic; mod script; mod script_last; mod skip_gc_glob_profile; mod target; mod target_build; mod target_dev; mod update_lock_file; mod workspace; mod workspace_non_virtual; mod workspace_preserved; cargo-0.91.0/tests/testsuite/cargo_remove/multiple_deps/mod.rs000064400000000000000000000025611046102023000226500ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["docopt", "semver"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/multiple_deps/stderr.term.svg000064400000000000000000000015331046102023000245130ustar 00000000000000 Removing docopt from dependencies Removing semver from dependencies cargo-0.91.0/tests/testsuite/cargo_remove/multiple_dev/mod.rs000064400000000000000000000025701046102023000224730ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["--dev", "regex", "serde"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/multiple_dev/stderr.term.svg000064400000000000000000000015411046102023000243350ustar 00000000000000 Removing regex from dev-dependencies Removing serde from dev-dependencies cargo-0.91.0/tests/testsuite/cargo_remove/no_arg/mod.rs000064400000000000000000000025131046102023000212440ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .current_dir(cwd) .assert() .code(1) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/no_arg/stderr.term.svg000064400000000000000000000025151046102023000231130ustar 00000000000000 error: the following required arguments were not provided: <DEP_ID>... Usage: cargo[EXE] remove <DEP_ID>... For more information, try '--help'. cargo-0.91.0/tests/testsuite/cargo_remove/offline/mod.rs000064400000000000000000000030521046102023000214200ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; // run the metadata command to populate the cache snapbox::cmd::Command::cargo_ui() .arg("metadata") .current_dir(cwd) .assert() .success(); snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["docopt", "--offline"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/offline/stderr.term.svg000064400000000000000000000013331046102023000232650ustar 00000000000000 Removing docopt from dependencies cargo-0.91.0/tests/testsuite/cargo_remove/optional_dep_feature/mod.rs000064400000000000000000000025571046102023000241770ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["--dev", "serde"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/optional_dep_feature/stderr.term.svg000064400000000000000000000013361046102023000260360ustar 00000000000000 Removing serde from dev-dependencies cargo-0.91.0/tests/testsuite/cargo_remove/optional_dep_feature_formatting/mod.rs000064400000000000000000000025571046102023000264310ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["docopt", "toml"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/optional_dep_feature_formatting/stderr.term.svg000064400000000000000000000015311046102023000302650ustar 00000000000000 Removing docopt from dependencies Removing toml from dependencies cargo-0.91.0/tests/testsuite/cargo_remove/optional_feature/mod.rs000064400000000000000000000025471046102023000233460ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["semver"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/optional_feature/stderr.term.svg000064400000000000000000000013331046102023000252030ustar 00000000000000 Removing semver from dependencies cargo-0.91.0/tests/testsuite/cargo_remove/package/mod.rs000064400000000000000000000030551046102023000213740ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("dbus", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("ncurses", "20.0.0+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["docopt", "--package", "dep-a"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/package/stderr.term.svg000064400000000000000000000013331046102023000232360ustar 00000000000000 Removing docopt from dependencies cargo-0.91.0/tests/testsuite/cargo_remove/remove_basic/mod.rs000064400000000000000000000025471046102023000224440ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["docopt"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/remove_basic/stderr.term.svg000064400000000000000000000013331046102023000243010ustar 00000000000000 Removing docopt from dependencies cargo-0.91.0/tests/testsuite/cargo_remove/script/in/cargo-remove-test-fixture.rs000064400000000000000000000004241046102023000261600ustar 00000000000000--- edition = "2015" [build-dependencies] semver = "0.1.0" [dependencies] docopt = "0.6" rustc-serialize = "0.4" semver = "0.1" toml = "0.1" clippy = "0.4" [dev-dependencies] regex = "0.1.1" serde = "1.0.90" [features] std = ["serde/std", "semver/std"] --- fn main() { } cargo-0.91.0/tests/testsuite/cargo_remove/script/mod.rs000064400000000000000000000027411046102023000213060ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .masquerade_as_nightly_cargo(&["script"]) .arg("-Zscript") .arg("remove") .arg_line("--manifest-path cargo-remove-test-fixture.rs docopt") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/script/out/cargo-remove-test-fixture.rs000064400000000000000000000004051046102023000263600ustar 00000000000000--- edition = "2015" [build-dependencies] semver = "0.1.0" [dependencies] rustc-serialize = "0.4" semver = "0.1" toml = "0.1" clippy = "0.4" [dev-dependencies] regex = "0.1.1" serde = "1.0.90" [features] std = ["serde/std", "semver/std"] --- fn main() { } cargo-0.91.0/tests/testsuite/cargo_remove/script/stderr.term.svg000064400000000000000000000021521046102023000231470ustar 00000000000000 warning: `package.edition` is unspecified, defaulting to `[..]` Removing docopt from dependencies warning: `package.edition` is unspecified, defaulting to `[..]` cargo-0.91.0/tests/testsuite/cargo_remove/script_last/in/cargo-remove-test-fixture.rs000064400000000000000000000000651046102023000272040ustar 00000000000000--- [dependencies] docopt = "0.6" --- fn main() { } cargo-0.91.0/tests/testsuite/cargo_remove/script_last/mod.rs000064400000000000000000000016251046102023000223310ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .masquerade_as_nightly_cargo(&["script"]) .arg("-Zscript") .arg("remove") .arg_line("--manifest-path cargo-remove-test-fixture.rs docopt") .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/script_last/out/cargo-remove-test-fixture.rs000064400000000000000000000000301046102023000273750ustar 00000000000000--- --- fn main() { } cargo-0.91.0/tests/testsuite/cargo_remove/script_last/stderr.term.svg000064400000000000000000000021521046102023000241720ustar 00000000000000 warning: `package.edition` is unspecified, defaulting to `[..]` Removing docopt from dependencies warning: `package.edition` is unspecified, defaulting to `[..]` cargo-0.91.0/tests/testsuite/cargo_remove/skip_gc_glob_profile/mod.rs000064400000000000000000000014271046102023000241440ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["toml"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/skip_gc_glob_profile/stderr.term.svg000064400000000000000000000013311046102023000260030ustar 00000000000000 Removing toml from dependencies cargo-0.91.0/tests/testsuite/cargo_remove/target/mod.rs000064400000000000000000000030731046102023000212670ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("dbus", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("ncurses", "20.0.0+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["--target", "wasm32-unknown-unknown", "dbus"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/target/stderr.term.svg000064400000000000000000000013751046102023000231370ustar 00000000000000 Removing dbus from dependencies for target `wasm32-unknown-unknown` cargo-0.91.0/tests/testsuite/cargo_remove/target_build/mod.rs000064400000000000000000000031101046102023000224360ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("dbus", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("ncurses", "20.0.0+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["--build", "--target", "wasm32-unknown-unknown", "semver"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/target_build/stderr.term.svg000064400000000000000000000014051046102023000243100ustar 00000000000000 Removing semver from build-dependencies for target `wasm32-unknown-unknown` cargo-0.91.0/tests/testsuite/cargo_remove/target_dev/mod.rs000064400000000000000000000031071046102023000221230ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("dbus", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("ncurses", "20.0.0+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["--dev", "--target", "wasm32-unknown-unknown", "ncurses"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/target_dev/stderr.term.svg000064400000000000000000000014041046102023000237660ustar 00000000000000 Removing ncurses from dev-dependencies for target `wasm32-unknown-unknown` cargo-0.91.0/tests/testsuite/cargo_remove/update_lock_file/mod.rs000064400000000000000000000025601046102023000232720ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.1+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["rustc-serialize"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/update_lock_file/stderr.term.svg000064400000000000000000000013441046102023000251360ustar 00000000000000 Removing rustc-serialize from dependencies cargo-0.91.0/tests/testsuite/cargo_remove/workspace/mod.rs000064400000000000000000000026151046102023000220000ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["--package", "my-package", "--build", "semver"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/workspace/stderr.term.svg000064400000000000000000000013411046102023000236400ustar 00000000000000 Removing semver from build-dependencies cargo-0.91.0/tests/testsuite/cargo_remove/workspace_non_virtual/mod.rs000064400000000000000000000026151046102023000244200ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["--package", "my-package", "--build", "semver"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/workspace_non_virtual/stderr.term.svg000064400000000000000000000013411046102023000262600ustar 00000000000000 Removing semver from build-dependencies cargo-0.91.0/tests/testsuite/cargo_remove/workspace_preserved/mod.rs000064400000000000000000000026151046102023000240570ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::compare::assert_ui; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); cargo_test_support::registry::Package::new("serde", "1.0.90") .feature("std", &[]) .publish(); let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("remove") .args(["--package", "my-package", "--build", "semver"]) .current_dir(cwd) .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); assert_ui().subset_matches(current_dir!().join("out"), &project_root); } cargo-0.91.0/tests/testsuite/cargo_remove/workspace_preserved/stderr.term.svg000064400000000000000000000013411046102023000257170ustar 00000000000000 Removing semver from build-dependencies cargo-0.91.0/tests/testsuite/cargo_report/help/mod.rs000064400000000000000000000004631046102023000207470ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("report") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_report/help/stdout.term.svg000064400000000000000000000074501046102023000226360ustar 00000000000000 Generate and display various kinds of reports Usage: cargo[EXE] report [OPTIONS] <COMMAND> Commands: future-incompatibilities Reports any crates which will eventually stop compiling Options: -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Manifest Options: --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help report` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_report/mod.rs000064400000000000000000000000121046102023000200050ustar 00000000000000mod help; cargo-0.91.0/tests/testsuite/cargo_run/help/mod.rs000064400000000000000000000004601046102023000202350ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("run") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_run/help/stdout.term.svg000064400000000000000000000214561046102023000221310ustar 00000000000000 Run a binary or example of the local package Usage: cargo[EXE] run [OPTIONS] [ARGS]... Arguments: [ARGS]... Arguments for the binary or example to run Options: --message-format <FMT> Error format [possible values: human, short, json, json-diagnostic-short, json-diagnostic-rendered-ansi, json-render-diagnostics] -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Package Selection: -p, --package [<SPEC>] Package with the target to run Target Selection: --bin [<NAME>] Name of the bin target to run --example [<NAME>] Name of the example target to run Feature Selection: -F, --features <FEATURES> Space or comma separated list of features to activate --all-features Activate all available features --no-default-features Do not activate the `default` feature Compilation Options: -j, --jobs <N> Number of parallel jobs, defaults to # of CPUs. --keep-going Do not abort the build as soon as there is an error -r, --release Build artifacts in release mode, with optimizations --profile <PROFILE-NAME> Build artifacts with the specified profile --target [<TRIPLE>] Build for the target triple --target-dir <DIRECTORY> Directory for all generated artifacts --unit-graph Output build graph in JSON (unstable) --timings[=<FMTS>] Timing output formats (unstable) (comma separated): html, json Manifest Options: --manifest-path <PATH> Path to Cargo.toml --lockfile-path <PATH> Path to Cargo.lock (unstable) --ignore-rust-version Ignore `rust-version` specification in packages --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help run` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_run/mod.rs000064400000000000000000000000121046102023000172760ustar 00000000000000mod help; cargo-0.91.0/tests/testsuite/cargo_rustc/help/mod.rs000064400000000000000000000004621046102023000205730ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("rustc") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_rustc/help/stdout.term.svg000064400000000000000000000257441046102023000224710ustar 00000000000000 Compile a package, and pass extra options to the compiler Usage: cargo[EXE] rustc [OPTIONS] [ARGS]... Arguments: [ARGS]... Extra rustc flags Options: --print <INFO> Output compiler information without compiling --crate-type <CRATE-TYPE> Comma separated list of types of crates for the compiler to emit --future-incompat-report Outputs a future incompatibility report at the end of the build --message-format <FMT> Error format [possible values: human, short, json, json-diagnostic-short, json-diagnostic-rendered-ansi, json-render-diagnostics] -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Package Selection: -p, --package [<SPEC>] Package to build Target Selection: --lib Build only this package's library --bins Build all binaries --bin [<NAME>] Build only the specified binary --examples Build all examples --example [<NAME>] Build only the specified example --tests Build all targets that have `test = true` set --test [<NAME>] Build only the specified test target --benches Build all targets that have `bench = true` set --bench [<NAME>] Build only the specified bench target --all-targets Build all targets Feature Selection: -F, --features <FEATURES> Space or comma separated list of features to activate --all-features Activate all available features --no-default-features Do not activate the `default` feature Compilation Options: -j, --jobs <N> Number of parallel jobs, defaults to # of CPUs. --keep-going Do not abort the build as soon as there is an error -r, --release Build artifacts in release mode, with optimizations --profile <PROFILE-NAME> Build artifacts with the specified profile --target [<TRIPLE>] Target triple which compiles will be for --target-dir <DIRECTORY> Directory for all generated artifacts --unit-graph Output build graph in JSON (unstable) --timings[=<FMTS>] Timing output formats (unstable) (comma separated): html, json Manifest Options: --manifest-path <PATH> Path to Cargo.toml --lockfile-path <PATH> Path to Cargo.lock (unstable) --ignore-rust-version Ignore `rust-version` specification in packages --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help rustc` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_rustc/mod.rs000064400000000000000000000000121046102023000176320ustar 00000000000000mod help; cargo-0.91.0/tests/testsuite/cargo_rustdoc/help/mod.rs000064400000000000000000000004641046102023000211200ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("rustdoc") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_rustdoc/help/stdout.term.svg000064400000000000000000000253311046102023000230040ustar 00000000000000 Build a package's documentation, using specified custom flags. Usage: cargo[EXE] rustdoc [OPTIONS] [ARGS]... Arguments: [ARGS]... Extra rustdoc flags Options: --open Opens the docs in a browser after the operation --message-format <FMT> Error format [possible values: human, short, json, json-diagnostic-short, json-diagnostic-rendered-ansi, json-render-diagnostics] --output-format <FMT> The output type to write (unstable) [possible values: html, json] -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Package Selection: -p, --package [<SPEC>] Package to document Target Selection: --lib Build only this package's library --bins Build all binaries --bin [<NAME>] Build only the specified binary --examples Build all examples --example [<NAME>] Build only the specified example --tests Build all targets that have `test = true` set --test [<NAME>] Build only the specified test target --benches Build all targets that have `bench = true` set --bench [<NAME>] Build only the specified bench target --all-targets Build all targets Feature Selection: -F, --features <FEATURES> Space or comma separated list of features to activate --all-features Activate all available features --no-default-features Do not activate the `default` feature Compilation Options: -j, --jobs <N> Number of parallel jobs, defaults to # of CPUs. --keep-going Do not abort the build as soon as there is an error -r, --release Build artifacts in release mode, with optimizations --profile <PROFILE-NAME> Build artifacts with the specified profile --target [<TRIPLE>] Build for the target triple --target-dir <DIRECTORY> Directory for all generated artifacts --unit-graph Output build graph in JSON (unstable) --timings[=<FMTS>] Timing output formats (unstable) (comma separated): html, json Manifest Options: --manifest-path <PATH> Path to Cargo.toml --lockfile-path <PATH> Path to Cargo.lock (unstable) --ignore-rust-version Ignore `rust-version` specification in packages --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help rustdoc` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_rustdoc/mod.rs000064400000000000000000000000121046102023000201550ustar 00000000000000mod help; cargo-0.91.0/tests/testsuite/cargo_search/help/mod.rs000064400000000000000000000004631046102023000207010ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("search") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_search/help/stdout.term.svg000064400000000000000000000107211046102023000225630ustar 00000000000000 Search packages in the registry. Default registry is crates.io Usage: cargo[EXE] search [OPTIONS] [QUERY]... Arguments: [QUERY]... Options: --limit <LIMIT> Limit the number of results (default: 10, max: 100) --index <INDEX> Registry index URL to search packages in --registry <REGISTRY> Registry to search packages in -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Manifest Options: --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help search` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_search/mod.rs000064400000000000000000000000121046102023000177370ustar 00000000000000mod help; cargo-0.91.0/tests/testsuite/cargo_targets.rs000064400000000000000000000034241046102023000173760ustar 00000000000000//! Tests specifically related to target handling (lib, bins, examples, tests, benches). use crate::prelude::*; use cargo_test_support::project; use cargo_test_support::str; #[cargo_test] fn warn_unmatched_target_filters() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [lib] test = false bench = false "#, ) .file("src/lib.rs", r#"fn main() {}"#) .build(); p.cargo("check --tests --bins --examples --benches") .with_stderr_data(str![[r#" [WARNING] target filters `bins`, `tests`, `examples`, `benches` specified, but no targets matched; this is a no-op [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn reserved_windows_target_name() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [[bin]] name = "con" path = "src/main.rs" "#, ) .file("src/main.rs", "fn main() {}") .build(); if cfg!(windows) { p.cargo("check") .with_stderr_data(str![[r#" [WARNING] binary target `con` is a reserved Windows filename, this target will not work on Windows platforms [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } else { p.cargo("check") .with_stderr_data(str![[r#" [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } } cargo-0.91.0/tests/testsuite/cargo_test/help/mod.rs000064400000000000000000000004611046102023000204110ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("test") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_test/help/stdout.term.svg000064400000000000000000000276211046102023000223040ustar 00000000000000 Execute all unit and integration tests and build examples of a local package Usage: cargo[EXE] test [OPTIONS] [TESTNAME] [-- [ARGS]...] Arguments: [TESTNAME] If specified, only run tests containing this string in their names [ARGS]... Arguments for the test binary Options: --no-run Compile, but don't run tests --no-fail-fast Run all tests regardless of failure --future-incompat-report Outputs a future incompatibility report at the end of the build --message-format <FMT> Error format [possible values: human, short, json, json-diagnostic-short, json-diagnostic-rendered-ansi, json-render-diagnostics] -q, --quiet Display one character per test instead of one line -v, --verbose... Use verbose output (-vv very verbose/build.rs output) --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Package Selection: -p, --package [<SPEC>] Package to run tests for --workspace Test all packages in the workspace --exclude <SPEC> Exclude packages from the test --all Alias for --workspace (deprecated) Target Selection: --lib Test only this package's library --bins Test all binaries --bin [<NAME>] Test only the specified binary --examples Test all examples --example [<NAME>] Test only the specified example --tests Test all targets that have `test = true` set --test [<NAME>] Test only the specified test target --benches Test all targets that have `bench = true` set --bench [<NAME>] Test only the specified bench target --all-targets Test all targets (does not include doctests) --doc Test only this library's documentation Feature Selection: -F, --features <FEATURES> Space or comma separated list of features to activate --all-features Activate all available features --no-default-features Do not activate the `default` feature Compilation Options: -j, --jobs <N> Number of parallel jobs, defaults to # of CPUs. -r, --release Build artifacts in release mode, with optimizations --profile <PROFILE-NAME> Build artifacts with the specified profile --target [<TRIPLE>] Build for the target triple --target-dir <DIRECTORY> Directory for all generated artifacts --unit-graph Output build graph in JSON (unstable) --timings[=<FMTS>] Timing output formats (unstable) (comma separated): html, json Manifest Options: --manifest-path <PATH> Path to Cargo.toml --lockfile-path <PATH> Path to Cargo.lock (unstable) --ignore-rust-version Ignore `rust-version` specification in packages --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help test` for more detailed information. Run `cargo test -- --help` for test binary options. cargo-0.91.0/tests/testsuite/cargo_test/mod.rs000064400000000000000000000000351046102023000174560ustar 00000000000000mod help; mod no_keep_going; cargo-0.91.0/tests/testsuite/cargo_test/no_keep_going/mod.rs000064400000000000000000000010371046102023000222640ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("test") .arg("--keep-going") .current_dir(cwd) .assert() .code(1) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); } cargo-0.91.0/tests/testsuite/cargo_test/no_keep_going/stderr.term.svg000064400000000000000000000033211046102023000241270ustar 00000000000000 error: unexpected argument '--keep-going' found tip: use `--no-fail-fast` to run as many tests as possible regardless of failure Usage: cargo[EXE] test [OPTIONS] [TESTNAME] [-- [ARGS]...] For more information, try '--help'. cargo-0.91.0/tests/testsuite/cargo_tree/deps.rs000064400000000000000000001501311046102023000176150ustar 00000000000000//! Tests for the `cargo tree` command. use crate::prelude::*; use crate::utils::cross_compile::disabled as cross_compile_disabled; use cargo_test_support::cross_compile::alternate; use cargo_test_support::registry::{Dependency, Package}; use cargo_test_support::str; use cargo_test_support::{Project, basic_manifest, git, project, rustc_host}; use crate::features2::switch_to_resolver_2; fn make_simple_proj() -> Project { Package::new("c", "1.0.0").publish(); Package::new("b", "1.0.0").dep("c", "1.0").publish(); Package::new("a", "1.0.0").dep("b", "1.0").publish(); Package::new("bdep", "1.0.0").dep("b", "1.0").publish(); Package::new("devdep", "1.0.0").dep("b", "1.0.0").publish(); project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] a = "1.0" c = "1.0" [build-dependencies] bdep = "1.0" [dev-dependencies] devdep = "1.0" "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .build() } #[cargo_test] fn simple() { // A simple test with a few different dependencies. let p = make_simple_proj(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── a v1.0.0 │ └── b v1.0.0 │ └── c v1.0.0 └── c v1.0.0 [build-dependencies] └── bdep v1.0.0 └── b v1.0.0 (*) [dev-dependencies] └── devdep v1.0.0 └── b v1.0.0 (*) "#]]) .run(); p.cargo("tree -p bdep") .with_stdout_data(str![[r#" bdep v1.0.0 └── b v1.0.0 └── c v1.0.0 "#]]) .run(); } #[cargo_test] fn virtual_workspace() { // Multiple packages in a virtual workspace. Package::new("somedep", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "baz", "c"] "#, ) .file("a/Cargo.toml", &basic_manifest("a", "1.0.0")) .file("a/src/lib.rs", "") .file( "baz/Cargo.toml", r#" [package] name = "baz" version = "0.1.0" [dependencies] c = { path = "../c" } somedep = "1.0" "#, ) .file("baz/src/lib.rs", "") .file("c/Cargo.toml", &basic_manifest("c", "1.0.0")) .file("c/src/lib.rs", "") .build(); p.cargo("tree") .with_stdout_data(str![[r#" a v1.0.0 ([ROOT]/foo/a) baz v0.1.0 ([ROOT]/foo/baz) ├── c v1.0.0 ([ROOT]/foo/c) └── somedep v1.0.0 c v1.0.0 ([ROOT]/foo/c) "#]]) .run(); p.cargo("tree -p a") .with_stdout_data(str![[r#" a v1.0.0 ([ROOT]/foo/a) "#]]) .run(); p.cargo("tree") .cwd("baz") .with_stdout_data(str![[r#" baz v0.1.0 ([ROOT]/foo/baz) ├── c v1.0.0 ([ROOT]/foo/c) └── somedep v1.0.0 "#]]) .run(); // exclude baz p.cargo("tree --workspace --exclude baz") .with_stdout_data(str![[r#" a v1.0.0 ([ROOT]/foo/a) c v1.0.0 ([ROOT]/foo/c) "#]]) .run(); // exclude glob '*z' p.cargo("tree --workspace --exclude '*z'") .with_stdout_data(str![[r#" a v1.0.0 ([ROOT]/foo/a) c v1.0.0 ([ROOT]/foo/c) "#]]) .run(); // include glob '*z' p.cargo("tree -p '*z'") .with_stdout_data(str![[r#" baz v0.1.0 ([ROOT]/foo/baz) ├── c v1.0.0 ([ROOT]/foo/c) └── somedep v1.0.0 "#]]) .run(); } #[cargo_test] fn dedupe_edges() { // Works around https://github.com/rust-lang/cargo/issues/7985 Package::new("bitflags", "1.0.0").publish(); Package::new("manyfeat", "1.0.0") .feature("f1", &[]) .feature("f2", &[]) .feature("f3", &[]) .dep("bitflags", "1.0") .publish(); Package::new("a", "1.0.0") .feature_dep("manyfeat", "1.0", &["f1"]) .publish(); Package::new("b", "1.0.0") .feature_dep("manyfeat", "1.0", &["f2"]) .publish(); Package::new("c", "1.0.0") .feature_dep("manyfeat", "1.0", &["f3"]) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] a = "1.0" b = "1.0" c = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── a v1.0.0 │ └── manyfeat v1.0.0 │ └── bitflags v1.0.0 ├── b v1.0.0 │ └── manyfeat v1.0.0 (*) └── c v1.0.0 └── manyfeat v1.0.0 (*) "#]]) .run(); } #[cargo_test] fn renamed_deps() { // Handles renamed dependencies. Package::new("one", "1.0.0").publish(); Package::new("two", "1.0.0").publish(); Package::new("bar", "1.0.0").dep("one", "1.0").publish(); Package::new("bar", "2.0.0").dep("two", "1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" [dependencies] bar1 = {version = "1.0", package="bar"} bar2 = {version = "2.0", package="bar"} "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree") .with_stdout_data(str![[r#" foo v1.0.0 ([ROOT]/foo) ├── bar v1.0.0 │ └── one v1.0.0 └── bar v2.0.0 └── two v1.0.0 "#]]) .run(); } #[cargo_test] fn source_kinds() { // Handles git and path sources. Package::new("regdep", "1.0.0").publish(); let git_project = git::new("gitdep", |p| { p.file("Cargo.toml", &basic_manifest("gitdep", "1.0.0")) .file("src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [dependencies] regdep = "1.0" pathdep = {{ path = "pathdep" }} gitdep = {{ git = "{}" }} "#, git_project.url() ), ) .file("src/lib.rs", "") .file("pathdep/Cargo.toml", &basic_manifest("pathdep", "1.0.0")) .file("pathdep/src/lib.rs", "") .build(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── gitdep v1.0.0 ([ROOTURL]/gitdep#[..]) ├── pathdep v1.0.0 ([ROOT]/foo/pathdep) └── regdep v1.0.0 "#]]) .run(); } #[cargo_test] fn features() { // Exercises a variety of feature behaviors. Package::new("optdep_default", "1.0.0").publish(); Package::new("optdep", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.1.0" [dependencies] optdep_default = { version = "1.0", optional = true } optdep = { version = "1.0", optional = true } [features] default = ["optdep_default"] "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree") .with_stdout_data(str![[r#" a v0.1.0 ([ROOT]/foo) └── optdep_default v1.0.0 "#]]) .run(); p.cargo("tree --no-default-features") .with_stdout_data(str![[r#" a v0.1.0 ([ROOT]/foo) "#]]) .run(); p.cargo("tree --all-features") .with_stdout_data(str![[r#" a v0.1.0 ([ROOT]/foo) ├── optdep v1.0.0 └── optdep_default v1.0.0 "#]]) .run(); p.cargo("tree --features optdep") .with_stdout_data(str![[r#" a v0.1.0 ([ROOT]/foo) ├── optdep v1.0.0 └── optdep_default v1.0.0 "#]]) .run(); } #[cargo_test] fn filters_target() { // --target flag if cross_compile_disabled() { return; } Package::new("targetdep", "1.0.0").publish(); Package::new("hostdep", "1.0.0").publish(); Package::new("devdep", "1.0.0").publish(); Package::new("build_target_dep", "1.0.0").publish(); Package::new("build_host_dep", "1.0.0") .target_dep("targetdep", "1.0", alternate()) .target_dep("hostdep", "1.0", rustc_host()) .publish(); Package::new("pm_target", "1.0.0") .proc_macro(true) .publish(); Package::new("pm_host", "1.0.0").proc_macro(true).publish(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [target.'{alt}'.dependencies] targetdep = "1.0" pm_target = "1.0" [target.'{host}'.dependencies] hostdep = "1.0" pm_host = "1.0" [target.'{alt}'.dev-dependencies] devdep = "1.0" [target.'{alt}'.build-dependencies] build_target_dep = "1.0" [target.'{host}'.build-dependencies] build_host_dep = "1.0" "#, alt = alternate(), host = rustc_host() ), ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .build(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── hostdep v1.0.0 └── pm_host v1.0.0 (proc-macro) [build-dependencies] └── build_host_dep v1.0.0 └── hostdep v1.0.0 "#]]) .run(); p.cargo("tree --target") .arg(alternate()) .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── pm_target v1.0.0 (proc-macro) └── targetdep v1.0.0 [build-dependencies] └── build_host_dep v1.0.0 └── hostdep v1.0.0 [dev-dependencies] └── devdep v1.0.0 "#]]) .run(); p.cargo("tree --target") .arg(rustc_host()) .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── hostdep v1.0.0 └── pm_host v1.0.0 (proc-macro) [build-dependencies] └── build_host_dep v1.0.0 └── hostdep v1.0.0 "#]]) .run(); p.cargo("tree --target=all") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── hostdep v1.0.0 ├── pm_host v1.0.0 (proc-macro) ├── pm_target v1.0.0 (proc-macro) └── targetdep v1.0.0 [build-dependencies] ├── build_host_dep v1.0.0 │ ├── hostdep v1.0.0 │ └── targetdep v1.0.0 └── build_target_dep v1.0.0 [dev-dependencies] └── devdep v1.0.0 "#]]) .run(); // no-proc-macro p.cargo("tree --target=all -e no-proc-macro") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── hostdep v1.0.0 └── targetdep v1.0.0 [build-dependencies] ├── build_host_dep v1.0.0 │ ├── hostdep v1.0.0 │ └── targetdep v1.0.0 └── build_target_dep v1.0.0 [dev-dependencies] └── devdep v1.0.0 "#]]) .run(); } #[cargo_test] fn no_selected_target_dependency() { // --target flag if cross_compile_disabled() { return; } Package::new("targetdep", "1.0.0").publish(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [target.'{alt}'.dependencies] targetdep = "1.0" "#, alt = alternate(), ), ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .build(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) "#]]) .run(); p.cargo("tree -i targetdep") .with_stderr_data(str![[r#" [WARNING] nothing to print. To find dependencies that require specific target platforms, try to use option `--target all` first, and then narrow your search scope accordingly. "#]]) .run(); p.cargo("tree -i targetdep --target all") .with_stdout_data(str![[r#" targetdep v1.0.0 └── foo v0.1.0 ([ROOT]/foo) "#]]) .run(); } #[cargo_test] fn dep_kinds() { Package::new("inner-devdep", "1.0.0").publish(); Package::new("inner-builddep", "1.0.0").publish(); Package::new("inner-normal", "1.0.0").publish(); Package::new("inner-pm", "1.0.0").proc_macro(true).publish(); Package::new("inner-buildpm", "1.0.0") .proc_macro(true) .publish(); Package::new("normaldep", "1.0.0") .dep("inner-normal", "1.0") .dev_dep("inner-devdep", "1.0") .build_dep("inner-builddep", "1.0") .publish(); Package::new("devdep", "1.0.0") .dep("inner-normal", "1.0") .dep("inner-pm", "1.0") .dev_dep("inner-devdep", "1.0") .build_dep("inner-builddep", "1.0") .build_dep("inner-buildpm", "1.0") .publish(); Package::new("builddep", "1.0.0") .dep("inner-normal", "1.0") .dev_dep("inner-devdep", "1.0") .build_dep("inner-builddep", "1.0") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] normaldep = "1.0" [dev-dependencies] devdep = "1.0" [build-dependencies] builddep = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── normaldep v1.0.0 └── inner-normal v1.0.0 [build-dependencies] └── inner-builddep v1.0.0 [build-dependencies] └── builddep v1.0.0 └── inner-normal v1.0.0 [build-dependencies] └── inner-builddep v1.0.0 [dev-dependencies] └── devdep v1.0.0 ├── inner-normal v1.0.0 └── inner-pm v1.0.0 (proc-macro) [build-dependencies] ├── inner-builddep v1.0.0 └── inner-buildpm v1.0.0 (proc-macro) "#]]) .run(); p.cargo("tree -e no-dev") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── normaldep v1.0.0 └── inner-normal v1.0.0 [build-dependencies] └── inner-builddep v1.0.0 [build-dependencies] └── builddep v1.0.0 └── inner-normal v1.0.0 [build-dependencies] └── inner-builddep v1.0.0 "#]]) .run(); p.cargo("tree -e normal") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── normaldep v1.0.0 └── inner-normal v1.0.0 "#]]) .run(); p.cargo("tree -e dev,build") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) [build-dependencies] └── builddep v1.0.0 [build-dependencies] └── inner-builddep v1.0.0 [dev-dependencies] └── devdep v1.0.0 [build-dependencies] ├── inner-builddep v1.0.0 └── inner-buildpm v1.0.0 (proc-macro) "#]]) .run(); p.cargo("tree -e dev,build,no-proc-macro") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) [build-dependencies] └── builddep v1.0.0 [build-dependencies] └── inner-builddep v1.0.0 [dev-dependencies] └── devdep v1.0.0 [build-dependencies] └── inner-builddep v1.0.0 "#]]) .run(); } #[cargo_test] fn cyclic_dev_dep() { // Cyclical dev-dependency and inverse flag. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dev-dependencies] dev-dep = { path = "dev-dep" } "#, ) .file("src/lib.rs", "") .file( "dev-dep/Cargo.toml", r#" [package] name = "dev-dep" version = "0.1.0" [dependencies] foo = { path=".." } "#, ) .file("dev-dep/src/lib.rs", "") .build(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) [dev-dependencies] └── dev-dep v0.1.0 ([ROOT]/foo/dev-dep) └── foo v0.1.0 ([ROOT]/foo) (*) "#]]) .run(); p.cargo("tree --invert foo") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── dev-dep v0.1.0 ([ROOT]/foo/dev-dep) [dev-dependencies] └── foo v0.1.0 ([ROOT]/foo) (*) "#]]) .run(); } #[cargo_test] fn invert() { Package::new("b1", "1.0.0").dep("c", "1.0").publish(); Package::new("b2", "1.0.0").dep("d", "1.0").publish(); Package::new("c", "1.0.0").publish(); Package::new("d", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] b1 = "1.0" b2 = "1.0" c = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── b1 v1.0.0 │ └── c v1.0.0 ├── b2 v1.0.0 │ └── d v1.0.0 └── c v1.0.0 "#]]) .run(); p.cargo("tree --invert c") .with_stdout_data(str![[r#" c v1.0.0 ├── b1 v1.0.0 │ └── foo v0.1.0 ([ROOT]/foo) └── foo v0.1.0 ([ROOT]/foo) "#]]) .run(); } #[cargo_test] fn invert_with_build_dep() { // -i for a common dependency between normal and build deps. Package::new("common", "1.0.0").publish(); Package::new("bdep", "1.0.0").dep("common", "1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] common = "1.0" [build-dependencies] bdep = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── common v1.0.0 [build-dependencies] └── bdep v1.0.0 └── common v1.0.0 "#]]) .run(); p.cargo("tree -i common") .with_stdout_data(str![[r#" common v1.0.0 ├── bdep v1.0.0 │ [build-dependencies] │ └── foo v0.1.0 ([ROOT]/foo) └── foo v0.1.0 ([ROOT]/foo) "#]]) .run(); } #[cargo_test] fn no_indent() { let p = make_simple_proj(); p.cargo("tree --prefix=none") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) a v1.0.0 b v1.0.0 c v1.0.0 c v1.0.0 bdep v1.0.0 b v1.0.0 (*) devdep v1.0.0 b v1.0.0 (*) "#]]) .run(); } #[cargo_test] fn prefix_depth() { let p = make_simple_proj(); p.cargo("tree --prefix=depth") .with_stdout_data(str![[r#" 0foo v0.1.0 ([ROOT]/foo) 1a v1.0.0 2b v1.0.0 3c v1.0.0 1c v1.0.0 1bdep v1.0.0 2b v1.0.0 (*) 1devdep v1.0.0 2b v1.0.0 (*) "#]]) .run(); } #[cargo_test] fn no_dedupe() { let p = make_simple_proj(); p.cargo("tree --no-dedupe") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── a v1.0.0 │ └── b v1.0.0 │ └── c v1.0.0 └── c v1.0.0 [build-dependencies] └── bdep v1.0.0 └── b v1.0.0 └── c v1.0.0 [dev-dependencies] └── devdep v1.0.0 └── b v1.0.0 └── c v1.0.0 "#]]) .run(); } #[cargo_test] fn no_dedupe_cycle() { // --no-dedupe with a dependency cycle let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dev-dependencies] bar = {path = "bar"} "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" [dependencies] foo = {path=".."} "#, ) .file("bar/src/lib.rs", "") .build(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) [dev-dependencies] └── bar v0.1.0 ([ROOT]/foo/bar) └── foo v0.1.0 ([ROOT]/foo) (*) "#]]) .run(); p.cargo("tree --no-dedupe") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) [dev-dependencies] └── bar v0.1.0 ([ROOT]/foo/bar) └── foo v0.1.0 ([ROOT]/foo) (*) "#]]) .run(); } #[cargo_test] fn duplicates() { Package::new("dog", "1.0.0").publish(); Package::new("dog", "2.0.0").publish(); Package::new("cat", "1.0.0").publish(); Package::new("cat", "2.0.0").publish(); Package::new("dep", "1.0.0") .dep("dog", "1.0") .dep("cat", "1.0") .publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" [dependencies] dog1 = { version = "1.0", package = "dog" } dog2 = { version = "2.0", package = "dog" } "#, ) .file("a/src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" [dependencies] dep = "1.0" cat = "2.0" "#, ) .file("b/src/lib.rs", "") .build(); p.cargo("tree -p a") .with_stdout_data(str![[r#" a v0.1.0 ([ROOT]/foo/a) ├── dog v1.0.0 └── dog v2.0.0 "#]]) .run(); p.cargo("tree -p b") .with_stdout_data(str![[r#" b v0.1.0 ([ROOT]/foo/b) ├── cat v2.0.0 └── dep v1.0.0 ├── cat v1.0.0 └── dog v1.0.0 "#]]) .run(); p.cargo("tree -p a -d") .with_stdout_data(str![[r#" dog v1.0.0 └── a v0.1.0 ([ROOT]/foo/a) dog v2.0.0 └── a v0.1.0 ([ROOT]/foo/a) "#]]) .run(); p.cargo("tree -p b -d") .with_stdout_data(str![[r#" cat v1.0.0 └── dep v1.0.0 └── b v0.1.0 ([ROOT]/foo/b) cat v2.0.0 └── b v0.1.0 ([ROOT]/foo/b) "#]]) .run(); } #[cargo_test] fn duplicates_with_target() { // --target flag if cross_compile_disabled() { return; } Package::new("a", "1.0.0").publish(); Package::new("dog", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] a = "1.0" dog = "1.0" [build-dependencies] a = "1.0" dog = "1.0" "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .build(); p.cargo("tree -d").with_stdout_data(str![""]).run(); p.cargo("tree -d --target") .arg(alternate()) .with_stdout_data(str![""]) .run(); p.cargo("tree -d --target") .arg(rustc_host()) .with_stdout_data(str![""]) .run(); p.cargo("tree -d --target=all") .with_stdout_data(str![""]) .run(); } #[cargo_test] fn duplicates_with_proc_macro() { Package::new("dupe-dep", "1.0.0").publish(); Package::new("dupe-dep", "2.0.0").publish(); Package::new("proc", "1.0.0") .proc_macro(true) .dep("dupe-dep", "1.0") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] proc = "1.0" dupe-dep = "2.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── dupe-dep v2.0.0 └── proc v1.0.0 (proc-macro) └── dupe-dep v1.0.0 "#]]) .run(); p.cargo("tree --duplicates") .with_stdout_data(str![[r#" dupe-dep v1.0.0 └── proc v1.0.0 (proc-macro) └── foo v0.1.0 ([ROOT]/foo) dupe-dep v2.0.0 └── foo v0.1.0 ([ROOT]/foo) "#]]) .run(); p.cargo("tree --duplicates --edges no-proc-macro") .with_stdout_data(str![""]) .run(); } #[cargo_test] fn charset() { let p = make_simple_proj(); p.cargo("tree --charset ascii") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) |-- a v1.0.0 | `-- b v1.0.0 | `-- c v1.0.0 `-- c v1.0.0 [build-dependencies] `-- bdep v1.0.0 `-- b v1.0.0 (*) [dev-dependencies] `-- devdep v1.0.0 `-- b v1.0.0 (*) "#]]) .run(); } #[cargo_test] fn format() { Package::new("dep", "1.0.0").publish(); Package::new("other-dep", "1.0.0").publish(); Package::new("dep_that_is_awesome", "1.0.0") .file( "Cargo.toml", r#" [package] name = "dep_that_is_awesome" version = "1.0.0" [lib] name = "awesome_dep" "#, ) .file("src/lib.rs", "pub struct Straw;") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" license = "MIT" repository = "https://github.com/rust-lang/cargo" [dependencies] dep = {version="1.0", optional=true} other-dep = {version="1.0", optional=true} dep_that_is_awesome = {version="1.0", optional=true} [features] default = ["foo"] foo = ["bar"] bar = [] "#, ) .file("src/main.rs", "") .build(); p.cargo("tree --format <<<{p}>>>") .with_stdout_data(str![[r#" <<>> "#]]) .run(); p.cargo("tree --format {}") .with_stderr_data(str![[r#" [ERROR] tree format `{}` not valid Caused by: unsupported pattern `` "#]]) .with_status(101) .run(); p.cargo("tree --format {p}-{{hello}}") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo)-{hello} "#]]) .run(); p.cargo("tree --format") .arg("{p} {l} {r}") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) MIT https://github.com/rust-lang/cargo "#]]) .run(); p.cargo("tree --format") .arg("{p} {f}") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) bar,default,foo "#]]) .run(); p.cargo("tree --all-features --format") .arg("{p} [{f}]") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) [bar,default,dep,dep_that_is_awesome,foo,other-dep] ├── dep v1.0.0 [] ├── dep_that_is_awesome v1.0.0 [] └── other-dep v1.0.0 [] "#]]) .run(); p.cargo("tree") .arg("--features=other-dep,dep_that_is_awesome") .arg("--format={lib}") .with_stdout_data(str![[r#" ├── awesome_dep └── other_dep "#]]) .run(); } #[cargo_test] fn dev_dep_feature() { // New feature resolver with optional dep Package::new("optdep", "1.0.0").publish(); Package::new("bar", "1.0.0") .add_dep(Dependency::new("optdep", "1.0").optional(true)) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dev-dependencies] bar = { version = "1.0", features = ["optdep"] } [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); // Old behavior. p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── bar v1.0.0 └── optdep v1.0.0 [dev-dependencies] └── bar v1.0.0 (*) "#]]) .run(); p.cargo("tree -e normal") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── bar v1.0.0 └── optdep v1.0.0 "#]]) .run(); // New behavior. switch_to_resolver_2(&p); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── bar v1.0.0 └── optdep v1.0.0 [dev-dependencies] └── bar v1.0.0 (*) "#]]) .run(); p.cargo("tree -e normal") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── bar v1.0.0 "#]]) .run(); } #[cargo_test] fn host_dep_feature() { // New feature resolver with optional build dep Package::new("optdep", "1.0.0").publish(); Package::new("bar", "1.0.0") .add_dep(Dependency::new("optdep", "1.0").optional(true)) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [build-dependencies] bar = { version = "1.0", features = ["optdep"] } [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .build(); // Old behavior p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── bar v1.0.0 └── optdep v1.0.0 [build-dependencies] └── bar v1.0.0 (*) "#]]) .run(); // -p p.cargo("tree -p bar") .with_stdout_data(str![[r#" bar v1.0.0 └── optdep v1.0.0 "#]]) .run(); // invert p.cargo("tree -i optdep") .with_stdout_data(str![[r#" optdep v1.0.0 └── bar v1.0.0 └── foo v0.1.0 ([ROOT]/foo) [build-dependencies] └── foo v0.1.0 ([ROOT]/foo) "#]]) .run(); // New behavior. switch_to_resolver_2(&p); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── bar v1.0.0 [build-dependencies] └── bar v1.0.0 └── optdep v1.0.0 "#]]) .run(); p.cargo("tree -p bar") .with_stdout_data(str![[r#" bar v1.0.0 bar v1.0.0 └── optdep v1.0.0 "#]]) .run(); p.cargo("tree -i optdep") .with_stdout_data(str![[r#" optdep v1.0.0 └── bar v1.0.0 [build-dependencies] └── foo v0.1.0 ([ROOT]/foo) "#]]) .run(); // Check that -d handles duplicates with features. p.cargo("tree -d") .with_stdout_data(str![[r#" bar v1.0.0 └── foo v0.1.0 ([ROOT]/foo) bar v1.0.0 [build-dependencies] └── foo v0.1.0 ([ROOT]/foo) "#]]) .run(); } #[cargo_test] fn proc_macro_features() { // New feature resolver with a proc-macro Package::new("optdep", "1.0.0").publish(); Package::new("somedep", "1.0.0") .add_dep(Dependency::new("optdep", "1.0").optional(true)) .publish(); Package::new("pm", "1.0.0") .proc_macro(true) .feature_dep("somedep", "1.0", &["optdep"]) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] pm = "1.0" somedep = "1.0" "#, ) .file("src/lib.rs", "") .build(); // Old behavior p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── pm v1.0.0 (proc-macro) │ └── somedep v1.0.0 │ └── optdep v1.0.0 └── somedep v1.0.0 (*) "#]]) .run(); // Old behavior + no-proc-macro p.cargo("tree -e no-proc-macro") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── somedep v1.0.0 └── optdep v1.0.0 "#]]) .run(); // -p p.cargo("tree -p somedep") .with_stdout_data(str![[r#" somedep v1.0.0 └── optdep v1.0.0 "#]]) .run(); // -p -e no-proc-macro p.cargo("tree -p somedep -e no-proc-macro") .with_stdout_data(str![[r#" somedep v1.0.0 └── optdep v1.0.0 "#]]) .run(); // invert p.cargo("tree -i somedep") .with_stdout_data(str![[r#" somedep v1.0.0 ├── foo v0.1.0 ([ROOT]/foo) └── pm v1.0.0 (proc-macro) └── foo v0.1.0 ([ROOT]/foo) "#]]) .run(); // invert + no-proc-macro p.cargo("tree -i somedep -e no-proc-macro") .with_stdout_data(str![[r#" somedep v1.0.0 └── foo v0.1.0 ([ROOT]/foo) "#]]) .run(); // New behavior. switch_to_resolver_2(&p); // Note the missing (*) p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── pm v1.0.0 (proc-macro) │ └── somedep v1.0.0 │ └── optdep v1.0.0 └── somedep v1.0.0 "#]]) .run(); p.cargo("tree -e no-proc-macro") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── somedep v1.0.0 "#]]) .run(); p.cargo("tree -p somedep") .with_stdout_data(str![[r#" somedep v1.0.0 somedep v1.0.0 └── optdep v1.0.0 "#]]) .run(); p.cargo("tree -i somedep") .with_stdout_data(str![[r#" somedep v1.0.0 └── foo v0.1.0 ([ROOT]/foo) somedep v1.0.0 └── pm v1.0.0 (proc-macro) └── foo v0.1.0 ([ROOT]/foo) "#]]) .run(); p.cargo("tree -i somedep -e no-proc-macro") .with_stdout_data(str![[r#" somedep v1.0.0 └── foo v0.1.0 ([ROOT]/foo) "#]]) .run(); } #[cargo_test] fn itarget_opt_dep() { // New feature resolver with optional target dep Package::new("optdep", "1.0.0").publish(); Package::new("common", "1.0.0") .add_dep(Dependency::new("optdep", "1.0").optional(true)) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" [dependencies] common = "1.0" [target.'cfg(whatever)'.dependencies] common = { version = "1.0", features = ["optdep"] } "#, ) .file("src/lib.rs", "") .build(); // Old behavior p.cargo("tree") .with_stdout_data(str![[r#" foo v1.0.0 ([ROOT]/foo) └── common v1.0.0 └── optdep v1.0.0 "#]]) .run(); // New behavior. switch_to_resolver_2(&p); p.cargo("tree") .with_stdout_data(str![[r#" foo v1.0.0 ([ROOT]/foo) └── common v1.0.0 "#]]) .run(); } #[cargo_test] fn ambiguous_name() { // -p that is ambiguous. Package::new("dep", "1.0.0").publish(); Package::new("dep", "2.0.0").publish(); Package::new("bar", "1.0.0").dep("dep", "2.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] dep = "1.0" bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree -p dep") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 3 packages to latest compatible versions [ADDING] dep v1.0.0 (available: v2.0.0) [DOWNLOADING] crates ... [DOWNLOADED] dep v2.0.0 (registry `dummy-registry`) [DOWNLOADED] dep v1.0.0 (registry `dummy-registry`) [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [ERROR] There are multiple `dep` packages in your project, and the specification `dep` is ambiguous. Please re-run this command with one of the following specifications: dep@1.0.0 dep@2.0.0 "#]]) .with_status(101) .run(); } #[cargo_test] fn workspace_features_are_local() { // The features for workspace packages should be the same as `cargo build` // (i.e., the features selected depend on the "current" package). Package::new("optdep", "1.0.0").publish(); Package::new("somedep", "1.0.0") .add_dep(Dependency::new("optdep", "1.0").optional(true)) .publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" [dependencies] somedep = {version="1.0", features=["optdep"]} "#, ) .file("a/src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" [dependencies] somedep = "1.0" "#, ) .file("b/src/lib.rs", "") .build(); p.cargo("tree") .with_stdout_data(str![[r#" a v0.1.0 ([ROOT]/foo/a) └── somedep v1.0.0 └── optdep v1.0.0 b v0.1.0 ([ROOT]/foo/b) └── somedep v1.0.0 (*) "#]]) .run(); p.cargo("tree -p a") .with_stdout_data(str![[r#" a v0.1.0 ([ROOT]/foo/a) └── somedep v1.0.0 └── optdep v1.0.0 "#]]) .run(); p.cargo("tree -p b") .with_stdout_data(str![[r#" b v0.1.0 ([ROOT]/foo/b) └── somedep v1.0.0 "#]]) .run(); } #[cargo_test] fn unknown_edge_kind() { let p = project() .file("Cargo.toml", "") .file("src/lib.rs", "") .build(); p.cargo("tree -e unknown") .with_stderr_data(str![[r#" [ERROR] unknown edge kind `unknown`, valid values are "normal", "build", "dev", "no-normal", "no-build", "no-dev", "no-proc-macro", "features", or "all" "#]]) .with_status(101) .run(); } #[cargo_test] fn mixed_no_edge_kinds() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree -e no-build,normal") .with_stderr_data(str![[r#" [ERROR] `normal` dependency kind cannot be mixed with "no-normal", "no-build", or "no-dev" dependency kinds "#]]) .with_status(101) .run(); // `no-proc-macro` can be mixed with others p.cargo("tree -e no-proc-macro,normal") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) "#]]) .run(); } #[cargo_test] fn depth_limit() { let p = make_simple_proj(); p.cargo("tree --depth 0") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) [build-dependencies] [dev-dependencies] "#]]) .run(); p.cargo("tree --depth 1") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── a v1.0.0 └── c v1.0.0 [build-dependencies] └── bdep v1.0.0 [dev-dependencies] └── devdep v1.0.0 "#]]) .run(); p.cargo("tree --depth 2") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── a v1.0.0 │ └── b v1.0.0 └── c v1.0.0 [build-dependencies] └── bdep v1.0.0 └── b v1.0.0 (*) [dev-dependencies] └── devdep v1.0.0 └── b v1.0.0 (*) "#]]) .run(); // specify a package p.cargo("tree -p bdep --depth 1") .with_stdout_data(str![[r#" bdep v1.0.0 └── b v1.0.0 "#]]) .run(); // different prefix p.cargo("tree --depth 1 --prefix depth") .with_stdout_data(str![[r#" 0foo v0.1.0 ([ROOT]/foo) 1a v1.0.0 1c v1.0.0 1bdep v1.0.0 1devdep v1.0.0 "#]]) .run(); // with edge-kinds p.cargo("tree --depth 1 -e no-dev") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── a v1.0.0 └── c v1.0.0 [build-dependencies] └── bdep v1.0.0 "#]]) .run(); // invert p.cargo("tree --depth 1 --invert c") .with_stdout_data(str![[r#" c v1.0.0 ├── b v1.0.0 └── foo v0.1.0 ([ROOT]/foo) "#]]) .run(); } #[cargo_test] fn depth_workspace() { Package::new("somedep", "1.0.0").publish(); Package::new("otherdep", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b", "c"] "#, ) .file("a/Cargo.toml", &basic_manifest("a", "1.0.0")) .file("a/src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" [dependencies] c = { path = "../c" } somedep = "1" "#, ) .file("b/src/lib.rs", "") .file( "c/Cargo.toml", r#" [package] name = "c" version = "0.1.0" [dependencies] somedep = "1" otherdep = "1" "#, ) .file("c/src/lib.rs", "") .build(); p.cargo("tree --depth workspace") .with_stdout_data(str![[r#" a v1.0.0 ([ROOT]/foo/a) b v0.1.0 ([ROOT]/foo/b) └── c v0.1.0 ([ROOT]/foo/c) c v0.1.0 ([ROOT]/foo/c) (*) "#]]) .run(); } #[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")] fn depth_public() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["diamond", "left-pub", "right-priv", "dep"] "#, ) .file( "diamond/Cargo.toml", r#" cargo-features = ["public-dependency"] [package] name = "diamond" version = "0.1.0" [dependencies] left-pub = { path = "../left-pub", public = true } right-priv = { path = "../right-priv", public = true } "#, ) .file("diamond/src/lib.rs", "") .file( "left-pub/Cargo.toml", r#" cargo-features = ["public-dependency"] [package] name = "left-pub" version = "0.1.0" [dependencies] dep = { path = "../dep", public = true } "#, ) .file("left-pub/src/lib.rs", "") .file( "right-priv/Cargo.toml", r#" [package] name = "right-priv" version = "0.1.0" [dependencies] dep = { path = "../dep" } "#, ) .file("right-priv/src/lib.rs", "") .file( "dep/Cargo.toml", r#" [package] name = "dep" version = "0.1.0" "#, ) .file("dep/src/lib.rs", "") .build(); p.cargo("tree --depth public") .masquerade_as_nightly_cargo(&["public-dependency", "depth-public"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] `--depth public` requires `-Zunstable-options` "#]]) .run(); p.cargo("tree --depth public -p left-pub") .arg("-Zunstable-options") .masquerade_as_nightly_cargo(&["public-dependency", "depth-public"]) .with_stdout_data(str![[r#" left-pub v0.1.0 ([ROOT]/foo/left-pub) └── dep v0.1.0 ([ROOT]/foo/dep) "#]]) .run(); p.cargo("tree --depth public -p right-priv") .arg("-Zunstable-options") .masquerade_as_nightly_cargo(&["public-dependency", "depth-public"]) .with_stdout_data(str![[r#" right-priv v0.1.0 ([ROOT]/foo/right-priv) "#]]) .run(); p.cargo("tree --depth public -p diamond") .arg("-Zunstable-options") .masquerade_as_nightly_cargo(&["public-dependency", "depth-public"]) .with_stdout_data(str![[r#" diamond v0.1.0 ([ROOT]/foo/diamond) ├── left-pub v0.1.0 ([ROOT]/foo/left-pub) │ └── dep v0.1.0 ([ROOT]/foo/dep) └── right-priv v0.1.0 ([ROOT]/foo/right-priv) "#]]) .run(); p.cargo("tree --depth public") .arg("-Zunstable-options") .masquerade_as_nightly_cargo(&["public-dependency", "depth-public"]) .with_stdout_data(str![[r#" dep v0.1.0 ([ROOT]/foo/dep) diamond v0.1.0 ([ROOT]/foo/diamond) ├── left-pub v0.1.0 ([ROOT]/foo/left-pub) │ └── dep v0.1.0 ([ROOT]/foo/dep) └── right-priv v0.1.0 ([ROOT]/foo/right-priv) left-pub v0.1.0 ([ROOT]/foo/left-pub) (*) right-priv v0.1.0 ([ROOT]/foo/right-priv) (*) "#]]) .run(); p.cargo("tree --depth public --invert dep") .arg("-Zunstable-options") .masquerade_as_nightly_cargo(&["public-dependency", "depth-public"]) .with_stdout_data(str![[r#" dep v0.1.0 ([ROOT]/foo/dep) └── left-pub v0.1.0 ([ROOT]/foo/left-pub) └── diamond v0.1.0 ([ROOT]/foo/diamond) "#]]) .run(); } #[cargo_test] fn prune() { let p = make_simple_proj(); p.cargo("tree --prune c") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── a v1.0.0 └── b v1.0.0 [build-dependencies] └── bdep v1.0.0 └── b v1.0.0 (*) [dev-dependencies] └── devdep v1.0.0 └── b v1.0.0 (*) "#]]) .run(); // multiple prune p.cargo("tree --prune c --prune bdep") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── a v1.0.0 └── b v1.0.0 [build-dependencies] [dev-dependencies] └── devdep v1.0.0 └── b v1.0.0 (*) "#]]) .run(); // with edge-kinds p.cargo("tree --prune c -e normal") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── a v1.0.0 └── b v1.0.0 "#]]) .run(); // pruning self does not works p.cargo("tree --prune foo") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── a v1.0.0 │ └── b v1.0.0 │ └── c v1.0.0 └── c v1.0.0 [build-dependencies] └── bdep v1.0.0 └── b v1.0.0 (*) [dev-dependencies] └── devdep v1.0.0 └── b v1.0.0 (*) "#]]) .run(); // dep not exist p.cargo("tree --prune no-dep") .with_stderr_data(str![[r#" [ERROR] package ID specification `no-dep` did not match any packages [HELP] a package with a similar name exists: `bdep` "#]]) .with_status(101) .run(); } #[cargo_test] fn cyclic_features() { // Check for stack overflow with cyclic features (oops!). let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" [features] a = ["b"] b = ["a"] default = ["a"] "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree -e features") .with_stdout_data(str![[r#" foo v1.0.0 ([ROOT]/foo) "#]]) .run(); p.cargo("tree -e features -i foo") .with_stdout_data(str![[r#" foo v1.0.0 ([ROOT]/foo) ├── foo feature "a" │ ├── foo feature "b" │ │ └── foo feature "a" (*) │ └── foo feature "default" (command-line) ├── foo feature "b" (*) └── foo feature "default" (command-line) "#]]) .run(); } #[cargo_test] fn dev_dep_cycle_with_feature() { // Cycle with features and a dev-dependency. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" [dev-dependencies] bar = { path = "bar" } [features] a = ["bar/feat1"] "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "1.0.0" [dependencies] foo = { path = ".." } [features] feat1 = ["foo/a"] "#, ) .file("bar/src/lib.rs", "") .build(); p.cargo("tree -e features --features a") .with_stdout_data(str![[r#" foo v1.0.0 ([ROOT]/foo) [dev-dependencies] └── bar feature "default" └── bar v1.0.0 ([ROOT]/foo/bar) └── foo feature "default" (command-line) └── foo v1.0.0 ([ROOT]/foo) (*) "#]]) .run(); p.cargo("tree -e features --features a -i foo") .with_stdout_data(str![[r#" foo v1.0.0 ([ROOT]/foo) ├── foo feature "a" (command-line) │ └── bar feature "feat1" │ └── foo feature "a" (command-line) (*) └── foo feature "default" (command-line) └── bar v1.0.0 ([ROOT]/foo/bar) ├── bar feature "default" │ [dev-dependencies] │ └── foo v1.0.0 ([ROOT]/foo) (*) └── bar feature "feat1" (*) "#]]) .run(); } #[cargo_test] fn dev_dep_cycle_with_feature_nested() { // Checks for an issue where a cyclic dev dependency tries to activate a // feature on its parent that tries to activate the feature back on the // dev-dependency. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" [dev-dependencies] bar = { path = "bar" } [features] a = ["bar/feat1"] b = ["a"] "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "1.0.0" [dependencies] foo = { path = ".." } [features] feat1 = ["foo/b"] "#, ) .file("bar/src/lib.rs", "") .build(); p.cargo("tree -e features") .with_stdout_data(str![[r#" foo v1.0.0 ([ROOT]/foo) [dev-dependencies] └── bar feature "default" └── bar v1.0.0 ([ROOT]/foo/bar) └── foo feature "default" (command-line) └── foo v1.0.0 ([ROOT]/foo) (*) "#]]) .run(); p.cargo("tree -e features --features a -i foo") .with_stdout_data(str![[r#" foo v1.0.0 ([ROOT]/foo) ├── foo feature "a" (command-line) │ └── foo feature "b" │ └── bar feature "feat1" │ └── foo feature "a" (command-line) (*) ├── foo feature "b" (*) └── foo feature "default" (command-line) └── bar v1.0.0 ([ROOT]/foo/bar) ├── bar feature "default" │ [dev-dependencies] │ └── foo v1.0.0 ([ROOT]/foo) (*) └── bar feature "feat1" (*) "#]]) .run(); p.cargo("tree -e features --features b -i foo") .with_stdout_data(str![[r#" foo v1.0.0 ([ROOT]/foo) ├── foo feature "a" │ └── foo feature "b" (command-line) │ └── bar feature "feat1" │ └── foo feature "a" (*) ├── foo feature "b" (command-line) (*) └── foo feature "default" (command-line) └── bar v1.0.0 ([ROOT]/foo/bar) ├── bar feature "default" │ [dev-dependencies] │ └── foo v1.0.0 ([ROOT]/foo) (*) └── bar feature "feat1" (*) "#]]) .run(); p.cargo("tree -e features --features bar/feat1 -i foo") .with_stdout_data(str![[r#" foo v1.0.0 ([ROOT]/foo) ├── foo feature "a" │ └── foo feature "b" │ └── bar feature "feat1" (command-line) │ └── foo feature "a" (*) ├── foo feature "b" (*) └── foo feature "default" (command-line) └── bar v1.0.0 ([ROOT]/foo/bar) ├── bar feature "default" │ [dev-dependencies] │ └── foo v1.0.0 ([ROOT]/foo) (*) └── bar feature "feat1" (command-line) (*) "#]]) .run(); } #[cargo_test] fn no_proc_macro_order() { Package::new("dep", "1.0.0").publish(); Package::new("pm", "1.0.0").proc_macro(true).publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] pm = "1.0" dep = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── dep v1.0.0 └── pm v1.0.0 (proc-macro) "#]]) .run(); // no-proc-macro combined with other edge kinds p.cargo("tree -e normal,no-proc-macro") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── dep v1.0.0 "#]]) .run(); // change flag order, expecting the same output p.cargo("tree -e no-proc-macro,normal") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── dep v1.0.0 "#]]) .run(); } cargo-0.91.0/tests/testsuite/cargo_tree/dupe/mod.rs000064400000000000000000000015621046102023000204010ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::project; use cargo_test_support::registry::Package; #[cargo_test] fn case() { Package::new("a", "1.0.0").dep("b", "1.0").publish(); Package::new("b", "1.0.0").dep("c", "1.0").publish(); Package::new("c", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] a = "1.0" b = "1.0" "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .build(); snapbox::cmd::Command::cargo_ui() .arg("tree") .current_dir(p.root()) .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(file!["stderr.term.svg"]); } cargo-0.91.0/tests/testsuite/cargo_tree/dupe/stderr.term.svg000064400000000000000000000026031046102023000222430ustar 00000000000000 Updating `dummy-registry` index Locking 3 packages to latest compatible versions Downloading crates ... Downloaded c v1.0.0 (registry `dummy-registry`) Downloaded b v1.0.0 (registry `dummy-registry`) Downloaded a v1.0.0 (registry `dummy-registry`) cargo-0.91.0/tests/testsuite/cargo_tree/dupe/stdout.term.svg000064400000000000000000000024041046102023000222610ustar 00000000000000 foo v0.1.0 ([ROOT]/foo) ├── a v1.0.0 └── b v1.0.0 └── c v1.0.0 └── b v1.0.0 (*) cargo-0.91.0/tests/testsuite/cargo_tree/edge_kind/mod.rs000064400000000000000000000061571046102023000213620ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::project; use cargo_test_support::registry::Package; #[cargo_test] fn case() { Package::new("normal_a", "1.0.0") .dep("normal_b", "1.0") .publish(); Package::new("normal_b", "1.0.0") .dep("normal_c", "1.0") .build_dep("normal_b_build_a", "1.0.0") .dev_dep("normal_b_dev_a", "1.0.0") .publish(); Package::new("normal_c", "1.0.0").publish(); Package::new("normal_b_build_a", "1.0.0") .dep("normal_b_build_a_normal_a", "1.0.0") .publish(); Package::new("normal_b_build_a_normal_a", "1.0.0").publish(); Package::new("normal_b_dev_a", "1.0.0") .dep("normal_b_dev_a_normal_a", "1.0.0") .publish(); Package::new("normal_b_dev_a_normal_a", "1.0.0").publish(); Package::new("normal_d", "1.0.0").publish(); Package::new("build_a", "1.0.0") .dep("build_b", "1.0") .publish(); Package::new("build_b", "1.0.0") .dep("build_c", "1.0") .build_dep("build_b_build_a", "1.0.0") .dev_dep("build_b_dev_a", "1.0.0") .publish(); Package::new("build_c", "1.0.0").publish(); Package::new("build_b_build_a", "1.0.0") .dep("build_b_build_a_normal_a", "1.0.0") .publish(); Package::new("build_b_build_a_normal_a", "1.0.0").publish(); Package::new("build_b_dev_a", "1.0.0") .dep("build_b_dev_a_normal_a", "1.0.0") .publish(); Package::new("build_b_dev_a_normal_a", "1.0.0").publish(); Package::new("build_d", "1.0.0").publish(); Package::new("dev_a", "1.0.0").dep("dev_b", "1.0").publish(); Package::new("dev_b", "1.0.0") .dep("dev_c", "1.0") .build_dep("dev_b_build_a", "1.0.0") .dev_dep("dev_b_dev_a", "1.0.0") .publish(); Package::new("dev_c", "1.0.0").publish(); Package::new("dev_b_build_a", "1.0.0") .dep("dev_b_build_a_normal_a", "1.0.0") .publish(); Package::new("dev_b_build_a_normal_a", "1.0.0").publish(); Package::new("dev_b_dev_a", "1.0.0") .dep("dev_b_dev_a_normal_a", "1.0.0") .publish(); Package::new("dev_b_dev_a_normal_a", "1.0.0").publish(); Package::new("dev_d", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [features] default = ["foo"] foo = ["dep:normal_a"] [dependencies] normal_a = { version = "1.0", optional = true } normal_d = "1.0" [build-dependencies] build_a = "1.0" build_d = "1.0" [dev-dependencies] dev_a = "1.0" dev_d = "1.0" "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .build(); snapbox::cmd::Command::cargo_ui() .arg("tree") .arg("--edges=features") .current_dir(p.root()) .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(file!["stderr.term.svg"]); } cargo-0.91.0/tests/testsuite/cargo_tree/edge_kind/stderr.term.svg000064400000000000000000000071701046102023000232230ustar 00000000000000 Updating `dummy-registry` index Locking 18 packages to latest compatible versions Downloading crates ... Downloaded normal_d v1.0.0 (registry `dummy-registry`) Downloaded normal_c v1.0.0 (registry `dummy-registry`) Downloaded normal_b_build_a_normal_a v1.0.0 (registry `dummy-registry`) Downloaded normal_b_build_a v1.0.0 (registry `dummy-registry`) Downloaded normal_b v1.0.0 (registry `dummy-registry`) Downloaded normal_a v1.0.0 (registry `dummy-registry`) Downloaded dev_d v1.0.0 (registry `dummy-registry`) Downloaded dev_c v1.0.0 (registry `dummy-registry`) Downloaded dev_b_build_a_normal_a v1.0.0 (registry `dummy-registry`) Downloaded dev_b_build_a v1.0.0 (registry `dummy-registry`) Downloaded dev_b v1.0.0 (registry `dummy-registry`) Downloaded dev_a v1.0.0 (registry `dummy-registry`) Downloaded build_d v1.0.0 (registry `dummy-registry`) Downloaded build_c v1.0.0 (registry `dummy-registry`) Downloaded build_b_build_a_normal_a v1.0.0 (registry `dummy-registry`) Downloaded build_b_build_a v1.0.0 (registry `dummy-registry`) Downloaded build_b v1.0.0 (registry `dummy-registry`) Downloaded build_a v1.0.0 (registry `dummy-registry`) cargo-0.91.0/tests/testsuite/cargo_tree/edge_kind/stdout.term.svg000064400000000000000000000307421046102023000232430ustar 00000000000000 foo v0.1.0 ([ROOT]/foo) ├── normal_a feature "default" └── normal_a v1.0.0 └── normal_b feature "default" └── normal_b v1.0.0 └── normal_c feature "default" └── normal_c v1.0.0 [build-dependencies] └── normal_b_build_a feature "default" └── normal_b_build_a v1.0.0 └── normal_b_build_a_normal_a feature "default" └── normal_b_build_a_normal_a v1.0.0 └── normal_d feature "default" └── normal_d v1.0.0 [build-dependencies] ├── build_a feature "default" └── build_a v1.0.0 └── build_b feature "default" └── build_b v1.0.0 └── build_c feature "default" └── build_c v1.0.0 [build-dependencies] └── build_b_build_a feature "default" └── build_b_build_a v1.0.0 └── build_b_build_a_normal_a feature "default" └── build_b_build_a_normal_a v1.0.0 └── build_d feature "default" └── build_d v1.0.0 [dev-dependencies] ├── dev_a feature "default" └── dev_a v1.0.0 └── dev_b feature "default" └── dev_b v1.0.0 └── dev_c feature "default" └── dev_c v1.0.0 [build-dependencies] └── dev_b_build_a feature "default" └── dev_b_build_a v1.0.0 └── dev_b_build_a_normal_a feature "default" └── dev_b_build_a_normal_a v1.0.0 └── dev_d feature "default" └── dev_d v1.0.0 cargo-0.91.0/tests/testsuite/cargo_tree/features.rs000064400000000000000000000351321046102023000205030ustar 00000000000000//! Tests for the `cargo tree` command with -e features option. use crate::prelude::*; use cargo_test_support::project; use cargo_test_support::registry::{Dependency, Package}; use cargo_test_support::str; #[cargo_test] fn dep_feature_various() { // Checks different ways of setting features via dependencies. Package::new("optdep", "1.0.0") .feature("default", &["cat"]) .feature("cat", &[]) .publish(); Package::new("defaultdep", "1.0.0") .feature("default", &["f1"]) .feature("f1", &["optdep"]) .add_dep(Dependency::new("optdep", "1.0").optional(true)) .publish(); Package::new("nodefaultdep", "1.0.0") .feature("default", &["f1"]) .feature("f1", &[]) .publish(); Package::new("nameddep", "1.0.0") .add_dep(Dependency::new("serde", "1.0").optional(true)) .feature("default", &["serde-stuff"]) .feature("serde-stuff", &["serde/derive"]) .feature("vehicle", &["car"]) .feature("car", &[]) .publish(); Package::new("serde_derive", "1.0.0").publish(); Package::new("serde", "1.0.0") .feature("derive", &["serde_derive"]) .add_dep(Dependency::new("serde_derive", "1.0").optional(true)) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] defaultdep = "1.0" nodefaultdep = {version="1.0", default-features = false} nameddep = {version="1.0", features = ["vehicle", "serde"]} "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree -e features") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── nodefaultdep v1.0.0 ├── defaultdep feature "default" │ ├── defaultdep v1.0.0 │ │ └── optdep feature "default" │ │ ├── optdep v1.0.0 │ │ └── optdep feature "cat" │ │ └── optdep v1.0.0 │ └── defaultdep feature "f1" │ ├── defaultdep v1.0.0 (*) │ └── defaultdep feature "optdep" │ └── defaultdep v1.0.0 (*) ├── nameddep feature "default" │ ├── nameddep v1.0.0 │ │ └── serde feature "default" │ │ └── serde v1.0.0 │ │ └── serde_derive feature "default" │ │ └── serde_derive v1.0.0 │ └── nameddep feature "serde-stuff" │ ├── nameddep v1.0.0 (*) │ ├── nameddep feature "serde" │ │ └── nameddep v1.0.0 (*) │ └── serde feature "derive" │ ├── serde v1.0.0 (*) │ └── serde feature "serde_derive" │ └── serde v1.0.0 (*) ├── nameddep feature "serde" (*) └── nameddep feature "vehicle" ├── nameddep v1.0.0 (*) └── nameddep feature "car" └── nameddep v1.0.0 (*) "#]]) .run(); } #[cargo_test] fn graph_features_ws_interdependent() { // A workspace with interdependent crates. let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" [dependencies] b = {path="../b", features=["feat2"]} [features] default = ["a1"] a1 = [] a2 = [] "#, ) .file("a/src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" [features] default = ["feat1"] feat1 = [] feat2 = [] "#, ) .file("b/src/lib.rs", "") .build(); p.cargo("tree -e features") .with_stdout_data(str![[r#" a v0.1.0 ([ROOT]/foo/a) ├── b feature "default" (command-line) │ ├── b v0.1.0 ([ROOT]/foo/b) │ └── b feature "feat1" │ └── b v0.1.0 ([ROOT]/foo/b) └── b feature "feat2" └── b v0.1.0 ([ROOT]/foo/b) b v0.1.0 ([ROOT]/foo/b) "#]]) .run(); p.cargo("tree -e features -i a -i b") .with_stdout_data(str![[r#" a v0.1.0 ([ROOT]/foo/a) ├── a feature "a1" │ └── a feature "default" (command-line) └── a feature "default" (command-line) b v0.1.0 ([ROOT]/foo/b) ├── b feature "default" (command-line) │ └── a v0.1.0 ([ROOT]/foo/a) (*) ├── b feature "feat1" │ └── b feature "default" (command-line) (*) └── b feature "feat2" └── a v0.1.0 ([ROOT]/foo/a) (*) "#]]) .run(); } #[cargo_test] fn slash_feature_name() { // dep_name/feat_name syntax Package::new("opt", "1.0.0").feature("feat1", &[]).publish(); Package::new("notopt", "1.0.0") .feature("cat", &[]) .feature("animal", &["cat"]) .publish(); Package::new("opt2", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] opt = {version = "1.0", optional=true} opt2 = {version = "1.0", optional=true} notopt = "1.0" [features] f1 = ["opt/feat1", "notopt/animal"] f2 = ["f1"] "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree -e features --features f1") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── notopt feature "default" │ └── notopt v1.0.0 └── opt feature "default" └── opt v1.0.0 "#]]) .run(); p.cargo("tree -e features --features f1 -i foo") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── foo feature "default" (command-line) ├── foo feature "f1" (command-line) └── foo feature "opt" └── foo feature "f1" (command-line) "#]]) .run(); p.cargo("tree -e features --features f1 -i notopt") .with_stdout_data(str![[r#" notopt v1.0.0 ├── notopt feature "animal" │ └── foo feature "f1" (command-line) ├── notopt feature "cat" │ └── notopt feature "animal" (*) └── notopt feature "default" └── foo v0.1.0 ([ROOT]/foo) ├── foo feature "default" (command-line) ├── foo feature "f1" (command-line) └── foo feature "opt" └── foo feature "f1" (command-line) "#]]) .run(); p.cargo("tree -e features --features notopt/animal -i notopt") .with_stdout_data(str![[r#" notopt v1.0.0 ├── notopt feature "animal" (command-line) ├── notopt feature "cat" │ └── notopt feature "animal" (command-line) └── notopt feature "default" └── foo v0.1.0 ([ROOT]/foo) └── foo feature "default" (command-line) "#]]) .run(); p.cargo("tree -e features --all-features") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── notopt feature "default" │ └── notopt v1.0.0 ├── opt feature "default" │ └── opt v1.0.0 └── opt2 feature "default" └── opt2 v1.0.0 "#]]) .run(); p.cargo("tree -e features --all-features -i opt2") .with_stdout_data(str![[r#" opt2 v1.0.0 └── opt2 feature "default" └── foo v0.1.0 ([ROOT]/foo) ├── foo feature "default" (command-line) ├── foo feature "f1" (command-line) │ └── foo feature "f2" (command-line) ├── foo feature "f2" (command-line) ├── foo feature "opt" (command-line) │ └── foo feature "f1" (command-line) (*) └── foo feature "opt2" (command-line) "#]]) .run(); } #[cargo_test] fn features_enables_inactive_target() { // Features that enable things on targets that are not enabled. Package::new("optdep", "1.0.0") .feature("feat1", &[]) .publish(); Package::new("dep1", "1.0.0") .feature("somefeat", &[]) .publish(); Package::new("dep2", "1.0.0") .add_dep( Dependency::new("optdep", "1.0.0") .optional(true) .target("cfg(whatever)"), ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [target.'cfg(whatever)'.dependencies] optdep = {version="1.0", optional=true} dep1 = "1.0" [dependencies] dep2 = "1.0" [features] f1 = ["optdep"] f2 = ["optdep/feat1"] f3 = ["dep1/somefeat"] f4 = ["dep2/optdep"] "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree -e features") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── dep2 feature "default" └── dep2 v1.0.0 "#]]) .run(); p.cargo("tree -e features --all-features") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── dep2 feature "default" └── dep2 v1.0.0 "#]]) .run(); p.cargo("tree -e features --all-features --target=all") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── dep1 feature "default" │ └── dep1 v1.0.0 ├── dep2 feature "default" │ └── dep2 v1.0.0 │ └── optdep feature "default" │ └── optdep v1.0.0 └── optdep feature "default" (*) "#]]) .run(); } #[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")] fn depth_public_no_features() { Package::new("pub-defaultdep", "1.0.0").publish(); Package::new("priv-defaultdep", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" cargo-features = ["public-dependency"] [package] name = "foo" version = "0.1.0" [dependencies] pub-defaultdep = { version = "1.0.0", public = true } priv-defaultdep = "1.0.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree -e features --depth public") .arg("-Zunstable-options") .masquerade_as_nightly_cargo(&["public-dependency", "depth-public"]) .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── pub-defaultdep feature "default" └── pub-defaultdep v1.0.0 "#]]) .run(); } #[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")] fn depth_public_transitive_features() { Package::new("pub-defaultdep", "1.0.0") .feature("default", &["f1"]) .feature("f1", &["f2"]) .feature("f2", &["optdep"]) .add_dep(Dependency::new("optdep", "1.0").optional(true).public(true)) .publish(); Package::new("priv-defaultdep", "1.0.0") .feature("default", &["f1"]) .feature("f1", &["f2"]) .feature("f2", &["optdep"]) .add_dep(Dependency::new("optdep", "1.0").optional(true)) .publish(); Package::new("optdep", "1.0.0") .feature("default", &["f"]) .feature("f", &[]) .publish(); let p = project() .file( "Cargo.toml", r#" cargo-features = ["public-dependency"] [package] name = "foo" version = "0.1.0" [dependencies] pub-defaultdep = { version = "1.0.0", public = true } priv-defaultdep = { version = "1.0.0", public = true } "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree -e features --depth public") .arg("-Zunstable-options") .masquerade_as_nightly_cargo(&["public-dependency", "depth-public"]) .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── priv-defaultdep feature "default" │ ├── priv-defaultdep v1.0.0 │ └── priv-defaultdep feature "f1" │ ├── priv-defaultdep v1.0.0 (*) │ └── priv-defaultdep feature "f2" │ ├── priv-defaultdep v1.0.0 (*) │ └── priv-defaultdep feature "optdep" │ └── priv-defaultdep v1.0.0 (*) └── pub-defaultdep feature "default" ├── pub-defaultdep v1.0.0 │ └── optdep feature "default" │ ├── optdep v1.0.0 │ └── optdep feature "f" │ └── optdep v1.0.0 └── pub-defaultdep feature "f1" ├── pub-defaultdep v1.0.0 (*) └── pub-defaultdep feature "f2" ├── pub-defaultdep v1.0.0 (*) └── pub-defaultdep feature "optdep" └── pub-defaultdep v1.0.0 (*) "#]]) .run(); } #[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")] fn depth_public_cli() { Package::new("priv", "1.0.0").feature("f", &[]).publish(); Package::new("pub", "1.0.0").feature("f", &[]).publish(); let p = project() .file( "Cargo.toml", r#" cargo-features = ["public-dependency"] [package] name = "foo" version = "0.1.0" [features] priv-indirect = ["priv"] priv = ["dep:priv", "priv?/f"] pub-indirect = ["pub"] pub = ["dep:pub", "priv?/f"] [dependencies] priv = { version = "1.0.0", optional = true } pub = { version = "1.0.0", optional = true, public = true } "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree -e features --depth public") .arg("-Zunstable-options") .masquerade_as_nightly_cargo(&["public-dependency", "depth-public"]) .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) "#]]) .run(); p.cargo("tree -e features --depth public --features pub-indirect") .arg("-Zunstable-options") .masquerade_as_nightly_cargo(&["public-dependency", "depth-public"]) .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── pub feature "default" └── pub v1.0.0 "#]]) .run(); p.cargo("tree -e features --depth public --features priv-indirect") .arg("-Zunstable-options") .masquerade_as_nightly_cargo(&["public-dependency", "depth-public"]) .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) "#]]) .run(); } cargo-0.91.0/tests/testsuite/cargo_tree/help/mod.rs000064400000000000000000000004611046102023000203710ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("tree") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_tree/help/stdout.term.svg000064400000000000000000000214351046102023000222610ustar 00000000000000 Display a tree visualization of a dependency graph Usage: cargo[EXE] tree [OPTIONS] Options: -e, --edges <KINDS> The kinds of dependencies to display (features, normal, build, dev, all, no-normal, no-build, no-dev, no-proc-macro) -i, --invert [<SPEC>] Invert the tree direction and focus on the given package --prune <SPEC> Prune the given package from the display of the dependency tree --depth <DEPTH> Maximum display depth of the dependency tree --prefix <PREFIX> Change the prefix (indentation) of how each entry is displayed [default: indent] [possible values: depth, indent, none] --no-dedupe Do not de-duplicate (repeats all shared dependencies) -d, --duplicates Show only dependencies which come in multiple versions (implies -i) --charset <CHARSET> Character set to use in output [possible values: utf8, ascii] -f, --format <FORMAT> Format string used for printing dependencies [default: {p}] -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Package Selection: -p, --package [<SPEC>] Package to be used as the root of the tree --workspace Display the tree for all packages in the workspace --exclude <SPEC> Exclude specific workspace members Feature Selection: -F, --features <FEATURES> Space or comma separated list of features to activate --all-features Activate all available features --no-default-features Do not activate the `default` feature Compilation Options: --target [<TRIPLE>] Filter dependencies matching the given target-triple (default host platform). Pass `all` to include all targets. Manifest Options: --manifest-path <PATH> Path to Cargo.toml --lockfile-path <PATH> Path to Cargo.lock (unstable) --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help tree` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_tree/mod.rs000064400000000000000000000000731046102023000174400ustar 00000000000000mod deps; mod dupe; mod edge_kind; mod features; mod help; cargo-0.91.0/tests/testsuite/cargo_uninstall/help/mod.rs000064400000000000000000000004661046102023000214500ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("uninstall") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_uninstall/help/stdout.term.svg000064400000000000000000000113231046102023000233260ustar 00000000000000 Remove a Rust binary Usage: cargo[EXE] uninstall [OPTIONS] [SPEC]... Arguments: [SPEC]... Options: --root <DIR> Directory to uninstall packages from -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Package Selection: -p, --package [<SPEC>] Package to uninstall Target Selection: --bin <NAME> Only uninstall the binary NAME Manifest Options: --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help uninstall` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_uninstall/mod.rs000064400000000000000000000000121046102023000205030ustar 00000000000000mod help; cargo-0.91.0/tests/testsuite/cargo_update/help/mod.rs000064400000000000000000000004631046102023000207160ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("update") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_update/help/stdout.term.svg000064400000000000000000000127741046102023000226120ustar 00000000000000 Update dependencies as recorded in the local lock file Usage: cargo[EXE] update [OPTIONS] [SPEC]... Options: -n, --dry-run Don't actually write the lockfile --recursive Force updating all dependencies of [SPEC]... as well --precise <PRECISE> Update [SPEC] to exactly PRECISE -b, --breaking Update [SPEC] to latest SemVer-breaking version (unstable) -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Package Selection: -w, --workspace Only update the workspace packages [SPEC]... Package to update Manifest Options: --manifest-path <PATH> Path to Cargo.toml --lockfile-path <PATH> Path to Cargo.lock (unstable) --ignore-rust-version Ignore `rust-version` specification in packages --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help update` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_update/mod.rs000064400000000000000000000000411046102023000177560ustar 00000000000000mod help; mod toolchain_pkgname; cargo-0.91.0/tests/testsuite/cargo_update/toolchain_pkgname/mod.rs000064400000000000000000000010361046102023000234450ustar 00000000000000use crate::prelude::*; use cargo_test_support::Project; use cargo_test_support::current_dir; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { let project = Project::from_template(current_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; snapbox::cmd::Command::cargo_ui() .arg("update") .arg("+stable") .current_dir(cwd) .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); } cargo-0.91.0/tests/testsuite/cargo_update/toolchain_pkgname/stderr.term.svg000064400000000000000000000016051046102023000253140ustar 00000000000000 error: invalid character `+` in package name: `+stable` Use `cargo +stable update` if you meant to use the `stable` toolchain. cargo-0.91.0/tests/testsuite/cargo_vendor/help/mod.rs000064400000000000000000000004631046102023000207310ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("vendor") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_vendor/help/stdout.term.svg000064400000000000000000000120731046102023000226150ustar 00000000000000 Vendor all dependencies for a project locally Usage: cargo[EXE] vendor [OPTIONS] [path] Arguments: [path] Where to vendor crates (`vendor` by default) Options: --no-delete Don't delete older crates in the vendor directory -s, --sync <TOML> Additional `Cargo.toml` to sync and vendor --respect-source-config Respect `[source]` config in `.cargo/config` --versioned-dirs Always include version in subdir name -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Manifest Options: --manifest-path <PATH> Path to Cargo.toml --lockfile-path <PATH> Path to Cargo.lock (unstable) --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help vendor` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_vendor/mod.rs000064400000000000000000000000121046102023000177670ustar 00000000000000mod help; cargo-0.91.0/tests/testsuite/cargo_verify_project/help/mod.rs000064400000000000000000000004731046102023000224670ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("verify-project") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_verify_project/help/stdout.term.svg000064400000000000000000000071751046102023000243610ustar 00000000000000 DEPRECATED: Check correctness of crate manifest. See https://github.com/rust-lang/cargo/issues/14679. Usage: cargo[EXE] verify-project [OPTIONS] Options: -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Manifest Options: --manifest-path <PATH> Path to Cargo.toml --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline cargo-0.91.0/tests/testsuite/cargo_verify_project/mod.rs000064400000000000000000000000121046102023000215240ustar 00000000000000mod help; cargo-0.91.0/tests/testsuite/cargo_version/help/mod.rs000064400000000000000000000004641046102023000211220ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("version") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_version/help/stdout.term.svg000064400000000000000000000066341046102023000230130ustar 00000000000000 Show version information Usage: cargo[EXE] version [OPTIONS] Options: -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Manifest Options: --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help version` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_version/mod.rs000064400000000000000000000000121046102023000201570ustar 00000000000000mod help; cargo-0.91.0/tests/testsuite/cargo_yank/help/mod.rs000064400000000000000000000004611046102023000203740ustar 00000000000000use crate::prelude::*; use cargo_test_support::file; use cargo_test_support::str; #[cargo_test] fn case() { snapbox::cmd::Command::cargo_ui() .arg("yank") .arg("--help") .assert() .success() .stdout_eq(file!["stdout.term.svg"]) .stderr_eq(str![""]); } cargo-0.91.0/tests/testsuite/cargo_yank/help/stdout.term.svg000064400000000000000000000114661046102023000222670ustar 00000000000000 Remove a pushed crate from the index Usage: cargo[EXE] yank [OPTIONS] [CRATE] Arguments: [CRATE] Options: --version <VERSION> The version to yank or un-yank --undo Undo a yank, putting a version back into the index --index <INDEX> Registry index URL to yank from --registry <REGISTRY> Registry to yank from --token <TOKEN> API token to use when authenticating -v, --verbose... Use verbose output (-vv very verbose/build.rs output) -q, --quiet Do not print cargo log messages --color <WHEN> Coloring [possible values: auto, always, never] --config <KEY=VALUE|PATH> Override a configuration value -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help Manifest Options: --locked Assert that `Cargo.lock` will remain unchanged --offline Run without accessing the network --frozen Equivalent to specifying both --locked and --offline Run `cargo help yank` for more detailed information. cargo-0.91.0/tests/testsuite/cargo_yank/mod.rs000064400000000000000000000000121046102023000174340ustar 00000000000000mod help; cargo-0.91.0/tests/testsuite/cfg.rs000064400000000000000000000522321046102023000153120ustar 00000000000000//! Tests for `cfg()` expressions. use crate::prelude::*; use cargo_test_support::registry::Package; use cargo_test_support::rustc_host; use cargo_test_support::{basic_manifest, project, str}; #[cargo_test] fn cfg_easy() { let p = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] [target.'cfg(unix)'.dependencies] b = { path = 'b' } [target."cfg(windows)".dependencies] b = { path = 'b' } "#, ) .file("src/lib.rs", "extern crate b;") .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) .file("b/src/lib.rs", "") .build(); p.cargo("check -v").run(); } #[cargo_test] fn dont_include() { let other_family = if cfg!(unix) { "windows" } else { "unix" }; let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] [target.'cfg({})'.dependencies] b = {{ path = 'b' }} "#, other_family ), ) .file("src/lib.rs", "") .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) .file("b/src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [CHECKING] a v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn works_through_the_registry() { Package::new("baz", "0.1.0").publish(); Package::new("bar", "0.1.0") .target_dep("baz", "0.1.0", "cfg(unix)") .target_dep("baz", "0.1.0", "cfg(windows)") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file( "src/lib.rs", "#[allow(unused_extern_crates)] extern crate bar;", ) .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] baz v0.1.0 (registry `dummy-registry`) [DOWNLOADED] bar v0.1.0 (registry `dummy-registry`) [CHECKING] baz v0.1.0 [CHECKING] bar v0.1.0 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn ignore_version_from_other_platform() { let this_family = if cfg!(unix) { "unix" } else { "windows" }; let other_family = if cfg!(unix) { "windows" } else { "unix" }; Package::new("bar", "0.1.0").publish(); Package::new("bar", "0.2.0").publish(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [target.'cfg({})'.dependencies] bar = "0.1.0" [target.'cfg({})'.dependencies] bar = "0.2.0" "#, this_family, other_family ), ) .file( "src/lib.rs", "#[allow(unused_extern_crates)] extern crate bar;", ) .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [ADDING] bar v0.1.0 (available: v0.2.0) [DOWNLOADING] crates ... [DOWNLOADED] bar v0.1.0 (registry `dummy-registry`) [CHECKING] bar v0.1.0 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn bad_target_spec() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [target.'cfg(4)'.dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: failed to parse `4` as a cfg expression: unexpected character `4` in cfg, expected parens, a comma, an identifier, or a string "#]]) .run(); } #[cargo_test] fn bad_target_spec2() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [target.'cfg(bar =)'.dependencies] baz = "0.1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: failed to parse `bar =` as a cfg expression: expected a string, but cfg expression ended "#]]) .run(); } #[cargo_test] fn multiple_match_ok() { let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] [target.'cfg(unix)'.dependencies] b = {{ path = 'b' }} [target.'cfg(target_family = "unix")'.dependencies] b = {{ path = 'b' }} [target."cfg(windows)".dependencies] b = {{ path = 'b' }} [target.'cfg(target_family = "windows")'.dependencies] b = {{ path = 'b' }} [target."cfg(any(windows, unix))".dependencies] b = {{ path = 'b' }} [target.{}.dependencies] b = {{ path = 'b' }} "#, rustc_host() ), ) .file("src/lib.rs", "extern crate b;") .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) .file("b/src/lib.rs", "") .build(); p.cargo("check -v").run(); } #[cargo_test] fn any_ok() { let p = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] [target."cfg(any(windows, unix))".dependencies] b = { path = 'b' } "#, ) .file("src/lib.rs", "extern crate b;") .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) .file("b/src/lib.rs", "") .build(); p.cargo("check -v").run(); } // https://github.com/rust-lang/cargo/issues/5313 #[cargo_test] #[cfg(all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"))] fn cfg_looks_at_rustflags_for_target() { let p = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] [target.'cfg(with_b)'.dependencies] b = { path = 'b' } "#, ) .file( "src/main.rs", r#" #[cfg(with_b)] extern crate b; fn main() { b::foo(); } "#, ) .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) .file("b/src/lib.rs", "pub fn foo() {}") .build(); p.cargo("check --target x86_64-unknown-linux-gnu") .env("RUSTFLAGS", "--cfg with_b") .run(); } #[cargo_test] fn bad_cfg_discovery() { // Check error messages when `rustc -v` and `rustc --print=*` parsing fails. // // This is a `rustc` replacement which behaves differently based on an // environment variable. let p = project() .at("compiler") .file("Cargo.toml", &basic_manifest("compiler", "0.1.0")) .file( "src/main.rs", r#" fn run_rustc() -> String { let mut cmd = std::process::Command::new("rustc"); for arg in std::env::args_os().skip(1) { cmd.arg(arg); } String::from_utf8(cmd.output().unwrap().stdout).unwrap() } fn main() { let mode = std::env::var("FUNKY_MODE").unwrap(); if mode == "bad-version" { println!("foo"); return; } if std::env::args_os().any(|a| a == "-vV") { print!("{}", run_rustc()); return; } if mode == "no-crate-types" { return; } if mode == "bad-crate-type" { println!("foo"); return; } let output = run_rustc(); let mut lines = output.lines(); let sysroot = loop { let line = lines.next().unwrap(); if line.contains("___") { println!("{}", line); } else { break line; } }; if mode == "no-sysroot" { return; } println!("{}", sysroot); if mode == "no-split-debuginfo" { return; } loop { let line = lines.next().unwrap(); if line == "___" { println!("\n{line}"); break; } else { // As the number split-debuginfo options varies, // concat them into one line. print!("{line},"); } }; if mode != "bad-cfg" { panic!("unexpected"); } println!("123"); } "#, ) .build(); p.cargo("build").run(); let funky_rustc = p.bin("compiler"); let p = project().file("src/lib.rs", "").build(); p.cargo("check") .env("RUSTC", &funky_rustc) .env("FUNKY_MODE", "bad-version") .with_status(101) .with_stderr_data(str![[r#" [ERROR] `rustc -vV` didn't have a line for `host:`, got: foo "#]]) .run(); p.cargo("check") .env("RUSTC", &funky_rustc) .env("FUNKY_MODE", "no-crate-types") .with_status(101) .with_stderr_data(str![[r#" [ERROR] malformed output when learning about crate-type bin information command was: `[ROOT]/compiler/target/debug/compiler[..] --crate-name ___ [..]` (no output received) "#]]) .run(); p.cargo("check") .env("RUSTC", &funky_rustc) .env("FUNKY_MODE", "no-sysroot") .with_status(101) .with_stderr_data(str![[r#" [ERROR] output of --print=sysroot missing when learning about target-specific information from rustc command was: `[ROOT]/compiler/target/debug/compiler[..]--crate-type [..]` --- stdout ___[EXE] lib___.rlib [..]___.[..] [..]___.[..] [..]___.[..] [..]___.[..] "#]]) .run(); p.cargo("check") .env("RUSTC", &funky_rustc) .env("FUNKY_MODE", "no-split-debuginfo") .with_status(101) .with_stderr_data(str![[r#" [ERROR] output of --print=split-debuginfo missing when learning about target-specific information from rustc command was: `[ROOT]/compiler/target/debug/compiler[..]--crate-type [..]` --- stdout ___[EXE] lib___.rlib [..]___.[..] [..]___.[..] [..]___.[..] [..]___.[..] [..] "#]]) .run(); p.cargo("check") .env("RUSTC", &funky_rustc) .env("FUNKY_MODE", "bad-cfg") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse the cfg from `rustc --print=cfg`, got: ___[EXE] lib___.rlib [..]___.[..] [..]___.[..] [..]___.[..] [..]___.[..] [..] [..],[..] ___ 123 Caused by: failed to parse `123` as a cfg expression: unexpected character `1` in cfg, expected parens, a comma, an identifier, or a string "#]]) .run(); } #[cargo_test] fn exclusive_dep_kinds() { // Checks for a bug where the same package with different cfg expressions // was not being filtered correctly. Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [target.'cfg(abc)'.dependencies] bar = "1.0" [target.'cfg(not(abc))'.build-dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .file("build.rs", "extern crate bar; fn main() {}") .build(); p.cargo("check").run(); p.change_file("src/lib.rs", "extern crate bar;"); p.cargo("check") .with_status(101) // can't find crate for `bar` .with_stderr_data(str![[r#" [COMPILING] foo v0.1.0 ([ROOT]/foo) error[E0463]: can't find crate for `bar` ... "#]]) .run(); } #[cargo_test] fn cfg_raw_idents() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [target.'cfg(any(r#true, r#all, r#target_os = "<>"))'.dependencies] b = { path = "b/" } "#, ) .file("src/lib.rs", "") .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) .file("b/src/lib.rs", "pub fn foo() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [CHECKING] b v0.0.1 ([ROOT]/foo/b) [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn cfg_raw_idents_empty() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [target.'cfg(r#))'.dependencies] "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: failed to parse `r#)` as a cfg expression: unexpected character `)` in cfg, expected parens, a comma, an identifier, or a string "#]]) .run(); } #[cargo_test] fn cfg_raw_idents_not_really() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [target.'cfg(r#11))'.dependencies] "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: failed to parse `r#11)` as a cfg expression: unexpected character `1` in cfg, expected parens, a comma, an identifier, or a string "#]]) .run(); } #[cargo_test] fn cfg_keywords() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [target.'cfg(any(async, fn, const, return, true))'.dependencies] b = { path = "b/" } "#, ) .file( ".cargo/config.toml", r#" [target."cfg(any(for, match, extern, crate, false))"] "#, ) .file("src/lib.rs", "") .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) .file("b/src/lib.rs", "pub fn foo() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [CHECKING] b v0.0.1 ([ROOT]/foo/b) [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn cfg_booleans() { let p = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] [target.'cfg(true)'.dependencies] b = { path = 'b' } [target.'cfg(false)'.dependencies] c = { path = 'c' } "#, ) .file("src/lib.rs", "") .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) .file("b/src/lib.rs", "") .file("c/Cargo.toml", &basic_manifest("c", "0.0.1")) .file("c/src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [CHECKING] b v0.0.1 ([ROOT]/foo/b) [CHECKING] a v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn cfg_booleans_config() { let p = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" "#, ) .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [target.'cfg(true)'] rustflags = [] "#, ) .build(); p.cargo("check") .with_stderr_data(str![[r#" [CHECKING] a v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn cfg_booleans_not() { let p = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] [target.'cfg(not(false))'.dependencies] b = { path = 'b' } "#, ) .file("src/lib.rs", "") .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) .file("b/src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [CHECKING] b v0.0.1 ([ROOT]/foo/b) [CHECKING] a v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn cfg_booleans_combinators() { let p = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] [target.'cfg(all(any(true), not(false), true))'.dependencies] b = { path = 'b' } "#, ) .file("src/lib.rs", "") .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) .file("b/src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [CHECKING] b v0.0.1 ([ROOT]/foo/b) [CHECKING] a v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn cfg_booleans_rustflags_no_effect() { let p = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] [target.'cfg(true)'.dependencies] b = { path = 'b' } [target.'cfg(false)'.dependencies] c = { path = 'c' } "#, ) .file("src/lib.rs", "") .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) .file("b/src/lib.rs", "") .file("c/Cargo.toml", &basic_manifest("c", "0.0.1")) .file("c/src/lib.rs", "") .build(); p.cargo("check") .env("RUSTFLAGS", "--cfg false") .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [CHECKING] b v0.0.1 ([ROOT]/foo/b) [CHECKING] a v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } cargo-0.91.0/tests/testsuite/check.rs000064400000000000000000001314561046102023000156360ustar 00000000000000//! Tests for the `cargo check` command. use std::fmt::{self, Write}; use crate::prelude::*; use crate::utils::tools; use cargo_test_support::compare::assert_e2e; use cargo_test_support::install::exe; use cargo_test_support::registry::Package; use cargo_test_support::str; use cargo_test_support::{basic_bin_manifest, basic_manifest, git, project}; #[cargo_test] fn check_success() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "../bar" "#, ) .file( "src/main.rs", "extern crate bar; fn main() { ::bar::baz(); }", ) .build(); let _bar = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "pub fn baz() {}") .build(); foo.cargo("check").run(); } #[cargo_test] fn check_fail() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "../bar" "#, ) .file( "src/main.rs", "extern crate bar; fn main() { ::bar::baz(42); }", ) .build(); let _bar = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "pub fn baz() {}") .build(); foo.cargo("check") .with_status(101) .with_stderr_data(str![[r#" ... error[E0061]: this function takes 0 arguments but 1 argument was supplied ... "#]]) .run(); } #[cargo_test] fn custom_derive() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "../bar" "#, ) .file( "src/main.rs", r#" #[macro_use] extern crate bar; trait B { fn b(&self); } #[derive(B)] struct A; fn main() { let a = A; a.b(); } "#, ) .build(); let _bar = project() .at("bar") .file( "Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] [lib] proc-macro = true "#, ) .file( "src/lib.rs", r#" extern crate proc_macro; use proc_macro::TokenStream; #[proc_macro_derive(B)] pub fn derive(_input: TokenStream) -> TokenStream { format!("impl B for A {{ fn b(&self) {{}} }}").parse().unwrap() } "#, ) .build(); foo.cargo("check").run(); } #[cargo_test] fn check_build() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "../bar" "#, ) .file( "src/main.rs", "extern crate bar; fn main() { ::bar::baz(); }", ) .build(); let _bar = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "pub fn baz() {}") .build(); foo.cargo("check").run(); foo.cargo("build").run(); } #[cargo_test] fn build_check() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "../bar" "#, ) .file( "src/main.rs", "extern crate bar; fn main() { ::bar::baz(); }", ) .build(); let _bar = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "pub fn baz() {}") .build(); foo.cargo("build -v").run(); foo.cargo("check -v").run(); } // Checks that where a project has both a lib and a bin, the lib is only checked // not built. #[cargo_test] fn issue_3418() { let foo = project() .file("src/lib.rs", "") .file("src/main.rs", "fn main() {}") .build(); foo.cargo("check -v") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] src/lib.rs [..]--emit=[..]metadata [..]` [RUNNING] `rustc --crate-name foo [..] src/main.rs [..]--emit=[..]metadata [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } // Check on a dylib should have a different metadata hash than build. #[cargo_test] fn dylib_check_preserves_build_cache() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [lib] crate-type = ["dylib"] [dependencies] "#, ) .file("src/lib.rs", "") .build(); p.cargo("build") .with_stderr_data(str![[r#" [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check").run(); p.cargo("build") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } // test `cargo rustc --profile check` #[cargo_test] fn rustc_check() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "../bar" "#, ) .file( "src/main.rs", "extern crate bar; fn main() { ::bar::baz(); }", ) .build(); let _bar = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "pub fn baz() {}") .build(); foo.cargo("rustc --profile check -- --emit=metadata").run(); // Verify compatible usage of --profile with --release, issue #7488 foo.cargo("rustc --profile check --release -- --emit=metadata") .with_status(1) .with_stderr_data(str![[r#" [ERROR] the argument '--profile ' cannot be used with '--release' Usage: cargo[EXE] rustc --profile [ARGS]... For more information, try '--help'. "#]]) .run(); foo.cargo("rustc --profile test --release -- --emit=metadata") .with_status(1) .with_stderr_data(str![[r#" [ERROR] the argument '--profile ' cannot be used with '--release' Usage: cargo[EXE] rustc --profile [ARGS]... For more information, try '--help'. "#]]) .run(); } #[cargo_test] fn rustc_check_err() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "../bar" "#, ) .file( "src/main.rs", "extern crate bar; fn main() { ::bar::qux(); }", ) .build(); let _bar = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "pub fn baz() {}") .build(); foo.cargo("rustc --profile check -- --emit=metadata") .with_status(101) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.1.0 ([ROOT]/bar) [CHECKING] foo v0.0.1 ([ROOT]/foo) error[E0425]: [..] ... "#]]) .run(); } #[cargo_test] fn check_all() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [workspace] [dependencies] b = { path = "b" } "#, ) .file("src/main.rs", "fn main() {}") .file("examples/a.rs", "fn main() {}") .file("tests/a.rs", "") .file("src/lib.rs", "") .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) .file("b/src/main.rs", "fn main() {}") .file("b/src/lib.rs", "") .build(); p.cargo("check --workspace -v") .with_stderr_data( str![[r#" [CHECKING] b v0.0.1 ([ROOT]/foo/b) [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] src/lib.rs [..]` [RUNNING] `rustc --crate-name foo [..] src/main.rs [..]` [RUNNING] `rustc --crate-name b [..] b/src/lib.rs [..]` [RUNNING] `rustc --crate-name b [..] b/src/main.rs [..]` ... "#]] .unordered(), ) .run(); } #[cargo_test] fn check_all_exclude() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "pub fn baz() { break_the_build(); }") .build(); p.cargo("check --workspace --exclude baz") .with_stderr_does_not_contain("[CHECKING] baz v0.1.0 [..]") .with_stderr_data(str![[r#" [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn check_all_exclude_glob() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "pub fn baz() { break_the_build(); }") .build(); p.cargo("check --workspace --exclude '*z'") .with_stderr_does_not_contain("[CHECKING] baz v0.1.0 [..]") .with_stderr_data(str![[r#" [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn check_virtual_all_implied() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "pub fn baz() {}") .build(); p.cargo("check -v") .with_stderr_data( str![[r#" [CHECKING] baz v0.1.0 ([ROOT]/foo/baz) [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [RUNNING] `rustc --crate-name baz [..] baz/src/lib.rs [..]` [RUNNING] `rustc --crate-name bar [..] bar/src/lib.rs [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn check_virtual_manifest_one_project() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "pub fn baz() { break_the_build(); }") .build(); p.cargo("check -p bar") .with_stderr_does_not_contain("[CHECKING] baz v0.1.0 [..]") .with_stderr_data(str![[r#" [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn check_virtual_manifest_glob() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() { break_the_build(); }") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "pub fn baz() {}") .build(); p.cargo("check -p '*z'") .with_stderr_does_not_contain("[CHECKING] bar v0.1.0 [..]") .with_stderr_data(str![[r#" [CHECKING] baz v0.1.0 ([ROOT]/foo/baz) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn exclude_warns_on_non_existing_package() { let p = project().file("src/lib.rs", "").build(); p.cargo("check --workspace --exclude bar") .with_stdout_data("") .with_stderr_data(str![[r#" [WARNING] excluded package(s) `bar` not found in workspace `[ROOT]/foo` [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn targets_selected_default() { let foo = project() .file("src/main.rs", "fn main() {}") .file("src/lib.rs", "pub fn smth() {}") .file("examples/example1.rs", "fn main() {}") .file("tests/test2.rs", "#[test] fn t() {}") .file("benches/bench3.rs", "") .build(); foo.cargo("check -v") .with_stderr_contains("[..] --crate-name foo [..] src/lib.rs [..]") .with_stderr_contains("[..] --crate-name foo [..] src/main.rs [..]") .with_stderr_does_not_contain("[..] --crate-name example1 [..] examples/example1.rs [..]") .with_stderr_does_not_contain("[..] --crate-name test2 [..] tests/test2.rs [..]") .with_stderr_does_not_contain("[..] --crate-name bench3 [..] benches/bench3.rs [..]") .run(); } #[cargo_test] fn targets_selected_all() { let foo = project() .file("src/main.rs", "fn main() {}") .file("src/lib.rs", "pub fn smth() {}") .file("examples/example1.rs", "fn main() {}") .file("tests/test2.rs", "#[test] fn t() {}") .file("benches/bench3.rs", "") .build(); foo.cargo("check --all-targets -v") .with_stderr_data( str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] src/lib.rs [..]` [RUNNING] `rustc --crate-name foo [..] src/main.rs [..]` [RUNNING] `rustc --crate-name example1 [..] examples/example1.rs [..]` [RUNNING] `rustc --crate-name test2 [..] tests/test2.rs [..]` [RUNNING] `rustc --crate-name bench3 [..] benches/bench3.rs [..]` ... "#]] .unordered(), ) .run(); } #[cargo_test] fn check_unit_test_profile() { let foo = project() .file( "src/lib.rs", r#" #[cfg(test)] mod tests { #[test] fn it_works() { badtext } } "#, ) .build(); foo.cargo("check").run(); foo.cargo("check --profile test") .with_status(101) .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) error[E0425]: cannot find value `badtext` in this scope ... "#]]) .run(); } // Verify what is checked with various command-line filters. #[cargo_test] fn check_filters() { let p = project() .file( "src/lib.rs", r#" fn unused_normal_lib() {} #[cfg(test)] mod tests { fn unused_unit_lib() {} } "#, ) .file( "src/main.rs", r#" fn main() {} fn unused_normal_bin() {} #[cfg(test)] mod tests { fn unused_unit_bin() {} } "#, ) .file( "tests/t1.rs", r#" fn unused_normal_t1() {} #[cfg(test)] mod tests { fn unused_unit_t1() {} } "#, ) .file( "examples/ex1.rs", r#" fn main() {} fn unused_normal_ex1() {} #[cfg(test)] mod tests { fn unused_unit_ex1() {} } "#, ) .file( "benches/b1.rs", r#" fn unused_normal_b1() {} #[cfg(test)] mod tests { fn unused_unit_b1() {} } "#, ) .build(); p.cargo("check") .with_stderr_contains("[..]unused_normal_lib[..]") .with_stderr_contains("[..]unused_normal_bin[..]") .with_stderr_does_not_contain("[..]unused_normal_t1[..]") .with_stderr_does_not_contain("[..]unused_normal_ex1[..]") .with_stderr_does_not_contain("[..]unused_normal_b1[..]") .with_stderr_does_not_contain("[..]unused_unit_[..]") .run(); p.root().join("target").rm_rf(); p.cargo("check --tests -v") .with_stderr_contains("[..] --crate-name foo [..] src/lib.rs [..] --test [..]") .with_stderr_contains("[..] --crate-name foo [..] src/lib.rs [..] --crate-type lib [..]") .with_stderr_contains("[..] --crate-name foo [..] src/main.rs [..] --test [..]") .with_stderr_contains("[..]unused_unit_lib[..]") .with_stderr_contains("[..]unused_unit_bin[..]") .with_stderr_contains("[..]unused_normal_lib[..]") .with_stderr_contains("[..]unused_normal_bin[..]") .with_stderr_contains("[..]unused_unit_t1[..]") .with_stderr_does_not_contain("[..]unused_normal_ex1[..]") .with_stderr_does_not_contain("[..]unused_unit_ex1[..]") .with_stderr_does_not_contain("[..]unused_normal_b1[..]") .with_stderr_does_not_contain("[..]unused_unit_b1[..]") .with_stderr_does_not_contain("[..]--crate-type bin[..]") .run(); p.root().join("target").rm_rf(); p.cargo("check --test t1 -v") .with_stderr_contains("[..]unused_normal_lib[..]") .with_stderr_contains("[..]unused_unit_t1[..]") .with_stderr_does_not_contain("[..]unused_unit_lib[..]") .with_stderr_does_not_contain("[..]unused_normal_bin[..]") .with_stderr_does_not_contain("[..]unused_unit_bin[..]") .with_stderr_does_not_contain("[..]unused_normal_ex1[..]") .with_stderr_does_not_contain("[..]unused_normal_b1[..]") .with_stderr_does_not_contain("[..]unused_unit_ex1[..]") .with_stderr_does_not_contain("[..]unused_unit_b1[..]") .run(); p.root().join("target").rm_rf(); p.cargo("check --all-targets -v") .with_stderr_contains("[..]unused_normal_lib[..]") .with_stderr_contains("[..]unused_normal_bin[..]") .with_stderr_contains("[..]unused_normal_t1[..]") .with_stderr_contains("[..]unused_normal_ex1[..]") .with_stderr_contains("[..]unused_normal_b1[..]") .with_stderr_contains("[..]unused_unit_b1[..]") .with_stderr_contains("[..]unused_unit_t1[..]") .with_stderr_contains("[..]unused_unit_lib[..]") .with_stderr_contains("[..]unused_unit_bin[..]") .with_stderr_does_not_contain("[..]unused_unit_ex1[..]") .run(); } #[cargo_test] fn check_artifacts() { // Verify which artifacts are created when running check (#4059). let p = project() .file("src/lib.rs", "") .file("src/main.rs", "fn main() {}") .file("tests/t1.rs", "") .file("examples/ex1.rs", "fn main() {}") .file("benches/b1.rs", "") .build(); p.cargo("check").run(); assert!(!p.root().join("target/debug/libfoo.rmeta").is_file()); assert!(!p.root().join("target/debug/libfoo.rlib").is_file()); assert!(!p.root().join("target/debug").join(exe("foo")).is_file()); assert_eq!(p.glob("target/debug/deps/libfoo-*.rmeta").count(), 2); p.root().join("target").rm_rf(); p.cargo("check --lib").run(); assert!(!p.root().join("target/debug/libfoo.rmeta").is_file()); assert!(!p.root().join("target/debug/libfoo.rlib").is_file()); assert!(!p.root().join("target/debug").join(exe("foo")).is_file()); assert_eq!(p.glob("target/debug/deps/libfoo-*.rmeta").count(), 1); p.root().join("target").rm_rf(); p.cargo("check --bin foo").run(); assert!(!p.root().join("target/debug/libfoo.rmeta").is_file()); assert!(!p.root().join("target/debug/libfoo.rlib").is_file()); assert!(!p.root().join("target/debug").join(exe("foo")).is_file()); assert_eq!(p.glob("target/debug/deps/libfoo-*.rmeta").count(), 2); p.root().join("target").rm_rf(); p.cargo("check --test t1").run(); assert!(!p.root().join("target/debug/libfoo.rmeta").is_file()); assert!(!p.root().join("target/debug/libfoo.rlib").is_file()); assert!(!p.root().join("target/debug").join(exe("foo")).is_file()); assert_eq!(p.glob("target/debug/t1-*").count(), 0); assert_eq!(p.glob("target/debug/deps/libfoo-*.rmeta").count(), 1); assert_eq!(p.glob("target/debug/deps/libt1-*.rmeta").count(), 1); p.root().join("target").rm_rf(); p.cargo("check --example ex1").run(); assert!(!p.root().join("target/debug/libfoo.rmeta").is_file()); assert!(!p.root().join("target/debug/libfoo.rlib").is_file()); assert!( !p.root() .join("target/debug/examples") .join(exe("ex1")) .is_file() ); assert_eq!(p.glob("target/debug/deps/libfoo-*.rmeta").count(), 1); assert_eq!(p.glob("target/debug/examples/libex1-*.rmeta").count(), 1); p.root().join("target").rm_rf(); p.cargo("check --bench b1").run(); assert!(!p.root().join("target/debug/libfoo.rmeta").is_file()); assert!(!p.root().join("target/debug/libfoo.rlib").is_file()); assert!(!p.root().join("target/debug").join(exe("foo")).is_file()); assert_eq!(p.glob("target/debug/b1-*").count(), 0); assert_eq!(p.glob("target/debug/deps/libfoo-*.rmeta").count(), 1); assert_eq!(p.glob("target/debug/deps/libb1-*.rmeta").count(), 1); } #[cargo_test] fn short_message_format() { let foo = project() .file("src/lib.rs", "fn foo() { let _x: bool = 'a'; }") .build(); foo.cargo("check --message-format=short") .with_status(101) .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) src/lib.rs:1:27: error[E0308]: mismatched types[..] [ERROR] could not compile `foo` (lib) due to 1 previous error "#]]) .run(); } #[cargo_test] fn proc_macro() { let p = project() .file( "Cargo.toml", r#" [package] name = "demo" version = "0.0.1" edition = "2015" [lib] proc-macro = true "#, ) .file( "src/lib.rs", r#" extern crate proc_macro; use proc_macro::TokenStream; #[proc_macro_derive(Foo)] pub fn demo(_input: TokenStream) -> TokenStream { "".parse().unwrap() } "#, ) .file( "src/main.rs", r#" #[macro_use] extern crate demo; #[derive(Foo)] struct A; fn main() {} "#, ) .build(); p.cargo("check -v").env("CARGO_LOG", "cargo=trace").run(); } #[cargo_test] fn check_keep_going() { let foo = project() .file("src/bin/one.rs", "compile_error!(\"ONE\"); fn main() {}") .file("src/bin/two.rs", "compile_error!(\"TWO\"); fn main() {}") .build(); // Due to -j1, without --keep-going only one of the two bins would be built. foo.cargo("check -j1 --keep-going") .with_status(101) .with_stderr_data( str![[r#" [ERROR] ONE [ERROR] TWO ... "#]] .unordered(), ) .run(); } #[cargo_test] fn does_not_use_empty_rustc_wrapper() { // An empty RUSTC_WRAPPER environment variable won't be used. // The env var will also override the config, essentially unsetting it. let p = project() .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [build] rustc-wrapper = "do-not-execute-me" "#, ) .build(); p.cargo("check").env("RUSTC_WRAPPER", "").run(); } #[cargo_test] fn does_not_use_empty_rustc_workspace_wrapper() { let p = project().file("src/lib.rs", "").build(); p.cargo("check").env("RUSTC_WORKSPACE_WRAPPER", "").run(); } #[cargo_test] fn error_from_deep_recursion() -> Result<(), fmt::Error> { let mut big_macro = String::new(); writeln!(big_macro, "macro_rules! m {{")?; for i in 0..130 { writeln!(big_macro, "({}) => {{ m!({}); }};", i, i + 1)?; } writeln!(big_macro, "}}")?; writeln!(big_macro, "m!(0);")?; let p = project().file("src/lib.rs", &big_macro).build(); p.cargo("check --message-format=json") .with_status(101) .with_stdout_data(str![[r#" {"reason":"compiler-message",[..]"message":"recursion limit reached while expanding `m!`",[..]rendered":"[..]recursion limit reached while expanding `m!`[..]"}} ... "#]]) .run(); Ok(()) } #[cargo_test] fn rustc_workspace_wrapper_affects_all_workspace_members() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "pub fn baz() {}") .build(); p.cargo("check") .env("RUSTC_WORKSPACE_WRAPPER", tools::echo_wrapper()) .with_stderr_data( str![[r#" WRAPPER CALLED: rustc --crate-name bar [..] WRAPPER CALLED: rustc --crate-name baz [..] ... "#]] .unordered(), ) .run(); } #[cargo_test] fn rustc_workspace_wrapper_includes_path_deps() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [workspace] members = ["bar"] [dependencies] baz = { path = "baz" } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "pub fn baz() {}") .build(); p.cargo("check --workspace") .env("RUSTC_WORKSPACE_WRAPPER", tools::echo_wrapper()) .with_stderr_data( str![[r#" WRAPPER CALLED: rustc --crate-name bar [..] WRAPPER CALLED: rustc --crate-name baz [..] WRAPPER CALLED: rustc --crate-name foo [..] ... "#]] .unordered(), ) .run(); } #[cargo_test] fn rustc_workspace_wrapper_respects_primary_units() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "pub fn baz() {}") .build(); p.cargo("check -p bar") .env("RUSTC_WORKSPACE_WRAPPER", tools::echo_wrapper()) .with_stderr_contains("WRAPPER CALLED: rustc --crate-name bar [..]") .with_stdout_does_not_contain("WRAPPER CALLED: rustc --crate-name baz [..]") .run(); } #[cargo_test] fn rustc_workspace_wrapper_excludes_published_deps() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [workspace] members = ["bar"] [dependencies] baz = "1.0.0" "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .build(); Package::new("baz", "1.0.0").publish(); p.cargo("check --workspace -v") .env("RUSTC_WORKSPACE_WRAPPER", tools::echo_wrapper()) .with_stderr_contains("WRAPPER CALLED: rustc --crate-name foo [..]") .with_stderr_contains("WRAPPER CALLED: rustc --crate-name bar [..]") .with_stderr_contains("[CHECKING] baz [..]") .with_stdout_does_not_contain("WRAPPER CALLED: rustc --crate-name baz [..]") .run(); } #[cargo_test] fn warn_manifest_with_project() { let p = project() .file( "Cargo.toml", r#" [project] name = "foo" version = "0.0.1" edition = "2015" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [WARNING] `[project]` is deprecated in favor of `[package]` [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn error_manifest_with_project_on_2024() { let p = project() .file( "Cargo.toml", r#" [project] name = "foo" version = "0.0.1" edition = "2024" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: `[project]` is not supported as of the 2024 Edition, please use `[package]` "#]]) .run(); } #[cargo_test] fn warn_manifest_package_and_project() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [project] name = "foo" version = "0.0.1" edition = "2015" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [WARNING] `[project]` is deprecated in favor of `[package]` [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn git_manifest_package_and_project() { let p = project(); let git_project = git::new("bar", |p| { p.file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" [project] name = "bar" version = "0.0.1" edition = "2015" "#, ) .file("src/lib.rs", "") }); let p = p .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies.bar] version = "0.0.1" git = '{}' "#, git_project.url() ), ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/bar` [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.0.1 ([ROOTURL]/bar#[..]) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn git_manifest_with_project() { let p = project(); let git_project = git::new("bar", |p| { p.file( "Cargo.toml", r#" [project] name = "bar" version = "0.0.1" edition = "2015" "#, ) .file("src/lib.rs", "") }); let p = p .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies.bar] version = "0.0.1" git = '{}' "#, git_project.url() ), ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/bar` [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.0.1 ([ROOTURL]/bar#[..]) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn check_fixable_warning() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" "#, ) .file("src/lib.rs", "use std::io;") .build(); foo.cargo("check") .with_stderr_data(str![[r#" ... [WARNING] `foo` (lib) generated 1 warning (run `cargo fix --lib -p foo` to apply 1 suggestion) ... "#]]) .run(); } #[cargo_test] fn check_fixable_test_warning() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" "#, ) .file( "src/lib.rs", "\ mod tests { #[test] fn t1() { use std::io; } } ", ) .build(); foo.cargo("check --all-targets") .with_stderr_data(str![[r#" ... [WARNING] `foo` (lib test) generated 1 warning (run `cargo fix --lib -p foo --tests` to apply 1 suggestion) ... "#]]) .run(); foo.cargo("fix --lib -p foo --tests --allow-no-vcs").run(); assert!(!foo.read_file("src/lib.rs").contains("use std::io;")); } #[cargo_test] fn check_fixable_error_no_fix() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" "#, ) .file( "src/lib.rs", "use std::io;\n#[derive(Debug(x))]\nstruct Foo;", ) .build(); foo.cargo("check") .with_status(101) .with_stderr_data( str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [ERROR] traits in `#[derive(...)]` don't accept arguments [WARNING] unused import: `std::io` [WARNING] `foo` (lib) generated 1 warning [ERROR] could not compile `foo` (lib) due to 1 previous error; 1 warning emitted ... "#]] .unordered(), ) .run(); } #[cargo_test] fn check_fixable_warning_workspace() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo", "bar"] "#, ) .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" "#, ) .file("foo/src/lib.rs", "use std::io;") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" [dependencies] foo = { path = "../foo" } "#, ) .file("bar/src/lib.rs", "use std::io;") .build(); p.cargo("check") .with_stderr_data( str![[r#" [WARNING] `foo` (lib) generated 1 warning (run `cargo fix --lib -p foo` to apply 1 suggestion) [WARNING] `bar` (lib) generated 1 warning (run `cargo fix --lib -p bar` to apply 1 suggestion) ... "#]] .unordered(), ) .run(); } #[cargo_test] fn check_fixable_example() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file( "src/main.rs", r#" fn hello() -> &'static str { "hello" } pub fn main() { println!("{}", hello()) } "#, ) .file("examples/ex1.rs", "use std::fmt; fn main() {}") .build(); p.cargo("check --all-targets") .with_stderr_data(str![[r#" ... [WARNING] `foo` (example "ex1") generated 1 warning (run `cargo fix --example "ex1"` to apply 1 suggestion) ... "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn check_fixable_bench() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file( "src/main.rs", r#" #![feature(test)] #[cfg(test)] extern crate test; fn hello() -> &'static str { "hello" } pub fn main() { println!("{}", hello()) } #[bench] fn bench_hello(_b: &mut test::Bencher) { use std::io; assert_eq!(hello(), "hello") } "#, ) .file( "benches/bench.rs", " #![feature(test)] extern crate test; #[bench] fn bench(_b: &mut test::Bencher) { use std::fmt; } ", ) .build(); p.cargo("check --all-targets") .with_stderr_data(str![[r#" ... [WARNING] `foo` (bench "bench") generated 1 warning (run `cargo fix --bench "bench"` to apply 1 suggestion) ... "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn check_fixable_mixed() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file( "src/main.rs", r#" #![feature(test)] #[cfg(test)] extern crate test; fn hello() -> &'static str { "hello" } pub fn main() { println!("{}", hello()) } #[bench] fn bench_hello(_b: &mut test::Bencher) { use std::io; assert_eq!(hello(), "hello") } #[test] fn t1() { use std::fmt; } "#, ) .file("examples/ex1.rs", "use std::fmt; fn main() {}") .file( "benches/bench.rs", " #![feature(test)] extern crate test; #[bench] fn bench(_b: &mut test::Bencher) { use std::fmt; } ", ) .build(); p.cargo("check --all-targets") .with_stderr_data(str![[r#" [WARNING] `foo` (example "ex1") generated 1 warning (run `cargo fix --example "ex1"` to apply 1 suggestion) [WARNING] `foo` (bench "bench") generated 1 warning (run `cargo fix --bench "bench"` to apply 1 suggestion) [WARNING] `foo` (bin "foo" test) generated 2 warnings (run `cargo fix --bin "foo" --tests` to apply 2 suggestions) ... "#]].unordered()) .run(); } #[cargo_test] fn check_fixable_warning_for_clippy() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" "#, ) // We don't want to show a warning that is `clippy` // specific since we are using a `rustc` wrapper // inplace of `clippy` .file("src/lib.rs", "use std::io;") .build(); foo.cargo("check") // We can't use `clippy` so we use a `rustc` workspace wrapper instead .env("RUSTC_WORKSPACE_WRAPPER", tools::wrapped_clippy_driver()) .with_stderr_data(str![[r#" ... [WARNING] `foo` (lib) generated 1 warning (run `cargo clippy --fix --lib -p foo` to apply 1 suggestion) ... "#]]) .run(); } #[cargo_test] fn check_unused_manifest_keys() { Package::new("dep", "0.1.0").publish(); Package::new("foo", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.2.0" edition = "2015" authors = [] [dependencies] dep = { version = "0.1.0", wxz = "wxz" } foo = { version = "0.1.0", abc = "abc" } [dev-dependencies] foo = { version = "0.1.0", wxz = "wxz" } [build-dependencies] foo = { version = "0.1.0", wxz = "wxz" } [target.'cfg(windows)'.dependencies] foo = { version = "0.1.0", wxz = "wxz" } [target.wasm32-wasip1.dev-dependencies] foo = { version = "0.1.0", wxz = "wxz" } [target.bar.build-dependencies] foo = { version = "0.1.0", wxz = "wxz" } "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_stderr_data( str![[r#" [WARNING] unused manifest key: dependencies.dep.wxz [WARNING] unused manifest key: dependencies.foo.abc [WARNING] unused manifest key: dev-dependencies.foo.wxz [WARNING] unused manifest key: build-dependencies.foo.wxz [WARNING] unused manifest key: target.bar.build-dependencies.foo.wxz [WARNING] unused manifest key: target.cfg(windows).dependencies.foo.wxz [WARNING] unused manifest key: target.wasm32-wasip1.dev-dependencies.foo.wxz [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] foo v0.1.0 (registry `dummy-registry`) [DOWNLOADED] dep v0.1.0 (registry `dummy-registry`) [CHECKING] foo v0.1.0 [CHECKING] dep v0.1.0 [CHECKING] bar v0.2.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn versionless_package() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" description = "foo" edition = "2015" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn pkgid_querystring_works() { let git_project = git::new("gitdep", |p| { p.file("Cargo.toml", &basic_manifest("gitdep", "1.0.0")) .file("src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" edition = "2015" [dependencies] gitdep = {{ git = "{}", branch = "master" }} "#, git_project.url() ), ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); let output = p.cargo("pkgid").arg("gitdep").run(); let gitdep_pkgid = String::from_utf8(output.stdout).unwrap(); let gitdep_pkgid = gitdep_pkgid.trim(); assert_e2e().eq( gitdep_pkgid, str!["git+[ROOTURL]/gitdep?branch=master#1.0.0"], ); p.cargo("build -p") .arg(gitdep_pkgid) .with_stderr_data(str![[r#" [COMPILING] gitdep v1.0.0 ([ROOTURL]/gitdep?branch=master#[..]) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } cargo-0.91.0/tests/testsuite/check_cfg.rs000064400000000000000000000575661046102023000164660ustar 00000000000000//! Tests for Cargo usage of rustc `--check-cfg`. use crate::prelude::*; use cargo_test_support::{basic_manifest, project, str}; macro_rules! x { ($tool:tt => $what:tt $(of $who:tt)?) => {{ #[cfg(windows)] { concat!("[RUNNING] [..]", $tool, "[..] --check-cfg ", $what, '(', $($who,)* ')', "[..]") } #[cfg(not(windows))] { concat!("[RUNNING] [..]", $tool, "[..] --check-cfg '", $what, '(', $($who,)* ')', "'", "[..]") } }}; ($tool:tt => $what:tt of $who:tt with $($first_value:tt $($other_values:tt)*)?) => {{ #[cfg(windows)] { concat!("[RUNNING] [..]", $tool, "[..] --check-cfg \"", $what, '(', $who, ", values(", $("/\"", $first_value, "/\"", $(", ", "/\"", $other_values, "/\"",)*)* "))", '"', "[..]") } #[cfg(not(windows))] { concat!("[RUNNING] [..]", $tool, "[..] --check-cfg '", $what, '(', $who, ", values(", $("\"", $first_value, "\"", $(", ", "\"", $other_values, "\"",)*)* "))", "'", "[..]") } }}; } #[cargo_test] fn features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [features] f_a = [] f_b = [] "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check -v") .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "f_a" "f_b")) .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) .run(); } #[cargo_test] fn features_with_deps() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { path = "bar/" } [features] f_a = [] f_b = [] "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "#[allow(dead_code)] fn bar() {}") .build(); p.cargo("check -v") .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "f_a" "f_b")) .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) .run(); } #[cargo_test] fn features_with_opt_deps() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { path = "bar/", optional = true } [features] default = ["bar"] f_a = [] f_b = [] "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "#[allow(dead_code)] fn bar() {}") .build(); p.cargo("check -v") .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "bar" "default" "f_a" "f_b")) .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) .run(); } #[cargo_test] fn features_with_namespaced_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { path = "bar/", optional = true } [features] f_a = ["dep:bar"] f_b = [] "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "#[allow(dead_code)] fn bar() {}") .build(); p.cargo("check -v") .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "f_a" "f_b")) .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) .run(); } #[cargo_test] fn features_fingerprint() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [features] f_a = [] f_b = [] "#, ) .file("src/lib.rs", "#[cfg(feature = \"f_b\")] fn entry() {}") .build(); p.cargo("check -v") .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "f_a" "f_b")) .with_stderr_does_not_contain("[..]unexpected_cfgs[..]") .run(); p.cargo("check -v") .with_stderr_does_not_contain("[..]rustc[..]") .run(); // checking that re-ordering the features does not invalid the fingerprint p.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [features] f_b = [] f_a = [] "#, ); p.cargo("check -v") .with_stderr_does_not_contain("[..]rustc[..]") .run(); p.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [features] f_a = [] "#, ); p.cargo("check -v") // we check that the fingerprint is indeed dirty // that is cause rustc to be called again with the new check-cfg args // and that we indeed found a new warning from the unexpected_cfgs lint .with_stderr_data(format!( "\ [DIRTY] foo v0.1.0 ([ROOT]/foo): the list of declared features changed [CHECKING] foo v0.1.0 ([ROOT]/foo) {running_rustc} [WARNING] unexpected `cfg` condition value: `f_b` ... ", running_rustc = x!("rustc" => "cfg" of "feature" with "f_a") )) .run(); } #[cargo_test] fn well_known_names_values() { let p = project() .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check -v") .with_stderr_contains(x!("rustc" => "cfg" of "feature" with)) .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) .run(); } #[cargo_test] fn features_test() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [features] f_a = [] f_b = [] "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("test -v") .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "f_a" "f_b")) .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) .run(); } #[cargo_test] fn features_doctest() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [features] default = ["f_a"] f_a = [] f_b = [] "#, ) .file("src/lib.rs", "#[allow(dead_code)] fn foo() {}") .build(); p.cargo("test -v --doc") .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "default" "f_a" "f_b")) .with_stderr_contains(x!("rustdoc" => "cfg" of "feature" with "default" "f_a" "f_b")) .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) .with_stderr_contains(x!("rustdoc" => "cfg" of "docsrs,test")) .run(); } #[cargo_test] fn well_known_names_values_test() { let p = project() .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("src/main.rs", "fn main() {}") .build(); p.cargo("test -v") .with_stderr_contains(x!("rustc" => "cfg" of "feature" with)) .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) .run(); } #[cargo_test] fn well_known_names_values_doctest() { let p = project() .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("src/lib.rs", "#[allow(dead_code)] fn foo() {}") .build(); p.cargo("test -v --doc") .with_stderr_contains(x!("rustc" => "cfg" of "feature" with)) .with_stderr_contains(x!("rustdoc" => "cfg" of "feature" with)) .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) .with_stderr_contains(x!("rustdoc" => "cfg" of "docsrs,test")) .run(); } #[cargo_test] fn features_doc() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [features] default = ["f_a"] f_a = [] f_b = [] "#, ) .file("src/lib.rs", "#[allow(dead_code)] fn foo() {}") .build(); p.cargo("doc -v") .with_stderr_contains(x!("rustdoc" => "cfg" of "feature" with "default" "f_a" "f_b")) .with_stderr_contains(x!("rustdoc" => "cfg" of "docsrs,test")) .run(); } #[cargo_test] fn build_script_feedback() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" "#, ) .file("src/main.rs", "fn main() {}") .file( "build.rs", r#"fn main() { println!("cargo::rustc-check-cfg=cfg(foo)"); }"#, ) .build(); p.cargo("check -v") .with_stderr_contains(x!("rustc" => "cfg" of "foo")) .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) .run(); } #[cargo_test] fn build_script_doc() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" "#, ) .file("src/main.rs", "fn main() {}") .file( "build.rs", r#"fn main() { println!("cargo::rustc-check-cfg=cfg(foo)"); }"#, ) .build(); p.cargo("doc -v") .with_stderr_does_not_contain("rustc [..] --check-cfg [..]") .with_stderr_data(format!( "\ [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..] build.rs [..]` [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) {running_rustdoc} [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html ", running_rustdoc = x!("rustdoc" => "cfg" of "foo") )) .run(); } #[cargo_test] fn build_script_override() { let target = cargo_test_support::rustc_host(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] links = "a" build = "build.rs" "#, ) .file("src/main.rs", "fn main() {}") .file("build.rs", "") .file( ".cargo/config.toml", &format!( r#" [target.{}.a] rustc-check-cfg = ["cfg(foo)"] "#, target ), ) .build(); p.cargo("check -v") .with_stderr_contains(x!("rustc" => "cfg" of "foo")) .with_stderr_contains(x!("rustc" => "cfg" of "feature" with)) .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) .run(); } #[cargo_test] fn build_script_test() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" "#, ) .file( "build.rs", r#"fn main() { println!("cargo::rustc-check-cfg=cfg(foo)"); println!("cargo::rustc-cfg=foo"); }"#, ) .file( "src/lib.rs", r#" /// /// ``` /// extern crate foo; /// /// fn main() { /// foo::foo() /// } /// ``` /// #[cfg(foo)] pub fn foo() {} #[cfg(foo)] #[test] fn test_foo() { foo() } "#, ) .file("tests/test.rs", "#[cfg(foo)] #[test] fn test_bar() {}") .build(); p.cargo("test -v") .with_stderr_data( format!( "\ {running_rustc} {running_rustdoc} ... ", running_rustc = x!("rustc" => "cfg" of "foo"), running_rustdoc = x!("rustdoc" => "cfg" of "foo") ) .unordered(), ) .with_stdout_data( str![[r#" test test_foo ... ok test test_bar ... ok test [..] ... ok ... "#]] .unordered(), ) .run(); } #[cargo_test] fn config_simple() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ["cfg(has_foo)", "cfg(has_bar, values(\"yes\", \"no\"))"] } "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check -v") .with_stderr_contains(x!("rustc" => "cfg" of "has_foo")) .with_stderr_contains(x!("rustc" => "cfg" of "has_bar" with "yes" "no")) .with_stderr_does_not_contain("[..]unused manifest key[..]") .run(); } #[cargo_test] fn config_workspace() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo/"] [workspace.lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ["cfg(has_foo)"] } "#, ) .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [lints] workspace = true "#, ) .file("foo/src/main.rs", "fn main() {}") .build(); p.cargo("check -v") .with_stderr_data(format!( "\ ... {running_rustc} ... ", running_rustc = x!("rustc" => "cfg" of "has_foo") )) .with_stderr_does_not_contain("unexpected_cfgs") .run(); } #[cargo_test] fn config_workspace_not_inherited() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo/"] [workspace.lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ["cfg(has_foo)"] } "#, ) .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" "#, ) .file("foo/src/main.rs", "fn main() {}") .build(); p.cargo("check -v") .with_stderr_does_not_contain(x!("rustc" => "cfg" of "has_foo")) .with_stderr_does_not_contain("unexpected_cfgs") .run(); } #[cargo_test] fn config_invalid_position() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [lints.rust] use_bracket = { level = "warn", check-cfg = ["cfg(has_foo)"] } "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check -v") .with_stderr_data(str![[r#" [WARNING] unused manifest key: `lints.rust.use_bracket.check-cfg` ... "#]]) .with_stderr_does_not_contain(x!("rustc" => "cfg" of "has_foo")) .run(); } #[cargo_test] fn config_invalid_empty() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [lints.rust] unexpected_cfgs = { } "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] missing field `level` ... "#]]) .run(); } #[cargo_test] fn config_invalid_not_list() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = "cfg()" } "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: `lints.rust.unexpected_cfgs.check-cfg` must be a list of string "#]]) .run(); } #[cargo_test] fn config_invalid_not_list_string() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = [12] } "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: `lints.rust.unexpected_cfgs.check-cfg` must be a list of string "#]]) .run(); } #[cargo_test] fn config_and_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [features] my_feature = [] alloc = [] [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ["cfg(has_foo)", "cfg(has_bar, values(\"yes\", \"no\"))"] } "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check -v") .with_stderr_contains(x!("rustc" => "cfg" of "has_foo")) .with_stderr_contains(x!("rustc" => "cfg" of "has_bar" with "yes" "no")) .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "alloc" "my_feature")) .run(); } #[cargo_test] fn config_with_cargo_doc() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ["cfg(has_foo)"] } "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("doc -v") .with_stderr_data(format!( "\ ... {running_rustdoc} ... ", running_rustdoc = x!("rustdoc" => "cfg" of "has_foo") )) .run(); } #[cargo_test] fn config_with_cargo_test() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ["cfg(has_foo)"] } "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("test -v") .with_stderr_data(format!( "\ ... {running_rustc} ... ", running_rustc = x!("rustc" => "cfg" of "has_foo") )) .run(); } #[cargo_test] fn config_and_build_script() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2021" build = "build.rs" [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ["cfg(bar)"] } "#, ) .file("src/main.rs", "fn main() {}") .file( "build.rs", r#"fn main() { println!("cargo::rustc-check-cfg=cfg(foo)"); }"#, ) .build(); p.cargo("check -v") .with_stderr_contains(x!("rustc" => "cfg" of "foo")) // from build.rs .with_stderr_contains(x!("rustc" => "cfg" of "bar")) // from config .run(); } #[cargo_test] fn config_features_and_build_script() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2021" build = "build.rs" [features] serde = [] json = [] [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ["cfg(bar)"] } "#, ) .file("src/main.rs", "fn main() {}") .file( "build.rs", r#"fn main() { println!("cargo::rustc-check-cfg=cfg(foo)"); }"#, ) .build(); p.cargo("check -v") .with_stderr_contains(x!("rustc" => "cfg" of "foo")) // from build.rs .with_stderr_contains(x!("rustc" => "cfg" of "bar")) // from config .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "json" "serde")) // features .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) // Cargo well known .run(); } #[cargo_test] fn config_fingerprint() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ["cfg(bar)"] } "#, ) .file("src/lib.rs", "fn entry() {}") .build(); p.cargo("check -v") .with_stderr_contains(x!("rustc" => "cfg" of "bar")) .run(); p.cargo("check -v") .with_stderr_does_not_contain("[..]rustc[..]") .run(); // checking that changing the `check-cfg` config does invalid the fingerprint p.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ["cfg(bar)", "cfg(foo)"] } "#, ); p.cargo("check -v") // we check that the fingerprint is indeed dirty .with_stderr_contains("[..][DIRTY][..]the profile configuration changed") // that cause rustc to be called again with the new check-cfg args .with_stderr_contains(x!("rustc" => "cfg" of "bar")) .with_stderr_contains(x!("rustc" => "cfg" of "foo")) .run(); } cargo-0.91.0/tests/testsuite/clean.rs000064400000000000000000000614321046102023000156370ustar 00000000000000//! Tests for the `cargo clean` command. use crate::prelude::*; use cargo_test_support::compare::assert_e2e; use cargo_test_support::registry::Package; use cargo_test_support::str; use cargo_test_support::{ basic_bin_manifest, basic_manifest, git, main_file, project, project_in, rustc_host, }; use glob::GlobError; use std::env; use std::path::{Path, PathBuf}; #[cargo_test] fn cargo_clean_simple() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("build").run(); assert!(p.build_dir().is_dir()); p.cargo("clean").run(); assert!(!p.build_dir().is_dir()); } #[cargo_test] fn different_dir() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .file("src/bar/a.rs", "") .build(); p.cargo("build").run(); assert!(p.build_dir().is_dir()); p.cargo("clean") .cwd("src") .with_stderr_data(str![[r#" [REMOVED] [FILE_NUM] files, [FILE_SIZE]B total "#]]) .run(); assert!(!p.build_dir().is_dir()); } #[cargo_test] fn clean_multiple_packages() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.d1] path = "d1" [dependencies.d2] path = "d2" [[bin]] name = "foo" "#, ) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .file("d1/Cargo.toml", &basic_bin_manifest("d1")) .file("d1/src/main.rs", "fn main() { println!(\"d1\"); }") .file("d2/Cargo.toml", &basic_bin_manifest("d2")) .file("d2/src/main.rs", "fn main() { println!(\"d2\"); }") .build(); p.cargo("build -p d1 -p d2 -p foo").run(); let d1_path = &p .build_dir() .join("debug") .join(format!("d1{}", env::consts::EXE_SUFFIX)); let d2_path = &p .build_dir() .join("debug") .join(format!("d2{}", env::consts::EXE_SUFFIX)); assert!(p.bin("foo").is_file()); assert!(d1_path.is_file()); assert!(d2_path.is_file()); p.cargo("clean -p d1 -p d2") .cwd("src") .with_stderr_data(str![[r#" [REMOVED] [FILE_NUM] files, [FILE_SIZE]B total "#]]) .run(); assert!(p.bin("foo").is_file()); assert!(!d1_path.is_file()); assert!(!d2_path.is_file()); } #[cargo_test] fn clean_multiple_packages_in_glob_char_path() { let p = project_in("[d1]") .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); let foo_path = &p.build_dir().join("debug").join("deps"); #[cfg(not(target_env = "msvc"))] let file_glob = "foo-*"; #[cfg(target_env = "msvc")] let file_glob = "foo.pdb"; // Assert that build artifacts are produced p.cargo("build").run(); assert_ne!(get_build_artifacts(foo_path, file_glob).len(), 0); // Assert that build artifacts are destroyed p.cargo("clean -p foo").run(); assert_eq!(get_build_artifacts(foo_path, file_glob).len(), 0); } fn get_build_artifacts(path: &PathBuf, file_glob: &str) -> Vec> { let pattern = path.to_str().expect("expected utf-8 path"); let pattern = glob::Pattern::escape(pattern); let path = PathBuf::from(pattern).join(file_glob); let path = path.to_str().expect("expected utf-8 path"); glob::glob(path) .expect("expected glob to run") .into_iter() .collect::>>() } #[cargo_test] fn clean_p_only_cleans_specified_package() { let p = project() .file( "Cargo.toml", r#" [workspace] members = [ "foo", "foo_core", "foo-base", ] "#, ) .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("foo/src/lib.rs", "//! foo") .file("foo_core/Cargo.toml", &basic_manifest("foo_core", "0.1.0")) .file("foo_core/src/lib.rs", "//! foo_core") .file("foo-base/Cargo.toml", &basic_manifest("foo-base", "0.1.0")) .file("foo-base/src/lib.rs", "//! foo-base") .build(); let fingerprint_path = &p.build_dir().join("debug").join(".fingerprint"); p.cargo("build -p foo -p foo_core -p foo-base").run(); let mut fingerprint_names = get_fingerprints_without_hashes(fingerprint_path); // Artifacts present for all after building assert!(fingerprint_names.iter().any(|e| e == "foo")); let num_foo_core_artifacts = fingerprint_names .iter() .filter(|&e| e == "foo_core") .count(); assert_ne!(num_foo_core_artifacts, 0); let num_foo_base_artifacts = fingerprint_names .iter() .filter(|&e| e == "foo-base") .count(); assert_ne!(num_foo_base_artifacts, 0); p.cargo("clean -p foo").run(); fingerprint_names = get_fingerprints_without_hashes(fingerprint_path); // Cleaning `foo` leaves artifacts for the others assert!(!fingerprint_names.iter().any(|e| e == "foo")); assert_eq!( fingerprint_names .iter() .filter(|&e| e == "foo_core") .count(), num_foo_core_artifacts, ); assert_eq!( fingerprint_names .iter() .filter(|&e| e == "foo-base") .count(), num_foo_core_artifacts, ); } fn get_fingerprints_without_hashes(fingerprint_path: &Path) -> Vec { std::fs::read_dir(fingerprint_path) .expect("Build dir should be readable") .filter_map(|entry| entry.ok()) .map(|entry| { let name = entry.file_name(); let name = name .into_string() .expect("fingerprint name should be UTF-8"); name.rsplit_once('-') .expect("Name should contain at least one hyphen") .0 .to_owned() }) .collect() } #[cargo_test] fn clean_release() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] a = { path = "a" } "#, ) .file("src/main.rs", "fn main() {}") .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) .file("a/src/lib.rs", "") .build(); p.cargo("build --release").run(); p.cargo("clean -p foo").run(); p.cargo("build --release") .with_stderr_data(str![[r#" [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("clean -p foo --release").run(); p.cargo("build --release") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build").run(); p.cargo("clean").arg("--release").run(); assert!(p.build_dir().is_dir()); assert!(p.build_dir().join("debug").is_dir()); assert!(!p.build_dir().join("release").is_dir()); } #[cargo_test] fn clean_doc() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] a = { path = "a" } "#, ) .file("src/main.rs", "fn main() {}") .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) .file("a/src/lib.rs", "") .build(); p.cargo("doc").run(); let doc_path = &p.build_dir().join("doc"); assert!(doc_path.is_dir()); p.cargo("clean --doc") .with_stderr_data(str![[r#" [REMOVED] [FILE_NUM] files, [FILE_SIZE]B total "#]]) .run(); assert!(!doc_path.is_dir()); assert!(p.build_dir().is_dir()); } #[cargo_test] fn build_script() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" "#, ) .file("src/main.rs", "fn main() {}") .file( "build.rs", r#" use std::path::PathBuf; use std::env; fn main() { let out = PathBuf::from(env::var_os("OUT_DIR").unwrap()); if env::var("FIRST").is_ok() { std::fs::File::create(out.join("out")).unwrap(); } else { assert!(!out.join("out").exists()); } } "#, ) .file("a/src/lib.rs", "") .build(); p.cargo("build").env("FIRST", "1").run(); p.cargo("clean -p foo").run(); p.cargo("build -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..] build.rs [..]` [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc [..] src/main.rs [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn clean_git() { let git = git::new("dep", |project| { project .file("Cargo.toml", &basic_manifest("dep", "0.5.0")) .file("src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] dep = {{ git = '{}' }} "#, git.url() ), ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build").run(); p.cargo("clean -p dep") .with_stderr_data(str![[r#" [REMOVED] [FILE_NUM] files, [FILE_SIZE]B total "#]]) .run(); p.cargo("build").run(); } #[cargo_test] fn registry() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("bar", "0.1.0").publish(); p.cargo("build").run(); p.cargo("clean -p bar") .with_stderr_data(str![[r#" [REMOVED] [FILE_NUM] files, [FILE_SIZE]B total "#]]) .run(); p.cargo("build").run(); } #[cargo_test] fn clean_verbose() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = "0.1" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("bar", "0.1.0").publish(); p.cargo("build").run(); let mut expected = String::from( "\ [REMOVING] [ROOT]/foo/target/debug/.fingerprint/bar-[HASH] [REMOVING] [ROOT]/foo/target/debug/deps/libbar-[HASH].rlib [REMOVING] [ROOT]/foo/target/debug/deps/bar-[HASH].d [REMOVING] [ROOT]/foo/target/debug/deps/libbar-[HASH].rmeta ", ); if cfg!(target_os = "macos") { // Rust 1.69 has changed so that split-debuginfo=unpacked includes unpacked for rlibs. for _ in p.glob("target/debug/deps/bar-*.o") { expected.push_str("[REMOVING] [ROOT]/foo/target/debug/deps/bar-[HASH][..].o\n"); } } expected.push_str("[REMOVED] [FILE_NUM] files, [FILE_SIZE]B total\n"); p.cargo("clean -p bar --verbose") .with_stderr_data(&expected.unordered()) .run(); p.cargo("build").run(); } #[cargo_test] fn clean_remove_rlib_rmeta() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" "#, ) .file("src/lib.rs", "") .build(); p.cargo("build").run(); assert!(p.target_debug_dir().join("libfoo.rlib").exists()); let rmeta = p.glob("target/debug/deps/*.rmeta").next().unwrap().unwrap(); assert!(rmeta.exists()); p.cargo("clean -p foo").run(); assert!(!p.target_debug_dir().join("libfoo.rlib").exists()); assert!(!rmeta.exists()); } #[cargo_test] fn package_cleans_all_the_things() { // -p cleans everything // Use dashes everywhere to make sure dash/underscore stuff is handled. for crate_type in &["rlib", "dylib", "cdylib", "staticlib", "proc-macro"] { // Try each crate type individually since the behavior changes when // they are combined. let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo-bar" version = "0.1.0" edition = "2015" [lib] crate-type = ["{}"] "#, crate_type ), ) .file("src/lib.rs", "") .build(); p.cargo("build").run(); p.cargo("clean -p foo-bar").run(); assert_all_clean(&p.build_dir()); } let p = project() .file( "Cargo.toml", r#" [package] name = "foo-bar" version = "0.1.0" edition = "2018" [lib] crate-type = ["rlib", "dylib", "staticlib"] [[example]] name = "foo-ex-rlib" crate-type = ["rlib"] test = true [[example]] name = "foo-ex-cdylib" crate-type = ["cdylib"] test = true [[example]] name = "foo-ex-bin" test = true "#, ) .file("src/lib.rs", "") .file("src/lib/some-main.rs", "fn main() {}") .file("src/bin/other-main.rs", "fn main() {}") .file("examples/foo-ex-rlib.rs", "") .file("examples/foo-ex-cdylib.rs", "") .file("examples/foo-ex-bin.rs", "fn main() {}") .file("tests/foo-test.rs", "") .file("benches/foo-bench.rs", "") .file("build.rs", "fn main() {}") .build(); p.cargo("build --all-targets") .env("CARGO_INCREMENTAL", "1") .run(); p.cargo("test --all-targets") .env("CARGO_INCREMENTAL", "1") .run(); p.cargo("check --all-targets") .env("CARGO_INCREMENTAL", "1") .run(); p.cargo("clean -p foo-bar").run(); assert_all_clean(&p.build_dir()); // Try some targets. p.cargo("build --all-targets --target") .arg(rustc_host()) .run(); p.cargo("clean -p foo-bar --target").arg(rustc_host()).run(); assert_all_clean(&p.build_dir()); } // Ensures that all files for the package have been deleted. #[track_caller] fn assert_all_clean(build_dir: &Path) { let walker = walkdir::WalkDir::new(build_dir).into_iter(); for entry in walker.filter_entry(|e| { let path = e.path(); // This is a known limitation, clean can't differentiate between // the different build scripts from different packages. !(path .file_name() .unwrap() .to_str() .unwrap() .starts_with("build_script_build") && path .parent() .unwrap() .file_name() .unwrap() .to_str() .unwrap() == "incremental") }) { let entry = entry.unwrap(); let path = entry.path(); if let ".rustc_info.json" | ".cargo-lock" | "CACHEDIR.TAG" = path.file_name().unwrap().to_str().unwrap() { continue; } if path.is_symlink() || path.is_file() { panic!("{:?} was not cleaned", path); } } } #[cargo_test] fn clean_spec_version() { // clean -p foo where foo matches multiple versions Package::new("bar", "0.1.0").publish(); Package::new("bar", "0.2.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar1 = {version="0.1", package="bar"} bar2 = {version="0.2", package="bar"} "#, ) .file("src/lib.rs", "") .build(); p.cargo("build").run(); // Check suggestion for bad pkgid. p.cargo("clean -p baz") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package ID specification `baz` did not match any packages [HELP] a package with a similar name exists: `bar` "#]]) .run(); p.cargo("clean -p bar:0.1.0") .with_stderr_data(str![[r#" [WARNING] version qualifier in `-p bar:0.1.0` is ignored, cleaning all versions of `bar` found [REMOVED] [FILE_NUM] files, [FILE_SIZE]B total "#]]) .run(); let mut walker = walkdir::WalkDir::new(p.build_dir()) .into_iter() .filter_map(|e| e.ok()) .filter(|e| { let n = e.file_name().to_str().unwrap(); n.starts_with("bar") || n.starts_with("libbar") }); if let Some(e) = walker.next() { panic!("{:?} was not cleaned", e.path()); } } #[cargo_test] fn clean_spec_partial_version() { // clean -p foo where foo matches multiple versions Package::new("bar", "0.1.0").publish(); Package::new("bar", "0.2.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar1 = {version="0.1", package="bar"} bar2 = {version="0.2", package="bar"} "#, ) .file("src/lib.rs", "") .build(); p.cargo("build").run(); // Check suggestion for bad pkgid. p.cargo("clean -p baz") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package ID specification `baz` did not match any packages [HELP] a package with a similar name exists: `bar` "#]]) .run(); p.cargo("clean -p bar:0.1") .with_stderr_data(str![[r#" [WARNING] version qualifier in `-p bar:0.1` is ignored, cleaning all versions of `bar` found [REMOVED] [FILE_NUM] files, [FILE_SIZE]B total "#]]) .run(); let mut walker = walkdir::WalkDir::new(p.build_dir()) .into_iter() .filter_map(|e| e.ok()) .filter(|e| { let n = e.file_name().to_str().unwrap(); n.starts_with("bar") || n.starts_with("libbar") }); if let Some(e) = walker.next() { panic!("{:?} was not cleaned", e.path()); } } #[cargo_test] fn clean_spec_partial_version_ambiguous() { // clean -p foo where foo matches multiple versions Package::new("bar", "0.1.0").publish(); Package::new("bar", "0.2.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar1 = {version="0.1", package="bar"} bar2 = {version="0.2", package="bar"} "#, ) .file("src/lib.rs", "") .build(); p.cargo("build").run(); // Check suggestion for bad pkgid. p.cargo("clean -p baz") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package ID specification `baz` did not match any packages [HELP] a package with a similar name exists: `bar` "#]]) .run(); p.cargo("clean -p bar:0") .with_stderr_data(str![[r#" [WARNING] version qualifier in `-p bar:0` is ignored, cleaning all versions of `bar` found [REMOVED] [FILE_NUM] files, [FILE_SIZE]B total "#]]) .run(); let mut walker = walkdir::WalkDir::new(p.build_dir()) .into_iter() .filter_map(|e| e.ok()) .filter(|e| { let n = e.file_name().to_str().unwrap(); n.starts_with("bar") || n.starts_with("libbar") }); if let Some(e) = walker.next() { panic!("{:?} was not cleaned", e.path()); } } #[cargo_test] fn clean_spec_reserved() { // Clean when a target (like a test) has a reserved name. In this case, // make sure `clean -p` doesn't delete the reserved directory `build` when // there is a test named `build`. Package::new("bar", "1.0.0") .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .file("tests/build.rs", "") .build(); p.cargo("build --all-targets").run(); assert!(p.target_debug_dir().join("build").is_dir()); let build_test = p.glob("target/debug/deps/build-*").next().unwrap().unwrap(); assert!(build_test.exists()); // Tests are never "uplifted". assert!(p.glob("target/debug/build-*").next().is_none()); p.cargo("clean -p foo").run(); // Should not delete this. assert!(p.target_debug_dir().join("build").is_dir()); // This should not rebuild bar. p.cargo("build -v --all-targets") .with_stderr_data(str![[r#" [FRESH] bar v1.0.0 [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc [..]` [RUNNING] `rustc [..]` [RUNNING] `rustc [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn clean_dry_run() { // Basic `clean --dry-run` test. Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); // Start with no files. p.cargo("clean --dry-run") .with_stdout_data("") .with_stderr_data(str![[r#" [SUMMARY] 0 files [WARNING] no files deleted due to --dry-run "#]]) .run(); p.cargo("check").run(); let before = p.build_dir().ls_r(); p.cargo("clean --dry-run") .with_stderr_data(str![[r#" [SUMMARY] [FILE_NUM] files, [FILE_SIZE]B total [WARNING] no files deleted due to --dry-run "#]]) .run(); // Verify it didn't delete anything. let after = p.build_dir().ls_r(); assert_eq!(before, after); let mut expected = itertools::join(before.iter().map(|p| p.to_str().unwrap()), "\n"); expected.push_str("\n"); let expected = snapbox::filter::normalize_paths(&expected); let expected = assert_e2e().redactions().redact(&expected); eprintln!("{expected}"); // Verify the verbose output. p.cargo("clean --dry-run -v") .with_stdout_data(expected.unordered()) .with_stderr_data(str![[r#" [SUMMARY] [FILE_NUM] files, [FILE_SIZE]B total [WARNING] no files deleted due to --dry-run "#]]) .run(); } #[cargo_test] fn doc_with_package_selection() { // --doc with -p let p = project().file("src/lib.rs", "").build(); p.cargo("clean --doc -p foo") .with_status(101) .with_stderr_data(str![[r#" [ERROR] --doc cannot be used with -p "#]]) .run(); } #[cargo_test] fn quiet_does_not_show_summary() { // Checks that --quiet works with `cargo clean`, since there was a // subtle issue with how the flag is defined as a global flag. let p = project() .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("src/lib.rs", "") .build(); p.cargo("check").run(); p.cargo("clean --quiet --dry-run") .with_stdout_data("") .with_stderr_data("") .run(); // Verify exact same command without -q would actually display something. p.cargo("clean --dry-run") .with_stdout_data("") .with_stderr_data(str![[r#" [SUMMARY] [FILE_NUM] files, [FILE_SIZE]B total [WARNING] no files deleted due to --dry-run "#]]) .run(); } cargo-0.91.0/tests/testsuite/collisions.rs000064400000000000000000000430151046102023000167300ustar 00000000000000//! Tests for when multiple artifacts have the same output filename. //! See for more details. //! Ideally these should never happen, but I don't think we'll ever be able to //! prevent all collisions. use crate::prelude::*; use crate::utils::cross_compile::disabled as cross_compile_disabled; use cargo_test_support::registry::Package; use cargo_test_support::str; use cargo_test_support::{basic_manifest, cross_compile, project}; use std::env; #[cargo_test] fn collision_dylib() { // Path dependencies don't include metadata hash in filename for dylibs. let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "1.0.0" edition = "2015" [lib] crate-type = ["dylib"] "#, ) .file("a/src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "1.0.0" edition = "2015" [lib] crate-type = ["dylib"] name = "a" "#, ) .file("b/src/lib.rs", "") .build(); // `j=1` is required because on Windows you'll get an error due to // two processes writing to the file at the same time. p.cargo("build -j=1") .with_stderr_data(&format!("\ ... [WARNING] output filename collision. The lib target `a` in package `b v1.0.0 ([ROOT]/foo/b)` has the same output filename as the lib target `a` in package `a v1.0.0 ([ROOT]/foo/a)`. Colliding filename is: [ROOT]/foo/target/debug/deps/{}a{} The targets should have unique names. Consider changing their names to be unique or compiling them separately. This may become a hard error in the future; see . ... ", env::consts::DLL_PREFIX, env::consts::DLL_SUFFIX)) .run(); } #[cargo_test] fn collision_example() { // Examples in a workspace can easily collide. let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] "#, ) .file("a/Cargo.toml", &basic_manifest("a", "1.0.0")) .file("a/examples/ex1.rs", "fn main() {}") .file("b/Cargo.toml", &basic_manifest("b", "1.0.0")) .file("b/examples/ex1.rs", "fn main() {}") .build(); // `j=1` is required because on Windows you'll get an error due to // two processes writing to the file at the same time. p.cargo("build --examples -j=1") .with_stderr_data(str![[r#" ... [WARNING] output filename collision. The example target `ex1` in package `b v1.0.0 ([ROOT]/foo/b)` has the same output filename as the example target `ex1` in package `a v1.0.0 ([ROOT]/foo/a)`. Colliding filename is: [ROOT]/foo/target/debug/examples/ex1[EXE] The targets should have unique names. Consider changing their names to be unique or compiling them separately. This may become a hard error in the future; see . ... "#]]) .run(); } #[cargo_test] // See https://github.com/rust-lang/cargo/issues/7493 #[cfg_attr( any(target_env = "msvc", target_vendor = "apple"), ignore = "--artifact-dir and examples are currently broken on MSVC and apple" )] fn collision_export() { // `--artifact-dir` combines some things which can cause conflicts. let p = project() .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) .file("examples/foo.rs", "fn main() {}") .file("src/main.rs", "fn main() {}") .build(); // -j1 to avoid issues with two processes writing to the same file at the // same time. p.cargo("build -j1 --artifact-dir=out -Z unstable-options --bins --examples") .masquerade_as_nightly_cargo(&["artifact-dir"]) .with_stderr_data(str![[r#" [WARNING] `--artifact-dir` filename collision. The example target `foo` in package `foo v1.0.0 ([ROOT]/foo)` has the same output filename as the bin target `foo` in package `foo v1.0.0 ([ROOT]/foo)`. Colliding filename is: [ROOT]/foo/out/foo[EXE] The exported filenames should be unique. Consider changing their names to be unique or compiling them separately. This may become a hard error in the future; see . ... "#]]) .run(); } #[cargo_test] fn collision_doc() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] foo2 = { path = "foo2" } "#, ) .file("src/lib.rs", "") .file( "foo2/Cargo.toml", r#" [package] name = "foo2" version = "0.1.0" edition = "2015" [lib] name = "foo" "#, ) .file("foo2/src/lib.rs", "") .build(); p.cargo("doc -j=1") .with_stderr_data(str![[r#" ... [WARNING] output filename collision. The lib target `foo` in package `foo2 v0.1.0 ([ROOT]/foo/foo2)` has the same output filename as the lib target `foo` in package `foo v0.1.0 ([ROOT]/foo)`. Colliding filename is: [ROOT]/foo/target/doc/foo/index.html The targets should have unique names. This is a known bug where multiple crates with the same name use the same path; see . ... "#]]) .run(); } #[cargo_test] fn collision_doc_multiple_versions() { // Multiple versions of the same package. Package::new("old-dep", "1.0.0").publish(); Package::new("bar", "1.0.0").dep("old-dep", "1.0").publish(); // Note that this removes "old-dep". Just checking what happens when there // are orphans. Package::new("bar", "2.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" bar2 = { package="bar", version="2.0" } "#, ) .file("src/lib.rs", "") .build(); // Should only document bar 2.0, should not document old-dep. p.cargo("doc") .with_stderr_data( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 3 packages to latest compatible versions [ADDING] bar v1.0.0 (available: v2.0.0) [DOWNLOADING] crates ... [DOWNLOADED] bar v2.0.0 (registry `dummy-registry`) [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [DOWNLOADED] old-dep v1.0.0 (registry `dummy-registry`) [CHECKING] old-dep v1.0.0 [CHECKING] bar v2.0.0 [CHECKING] bar v1.0.0 [DOCUMENTING] bar v2.0.0 [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [DOCUMENTING] foo v0.1.0 ([ROOT]/foo) [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]] .unordered(), ) .run(); } #[cargo_test] fn collision_doc_host_target_feature_split() { // Same dependency built twice due to different features. // // foo v0.1.0 // ├── common v1.0.0 // │ └── common-dep v1.0.0 // └── pm v0.1.0 (proc-macro) // └── common v1.0.0 // └── common-dep v1.0.0 // [build-dependencies] // └── common-dep v1.0.0 // // Here `common` and `common-dep` are built twice. `common-dep` has // different features for host versus target. Package::new("common-dep", "1.0.0") .feature("bdep-feat", &[]) .file( "src/lib.rs", r#" /// Some doc pub fn f() {} /// Another doc #[cfg(feature = "bdep-feat")] pub fn bdep_func() {} "#, ) .publish(); Package::new("common", "1.0.0") .dep("common-dep", "1.0") .file( "src/lib.rs", r#" /// Some doc pub fn f() {} "#, ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" resolver = "2" [dependencies] pm = { path = "pm" } common = "1.0" [build-dependencies] common-dep = { version = "1.0", features = ["bdep-feat"] } "#, ) .file( "src/lib.rs", r#" /// Some doc pub fn f() {} "#, ) .file("build.rs", "fn main() {}") .file( "pm/Cargo.toml", r#" [package] name = "pm" version = "0.1.0" edition = "2018" [lib] proc-macro = true [dependencies] common = "1.0" "#, ) .file( "pm/src/lib.rs", r#" use proc_macro::TokenStream; /// Some doc #[proc_macro] pub fn pm(_input: TokenStream) -> TokenStream { "".parse().unwrap() } "#, ) .build(); // No warnings, no duplicates, common and common-dep only documented once. p.cargo("doc") // Cannot check full output due to https://github.com/rust-lang/cargo/issues/9076 .with_stderr_does_not_contain("[WARNING][..]") .run(); assert!(p.build_dir().join("doc/common_dep/fn.f.html").exists()); assert!( !p.build_dir() .join("doc/common_dep/fn.bdep_func.html") .exists() ); assert!(p.build_dir().join("doc/common/fn.f.html").exists()); assert!(p.build_dir().join("doc/pm/macro.pm.html").exists()); assert!(p.build_dir().join("doc/foo/fn.f.html").exists()); } #[cargo_test] fn collision_doc_profile_split() { // Same dependency built twice due to different profile settings. Package::new("common", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] pm = { path = "pm" } common = "1.0" [profile.dev] opt-level = 2 "#, ) .file("src/lib.rs", "") .file( "pm/Cargo.toml", r#" [package] name = "pm" version = "0.1.0" edition = "2015" [dependencies] common = "1.0" [lib] proc-macro = true "#, ) .file("pm/src/lib.rs", "") .build(); // Just to verify that common is normally built twice. // This is unordered because in rare cases `pm` may start // building in-between the two `common`. p.cargo("build -v") .with_stderr_data( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] common v1.0.0 (registry `dummy-registry`) [COMPILING] common v1.0.0 [RUNNING] `rustc --crate-name common [..] [RUNNING] `rustc --crate-name common [..] [COMPILING] pm v0.1.0 ([ROOT]/foo/pm) [RUNNING] `rustc --crate-name pm [..] [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] [FINISHED] `dev` profile [optimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); // Should only document common once, no warnings. p.cargo("doc") .with_stderr_data( str![[r#" [CHECKING] common v1.0.0 [DOCUMENTING] common v1.0.0 [DOCUMENTING] pm v0.1.0 ([ROOT]/foo/pm) [DOCUMENTING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [optimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]] .unordered(), ) .run(); } #[cargo_test] fn collision_doc_sources() { // Different sources with the same package. Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" bar2 = { path = "bar", package = "bar" } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "1.0.0")) .file("bar/src/lib.rs", "") .build(); p.cargo("doc -j=1") .with_stderr_data( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [WARNING] output filename collision. The lib target `bar` in package `bar v1.0.0` has the same output filename as the lib target `bar` in package `bar v1.0.0 ([ROOT]/foo/bar)`. Colliding filename is: [ROOT]/foo/target/doc/bar/index.html The targets should have unique names. This is a known bug where multiple crates with the same name use the same path; see . [CHECKING] bar v1.0.0 ([ROOT]/foo/bar) [DOCUMENTING] bar v1.0.0 ([ROOT]/foo/bar) [DOCUMENTING] bar v1.0.0 [CHECKING] bar v1.0.0 [DOCUMENTING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]] .unordered(), ) .run(); } #[cargo_test] fn collision_doc_target() { // collision in doc with --target, doesn't fail due to orphans if cross_compile_disabled() { return; } Package::new("orphaned", "1.0.0").publish(); Package::new("bar", "1.0.0") .dep("orphaned", "1.0") .publish(); Package::new("bar", "2.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar2 = { version = "2.0", package="bar" } bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("doc --target") .arg(cross_compile::alternate()) .with_stderr_data( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 3 packages to latest compatible versions [ADDING] bar v1.0.0 (available: v2.0.0) [DOWNLOADING] crates ... [DOWNLOADED] orphaned v1.0.0 (registry `dummy-registry`) [DOWNLOADED] bar v2.0.0 (registry `dummy-registry`) [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [CHECKING] orphaned v1.0.0 [DOCUMENTING] bar v2.0.0 [CHECKING] bar v2.0.0 [CHECKING] bar v1.0.0 [DOCUMENTING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/[ALT_TARGET]/doc/foo/index.html "#]] .unordered(), ) .run(); } #[cargo_test] fn collision_with_root() { // Check for a doc collision between a root package and a dependency. // In this case, `foo-macro` comes from both the workspace and crates.io. // This checks that the duplicate correction code doesn't choke on this // by removing the root unit. Package::new("foo-macro", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["abc", "foo-macro"] "#, ) .file( "abc/Cargo.toml", r#" [package] name = "abc" version = "1.0.0" edition = "2015" [dependencies] foo-macro = "1.0" "#, ) .file("abc/src/lib.rs", "") .file( "foo-macro/Cargo.toml", r#" [package] name = "foo-macro" version = "1.0.0" edition = "2015" [lib] proc-macro = true [dependencies] abc = {path="../abc"} "#, ) .file("foo-macro/src/lib.rs", "") .build(); p.cargo("doc -j=1") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] foo-macro v1.0.0 (registry `dummy-registry`) [WARNING] output filename collision. The lib target `foo_macro` in package `foo-macro v1.0.0` has the same output filename as the lib target `foo_macro` in package `foo-macro v1.0.0 ([ROOT]/foo/foo-macro)`. Colliding filename is: [ROOT]/foo/target/doc/foo_macro/index.html The targets should have unique names. This is a known bug where multiple crates with the same name use the same path; see . [CHECKING] foo-macro v1.0.0 [DOCUMENTING] foo-macro v1.0.0 [CHECKING] abc v1.0.0 ([ROOT]/foo/abc) [DOCUMENTING] foo-macro v1.0.0 ([ROOT]/foo/foo-macro) [DOCUMENTING] abc v1.0.0 ([ROOT]/foo/abc) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/abc/index.html and 1 other file "#]].unordered()) .run(); } cargo-0.91.0/tests/testsuite/compile_time_deps.rs000064400000000000000000000240671046102023000202410ustar 00000000000000use crate::prelude::*; use cargo_test_support::{project, str}; #[cargo_test] fn gated_by_unstable_opts() { let p = project() .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) .build(); p.cargo("check --compile-time-deps") .with_status(101) .with_stderr_data(str![[r#" [ERROR] the `--compile-time-deps` flag is unstable, and only available on the nightly channel of Cargo, but this is the `stable` channel See https://doc.rust-lang.org/book/appendix-07-nightly-rust.html for more information about Rust release channels. See https://github.com/rust-lang/cargo/issues/14434 for more information about the `--compile-time-deps` flag. "#]]) .run(); } #[cargo_test] fn non_comp_time_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2021" [dependencies] bar.path = "bar" "#, ) .file( "src/main.rs", r#" fn main() { bar::bar(); } "#, ) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2021" "#, ) .file("bar/src/lib.rs", r#"pub fn bar() {}"#) .build(); p.cargo("-Zunstable-options check --compile-time-deps") .masquerade_as_nightly_cargo(&["compile-time-deps"]) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn proc_macro_dep() { let p = project() .file( "Cargo.toml", r#" [workspace] resolver = "2" members = ["foo", "bar", "baz"] [workspace.dependencies] bar.path = "bar" baz.path = "baz" "#, ) .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2021" [dependencies] bar.workspace = true "#, ) .file( "foo/src/main.rs", r#" fn main() { bar::bar!(); } "#, ) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2021" [lib] proc-macro = true [dependencies] baz.workspace = true "#, ) .file( "bar/src/lib.rs", r#" extern crate proc_macro; use proc_macro::TokenStream; #[proc_macro] pub fn bar(input: TokenStream) -> TokenStream { baz::baz(); input } "#, ) .file( "bar/tests/simple.rs", r#" #[test] fn test_bar() { let _x: bool = bar::bar!(true); } "#, ) .file( "baz/Cargo.toml", r#" [package] name = "baz" version = "0.0.1" edition = "2021" "#, ) .file("baz/src/lib.rs", r#"pub fn baz() {}"#) .build(); p.cargo("-Zunstable-options check --package foo --compile-time-deps") .masquerade_as_nightly_cargo(&["compile-time-deps"]) .with_stderr_data(str![[r#" [COMPILING] baz v0.0.1 ([ROOT]/foo/baz) [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("clean").run(); p.cargo("-Zunstable-options check --package bar --compile-time-deps") .masquerade_as_nightly_cargo(&["compile-time-deps"]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("clean").run(); p.cargo("-Zunstable-options check --package bar --all-targets --compile-time-deps") .masquerade_as_nightly_cargo(&["compile-time-deps"]) .with_stderr_data(str![[r#" [COMPILING] baz v0.0.1 ([ROOT]/foo/baz) [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn build_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2021" [build-dependencies] bar.path = "bar" "#, ) .file("src/main.rs", r#"fn main() {}"#) .file( "build.rs", r#" fn main() { bar::bar(); std::fs::write("check-script-output", "build script run").unwrap(); } "#, ) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2021" [dependencies] baz.path = "baz" "#, ) .file( "bar/src/lib.rs", r#" pub fn bar() { baz::baz(); } "#, ) .file( "bar/baz/Cargo.toml", r#" [package] name = "baz" version = "0.0.1" edition = "2021" "#, ) .file("bar/baz/src/lib.rs", r#"pub fn baz() {}"#) .build(); p.cargo("-Zunstable-options check --compile-time-deps") .masquerade_as_nightly_cargo(&["compile-time-deps"]) .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] baz v0.0.1 ([ROOT]/foo/bar/baz) [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert_eq!(p.read_file("check-script-output"), "build script run"); } #[cargo_test] fn indirect_comp_time_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2021" [dependencies] bar.path = "bar" "#, ) .file("src/main.rs", r#"fn main() {}"#) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2021" [build-dependencies] baz.path = "baz" "#, ) .file("bar/src/lib.rs", r#"pub fn bar() {}"#) .file( "bar/build.rs", r#" fn main() { baz::baz(); } "#, ) .file( "bar/baz/Cargo.toml", r#" [package] name = "baz" version = "0.0.1" edition = "2021" "#, ) .file("bar/src/lib.rs", r#"pub fn baz() {}"#) .file( "bar/baz/Cargo.toml", r#" [package] name = "baz" version = "0.0.1" edition = "2021" "#, ) .file("bar/baz/src/lib.rs", r#"pub fn baz() {}"#) .build(); p.cargo("-Zunstable-options check --compile-time-deps") .masquerade_as_nightly_cargo(&["compile-time-deps"]) .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] baz v0.0.1 ([ROOT]/foo/bar/baz) [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn tests_target() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2021" [dev-dependencies] bar.path = "bar" "#, ) .file( "src/main.rs", r#" fn main() {} #[test] fn foo() { bar::bar!(); } "#, ) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2021" [lib] proc-macro = true "#, ) .file( "bar/src/lib.rs", r#" extern crate proc_macro; use proc_macro::TokenStream; #[proc_macro] pub fn bar(input: TokenStream) -> TokenStream { input } "#, ) .build(); p.cargo("-Zunstable-options check --tests --compile-time-deps") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .masquerade_as_nightly_cargo(&["compile-time-deps"]) .run(); p.cargo("clean").run(); p.cargo("-Zunstable-options check --compile-time-deps") .masquerade_as_nightly_cargo(&["compile-time-deps"]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } cargo-0.91.0/tests/testsuite/concurrent.rs000064400000000000000000000347741046102023000167500ustar 00000000000000//! Tests for running multiple `cargo` processes at the same time. use std::fs; use std::net::TcpListener; use std::process::Stdio; use std::sync::mpsc::channel; use std::thread; use std::{env, str}; use crate::prelude::*; use crate::utils::cargo_process; use cargo_test_support::git; use cargo_test_support::install::assert_has_installed_exe; use cargo_test_support::paths; use cargo_test_support::registry::Package; use cargo_test_support::str; use cargo_test_support::{basic_manifest, execs, project, slow_cpu_multiplier}; fn pkg(name: &str, vers: &str) { Package::new(name, vers) .file("src/main.rs", "fn main() {{}}") .publish(); } #[cargo_test] fn multiple_installs() { let p = project() .no_manifest() .file("a/Cargo.toml", &basic_manifest("foo", "0.0.0")) .file("a/src/main.rs", "fn main() {}") .file("b/Cargo.toml", &basic_manifest("bar", "0.0.0")) .file("b/src/main.rs", "fn main() {}"); let p = p.build(); let mut a = p.cargo("install").cwd("a").build_command(); let mut b = p.cargo("install").cwd("b").build_command(); a.stdout(Stdio::piped()).stderr(Stdio::piped()); b.stdout(Stdio::piped()).stderr(Stdio::piped()); let a = a.spawn().unwrap(); let b = b.spawn().unwrap(); let a = thread::spawn(move || a.wait_with_output().unwrap()); let b = b.wait_with_output().unwrap(); let a = a.join().unwrap(); execs().run_output(&a); execs().run_output(&b); assert_has_installed_exe(paths::cargo_home(), "foo"); assert_has_installed_exe(paths::cargo_home(), "bar"); } #[cargo_test] fn concurrent_installs() { const LOCKED_BUILD: &str = "waiting for file lock on build directory"; pkg("foo", "0.0.1"); pkg("bar", "0.0.1"); let mut a = cargo_process("install foo").build_command(); let mut b = cargo_process("install bar").build_command(); a.stdout(Stdio::piped()).stderr(Stdio::piped()); b.stdout(Stdio::piped()).stderr(Stdio::piped()); let a = a.spawn().unwrap(); let b = b.spawn().unwrap(); let a = thread::spawn(move || a.wait_with_output().unwrap()); let b = b.wait_with_output().unwrap(); let a = a.join().unwrap(); assert!(!str::from_utf8(&a.stderr).unwrap().contains(LOCKED_BUILD)); assert!(!str::from_utf8(&b.stderr).unwrap().contains(LOCKED_BUILD)); execs().run_output(&a); execs().run_output(&b); assert_has_installed_exe(paths::cargo_home(), "foo"); assert_has_installed_exe(paths::cargo_home(), "bar"); } #[cargo_test] fn one_install_should_be_bad() { let p = project() .no_manifest() .file("a/Cargo.toml", &basic_manifest("foo", "0.0.0")) .file("a/src/main.rs", "fn main() {}") .file("b/Cargo.toml", &basic_manifest("foo", "0.0.0")) .file("b/src/main.rs", "fn main() {}"); let p = p.build(); let mut a = p.cargo("install").cwd("a").build_command(); let mut b = p.cargo("install").cwd("b").build_command(); a.stdout(Stdio::piped()).stderr(Stdio::piped()); b.stdout(Stdio::piped()).stderr(Stdio::piped()); let a = a.spawn().unwrap(); let b = b.spawn().unwrap(); let a = thread::spawn(move || a.wait_with_output().unwrap()); let b = b.wait_with_output().unwrap(); let a = a.join().unwrap(); execs().run_output(&a); execs().run_output(&b); assert_has_installed_exe(paths::cargo_home(), "foo"); } #[cargo_test] fn multiple_registry_fetches() { let mut pkg = Package::new("bar", "1.0.2"); for i in 0..10 { let name = format!("foo{}", i); Package::new(&name, "1.0.0").publish(); pkg.dep(&name, "*"); } pkg.publish(); let p = project() .no_manifest() .file( "a/Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.0" [dependencies] bar = "*" "#, ) .file("a/src/main.rs", "fn main() {}") .file( "b/Cargo.toml", r#" [package] name = "bar" authors = [] version = "0.0.0" [dependencies] bar = "*" "#, ) .file("b/src/main.rs", "fn main() {}"); let p = p.build(); let mut a = p.cargo("build").cwd("a").build_command(); let mut b = p.cargo("build").cwd("b").build_command(); a.stdout(Stdio::piped()).stderr(Stdio::piped()); b.stdout(Stdio::piped()).stderr(Stdio::piped()); let a = a.spawn().unwrap(); let b = b.spawn().unwrap(); let a = thread::spawn(move || a.wait_with_output().unwrap()); let b = b.wait_with_output().unwrap(); let a = a.join().unwrap(); execs().run_output(&a); execs().run_output(&b); let suffix = env::consts::EXE_SUFFIX; assert!( p.root() .join("a/target/debug") .join(format!("foo{}", suffix)) .is_file() ); assert!( p.root() .join("b/target/debug") .join(format!("bar{}", suffix)) .is_file() ); } #[cargo_test] fn git_same_repo_different_tags() { let a = git::new("dep", |project| { project .file("Cargo.toml", &basic_manifest("dep", "0.5.0")) .file("src/lib.rs", "pub fn tag1() {}") }); let repo = git2::Repository::open(&a.root()).unwrap(); git::tag(&repo, "tag1"); a.change_file("src/lib.rs", "pub fn tag2() {}"); git::add(&repo); git::commit(&repo); git::tag(&repo, "tag2"); let p = project() .no_manifest() .file( "a/Cargo.toml", &format!( r#" [package] name = "foo" authors = [] version = "0.0.0" [dependencies] dep = {{ git = '{}', tag = 'tag1' }} "#, a.url() ), ) .file( "a/src/main.rs", "extern crate dep; fn main() { dep::tag1(); }", ) .file( "b/Cargo.toml", &format!( r#" [package] name = "bar" authors = [] version = "0.0.0" [dependencies] dep = {{ git = '{}', tag = 'tag2' }} "#, a.url() ), ) .file( "b/src/main.rs", "extern crate dep; fn main() { dep::tag2(); }", ); let p = p.build(); let mut a = p.cargo("build -v").cwd("a").build_command(); let mut b = p.cargo("build -v").cwd("b").build_command(); a.stdout(Stdio::piped()).stderr(Stdio::piped()); b.stdout(Stdio::piped()).stderr(Stdio::piped()); let a = a.spawn().unwrap(); let b = b.spawn().unwrap(); let a = thread::spawn(move || a.wait_with_output().unwrap()); let b = b.wait_with_output().unwrap(); let a = a.join().unwrap(); execs().run_output(&a); execs().run_output(&b); } #[cargo_test] fn git_same_branch_different_revs() { let a = git::new("dep", |project| { project .file("Cargo.toml", &basic_manifest("dep", "0.5.0")) .file("src/lib.rs", "pub fn f1() {}") }); let p = project() .no_manifest() .file( "a/Cargo.toml", &format!( r#" [package] name = "foo" authors = [] version = "0.0.0" [dependencies] dep = {{ git = '{}' }} "#, a.url() ), ) .file( "a/src/main.rs", "extern crate dep; fn main() { dep::f1(); }", ) .file( "b/Cargo.toml", &format!( r#" [package] name = "bar" authors = [] version = "0.0.0" [dependencies] dep = {{ git = '{}' }} "#, a.url() ), ) .file( "b/src/main.rs", "extern crate dep; fn main() { dep::f2(); }", ); let p = p.build(); // Generate a Cargo.lock pointing at the current rev, then clear out the // target directory p.cargo("build").cwd("a").run(); fs::remove_dir_all(p.root().join("a/target")).unwrap(); // Make a new commit on the master branch let repo = git2::Repository::open(&a.root()).unwrap(); a.change_file("src/lib.rs", "pub fn f2() {}"); git::add(&repo); git::commit(&repo); // Now run both builds in parallel. The build of `b` should pick up the // newest commit while the build of `a` should use the locked old commit. let mut a = p.cargo("build").cwd("a").build_command(); let mut b = p.cargo("build").cwd("b").build_command(); a.stdout(Stdio::piped()).stderr(Stdio::piped()); b.stdout(Stdio::piped()).stderr(Stdio::piped()); let a = a.spawn().unwrap(); let b = b.spawn().unwrap(); let a = thread::spawn(move || a.wait_with_output().unwrap()); let b = b.wait_with_output().unwrap(); let a = a.join().unwrap(); execs().run_output(&a); execs().run_output(&b); } #[cargo_test] fn same_project() { let p = project() .file("src/main.rs", "fn main() {}") .file("src/lib.rs", ""); let p = p.build(); let mut a = p.cargo("build").build_command(); let mut b = p.cargo("build").build_command(); a.stdout(Stdio::piped()).stderr(Stdio::piped()); b.stdout(Stdio::piped()).stderr(Stdio::piped()); let a = a.spawn().unwrap(); let b = b.spawn().unwrap(); let a = thread::spawn(move || a.wait_with_output().unwrap()); let b = b.wait_with_output().unwrap(); let a = a.join().unwrap(); execs().run_output(&a); execs().run_output(&b); } // Make sure that if Cargo dies while holding a lock that it's released and the // next Cargo to come in will take over cleanly. #[cargo_test] fn killing_cargo_releases_the_lock() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.0" build = "build.rs" "#, ) .file("src/main.rs", "fn main() {}") .file( "build.rs", r#" use std::net::TcpStream; fn main() { if std::env::var("A").is_ok() { TcpStream::connect(&std::env::var("ADDR").unwrap()[..]) .unwrap(); std::thread::sleep(std::time::Duration::new(10, 0)); } } "#, ); let p = p.build(); // Our build script will connect to our local TCP socket to inform us that // it's started and that's how we know that `a` will have the lock // when we kill it. let l = TcpListener::bind("127.0.0.1:0").unwrap(); let mut a = p.cargo("build").build_command(); let mut b = p.cargo("build").build_command(); a.stdout(Stdio::piped()).stderr(Stdio::piped()); b.stdout(Stdio::piped()).stderr(Stdio::piped()); a.env("ADDR", l.local_addr().unwrap().to_string()) .env("A", "a"); b.env("ADDR", l.local_addr().unwrap().to_string()) .env_remove("A"); // Spawn `a`, wait for it to get to the build script (at which point the // lock is held), then kill it. let mut a = a.spawn().unwrap(); l.accept().unwrap(); a.kill().unwrap(); // Spawn `b`, then just finish the output of a/b the same way the above // tests does. let b = b.spawn().unwrap(); let a = thread::spawn(move || a.wait_with_output().unwrap()); let b = b.wait_with_output().unwrap(); let a = a.join().unwrap(); // We killed `a`, so it shouldn't succeed, but `b` should have succeeded. assert!(!a.status.success()); execs().run_output(&b); } #[cargo_test] fn debug_release_ok() { let p = project().file("src/main.rs", "fn main() {}"); let p = p.build(); p.cargo("build").run(); fs::remove_dir_all(p.root().join("target")).unwrap(); let mut a = p.cargo("build").build_command(); let mut b = p.cargo("build --release").build_command(); a.stdout(Stdio::piped()).stderr(Stdio::piped()); b.stdout(Stdio::piped()).stderr(Stdio::piped()); let a = a.spawn().unwrap(); let b = b.spawn().unwrap(); let a = thread::spawn(move || a.wait_with_output().unwrap()); let b = b.wait_with_output().unwrap(); let a = a.join().unwrap(); execs() .with_stderr_data(str![[r#" ... [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run_output(&a); execs() .with_stderr_data(str![[r#" ... [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]]) .run_output(&b); } #[cargo_test] fn no_deadlock_with_git_dependencies() { let dep1 = git::new("dep1", |project| { project .file("Cargo.toml", &basic_manifest("dep1", "0.5.0")) .file("src/lib.rs", "") }); let dep2 = git::new("dep2", |project| { project .file("Cargo.toml", &basic_manifest("dep2", "0.5.0")) .file("src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" authors = [] version = "0.0.0" [dependencies] dep1 = {{ git = '{}' }} dep2 = {{ git = '{}' }} "#, dep1.url(), dep2.url() ), ) .file("src/main.rs", "fn main() { }"); let p = p.build(); let n_concurrent_builds = 5; let (tx, rx) = channel(); for _ in 0..n_concurrent_builds { let cmd = p .cargo("build") .build_command() .stdout(Stdio::piped()) .stderr(Stdio::piped()) .spawn(); let tx = tx.clone(); thread::spawn(move || { let result = cmd.unwrap().wait_with_output().unwrap(); tx.send(result).unwrap() }); } for _ in 0..n_concurrent_builds { let result = rx.recv_timeout(slow_cpu_multiplier(30)).expect("Deadlock!"); execs().run_output(&result); } } cargo-0.91.0/tests/testsuite/config.rs000064400000000000000000001723531046102023000160270ustar 00000000000000//! Tests for config settings. use std::borrow::Borrow; use std::collections::{BTreeMap, HashMap}; use std::fs; use std::io; use std::os; use std::path::{Path, PathBuf}; use crate::prelude::*; use cargo::CargoResult; use cargo::core::features::{GitFeatures, GitoxideFeatures}; use cargo::core::{PackageIdSpec, Shell}; use cargo::util::auth::RegistryConfig; use cargo::util::context::{ self, Definition, GlobalContext, JobsConfig, SslVersionConfig, StringList, }; use cargo_test_support::compare::assert_e2e; use cargo_test_support::str; use cargo_test_support::{paths, project, project_in_home, symlink_supported, t}; use cargo_util_schemas::manifest::TomlTrimPaths; use cargo_util_schemas::manifest::TomlTrimPathsValue; use cargo_util_schemas::manifest::{self as cargo_toml, TomlDebugInfo, VecStringOrBool as VSOB}; use serde::Deserialize; /// Helper for constructing a `GlobalContext` object. pub struct GlobalContextBuilder { env: HashMap, unstable: Vec, config_args: Vec, cwd: Option, root: Option, enable_nightly_features: bool, } impl GlobalContextBuilder { pub fn new() -> GlobalContextBuilder { GlobalContextBuilder { env: HashMap::new(), unstable: Vec::new(), config_args: Vec::new(), root: None, cwd: None, enable_nightly_features: false, } } /// Passes a `-Z` flag. pub fn unstable_flag(&mut self, s: impl Into) -> &mut Self { self.unstable.push(s.into()); self } /// Sets an environment variable. pub fn env(&mut self, key: impl Into, val: impl Into) -> &mut Self { self.env.insert(key.into(), val.into()); self } /// Unconditionally enable nightly features, even on stable channels. pub fn nightly_features_allowed(&mut self, allowed: bool) -> &mut Self { self.enable_nightly_features = allowed; self } /// Passes a `--config` flag. pub fn config_arg(&mut self, arg: impl Into) -> &mut Self { self.config_args.push(arg.into()); self } /// Sets the current working directory where config files will be loaded. /// /// Default is the root from [`GlobalContextBuilder::root`] or [`paths::root`]. pub fn cwd(&mut self, path: impl AsRef) -> &mut Self { let path = path.as_ref(); let cwd = self .root .as_ref() .map_or_else(|| paths::root().join(path), |r| r.join(path)); self.cwd = Some(cwd); self } /// Sets the test root directory. /// /// This generally should not be necessary. It is only useful if you want /// to create a [`GlobalContext`] from within a thread. Since Cargo's /// testsuite uses thread-local storage, this can be used to avoid accessing /// that thread-local storage. /// /// Default is [`paths::root`]. pub fn root(&mut self, path: impl Into) -> &mut Self { self.root = Some(path.into()); self } /// Creates the [`GlobalContext`]. pub fn build(&self) -> GlobalContext { self.build_err().unwrap() } /// Creates the [`GlobalContext`], returning a Result. pub fn build_err(&self) -> CargoResult { let root = self.root.clone().unwrap_or_else(|| paths::root()); let output = Box::new(fs::File::create(root.join("shell.out")).unwrap()); let shell = Shell::from_write(output); let cwd = self.cwd.clone().unwrap_or_else(|| root.clone()); let homedir = root.join("home").join(".cargo"); let mut gctx = GlobalContext::new(shell, cwd, homedir); gctx.nightly_features_allowed = self.enable_nightly_features || !self.unstable.is_empty(); gctx.set_env(self.env.clone()); gctx.set_search_stop_path(&root); gctx.configure( 0, false, None, false, false, false, &None, &self.unstable, &self.config_args, )?; Ok(gctx) } } fn new_gctx() -> GlobalContext { GlobalContextBuilder::new().build() } /// Read the output from Config. pub fn read_output(gctx: GlobalContext) -> String { drop(gctx); // Paranoid about flushing the file. let path = paths::root().join("shell.out"); fs::read_to_string(path).unwrap() } #[cargo_test] fn read_env_vars_for_config() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.0" build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" use std::env; fn main() { assert_eq!(env::var("NUM_JOBS").unwrap(), "100"); } "#, ) .build(); p.cargo("check").env("CARGO_BUILD_JOBS", "100").run(); } pub fn write_config_extless(config: &str) { write_config_at(paths::root().join(".cargo/config"), config); } pub fn write_config_at(path: impl AsRef, contents: &str) { let path = paths::root().join(path.as_ref()); fs::create_dir_all(path.parent().unwrap()).unwrap(); fs::write(path, contents).unwrap(); } pub fn write_config_toml(config: &str) { write_config_at(paths::root().join(".cargo/config.toml"), config); } #[cfg(unix)] fn symlink_file(target: &Path, link: &Path) -> io::Result<()> { os::unix::fs::symlink(target, link) } #[cfg(windows)] fn symlink_file(target: &Path, link: &Path) -> io::Result<()> { os::windows::fs::symlink_file(target, link) } fn make_config_symlink_to_config_toml_absolute() { let toml_path = paths::root().join(".cargo/config.toml"); let symlink_path = paths::root().join(".cargo/config"); t!(symlink_file(&toml_path, &symlink_path)); } fn make_config_symlink_to_config_toml_relative() { let symlink_path = paths::root().join(".cargo/config"); t!(symlink_file(Path::new("config.toml"), &symlink_path)); } fn rename_config_toml_to_config_replacing_with_symlink() { let root = paths::root(); t!(fs::rename( root.join(".cargo/config.toml"), root.join(".cargo/config") )); t!(symlink_file( Path::new("config"), &root.join(".cargo/config.toml") )); } #[track_caller] pub fn assert_error>(error: E, msgs: impl IntoData) { let causes = error .borrow() .chain() .enumerate() .map(|(i, e)| { if i == 0 { e.to_string() } else { format!("Caused by:\n {}", e) } }) .collect::>() .join("\n\n"); assert_e2e().eq(&causes, msgs); } #[cargo_test] fn get_config() { write_config_toml( "\ [S] f1 = 123 ", ); let gctx = new_gctx(); #[derive(Debug, Deserialize, Eq, PartialEq)] struct S { f1: Option, } let s: S = gctx.get("S").unwrap(); assert_eq!(s, S { f1: Some(123) }); let gctx = GlobalContextBuilder::new().env("CARGO_S_F1", "456").build(); let s: S = gctx.get("S").unwrap(); assert_eq!(s, S { f1: Some(456) }); } #[cfg(windows)] #[cargo_test] fn environment_variable_casing() { // Issue #11814: Environment variable names are case-insensitive on Windows. let gctx = GlobalContextBuilder::new() .env("Path", "abc") .env("Two-Words", "abc") .env("two_words", "def") .build(); let var = gctx.get_env("PATH").unwrap(); assert_eq!(var, String::from("abc")); let var = gctx.get_env("path").unwrap(); assert_eq!(var, String::from("abc")); let var = gctx.get_env("TWO-WORDS").unwrap(); assert_eq!(var, String::from("abc")); // Make sure that we can still distinguish between dashes and underscores // in variable names. let var = gctx.get_env("Two_Words").unwrap(); assert_eq!(var, String::from("def")); } #[cargo_test] fn config_works_without_extension() { write_config_extless( "\ [foo] f1 = 1 ", ); let gctx = new_gctx(); assert_eq!(gctx.get::>("foo.f1").unwrap(), Some(1)); // It should NOT have warned for the symlink. let output = read_output(gctx); let expected = str![[r#" [WARNING] `[ROOT]/.cargo/config` is deprecated in favor of `config.toml` [NOTE] if you need to support cargo 1.38 or earlier, you can symlink `config` to `config.toml` "#]]; assert_e2e().eq(&output, expected); } #[cargo_test] fn home_config_works_without_extension() { write_config_at( paths::cargo_home().join("config"), "\ [foo] f1 = 1 ", ); let p = project_in_home("foo").file("src/lib.rs", "").build(); p.cargo("-vV") .with_stderr_data(str![[r#" [WARNING] `[ROOT]/home/.cargo/config` is deprecated in favor of `config.toml` [NOTE] if you need to support cargo 1.38 or earlier, you can symlink `config` to `config.toml` "#]]) .run(); } #[cargo_test] fn config_ambiguous_filename_symlink_doesnt_warn() { // Windows requires special permissions to create symlinks. // If we don't have permission, just skip this test. if !symlink_supported() { return; }; write_config_toml( "\ [foo] f1 = 1 ", ); make_config_symlink_to_config_toml_absolute(); let gctx = new_gctx(); assert_eq!(gctx.get::>("foo.f1").unwrap(), Some(1)); // It should NOT have warned for the symlink. let output = read_output(gctx); assert_e2e().eq(&output, str![[""]]); } #[cargo_test] fn config_ambiguous_filename_symlink_doesnt_warn_relative() { // Windows requires special permissions to create symlinks. // If we don't have permission, just skip this test. if !symlink_supported() { return; }; write_config_toml( "\ [foo] f1 = 1 ", ); make_config_symlink_to_config_toml_relative(); let gctx = new_gctx(); assert_eq!(gctx.get::>("foo.f1").unwrap(), Some(1)); // It should NOT have warned for the symlink. let output = read_output(gctx); assert_e2e().eq(&output, str![[""]]); } #[cargo_test] fn config_ambiguous_filename_symlink_doesnt_warn_backward() { // Windows requires special permissions to create symlinks. // If we don't have permission, just skip this test. if !symlink_supported() { return; }; write_config_toml( "\ [foo] f1 = 1 ", ); rename_config_toml_to_config_replacing_with_symlink(); let gctx = new_gctx(); assert_eq!(gctx.get::>("foo.f1").unwrap(), Some(1)); // It should NOT have warned for this situation. let output = read_output(gctx); assert_e2e().eq(&output, str![[""]]); } #[cargo_test] fn config_ambiguous_filename() { write_config_extless( "\ [foo] f1 = 1 ", ); write_config_toml( "\ [foo] f1 = 2 ", ); let gctx = new_gctx(); // It should use the value from the one without the extension for // backwards compatibility. assert_eq!(gctx.get::>("foo.f1").unwrap(), Some(1)); // But it also should have warned. let output = read_output(gctx); let expected = str![[r#" [WARNING] both `[ROOT]/.cargo/config` and `[ROOT]/.cargo/config.toml` exist. Using `[ROOT]/.cargo/config` "#]]; assert_e2e().eq(&output, expected); } #[cargo_test] fn config_unused_fields() { write_config_toml( "\ [S] unused = 456 ", ); let gctx = GlobalContextBuilder::new() .env("CARGO_S_UNUSED2", "1") .env("CARGO_S2_UNUSED", "2") .build(); #[derive(Debug, Deserialize, Eq, PartialEq)] struct S { f1: Option, } // This prints a warning (verified below). let s: S = gctx.get("S").unwrap(); assert_eq!(s, S { f1: None }); // This does not print anything, we cannot easily/reliably warn for // environment variables. let s: S = gctx.get("S2").unwrap(); assert_eq!(s, S { f1: None }); // Verify the warnings. let output = read_output(gctx); let expected = str![[r#" [WARNING] unused config key `S.unused` in `[ROOT]/.cargo/config.toml` "#]]; assert_e2e().eq(&output, expected); } #[cargo_test] fn config_load_toml_profile() { write_config_toml( "\ [profile.dev] opt-level = 's' lto = true codegen-units=4 debug = true debug-assertions = true rpath = true panic = 'abort' overflow-checks = true incremental = true [profile.dev.build-override] opt-level = 1 [profile.dev.package.bar] codegen-units = 9 [profile.no-lto] inherits = 'dev' dir-name = 'without-lto' lto = false ", ); let gctx = GlobalContextBuilder::new() .unstable_flag("advanced-env") .env("CARGO_PROFILE_DEV_CODEGEN_UNITS", "5") .env("CARGO_PROFILE_DEV_BUILD_OVERRIDE_CODEGEN_UNITS", "11") .env("CARGO_PROFILE_DEV_PACKAGE_env_CODEGEN_UNITS", "13") .env("CARGO_PROFILE_DEV_PACKAGE_bar_OPT_LEVEL", "2") .build(); // TODO: don't use actual `tomlprofile`. let p: cargo_toml::TomlProfile = gctx.get("profile.dev").unwrap(); let mut packages = BTreeMap::new(); let key = cargo_toml::ProfilePackageSpec::Spec(::cargo::core::PackageIdSpec::parse("bar").unwrap()); let o_profile = cargo_toml::TomlProfile { opt_level: Some(cargo_toml::TomlOptLevel("2".to_string())), codegen_units: Some(9), ..Default::default() }; packages.insert(key, o_profile); let key = cargo_toml::ProfilePackageSpec::Spec(::cargo::core::PackageIdSpec::parse("env").unwrap()); let o_profile = cargo_toml::TomlProfile { codegen_units: Some(13), ..Default::default() }; packages.insert(key, o_profile); assert_eq!( p, cargo_toml::TomlProfile { opt_level: Some(cargo_toml::TomlOptLevel("s".to_string())), lto: Some(cargo_toml::StringOrBool::Bool(true)), codegen_units: Some(5), debug: Some(cargo_toml::TomlDebugInfo::Full), debug_assertions: Some(true), rpath: Some(true), panic: Some("abort".to_string()), overflow_checks: Some(true), incremental: Some(true), package: Some(packages), build_override: Some(Box::new(cargo_toml::TomlProfile { opt_level: Some(cargo_toml::TomlOptLevel("1".to_string())), codegen_units: Some(11), ..Default::default() })), ..Default::default() } ); let p: cargo_toml::TomlProfile = gctx.get("profile.no-lto").unwrap(); assert_eq!( p, cargo_toml::TomlProfile { lto: Some(cargo_toml::StringOrBool::Bool(false)), dir_name: Some(String::from("without-lto")), inherits: Some(String::from("dev")), ..Default::default() } ); } #[cargo_test] fn profile_env_var_prefix() { // Check for a bug with collision on DEBUG vs DEBUG_ASSERTIONS. let gctx = GlobalContextBuilder::new() .env("CARGO_PROFILE_DEV_DEBUG_ASSERTIONS", "false") .build(); let p: cargo_toml::TomlProfile = gctx.get("profile.dev").unwrap(); assert_eq!(p.debug_assertions, Some(false)); assert_eq!(p.debug, None); let gctx = GlobalContextBuilder::new() .env("CARGO_PROFILE_DEV_DEBUG", "1") .build(); let p: cargo_toml::TomlProfile = gctx.get("profile.dev").unwrap(); assert_eq!(p.debug_assertions, None); assert_eq!(p.debug, Some(cargo_toml::TomlDebugInfo::Limited)); let gctx = GlobalContextBuilder::new() .env("CARGO_PROFILE_DEV_DEBUG_ASSERTIONS", "false") .env("CARGO_PROFILE_DEV_DEBUG", "1") .build(); let p: cargo_toml::TomlProfile = gctx.get("profile.dev").unwrap(); assert_eq!(p.debug_assertions, Some(false)); assert_eq!(p.debug, Some(cargo_toml::TomlDebugInfo::Limited)); } #[cargo_test] fn config_deserialize_any() { // Some tests to exercise deserialize_any for deserializers that need to // be told the format. write_config_toml( "\ a = true b = ['b'] c = ['c'] ", ); // advanced-env let gctx = GlobalContextBuilder::new() .unstable_flag("advanced-env") .env("CARGO_ENVB", "false") .env("CARGO_C", "['d']") .env("CARGO_ENVL", "['a', 'b']") .build(); assert_eq!(gctx.get::("a").unwrap(), VSOB::Bool(true)); assert_eq!( gctx.get::("b").unwrap(), VSOB::VecString(vec!["b".to_string()]) ); assert_eq!( gctx.get::("c").unwrap(), VSOB::VecString(vec!["c".to_string(), "d".to_string()]) ); assert_eq!(gctx.get::("envb").unwrap(), VSOB::Bool(false)); assert_eq!( gctx.get::("envl").unwrap(), VSOB::VecString(vec!["a".to_string(), "b".to_string()]) ); // Demonstrate where merging logic isn't very smart. This could be improved. let gctx = GlobalContextBuilder::new().env("CARGO_A", "x y").build(); assert_error( gctx.get::("a").unwrap_err(), str![[r#" error in environment variable `CARGO_A`: could not load config key `a` Caused by: invalid type: string "x y", expected a boolean or vector of strings "#]], ); // Normal env. let gctx = GlobalContextBuilder::new() .unstable_flag("advanced-env") .env("CARGO_B", "d e") .env("CARGO_C", "f g") .build(); assert_eq!( gctx.get::("b").unwrap(), VSOB::VecString(vec!["b".to_string(), "d".to_string(), "e".to_string()]) ); assert_eq!( gctx.get::("c").unwrap(), VSOB::VecString(vec!["c".to_string(), "f".to_string(), "g".to_string()]) ); // config-cli // This test demonstrates that ConfigValue::merge isn't very smart. // It would be nice if it was smarter. let gctx = GlobalContextBuilder::new() .config_arg("a = ['a']") .build_err(); assert_error( gctx.unwrap_err(), str![[r#" failed to merge --config key `a` into `[ROOT]/.cargo/config.toml` Caused by: failed to merge config value from `--config cli option` into `[ROOT]/.cargo/config.toml`: expected boolean, but found array "#]], ); // config-cli and advanced-env let gctx = GlobalContextBuilder::new() .unstable_flag("advanced-env") .config_arg("b=['clib']") .config_arg("c=['clic']") .env("CARGO_B", "env1 env2") .env("CARGO_C", "['e1', 'e2']") .build(); assert_eq!( gctx.get::("b").unwrap(), VSOB::VecString(vec![ "b".to_string(), "env1".to_string(), "env2".to_string(), "clib".to_string(), ]) ); assert_eq!( gctx.get::("c").unwrap(), VSOB::VecString(vec![ "c".to_string(), "e1".to_string(), "e2".to_string(), "clic".to_string(), ]) ); } #[cargo_test] fn config_toml_errors() { write_config_toml( "\ [profile.dev] opt-level = 'foo' ", ); let gctx = new_gctx(); assert_error( gctx.get::("profile.dev") .unwrap_err(), str![[r#" error in [ROOT]/.cargo/config.toml: could not load config key `profile.dev.opt-level` Caused by: must be `0`, `1`, `2`, `3`, `s` or `z`, but found the string: "foo" "#]], ); let gctx = GlobalContextBuilder::new() .env("CARGO_PROFILE_DEV_OPT_LEVEL", "asdf") .build(); assert_error( gctx.get::("profile.dev") .unwrap_err(), str![[r#" error in environment variable `CARGO_PROFILE_DEV_OPT_LEVEL`: could not load config key `profile.dev.opt-level` Caused by: must be `0`, `1`, `2`, `3`, `s` or `z`, but found the string: "asdf" "#]], ); } #[cargo_test] fn load_nested() { write_config_toml( "\ [nest.foo] f1 = 1 f2 = 2 [nest.bar] asdf = 3 ", ); let gctx = GlobalContextBuilder::new() .unstable_flag("advanced-env") .env("CARGO_NEST_foo_f2", "3") .env("CARGO_NESTE_foo_f1", "1") .env("CARGO_NESTE_foo_f2", "3") .env("CARGO_NESTE_bar_asdf", "3") .build(); type Nested = HashMap>; let n: Nested = gctx.get("nest").unwrap(); let mut expected = HashMap::new(); let mut foo = HashMap::new(); foo.insert("f1".to_string(), 1); foo.insert("f2".to_string(), 3); expected.insert("foo".to_string(), foo); let mut bar = HashMap::new(); bar.insert("asdf".to_string(), 3); expected.insert("bar".to_string(), bar); assert_eq!(n, expected); let n: Nested = gctx.get("neste").unwrap(); assert_eq!(n, expected); } #[cargo_test] fn get_errors() { write_config_toml( "\ [S] f1 = 123 f2 = 'asdf' big = 123456789 ", ); let gctx = GlobalContextBuilder::new() .env("CARGO_E_S", "asdf") .env("CARGO_E_BIG", "123456789") .build(); assert_error( gctx.get::("foo").unwrap_err(), str!["missing config key `foo`"], ); assert_error( gctx.get::("foo.bar").unwrap_err(), str!["missing config key `foo.bar`"], ); assert_error( gctx.get::("S.f2").unwrap_err(), str!["error in [ROOT]/.cargo/config.toml: `S.f2` expected an integer, but found a string"], ); assert_error( gctx.get::("S.big").unwrap_err(), str![[r#" error in [ROOT]/.cargo/config.toml: could not load config key `S.big` Caused by: invalid value: integer `123456789`, expected u8 "#]], ); // Environment variable type errors. assert_error( gctx.get::("e.s").unwrap_err(), str!["error in environment variable `CARGO_E_S`: invalid digit found in string"], ); assert_error( gctx.get::("e.big").unwrap_err(), str![[r#" error in environment variable `CARGO_E_BIG`: could not load config key `e.big` Caused by: invalid value: integer `123456789`, expected i8 "#]], ); #[derive(Debug, Deserialize)] #[allow(dead_code)] struct S { f1: i64, f2: String, f3: i64, big: i64, } assert_error(gctx.get::("S").unwrap_err(), str!["missing field `f3`"]); } #[cargo_test] fn config_get_option() { write_config_toml( "\ [foo] f1 = 1 ", ); let gctx = GlobalContextBuilder::new() .env("CARGO_BAR_ASDF", "3") .build(); assert_eq!(gctx.get::>("a").unwrap(), None); assert_eq!(gctx.get::>("a.b").unwrap(), None); assert_eq!(gctx.get::>("foo.f1").unwrap(), Some(1)); assert_eq!(gctx.get::>("bar.asdf").unwrap(), Some(3)); assert_eq!(gctx.get::>("bar.zzzz").unwrap(), None); } #[cargo_test] fn config_bad_toml() { write_config_toml("asdf"); let gctx = new_gctx(); assert_error( gctx.get::("foo").unwrap_err(), str![[r#" could not load Cargo configuration Caused by: could not parse TOML configuration in `[ROOT]/.cargo/config.toml` Caused by: TOML parse error at line 1, column 5 | 1 | asdf | ^ key with no value, expected `=` "#]], ); } #[cargo_test] fn config_get_list() { write_config_toml( "\ l1 = [] l2 = ['one', 'two'] l3 = 123 l4 = ['one', 'two'] [nested] l = ['x'] [nested2] l = ['y'] [nested-empty] ", ); type L = Vec; let gctx = GlobalContextBuilder::new() .unstable_flag("advanced-env") .env("CARGO_L4", "['three', 'four']") .env("CARGO_L5", "['a']") .env("CARGO_ENV_EMPTY", "[]") .env("CARGO_ENV_BLANK", "") .env("CARGO_ENV_NUM", "1") .env("CARGO_ENV_NUM_LIST", "[1]") .env("CARGO_ENV_TEXT", "asdf") .env("CARGO_LEPAIR", "['a', 'b']") .env("CARGO_NESTED2_L", "['z']") .env("CARGO_NESTEDE_L", "['env']") .env("CARGO_BAD_ENV", "[zzz]") .build(); assert_eq!(gctx.get::("unset").unwrap(), vec![] as Vec); assert_eq!(gctx.get::("l1").unwrap(), vec![] as Vec); assert_eq!(gctx.get::("l2").unwrap(), vec!["one", "two"]); assert_error( gctx.get::("l3").unwrap_err(), str![[r#" invalid configuration for key `l3` expected a list, but found a integer for `l3` in [ROOT]/.cargo/config.toml "#]], ); assert_eq!( gctx.get::("l4").unwrap(), vec!["one", "two", "three", "four"] ); assert_eq!(gctx.get::("l5").unwrap(), vec!["a"]); assert_eq!(gctx.get::("env-empty").unwrap(), vec![] as Vec); assert_eq!(gctx.get::("env-blank").unwrap(), vec![] as Vec); assert_eq!(gctx.get::("env-num").unwrap(), vec!["1".to_string()]); assert_error( gctx.get::("env-num-list").unwrap_err(), str!["error in environment variable `CARGO_ENV_NUM_LIST`: expected string, found integer"], ); assert_eq!(gctx.get::("env-text").unwrap(), vec!["asdf".to_string()]); // "invalid number" here isn't the best error, but I think it's just toml.rs. assert_error( gctx.get::("bad-env").unwrap_err(), str![[r#" error in environment variable `CARGO_BAD_ENV`: could not parse TOML list: TOML parse error at line 1, column 2 | 1 | [zzz] | ^^^ string values must be quoted, expected literal string "#]], ); // Try some other sequence-like types. assert_eq!( gctx.get::<(String, String, String, String)>("l4").unwrap(), ( "one".to_string(), "two".to_string(), "three".to_string(), "four".to_string() ) ); assert_eq!(gctx.get::<(String,)>("l5").unwrap(), ("a".to_string(),)); // Tuple struct #[derive(Debug, Deserialize, Eq, PartialEq)] struct TupS(String, String); assert_eq!( gctx.get::("lepair").unwrap(), TupS("a".to_string(), "b".to_string()) ); // Nested with an option. #[derive(Debug, Deserialize, Eq, PartialEq)] struct S { l: Option>, } assert_eq!(gctx.get::("nested-empty").unwrap(), S { l: None }); assert_eq!( gctx.get::("nested").unwrap(), S { l: Some(vec!["x".to_string()]), } ); assert_eq!( gctx.get::("nested2").unwrap(), S { l: Some(vec!["y".to_string(), "z".to_string()]), } ); assert_eq!( gctx.get::("nestede").unwrap(), S { l: Some(vec!["env".to_string()]), } ); } #[cargo_test] fn config_get_other_types() { write_config_toml( "\ ns = 123 ns2 = 456 ", ); let gctx = GlobalContextBuilder::new() .env("CARGO_NSE", "987") .env("CARGO_NS2", "654") .build(); #[derive(Debug, Deserialize, Eq, PartialEq)] #[serde(transparent)] struct NewS(i32); assert_eq!(gctx.get::("ns").unwrap(), NewS(123)); assert_eq!(gctx.get::("ns2").unwrap(), NewS(654)); assert_eq!(gctx.get::("nse").unwrap(), NewS(987)); assert_error( gctx.get::("unset").unwrap_err(), str!["missing config key `unset`"], ); } #[cargo_test] fn config_relative_path() { write_config_toml(&format!( "\ p1 = 'foo/bar' p2 = '../abc' p3 = 'b/c' abs = '{}' ", paths::home().display(), )); let gctx = GlobalContextBuilder::new() .env("CARGO_EPATH", "a/b") .env("CARGO_P3", "d/e") .build(); assert_eq!( gctx.get::("p1") .unwrap() .resolve_path(&gctx), paths::root().join("foo/bar") ); assert_eq!( gctx.get::("p2") .unwrap() .resolve_path(&gctx), paths::root().join("../abc") ); assert_eq!( gctx.get::("p3") .unwrap() .resolve_path(&gctx), paths::root().join("d/e") ); assert_eq!( gctx.get::("abs") .unwrap() .resolve_path(&gctx), paths::home() ); assert_eq!( gctx.get::("epath") .unwrap() .resolve_path(&gctx), paths::root().join("a/b") ); } #[cargo_test] fn config_get_integers() { write_config_toml( "\ npos = 123456789 nneg = -123456789 i64max = 9223372036854775807 ", ); let gctx = GlobalContextBuilder::new() .env("CARGO_EPOS", "123456789") .env("CARGO_ENEG", "-1") .env("CARGO_EI64MAX", "9223372036854775807") .build(); assert_eq!( gctx.get::("i64max").unwrap(), 9_223_372_036_854_775_807 ); assert_eq!( gctx.get::("i64max").unwrap(), 9_223_372_036_854_775_807 ); assert_eq!( gctx.get::("ei64max").unwrap(), 9_223_372_036_854_775_807 ); assert_eq!( gctx.get::("ei64max").unwrap(), 9_223_372_036_854_775_807 ); assert_error( gctx.get::("nneg").unwrap_err(), str![[r#" error in [ROOT]/.cargo/config.toml: could not load config key `nneg` Caused by: invalid value: integer `-123456789`, expected u32 "#]], ); assert_error( gctx.get::("eneg").unwrap_err(), str![[r#" error in environment variable `CARGO_ENEG`: could not load config key `eneg` Caused by: invalid value: integer `-1`, expected u32 "#]], ); assert_error( gctx.get::("npos").unwrap_err(), str![[r#" error in [ROOT]/.cargo/config.toml: could not load config key `npos` Caused by: invalid value: integer `123456789`, expected i8 "#]], ); assert_error( gctx.get::("epos").unwrap_err(), str![[r#" error in environment variable `CARGO_EPOS`: could not load config key `epos` Caused by: invalid value: integer `123456789`, expected i8 "#]], ); } #[cargo_test] fn config_get_ssl_version_missing() { write_config_toml( "\ [http] hello = 'world' ", ); let gctx = new_gctx(); assert!( gctx.get::>("http.ssl-version") .unwrap() .is_none() ); } #[cargo_test] fn config_get_ssl_version_single() { write_config_toml( "\ [http] ssl-version = 'tlsv1.2' ", ); let gctx = new_gctx(); let a = gctx .get::>("http.ssl-version") .unwrap() .unwrap(); match a { SslVersionConfig::Single(v) => assert_eq!(&v, "tlsv1.2"), SslVersionConfig::Range(_) => panic!("Did not expect ssl version min/max."), }; } #[cargo_test] fn config_get_ssl_version_min_max() { write_config_toml( "\ [http] ssl-version.min = 'tlsv1.2' ssl-version.max = 'tlsv1.3' ", ); let gctx = new_gctx(); let a = gctx .get::>("http.ssl-version") .unwrap() .unwrap(); match a { SslVersionConfig::Single(_) => panic!("Did not expect exact ssl version."), SslVersionConfig::Range(range) => { assert_eq!(range.min, Some(String::from("tlsv1.2"))); assert_eq!(range.max, Some(String::from("tlsv1.3"))); } }; } #[cargo_test] fn config_get_ssl_version_both_forms_configured() { // this is not allowed write_config_toml( "\ [http] ssl-version = 'tlsv1.1' ssl-version.min = 'tlsv1.2' ssl-version.max = 'tlsv1.3' ", ); let gctx = new_gctx(); assert_error( gctx.get::("http.ssl-version") .unwrap_err(), str![[r#" could not load Cargo configuration Caused by: could not parse TOML configuration in `[ROOT]/.cargo/config.toml` Caused by: TOML parse error at line 3, column 1 | 3 | ssl-version.min = 'tlsv1.2' | ^^^^^^^^^^^ cannot extend value of type string with a dotted key "#]], ); } #[cargo_test] /// Assert that unstable options can be configured with the `unstable` table in /// cargo config files fn unstable_table_notation() { write_config_toml( "\ [unstable] print-im-a-teapot = true ", ); let gctx = GlobalContextBuilder::new() .nightly_features_allowed(true) .build(); assert_eq!(gctx.cli_unstable().print_im_a_teapot, true); } #[cargo_test] /// Assert that dotted notation works for configuring unstable options fn unstable_dotted_notation() { write_config_toml( "\ unstable.print-im-a-teapot = true ", ); let gctx = GlobalContextBuilder::new() .nightly_features_allowed(true) .build(); assert_eq!(gctx.cli_unstable().print_im_a_teapot, true); } #[cargo_test] /// Assert that Zflags on the CLI take precedence over those from config fn unstable_cli_precedence() { write_config_toml( "\ unstable.print-im-a-teapot = true ", ); let gctx = GlobalContextBuilder::new() .nightly_features_allowed(true) .build(); assert_eq!(gctx.cli_unstable().print_im_a_teapot, true); let gctx = GlobalContextBuilder::new() .unstable_flag("print-im-a-teapot=no") .build(); assert_eq!(gctx.cli_unstable().print_im_a_teapot, false); } #[cargo_test] /// Assert that attempting to set an unstable flag that doesn't exist via config /// is ignored on stable fn unstable_invalid_flag_ignored_on_stable() { write_config_toml( "\ unstable.an-invalid-flag = 'yes' ", ); assert!(GlobalContextBuilder::new().build_err().is_ok()); } #[cargo_test] /// Assert that unstable options can be configured with the `unstable` table in /// cargo config files fn unstable_flags_ignored_on_stable() { write_config_toml( "\ [unstable] print-im-a-teapot = true ", ); // Enforce stable channel even when testing on nightly. let gctx = GlobalContextBuilder::new() .nightly_features_allowed(false) .build(); assert_eq!(gctx.cli_unstable().print_im_a_teapot, false); } #[cargo_test] fn table_merge_failure() { // Config::merge fails to merge entries in two tables. write_config_at( "foo/.cargo/config.toml", " [table] key = ['foo'] ", ); write_config_at( ".cargo/config.toml", " [table] key = 'bar' ", ); #[derive(Debug, Deserialize)] #[allow(dead_code)] struct Table { key: StringList, } let gctx = GlobalContextBuilder::new().cwd("foo").build(); assert_error( gctx.get::("table").unwrap_err(), str![[r#" could not load Cargo configuration Caused by: failed to merge configuration at `[ROOT]/.cargo/config.toml` Caused by: failed to merge key `table` between [ROOT]/foo/.cargo/config.toml and [ROOT]/.cargo/config.toml Caused by: failed to merge key `key` between [ROOT]/foo/.cargo/config.toml and [ROOT]/.cargo/config.toml Caused by: failed to merge config value from `[ROOT]/.cargo/config.toml` into `[ROOT]/foo/.cargo/config.toml`: expected array, but found string "#]], ); } #[cargo_test] fn non_string_in_array() { // Currently only strings are supported. write_config_toml("foo = [1, 2, 3]"); let gctx = new_gctx(); assert_error( gctx.get::>("foo").unwrap_err(), str![[r#" could not load Cargo configuration Caused by: failed to load TOML configuration from `[ROOT]/.cargo/config.toml` Caused by: failed to parse key `foo` Caused by: expected string but found integer in list "#]], ); } #[cargo_test] fn struct_with_opt_inner_struct() { // Struct with a key that is Option of another struct. // Check that can be defined with environment variable. #[derive(Deserialize)] struct Inner { value: Option, } #[derive(Deserialize)] struct Foo { inner: Option, } let gctx = GlobalContextBuilder::new() .env("CARGO_FOO_INNER_VALUE", "12") .build(); let f: Foo = gctx.get("foo").unwrap(); assert_eq!(f.inner.unwrap().value.unwrap(), 12); } #[cargo_test] fn struct_with_default_inner_struct() { // Struct with serde defaults. // Check that can be defined with environment variable. #[derive(Deserialize, Default)] #[serde(default)] struct Inner { value: i32, } #[derive(Deserialize, Default)] #[serde(default)] struct Foo { inner: Inner, } let gctx = GlobalContextBuilder::new() .env("CARGO_FOO_INNER_VALUE", "12") .build(); let f: Foo = gctx.get("foo").unwrap(); assert_eq!(f.inner.value, 12); } #[cargo_test] fn overlapping_env_config() { // Issue where one key is a prefix of another. #[derive(Deserialize)] #[serde(rename_all = "kebab-case")] struct Ambig { debug: Option, debug_assertions: Option, } let gctx = GlobalContextBuilder::new() .env("CARGO_AMBIG_DEBUG_ASSERTIONS", "true") .build(); let s: Ambig = gctx.get("ambig").unwrap(); assert_eq!(s.debug_assertions, Some(true)); assert_eq!(s.debug, None); let gctx = GlobalContextBuilder::new() .env("CARGO_AMBIG_DEBUG", "0") .build(); let s: Ambig = gctx.get("ambig").unwrap(); assert_eq!(s.debug_assertions, None); assert_eq!(s.debug, Some(0)); let gctx = GlobalContextBuilder::new() .env("CARGO_AMBIG_DEBUG", "1") .env("CARGO_AMBIG_DEBUG_ASSERTIONS", "true") .build(); let s: Ambig = gctx.get("ambig").unwrap(); assert_eq!(s.debug_assertions, Some(true)); assert_eq!(s.debug, Some(1)); } #[cargo_test] fn overlapping_env_with_defaults_errors_out() { // Issue where one key is a prefix of another. // This is a limitation of mapping environment variables on to a hierarchy. // Check that we error out when we hit ambiguity in this way, rather than // the more-surprising defaulting through. // If, in the future, we can handle this more correctly, feel free to delete // this test. #[derive(Deserialize, Default)] #[serde(default, rename_all = "kebab-case")] struct Ambig { debug: u32, debug_assertions: bool, } let gctx = GlobalContextBuilder::new() .env("CARGO_AMBIG_DEBUG_ASSERTIONS", "true") .build(); let err = gctx.get::("ambig").err().unwrap(); assert!(format!("{}", err).contains("missing config key `ambig.debug`")); let gctx = GlobalContextBuilder::new() .env("CARGO_AMBIG_DEBUG", "5") .build(); let s: Ambig = gctx.get("ambig").unwrap(); assert_eq!(s.debug_assertions, bool::default()); assert_eq!(s.debug, 5); let gctx = GlobalContextBuilder::new() .env("CARGO_AMBIG_DEBUG", "1") .env("CARGO_AMBIG_DEBUG_ASSERTIONS", "true") .build(); let s: Ambig = gctx.get("ambig").unwrap(); assert_eq!(s.debug_assertions, true); assert_eq!(s.debug, 1); } #[cargo_test] fn struct_with_overlapping_inner_struct_and_defaults() { // Struct with serde defaults. // Check that can be defined with environment variable. #[derive(Deserialize, Default)] #[serde(default)] struct Inner { value: i32, } // Containing struct with a prefix of inner // // This is a limitation of mapping environment variables on to a hierarchy. // Check that we error out when we hit ambiguity in this way, rather than // the more-surprising defaulting through. // If, in the future, we can handle this more correctly, feel free to delete // this case. #[derive(Deserialize, Default)] struct PrefixContainer { inn: bool, inner: Inner, } let gctx = GlobalContextBuilder::new() .env("CARGO_PREFIXCONTAINER_INNER_VALUE", "12") .build(); let err = gctx .get::("prefixcontainer") .err() .unwrap(); assert!(format!("{}", err).contains("missing field `inn`")); let gctx = GlobalContextBuilder::new() .env("CARGO_PREFIXCONTAINER_INNER_VALUE", "12") .env("CARGO_PREFIXCONTAINER_INN", "true") .build(); let f: PrefixContainer = gctx.get("prefixcontainer").unwrap(); assert_eq!(f.inner.value, 12); assert_eq!(f.inn, true); // Use default attribute of serde, then we can skip setting the inn field #[derive(Deserialize, Default)] #[serde(default)] struct PrefixContainerFieldDefault { inn: bool, inner: Inner, } let gctx = GlobalContextBuilder::new() .env("CARGO_PREFIXCONTAINER_INNER_VALUE", "12") .build(); let f = gctx .get::("prefixcontainer") .unwrap(); assert_eq!(f.inner.value, 12); assert_eq!(f.inn, false); // Containing struct where the inner value's field is a prefix of another // // This is a limitation of mapping environment variables on to a hierarchy. // Check that we error out when we hit ambiguity in this way, rather than // the more-surprising defaulting through. // If, in the future, we can handle this more correctly, feel free to delete // this case. #[derive(Deserialize, Default)] #[serde(default)] struct InversePrefixContainer { inner_field: bool, inner: Inner, } let gctx = GlobalContextBuilder::new() .env("CARGO_INVERSEPREFIXCONTAINER_INNER_VALUE", "12") .build(); let f: InversePrefixContainer = gctx.get("inverseprefixcontainer").unwrap(); assert_eq!(f.inner_field, bool::default()); assert_eq!(f.inner.value, 12); } #[cargo_test] fn string_list_tricky_env() { // Make sure StringList handles typed env values. let gctx = GlobalContextBuilder::new() .env("CARGO_KEY1", "123") .env("CARGO_KEY2", "true") .env("CARGO_KEY3", "1 2") .build(); let x = gctx.get::("key1").unwrap(); assert_eq!(x.as_slice(), &["123".to_string()]); let x = gctx.get::("key2").unwrap(); assert_eq!(x.as_slice(), &["true".to_string()]); let x = gctx.get::("key3").unwrap(); assert_eq!(x.as_slice(), &["1".to_string(), "2".to_string()]); } #[cargo_test] fn string_list_wrong_type() { // What happens if StringList is given then wrong type. write_config_toml("some_list = 123"); let gctx = GlobalContextBuilder::new().build(); assert_error( gctx.get::("some_list").unwrap_err(), str![[r#" invalid configuration for key `some_list` expected a string or array of strings, but found a integer for `some_list` in [ROOT]/.cargo/config.toml "#]], ); write_config_toml("some_list = \"1 2\""); let gctx = GlobalContextBuilder::new().build(); let x = gctx.get::("some_list").unwrap(); assert_eq!(x.as_slice(), &["1".to_string(), "2".to_string()]); } #[cargo_test] fn string_list_advanced_env() { // StringList with advanced env. let gctx = GlobalContextBuilder::new() .unstable_flag("advanced-env") .env("CARGO_KEY1", "[]") .env("CARGO_KEY2", "['1 2', '3']") .env("CARGO_KEY3", "[123]") .build(); let x = gctx.get::("key1").unwrap(); assert_eq!(x.as_slice(), &[] as &[String]); let x = gctx.get::("key2").unwrap(); assert_eq!(x.as_slice(), &["1 2".to_string(), "3".to_string()]); assert_error( gctx.get::("key3").unwrap_err(), str!["error in environment variable `CARGO_KEY3`: expected string, found integer"], ); } #[cargo_test] fn parse_strip_with_string() { write_config_toml( "\ [profile.release] strip = 'debuginfo' ", ); let gctx = new_gctx(); let p: cargo_toml::TomlProfile = gctx.get("profile.release").unwrap(); let strip = p.strip.unwrap(); assert_eq!( strip, cargo_toml::StringOrBool::String("debuginfo".to_string()) ); } #[cargo_test] fn cargo_target_empty_cfg() { write_config_toml( "\ [build] target-dir = '' ", ); let gctx = new_gctx(); assert_error( gctx.target_dir().unwrap_err(), str!["the target directory is set to an empty string in [ROOT]/.cargo/config.toml"], ); } #[cargo_test] fn cargo_target_empty_env() { let project = project().build(); project.cargo("check") .env("CARGO_TARGET_DIR", "") .with_stderr_data(str![[r#" [ERROR] the target directory is set to an empty string in the `CARGO_TARGET_DIR` environment variable "#]]) .with_status(101) .run(); } #[cargo_test] fn all_profile_options() { // Check that all profile options can be serialized/deserialized. let base_settings = cargo_toml::TomlProfile { opt_level: Some(cargo_toml::TomlOptLevel("0".to_string())), lto: Some(cargo_toml::StringOrBool::String("thin".to_string())), codegen_backend: Some(String::from("example")), codegen_units: Some(123), debug: Some(cargo_toml::TomlDebugInfo::Limited), split_debuginfo: Some("packed".to_string()), debug_assertions: Some(true), rpath: Some(true), panic: Some("abort".to_string()), overflow_checks: Some(true), incremental: Some(true), dir_name: Some(String::from("dir_name")), inherits: Some(String::from("debug")), strip: Some(cargo_toml::StringOrBool::String("symbols".to_string())), package: None, build_override: None, rustflags: None, trim_paths: None, hint_mostly_unused: None, }; let mut overrides = BTreeMap::new(); let key = cargo_toml::ProfilePackageSpec::Spec(PackageIdSpec::parse("foo").unwrap()); overrides.insert(key, base_settings.clone()); let profile = cargo_toml::TomlProfile { build_override: Some(Box::new(base_settings.clone())), package: Some(overrides), ..base_settings }; let profile_toml = toml::to_string(&profile).unwrap(); let roundtrip: cargo_toml::TomlProfile = toml::from_str(&profile_toml).unwrap(); let roundtrip_toml = toml::to_string(&roundtrip).unwrap(); assert_e2e().eq(&roundtrip_toml, &profile_toml); } #[cargo_test] fn value_in_array() { // Value in an array should work let root_path = paths::root().join(".cargo/config.toml"); write_config_at( &root_path, "\ [net.ssh] known-hosts = [ \"example.com ...\", \"example.net ...\", ] ", ); let foo_path = paths::root().join("foo/.cargo/config.toml"); write_config_at( &foo_path, "\ [net.ssh] known-hosts = [ \"example.org ...\", ] ", ); let gctx = GlobalContextBuilder::new() .cwd("foo") // environment variables don't actually work for known-hosts due to // space splitting, but this is included here just to validate that // they work (particularly if other Vec config vars are added // in the future). .env("CARGO_NET_SSH_KNOWN_HOSTS", "env-example") .build(); let net_config = gctx.net_config().unwrap(); let kh = net_config .ssh .as_ref() .unwrap() .known_hosts .as_ref() .unwrap(); assert_eq!(kh.len(), 4); assert_eq!(kh[0].val, "example.com ..."); assert_eq!(kh[0].definition, Definition::Path(root_path.clone())); assert_eq!(kh[1].val, "example.net ..."); assert_eq!(kh[1].definition, Definition::Path(root_path.clone())); assert_eq!(kh[2].val, "example.org ..."); assert_eq!(kh[2].definition, Definition::Path(foo_path.clone())); assert_eq!(kh[3].val, "env-example"); assert_eq!( kh[3].definition, Definition::Environment("CARGO_NET_SSH_KNOWN_HOSTS".to_string()) ); } #[cargo_test] fn debuginfo_parsing() { let gctx = GlobalContextBuilder::new().build(); let p: cargo_toml::TomlProfile = gctx.get("profile.dev").unwrap(); assert_eq!(p.debug, None); let env_test_cases = [ (TomlDebugInfo::None, ["false", "0", "none"].as_slice()), (TomlDebugInfo::LineDirectivesOnly, &["line-directives-only"]), (TomlDebugInfo::LineTablesOnly, &["line-tables-only"]), (TomlDebugInfo::Limited, &["1", "limited"]), (TomlDebugInfo::Full, &["true", "2", "full"]), ]; for (expected, config_strs) in env_test_cases { for &val in config_strs { let gctx = GlobalContextBuilder::new() .env("CARGO_PROFILE_DEV_DEBUG", val) .build(); let debug: TomlDebugInfo = gctx.get("profile.dev.debug").unwrap(); assert_eq!(debug, expected, "failed to parse {val}"); } } let toml_test_cases = [ (TomlDebugInfo::None, ["false", "0", "\"none\""].as_slice()), ( TomlDebugInfo::LineDirectivesOnly, &["\"line-directives-only\""], ), (TomlDebugInfo::LineTablesOnly, &["\"line-tables-only\""]), (TomlDebugInfo::Limited, &["1", "\"limited\""]), (TomlDebugInfo::Full, &["true", "2", "\"full\""]), ]; for (expected, config_strs) in toml_test_cases { for &val in config_strs { let gctx = GlobalContextBuilder::new() .config_arg(format!("profile.dev.debug={val}")) .build(); let debug: TomlDebugInfo = gctx.get("profile.dev.debug").unwrap(); assert_eq!(debug, expected, "failed to parse {val}"); } } let toml_err_cases = ["\"\"", "\"unrecognized\"", "3"]; for err_val in toml_err_cases { let gctx = GlobalContextBuilder::new() .config_arg(format!("profile.dev.debug={err_val}")) .build(); let err = gctx.get::("profile.dev.debug").unwrap_err(); assert!( err.to_string() .ends_with("could not load config key `profile.dev.debug`") ); } } #[cargo_test] fn build_jobs_missing() { write_config_toml( "\ [build] ", ); let gctx = new_gctx(); assert!( gctx.get::>("build.jobs") .unwrap() .is_none() ); } #[cargo_test] fn build_jobs_default() { write_config_toml( "\ [build] jobs = \"default\" ", ); let gctx = new_gctx(); let a = gctx .get::>("build.jobs") .unwrap() .unwrap(); match a { JobsConfig::String(v) => assert_eq!(&v, "default"), JobsConfig::Integer(_) => panic!("Did not except an integer."), } } #[cargo_test] fn build_jobs_integer() { write_config_toml( "\ [build] jobs = 2 ", ); let gctx = new_gctx(); let a = gctx .get::>("build.jobs") .unwrap() .unwrap(); match a { JobsConfig::String(_) => panic!("Did not except an integer."), JobsConfig::Integer(v) => assert_eq!(v, 2), } } #[cargo_test] fn trim_paths_parsing() { let gctx = GlobalContextBuilder::new().build(); let p: cargo_toml::TomlProfile = gctx.get("profile.dev").unwrap(); assert_eq!(p.trim_paths, None); let test_cases = [ (TomlTrimPathsValue::Diagnostics.into(), "diagnostics"), (TomlTrimPathsValue::Macro.into(), "macro"), (TomlTrimPathsValue::Object.into(), "object"), ]; for (expected, val) in test_cases { // env let gctx = GlobalContextBuilder::new() .env("CARGO_PROFILE_DEV_TRIM_PATHS", val) .build(); let trim_paths: TomlTrimPaths = gctx.get("profile.dev.trim-paths").unwrap(); assert_eq!(trim_paths, expected, "failed to parse {val}"); // config.toml let gctx = GlobalContextBuilder::new() .config_arg(format!("profile.dev.trim-paths='{val}'")) .build(); let trim_paths: TomlTrimPaths = gctx.get("profile.dev.trim-paths").unwrap(); assert_eq!(trim_paths, expected, "failed to parse {val}"); } let test_cases = [(TomlTrimPaths::none(), false), (TomlTrimPaths::All, true)]; for (expected, val) in test_cases { // env let gctx = GlobalContextBuilder::new() .env("CARGO_PROFILE_DEV_TRIM_PATHS", format!("{val}")) .build(); let trim_paths: TomlTrimPaths = gctx.get("profile.dev.trim-paths").unwrap(); assert_eq!(trim_paths, expected, "failed to parse {val}"); // config.toml let gctx = GlobalContextBuilder::new() .config_arg(format!("profile.dev.trim-paths={val}")) .build(); let trim_paths: TomlTrimPaths = gctx.get("profile.dev.trim-paths").unwrap(); assert_eq!(trim_paths, expected, "failed to parse {val}"); } let expected = vec![ TomlTrimPathsValue::Diagnostics, TomlTrimPathsValue::Macro, TomlTrimPathsValue::Object, ] .into(); let val = r#"["diagnostics", "macro", "object"]"#; // config.toml let gctx = GlobalContextBuilder::new() .config_arg(format!("profile.dev.trim-paths={val}")) .build(); let trim_paths: TomlTrimPaths = gctx.get("profile.dev.trim-paths").unwrap(); assert_eq!(trim_paths, expected, "failed to parse {val}"); } #[cargo_test] fn missing_fields() { #[derive(Deserialize, Default, Debug)] struct Foo { bar: Bar, } #[derive(Deserialize, Default, Debug)] struct Bar { bax: bool, baz: bool, } let gctx = GlobalContextBuilder::new() .env("CARGO_FOO_BAR_BAZ", "true") .build(); assert_error( gctx.get::("foo").unwrap_err(), str![[r#" could not load config key `foo.bar` Caused by: missing field `bax` "#]], ); let gctx: GlobalContext = GlobalContextBuilder::new() .env("CARGO_FOO_BAR_BAZ", "true") .env("CARGO_FOO_BAR_BAX", "true") .build(); let foo = gctx.get::("foo").unwrap(); assert_eq!(foo.bar.bax, true); assert_eq!(foo.bar.baz, true); let gctx: GlobalContext = GlobalContextBuilder::new() .config_arg("foo.bar.baz=true") .build(); assert_error( gctx.get::("foo").unwrap_err(), str![[r#" error in --config cli option: could not load config key `foo.bar` Caused by: missing field `bax` "#]], ); } #[cargo_test] fn git_features() { let gctx = GlobalContextBuilder::new() .env("CARGO_UNSTABLE_GIT", "shallow-index") .build(); assert!(do_check( gctx, Some(GitFeatures { shallow_index: true, ..GitFeatures::default() }), )); let gctx = GlobalContextBuilder::new() .env("CARGO_UNSTABLE_GIT", "shallow-index,abc") .build(); assert_error( gctx.get::>("unstable") .unwrap_err(), str![[r#" error in environment variable `CARGO_UNSTABLE_GIT`: could not load config key `unstable.git` Caused by: unstable 'git' only takes `shallow-index` and `shallow-deps` as valid inputs "#]], ); let gctx = GlobalContextBuilder::new() .env("CARGO_UNSTABLE_GIT", "shallow-deps") .build(); assert!(do_check( gctx, Some(GitFeatures { shallow_index: false, shallow_deps: true, }), )); let gctx = GlobalContextBuilder::new() .env("CARGO_UNSTABLE_GIT", "true") .build(); assert!(do_check(gctx, Some(GitFeatures::all()))); let gctx = GlobalContextBuilder::new() .env("CARGO_UNSTABLE_GIT_SHALLOW_INDEX", "true") .build(); assert!(do_check( gctx, Some(GitFeatures { shallow_index: true, ..Default::default() }), )); let gctx = GlobalContextBuilder::new() .env("CARGO_UNSTABLE_GIT_SHALLOW_INDEX", "true") .env("CARGO_UNSTABLE_GIT_SHALLOW_DEPS", "true") .build(); assert!(do_check( gctx, Some(GitFeatures { shallow_index: true, shallow_deps: true, ..Default::default() }), )); write_config_toml( "\ [unstable] git = 'shallow-index' ", ); let gctx = GlobalContextBuilder::new().build(); assert!(do_check( gctx, Some(GitFeatures { shallow_index: true, shallow_deps: false, }), )); write_config_toml( "\ [unstable.git] shallow_deps = false shallow_index = true ", ); let gctx = GlobalContextBuilder::new().build(); assert!(do_check( gctx, Some(GitFeatures { shallow_index: true, shallow_deps: false, ..Default::default() }), )); write_config_toml( "\ [unstable.git] ", ); let gctx = GlobalContextBuilder::new().build(); assert!(do_check(gctx, Some(Default::default()))); fn do_check(gctx: GlobalContext, expect: Option) -> bool { let unstable_flags = gctx .get::>("unstable") .unwrap() .unwrap(); unstable_flags.git == expect } } #[cargo_test] fn gitoxide_features() { let gctx = GlobalContextBuilder::new() .env("CARGO_UNSTABLE_GITOXIDE", "fetch") .build(); assert!(do_check( gctx, Some(GitoxideFeatures { fetch: true, ..GitoxideFeatures::default() }), )); let gctx = GlobalContextBuilder::new() .env("CARGO_UNSTABLE_GITOXIDE", "fetch,abc") .build(); assert_error( gctx.get::>("unstable") .unwrap_err(), str![[r#" error in environment variable `CARGO_UNSTABLE_GITOXIDE`: could not load config key `unstable.gitoxide` Caused by: unstable 'gitoxide' only takes `fetch` and `checkout` and `internal-use-git2` as valid inputs, for shallow fetches see `-Zgit=shallow-index,shallow-deps` "#]], ); let gctx = GlobalContextBuilder::new() .env("CARGO_UNSTABLE_GITOXIDE", "true") .build(); assert!(do_check(gctx, Some(GitoxideFeatures::all()))); let gctx = GlobalContextBuilder::new() .env("CARGO_UNSTABLE_GITOXIDE_FETCH", "true") .build(); assert!(do_check( gctx, Some(GitoxideFeatures { fetch: true, ..Default::default() }), )); write_config_toml( "\ [unstable] gitoxide = \"fetch\" ", ); let gctx = GlobalContextBuilder::new().build(); assert!(do_check( gctx, Some(GitoxideFeatures { fetch: true, ..GitoxideFeatures::default() }), )); write_config_toml( "\ [unstable.gitoxide] fetch = true checkout = false internal_use_git2 = false ", ); let gctx = GlobalContextBuilder::new().build(); assert!(do_check( gctx, Some(GitoxideFeatures { fetch: true, checkout: false, internal_use_git2: false, }), )); write_config_toml( "\ [unstable.gitoxide] ", ); let gctx = GlobalContextBuilder::new().build(); assert!(do_check(gctx, Some(Default::default()))); fn do_check(gctx: GlobalContext, expect: Option) -> bool { let unstable_flags = gctx .get::>("unstable") .unwrap() .unwrap(); unstable_flags.gitoxide == expect } } #[cargo_test] fn nonmergable_lists() { let root_path = paths::root().join(".cargo/config.toml"); write_config_at( &root_path, "\ [registries.example] credential-provider = ['a', 'b'] ", ); let foo_path = paths::root().join("foo/.cargo/config.toml"); write_config_at( &foo_path, "\ [registries.example] credential-provider = ['c', 'd'] ", ); let gctx = GlobalContextBuilder::new().cwd("foo").build(); let provider = gctx .get::>(&format!("registries.example")) .unwrap() .unwrap() .credential_provider .unwrap(); assert_eq!(provider.path.raw_value(), "c"); assert_eq!(provider.args, ["d"]); } #[cargo_test] fn build_std() { let gctx = GlobalContextBuilder::new() .env("CARGO_UNSTABLE_BUILD_STD", "core,std,panic_abort") .build(); let value = gctx .get::>("unstable") .unwrap() .unwrap() .build_std .unwrap(); assert_eq!( value, vec![ "core".to_string(), "std".to_string(), "panic_abort".to_string(), ], ); let gctx = GlobalContextBuilder::new() .config_arg("unstable.build-std=['core', 'std,panic_abort']") .build(); let value = gctx .get::>("unstable") .unwrap() .unwrap() .build_std .unwrap(); assert_eq!( value, vec![ "core".to_string(), "std".to_string(), "panic_abort".to_string(), ] ); let gctx = GlobalContextBuilder::new() .env( "CARGO_UNSTABLE_BUILD_STD_FEATURES", "backtrace,panic-unwind,windows_raw_dylib", ) .build(); let value = gctx .get::>("unstable") .unwrap() .unwrap() .build_std_features .unwrap(); assert_eq!( value, vec![ "backtrace".to_string(), "panic-unwind".to_string(), "windows_raw_dylib".to_string(), ] ); let gctx = GlobalContextBuilder::new() .config_arg("unstable.build-std-features=['backtrace', 'panic-unwind,windows_raw_dylib']") .build(); let value = gctx .get::>("unstable") .unwrap() .unwrap() .build_std_features .unwrap(); assert_eq!( value, vec![ "backtrace".to_string(), "panic-unwind".to_string(), "windows_raw_dylib".to_string(), ] ); } cargo-0.91.0/tests/testsuite/config_cli.rs000064400000000000000000000417521046102023000166540ustar 00000000000000//! Tests for the --config CLI option. use std::{collections::HashMap, fs}; use crate::prelude::*; use cargo::util::context::Definition; use cargo_test_support::compare::assert_e2e; use cargo_test_support::paths; use cargo_test_support::str; use super::config::{ GlobalContextBuilder, assert_error, read_output, write_config_at, write_config_toml, }; #[cargo_test] fn basic() { // Simple example. let gctx = GlobalContextBuilder::new() .config_arg("foo='bar'") .config_arg("net.git-fetch-with-cli=true") .build(); assert_eq!(gctx.get::("foo").unwrap(), "bar"); assert_eq!(gctx.net_config().unwrap().git_fetch_with_cli, Some(true)); } #[cargo_test] fn cli_priority() { // Command line takes priority over files and env vars. write_config_toml( " demo_list = ['a'] [build] jobs = 3 rustc = 'file' [term] quiet = false verbose = false ", ); let gctx = GlobalContextBuilder::new().build(); assert_eq!(gctx.get::("build.jobs").unwrap(), 3); assert_eq!(gctx.get::("build.rustc").unwrap(), "file"); assert_eq!(gctx.get::("term.quiet").unwrap(), false); assert_eq!(gctx.get::("term.verbose").unwrap(), false); let gctx = GlobalContextBuilder::new() .env("CARGO_BUILD_JOBS", "2") .env("CARGO_BUILD_RUSTC", "env") .env("CARGO_TERM_VERBOSE", "false") .env("CARGO_NET_GIT_FETCH_WITH_CLI", "false") .config_arg("build.jobs=1") .config_arg("build.rustc='cli'") .config_arg("term.verbose=true") .config_arg("net.git-fetch-with-cli=true") .build(); assert_eq!(gctx.get::("build.jobs").unwrap(), 1); assert_eq!(gctx.get::("build.rustc").unwrap(), "cli"); assert_eq!(gctx.get::("term.verbose").unwrap(), true); assert_eq!(gctx.net_config().unwrap().git_fetch_with_cli, Some(true)); // Setting both term.verbose and term.quiet is invalid and is tested // in the run test suite. let gctx = GlobalContextBuilder::new() .env("CARGO_TERM_QUIET", "false") .config_arg("term.quiet=true") .build(); assert_eq!(gctx.get::("term.quiet").unwrap(), true); } #[cargo_test] fn merge_primitives_for_multiple_cli_occurrences() { let config_path0 = ".cargo/file0.toml"; write_config_at(config_path0, "k = 'file0'"); let config_path1 = ".cargo/file1.toml"; write_config_at(config_path1, "k = 'file1'"); // k=env0 let gctx = GlobalContextBuilder::new().env("CARGO_K", "env0").build(); assert_eq!(gctx.get::("k").unwrap(), "env0"); // k=env0 // --config k='cli0' // --config k='cli1' let gctx = GlobalContextBuilder::new() .env("CARGO_K", "env0") .config_arg("k='cli0'") .config_arg("k='cli1'") .build(); assert_eq!(gctx.get::("k").unwrap(), "cli1"); // Env has a lower priority when comparing with file from CLI arg. // // k=env0 // --config k='cli0' // --config k='cli1' // --config .cargo/file0.toml let gctx = GlobalContextBuilder::new() .env("CARGO_K", "env0") .config_arg("k='cli0'") .config_arg("k='cli1'") .config_arg(config_path0) .build(); assert_eq!(gctx.get::("k").unwrap(), "file0"); // k=env0 // --config k='cli0' // --config k='cli1' // --config .cargo/file0.toml // --config k='cli2' let gctx = GlobalContextBuilder::new() .env("CARGO_K", "env0") .config_arg("k='cli0'") .config_arg("k='cli1'") .config_arg(config_path0) .config_arg("k='cli2'") .build(); assert_eq!(gctx.get::("k").unwrap(), "cli2"); // k=env0 // --config k='cli0' // --config k='cli1' // --config .cargo/file0.toml // --config k='cli2' // --config .cargo/file1.toml let gctx = GlobalContextBuilder::new() .env("CARGO_K", "env0") .config_arg("k='cli0'") .config_arg("k='cli1'") .config_arg(config_path0) .config_arg("k='cli2'") .config_arg(config_path1) .build(); assert_eq!(gctx.get::("k").unwrap(), "file1"); } #[cargo_test] fn merges_array() { // Array entries are appended. write_config_toml( " [build] rustflags = ['--file'] ", ); let gctx = GlobalContextBuilder::new() .config_arg("build.rustflags = ['--cli']") .build(); assert_eq!( gctx.get::>("build.rustflags").unwrap(), ["--file", "--cli"] ); // With normal env. let gctx = GlobalContextBuilder::new() .env("CARGO_BUILD_RUSTFLAGS", "--env1 --env2") .config_arg("build.rustflags = ['--cli']") .build(); assert_eq!( gctx.get::>("build.rustflags").unwrap(), ["--file", "--env1", "--env2", "--cli"] ); // With advanced-env. let gctx = GlobalContextBuilder::new() .unstable_flag("advanced-env") .env("CARGO_BUILD_RUSTFLAGS", "--env") .config_arg("build.rustflags = ['--cli']") .build(); assert_eq!( gctx.get::>("build.rustflags").unwrap(), ["--file", "--env", "--cli"] ); // Merges multiple instances. let gctx = GlobalContextBuilder::new() .config_arg("build.rustflags=['--one']") .config_arg("build.rustflags=['--two']") .build(); assert_eq!( gctx.get::>("build.rustflags").unwrap(), ["--file", "--one", "--two"] ); } #[cargo_test] fn string_list_array() { // Using the StringList type. write_config_toml( " [build] rustflags = ['--file'] ", ); let gctx = GlobalContextBuilder::new() .config_arg("build.rustflags = ['--cli']") .build(); assert_eq!( gctx.get::("build.rustflags") .unwrap() .as_slice(), ["--file", "--cli"] ); // With normal env. let gctx = GlobalContextBuilder::new() .env("CARGO_BUILD_RUSTFLAGS", "--env1 --env2") .config_arg("build.rustflags = ['--cli']") .build(); assert_eq!( gctx.get::("build.rustflags") .unwrap() .as_slice(), ["--file", "--env1", "--env2", "--cli"] ); // With advanced-env. let gctx = GlobalContextBuilder::new() .unstable_flag("advanced-env") .env("CARGO_BUILD_RUSTFLAGS", "['--env']") .config_arg("build.rustflags = ['--cli']") .build(); assert_eq!( gctx.get::("build.rustflags") .unwrap() .as_slice(), ["--file", "--env", "--cli"] ); } #[cargo_test] fn merges_table() { // Tables are merged. write_config_toml( " [foo] key1 = 1 key2 = 2 key3 = 3 ", ); let gctx = GlobalContextBuilder::new() .config_arg("foo.key2 = 4") .config_arg("foo.key3 = 5") .config_arg("foo.key4 = 6") .build(); assert_eq!(gctx.get::("foo.key1").unwrap(), 1); assert_eq!(gctx.get::("foo.key2").unwrap(), 4); assert_eq!(gctx.get::("foo.key3").unwrap(), 5); assert_eq!(gctx.get::("foo.key4").unwrap(), 6); // With env. let gctx = GlobalContextBuilder::new() .env("CARGO_FOO_KEY3", "7") .env("CARGO_FOO_KEY4", "8") .env("CARGO_FOO_KEY5", "9") .config_arg("foo.key2 = 4") .config_arg("foo.key3 = 5") .config_arg("foo.key4 = 6") .build(); assert_eq!(gctx.get::("foo.key1").unwrap(), 1); assert_eq!(gctx.get::("foo.key2").unwrap(), 4); assert_eq!(gctx.get::("foo.key3").unwrap(), 5); assert_eq!(gctx.get::("foo.key4").unwrap(), 6); assert_eq!(gctx.get::("foo.key5").unwrap(), 9); } #[cargo_test] fn merge_array_mixed_def_paths() { // Merging of arrays with different def sites. write_config_toml( " paths = ['file'] ", ); // Create a directory for CWD to differentiate the paths. let somedir = paths::root().join("somedir"); fs::create_dir(&somedir).unwrap(); let gctx = GlobalContextBuilder::new() .cwd(&somedir) .config_arg("paths=['cli']") // env is currently ignored for get_list() .env("CARGO_PATHS", "env") .build(); let paths = gctx.get_list("paths").unwrap().unwrap(); // The definition for the root value is somewhat arbitrary, but currently starts with the file because that is what is loaded first. assert_eq!(paths.definition, Definition::Path(paths::root())); assert_eq!(paths.val.len(), 2); assert_eq!(paths.val[0].0, "file"); assert_eq!(paths.val[0].1.root(&gctx), paths::root()); assert_eq!(paths.val[1].0, "cli"); assert_eq!(paths.val[1].1.root(&gctx), somedir); } #[cargo_test] fn enforces_format() { // These dotted key expressions should all be fine. let gctx = GlobalContextBuilder::new() .config_arg("a=true") .config_arg(" b.a = true ") .config_arg("c.\"b\".'a'=true") .config_arg("d.\"=\".'='=true") .config_arg("e.\"'\".'\"'=true") .build(); assert_eq!(gctx.get::("a").unwrap(), true); assert_eq!( gctx.get::>("b").unwrap(), HashMap::from([("a".to_string(), true)]) ); assert_eq!( gctx.get::>>("c") .unwrap(), HashMap::from([("b".to_string(), HashMap::from([("a".to_string(), true)]))]) ); assert_eq!( gctx.get::>>("d") .unwrap(), HashMap::from([("=".to_string(), HashMap::from([("=".to_string(), true)]))]) ); assert_eq!( gctx.get::>>("e") .unwrap(), HashMap::from([("'".to_string(), HashMap::from([("\"".to_string(), true)]))]) ); // But anything that's not a dotted key expression should be disallowed. let _ = GlobalContextBuilder::new() .config_arg("[a] foo=true") .build_err() .unwrap_err(); let _ = GlobalContextBuilder::new() .config_arg("a = true\nb = true") .build_err() .unwrap_err(); // We also disallow overwriting with tables since it makes merging unclear. let _ = GlobalContextBuilder::new() .config_arg("a = { first = true, second = false }") .build_err() .unwrap_err(); let _ = GlobalContextBuilder::new() .config_arg("a = { first = true }") .build_err() .unwrap_err(); } #[cargo_test] fn unused_key() { // Unused key passed on command line. let gctx = GlobalContextBuilder::new() .config_arg("build.unused = 2") .build(); gctx.build_config().unwrap(); let output = read_output(gctx); let expected = str![[r#" [WARNING] unused config key `build.unused` in `--config cli option` "#]]; assert_e2e().eq(&output, expected); } #[cargo_test] fn rerooted_remains() { // Re-rooting keeps cli args. let somedir = paths::root().join("somedir"); fs::create_dir_all(somedir.join(".cargo")).unwrap(); fs::write( somedir.join(".cargo").join("config"), " a = 'file1' b = 'file2' ", ) .unwrap(); let mut gctx = GlobalContextBuilder::new() .cwd(&somedir) .config_arg("b='cli1'") .config_arg("c='cli2'") .build(); assert_eq!(gctx.get::("a").unwrap(), "file1"); assert_eq!(gctx.get::("b").unwrap(), "cli1"); assert_eq!(gctx.get::("c").unwrap(), "cli2"); gctx.reload_rooted_at(paths::root()).unwrap(); assert_eq!(gctx.get::>("a").unwrap(), None); assert_eq!(gctx.get::("b").unwrap(), "cli1"); assert_eq!(gctx.get::("c").unwrap(), "cli2"); } #[cargo_test] fn bad_parse() { // Fail to TOML parse. let gctx = GlobalContextBuilder::new().config_arg("abc").build_err(); assert_error( gctx.unwrap_err(), str![[r#" failed to parse value from --config argument `abc` as a dotted key expression Caused by: TOML parse error at line 1, column 4 | 1 | abc | ^ key with no value, expected `=` "#]], ); let gctx = GlobalContextBuilder::new().config_arg("").build_err(); assert_error( gctx.unwrap_err(), str![ "--config argument `` was not a TOML dotted key expression (such as `build.jobs = 2`)" ], ); } #[cargo_test] fn too_many_values() { // Currently restricted to only 1 value. let gctx = GlobalContextBuilder::new() .config_arg("a=1\nb=2") .build_err(); assert_error( gctx.unwrap_err(), str![[r#" --config argument `a=1 b=2` was not a TOML dotted key expression (such as `build.jobs = 2`) "#]], ); } #[cargo_test] fn no_disallowed_values() { let gctx = GlobalContextBuilder::new() .config_arg("registry.token=\"hello\"") .build_err(); assert_error( gctx.unwrap_err(), str!["registry.token cannot be set through --config for security reasons"], ); let gctx = GlobalContextBuilder::new() .config_arg("registries.crates-io.token=\"hello\"") .build_err(); assert_error( gctx.unwrap_err(), str!["registries.crates-io.token cannot be set through --config for security reasons"], ); let gctx = GlobalContextBuilder::new() .config_arg("registry.secret-key=\"hello\"") .build_err(); assert_error( gctx.unwrap_err(), str!["registry.secret-key cannot be set through --config for security reasons"], ); let gctx = GlobalContextBuilder::new() .config_arg("registries.crates-io.secret-key=\"hello\"") .build_err(); assert_error( gctx.unwrap_err(), str!["registries.crates-io.secret-key cannot be set through --config for security reasons"], ); } #[cargo_test] fn no_inline_table_value() { // Disallow inline tables let gctx = GlobalContextBuilder::new() .config_arg("a.b={c = \"d\"}") .build_err(); assert_error( gctx.unwrap_err(), str![[ r#"--config argument `a.b={c = "d"}` sets a value to an inline table, which is not accepted"# ]], ); } #[cargo_test] fn no_array_of_tables_values() { // Disallow array-of-tables when not in dotted form let gctx = GlobalContextBuilder::new() .config_arg("[[a.b]]\nc = \"d\"") .build_err(); assert_error( gctx.unwrap_err(), str![[r#" --config argument `[[a.b]] c = "d"` was not a TOML dotted key expression (such as `build.jobs = 2`) "#]], ); } #[cargo_test] fn no_comments() { // Disallow comments in dotted form. let gctx = GlobalContextBuilder::new() .config_arg("a.b = \"c\" # exactly") .build_err(); assert_error( gctx.unwrap_err(), str![[r#"--config argument `a.b = "c" # exactly` includes non-whitespace decoration"#]], ); let gctx = GlobalContextBuilder::new() .config_arg("# exactly\na.b = \"c\"") .build_err(); assert_error( gctx.unwrap_err(), str![[r#" --config argument `# exactly a.b = "c"` includes non-whitespace decoration "#]], ); } #[cargo_test] fn bad_cv_convert() { // ConfigValue does not support all TOML types. let gctx = GlobalContextBuilder::new() .config_arg("a=2019-12-01") .build_err(); assert_error( gctx.unwrap_err(), str![[r#" failed to convert --config argument `a=2019-12-01` Caused by: failed to parse key `a` Caused by: found TOML configuration value of unknown type `datetime` "#]], ); } #[cargo_test] fn fail_to_merge_multiple_args() { // Error message when multiple args fail to merge. let gctx = GlobalContextBuilder::new() .config_arg("foo='a'") .config_arg("foo=['a']") .build_err(); // This is a little repetitive, but hopefully the user can figure it out. assert_error( gctx.unwrap_err(), str![[r#" failed to merge --config argument `foo=['a']` Caused by: failed to merge key `foo` between --config cli option and --config cli option Caused by: failed to merge config value from `--config cli option` into `--config cli option`: expected string, but found array "#]], ); } #[cargo_test] fn cli_path() { // --config path_to_file fs::write(paths::root().join("myconfig.toml"), "key = 123").unwrap(); let gctx = GlobalContextBuilder::new() .cwd(paths::root()) .config_arg("myconfig.toml") .build(); assert_eq!(gctx.get::("key").unwrap(), 123); let gctx = GlobalContextBuilder::new() .config_arg("missing.toml") .build_err(); assert_error( gctx.unwrap_err(), str![[r#" failed to parse value from --config argument `missing.toml` as a dotted key expression Caused by: TOML parse error at line 1, column 13 | 1 | missing.toml | ^ key with no value, expected `=` "#]], ); } cargo-0.91.0/tests/testsuite/config_include.rs000064400000000000000000000277771046102023000175430ustar 00000000000000//! Tests for `include` config field. use crate::prelude::*; use cargo_test_support::project; use cargo_test_support::str; use super::config::{GlobalContextBuilder, assert_error, write_config_at, write_config_toml}; #[cargo_test] fn gated() { // Requires -Z flag. write_config_toml("include='other.toml'"); write_config_at( ".cargo/other.toml", " othervalue = 1 ", ); let gctx = GlobalContextBuilder::new().build(); assert_eq!(gctx.get::>("othervalue").unwrap(), None); let gctx = GlobalContextBuilder::new() .unstable_flag("config-include") .build(); assert_eq!(gctx.get::("othervalue").unwrap(), 1); } #[cargo_test] fn simple() { // Simple test. write_config_at( ".cargo/config.toml", " include = 'other.toml' key1 = 1 key2 = 2 ", ); write_config_at( ".cargo/other.toml", " key2 = 3 key3 = 4 ", ); let gctx = GlobalContextBuilder::new() .unstable_flag("config-include") .build(); assert_eq!(gctx.get::("key1").unwrap(), 1); assert_eq!(gctx.get::("key2").unwrap(), 2); assert_eq!(gctx.get::("key3").unwrap(), 4); } #[cargo_test] fn enable_in_unstable_config() { // config-include enabled in the unstable config table: write_config_at( ".cargo/config.toml", " include = 'other.toml' key1 = 1 key2 = 2 [unstable] config-include = true ", ); write_config_at( ".cargo/other.toml", " key2 = 3 key3 = 4 ", ); let gctx = GlobalContextBuilder::new() .nightly_features_allowed(true) .build(); assert_eq!(gctx.get::("key1").unwrap(), 1); assert_eq!(gctx.get::("key2").unwrap(), 2); assert_eq!(gctx.get::("key3").unwrap(), 4); } #[cargo_test] fn mix_of_hierarchy_and_include() { write_config_at( "foo/.cargo/config.toml", " include = 'other.toml' key1 = 1 # also make sure unstable flags merge in the correct order [unstable] features = ['1'] ", ); write_config_at( "foo/.cargo/other.toml", " key1 = 2 key2 = 2 [unstable] features = ['2'] ", ); write_config_at( ".cargo/config.toml", " include = 'other.toml' key1 = 3 key2 = 3 key3 = 3 [unstable] features = ['3'] ", ); write_config_at( ".cargo/other.toml", " key1 = 4 key2 = 4 key3 = 4 key4 = 4 [unstable] features = ['4'] ", ); let gctx = GlobalContextBuilder::new() .unstable_flag("config-include") .cwd("foo") .nightly_features_allowed(true) .build(); assert_eq!(gctx.get::("key1").unwrap(), 1); assert_eq!(gctx.get::("key2").unwrap(), 2); assert_eq!(gctx.get::("key3").unwrap(), 3); assert_eq!(gctx.get::("key4").unwrap(), 4); assert_eq!( gctx.get::>("unstable.features").unwrap(), vec![ "4".to_string(), "3".to_string(), "2".to_string(), "1".to_string() ] ); } #[cargo_test] fn mix_of_hierarchy_and_include_with_enable_in_unstable_config() { // `mix_of_hierarchy_and_include`, but with the config-include // feature itself enabled in the unstable config table: write_config_at( "foo/.cargo/config.toml", " include = 'other.toml' key1 = 1 # also make sure unstable flags merge in the correct order [unstable] features = ['1'] config-include = true ", ); write_config_at( "foo/.cargo/other.toml", " key1 = 2 key2 = 2 [unstable] features = ['2'] ", ); write_config_at( ".cargo/config.toml", " include = 'other.toml' key1 = 3 key2 = 3 key3 = 3 [unstable] features = ['3'] ", ); write_config_at( ".cargo/other.toml", " key1 = 4 key2 = 4 key3 = 4 key4 = 4 [unstable] features = ['4'] ", ); let gctx = GlobalContextBuilder::new() .cwd("foo") .nightly_features_allowed(true) .build(); assert_eq!(gctx.get::("key1").unwrap(), 1); assert_eq!(gctx.get::("key2").unwrap(), 2); assert_eq!(gctx.get::("key3").unwrap(), 3); assert_eq!(gctx.get::("key4").unwrap(), 4); assert_eq!( gctx.get::>("unstable.features").unwrap(), vec![ "4".to_string(), "3".to_string(), "2".to_string(), "1".to_string() ] ); } #[cargo_test] fn works_with_cli() { write_config_at( ".cargo/config.toml", " include = 'other.toml' [build] rustflags = ['-W', 'unused'] ", ); write_config_at( ".cargo/other.toml", " [build] rustflags = ['-W', 'unsafe-code'] ", ); let p = project().file("src/lib.rs", "").build(); p.cargo("check -v") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..]-W unused` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check -v -Z config-include") .masquerade_as_nightly_cargo(&["config-include"]) .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..]-W unsafe-code -W unused` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn left_to_right_bottom_to_top() { // How it merges multiple nested includes. write_config_at( ".cargo/config.toml", " include = ['left-middle.toml', 'right-middle.toml'] top = 1 ", ); write_config_at( ".cargo/right-middle.toml", " include = 'right-bottom.toml' top = 0 right-middle = 0 ", ); write_config_at( ".cargo/right-bottom.toml", " top = -1 right-middle = -1 right-bottom = -1 ", ); write_config_at( ".cargo/left-middle.toml", " include = 'left-bottom.toml' top = -2 right-middle = -2 right-bottom = -2 left-middle = -2 ", ); write_config_at( ".cargo/left-bottom.toml", " top = -3 right-middle = -3 right-bottom = -3 left-middle = -3 left-bottom = -3 ", ); let gctx = GlobalContextBuilder::new() .unstable_flag("config-include") .build(); assert_eq!(gctx.get::("top").unwrap(), 1); assert_eq!(gctx.get::("right-middle").unwrap(), 0); assert_eq!(gctx.get::("right-bottom").unwrap(), -1); assert_eq!(gctx.get::("left-middle").unwrap(), -2); assert_eq!(gctx.get::("left-bottom").unwrap(), -3); } #[cargo_test] fn missing_file() { // Error when there's a missing file. write_config_toml("include='missing.toml'"); let gctx = GlobalContextBuilder::new() .unstable_flag("config-include") .build_err(); assert_error( gctx.unwrap_err(), &format!( "\ could not load Cargo configuration Caused by: failed to load config include `missing.toml` from `[..]/.cargo/config.toml` Caused by: failed to read configuration file `[..]/.cargo/missing.toml` Caused by: [NOT_FOUND]", ), ); } #[cargo_test] fn wrong_file_extension() { // Error when it doesn't end with `.toml`. write_config_toml("include='config.png'"); let gctx = GlobalContextBuilder::new() .unstable_flag("config-include") .build_err(); assert_error( gctx.unwrap_err(), "\ could not load Cargo configuration Caused by: expected a config include path ending with `.toml`, but found `config.png` from `[ROOT]/.cargo/config.toml`", ); } #[cargo_test] fn cycle() { // Detects a cycle. write_config_at(".cargo/config.toml", "include='one.toml'"); write_config_at(".cargo/one.toml", "include='two.toml'"); write_config_at(".cargo/two.toml", "include='config.toml'"); let gctx = GlobalContextBuilder::new() .unstable_flag("config-include") .build_err(); assert_error( gctx.unwrap_err(), "\ could not load Cargo configuration Caused by: failed to load config include `one.toml` from `[..]/.cargo/config.toml` Caused by: failed to load config include `two.toml` from `[..]/.cargo/one.toml` Caused by: failed to load config include `config.toml` from `[..]/.cargo/two.toml` Caused by: config `include` cycle detected with path `[..]/.cargo/config.toml`", ); } #[cargo_test] fn cli_include() { // Using --config with include. // CLI takes priority over files. write_config_at( ".cargo/config.toml", " foo = 1 bar = 2 ", ); write_config_at(".cargo/config-foo.toml", "foo = 2"); let gctx = GlobalContextBuilder::new() .unstable_flag("config-include") .config_arg("include='.cargo/config-foo.toml'") .build(); assert_eq!(gctx.get::("foo").unwrap(), 2); assert_eq!(gctx.get::("bar").unwrap(), 2); } #[cargo_test] fn bad_format() { // Not a valid format. write_config_toml("include = 1"); let gctx = GlobalContextBuilder::new() .unstable_flag("config-include") .build_err(); assert_error( gctx.unwrap_err(), "\ could not load Cargo configuration Caused by: `include` expected a string or list, but found integer in `[..]/.cargo/config.toml`", ); } #[cargo_test] fn cli_include_failed() { // Error message when CLI include fails to load. let gctx = GlobalContextBuilder::new() .unstable_flag("config-include") .config_arg("include='foobar.toml'") .build_err(); assert_error( gctx.unwrap_err(), &format!( "\ failed to load --config include Caused by: failed to load config include `foobar.toml` from `--config cli option` Caused by: failed to read configuration file `[..]/foobar.toml` Caused by: [NOT_FOUND]" ), ); } #[cargo_test] fn cli_merge_failed() { // Error message when CLI include merge fails. write_config_toml("foo = ['a']"); write_config_at( ".cargo/other.toml", " foo = 'b' ", ); let gctx = GlobalContextBuilder::new() .unstable_flag("config-include") .config_arg("include='.cargo/other.toml'") .build_err(); // Maybe this error message should mention it was from an include file? assert_error( gctx.unwrap_err(), "\ failed to merge --config key `foo` into `[..]/.cargo/config.toml` Caused by: failed to merge config value from `[..]/.cargo/other.toml` into `[..]/.cargo/config.toml`: \ expected array, but found string", ); } #[cargo_test] fn cli_include_take_priority_over_env() { write_config_at(".cargo/include.toml", "k='include'"); // k=env let gctx = GlobalContextBuilder::new().env("CARGO_K", "env").build(); assert_eq!(gctx.get::("k").unwrap(), "env"); // k=env // --config 'include=".cargo/include.toml"' let gctx = GlobalContextBuilder::new() .env("CARGO_K", "env") .unstable_flag("config-include") .config_arg("include='.cargo/include.toml'") .build(); assert_eq!(gctx.get::("k").unwrap(), "include"); // k=env // --config '.cargo/foo.toml' write_config_at(".cargo/foo.toml", "include='include.toml'"); let gctx = GlobalContextBuilder::new() .env("CARGO_K", "env") .unstable_flag("config-include") .config_arg(".cargo/foo.toml") .build(); assert_eq!(gctx.get::("k").unwrap(), "include"); } cargo-0.91.0/tests/testsuite/corrupt_git.rs000064400000000000000000000101331046102023000171060ustar 00000000000000//! Tests for corrupt git repos. use std::fs; use std::path::{Path, PathBuf}; use crate::prelude::*; use cargo_test_support::paths; use cargo_test_support::{basic_manifest, git, project}; use cargo_util::paths as cargopaths; #[cargo_test] fn deleting_database_files() { let project = project(); let git_project = git::new("bar", |project| { project .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) .file("src/lib.rs", "") }); let project = project .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" authors = [] [dependencies] bar = {{ git = '{}' }} "#, git_project.url() ), ) .file("src/lib.rs", "") .build(); project.cargo("check").run(); let mut files = Vec::new(); find_files(&paths::home().join(".cargo/git/db"), &mut files); assert!(!files.is_empty()); let log = "cargo::sources::git=trace"; for file in files { if !file.exists() { continue; } println!("deleting {}", file.display()); cargopaths::remove_file(&file).unwrap(); project.cargo("check -v").env("CARGO_LOG", log).run(); if !file.exists() { continue; } println!("truncating {}", file.display()); make_writable(&file); fs::OpenOptions::new() .write(true) .open(&file) .unwrap() .set_len(2) .unwrap(); project.cargo("check -v").env("CARGO_LOG", log).run(); } } #[cargo_test] fn deleting_checkout_files() { let project = project(); let git_project = git::new("bar", |project| { project .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) .file("src/lib.rs", "") }); let project = project .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" authors = [] [dependencies] bar = {{ git = '{}' }} "#, git_project.url() ), ) .file("src/lib.rs", "") .build(); project.cargo("check").run(); let dir = paths::home() .join(".cargo/git/checkouts") // get the first entry in the checkouts dir for the package's location .read_dir() .unwrap() .next() .unwrap() .unwrap() .path() // get the first child of that checkout dir for our checkout .read_dir() .unwrap() .next() .unwrap() .unwrap() .path() // and throw on .git to corrupt things .join(".git"); let mut files = Vec::new(); find_files(&dir, &mut files); assert!(!files.is_empty()); let log = "cargo::sources::git=trace"; for file in files { if !file.exists() { continue; } println!("deleting {}", file.display()); cargopaths::remove_file(&file).unwrap(); project.cargo("check -v").env("CARGO_LOG", log).run(); if !file.exists() { continue; } println!("truncating {}", file.display()); make_writable(&file); fs::OpenOptions::new() .write(true) .open(&file) .unwrap() .set_len(2) .unwrap(); project.cargo("check -v").env("CARGO_LOG", log).run(); } } fn make_writable(path: &Path) { let mut p = path.metadata().unwrap().permissions(); p.set_readonly(false); fs::set_permissions(path, p).unwrap(); } fn find_files(path: &Path, dst: &mut Vec) { for e in path.read_dir().unwrap() { let e = e.unwrap(); let path = e.path(); if e.file_type().unwrap().is_dir() { find_files(&path, dst); } else { dst.push(path); } } } cargo-0.91.0/tests/testsuite/credential_process.rs000064400000000000000000000575671046102023000204430ustar 00000000000000//! Tests for credential-process. use crate::prelude::*; use crate::utils::cargo_process; use cargo_test_support::registry::{Package, TestRegistry}; use cargo_test_support::{Project, basic_manifest, paths, project, registry, str}; fn toml_bin(proj: &Project, name: &str) -> String { proj.bin(name).display().to_string().replace('\\', "\\\\") } /// Setup for a test that will issue a command that needs to fetch a token. /// /// This does the following: /// /// * Spawn a thread that will act as an API server. /// * Create a simple credential-process that will generate a fake token. /// * Create a simple `foo` project to run the test against. /// * Configure the credential-process config. /// /// Returns the simple `foo` project to test against and the API server handle. fn get_token_test() -> (Project, TestRegistry) { // API server that checks that the token is included correctly. let server = registry::RegistryBuilder::new() .no_configure_token() .token(cargo_test_support::registry::Token::Plaintext( "sekrit".to_string(), )) .alternative() .http_api() .http_index() .auth_required() .build(); let provider = build_provider( "test-cred", r#"{"Ok":{"kind":"get","token":"sekrit","cache":"session","operation_independent":false}}"#, ); let p = project() .file( ".cargo/config.toml", &format!( r#" [registries.alternative] index = "{}" credential-provider = ["{provider}"] "#, server.index_url(), ), ) .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" description = "foo" license = "MIT" homepage = "https://example.com/" "#, ) .file("src/lib.rs", "") .build(); (p, server) } #[cargo_test] fn publish() { // Checks that credential-process is used for `cargo publish`. let (p, _t) = get_token_test(); p.cargo("publish --no-verify --registry alternative") .with_stderr_data(str![[r#" [UPDATING] `alternative` index {"v":1,"registry":{"index-url":"[..]","name":"alternative","headers":[..]},"kind":"get","operation":"read"} [PACKAGING] foo v0.1.0 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] foo v0.1.0 ([ROOT]/foo) {"v":1,"registry":{"index-url":"[..]","name":"alternative"},"kind":"get","operation":"publish","name":"foo","vers":"0.1.0","cksum":"[..]"} [UPLOADED] foo v0.1.0 to registry `alternative` [NOTE] waiting for foo v0.1.0 to be available at registry `alternative`. You may press ctrl-c [..] [PUBLISHED] foo v0.1.0 at registry `alternative` "#]]) .run(); } #[cargo_test] fn credential_provider_auth_failure() { let _reg = registry::RegistryBuilder::new() .http_index() .auth_required() .alternative() .no_configure_token() .credential_provider(&["cargo:token-from-stdout", "true"]) .build(); cargo_process("install libc --registry=alternative") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [ERROR] token rejected for `alternative` You may need to log in using this registry's credential provider Caused by: failed to get successful HTTP response from [..] body: [..] "#]]) .run(); } #[cargo_test] fn basic_unsupported() { // Non-action commands don't support login/logout. let registry = registry::RegistryBuilder::new() .no_configure_token() .credential_provider(&["cargo:token-from-stdout", "false"]) .build(); cargo_process("login") .with_stdin("abcdefg") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] crates.io index [ERROR] credential provider `cargo:token-from-stdout false` failed action `login` Caused by: requested operation not supported "#]]) .run(); cargo_process("logout") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" [ERROR] credential provider `cargo:token-from-stdout false` failed action `logout` Caused by: requested operation not supported "#]]) .run(); } #[cargo_test] fn login() { let registry = registry::RegistryBuilder::new() .no_configure_token() .credential_provider(&[ &build_provider("test-cred", r#"{"Ok": {"kind": "login"}}"#), "cfg1", "--cfg2", ]) .build(); cargo_process("login -- cmd3 --cmd4") .with_stdin("abcdefg") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index {"v":1,"registry":{"index-url":"https://github.com/rust-lang/crates.io-index","name":"crates-io"},"kind":"login","token":"abcdefg","login-url":"[ROOTURL]/api/me","args":["cfg1","--cfg2","cmd3","--cmd4"]} "#]]) .run(); } #[cargo_test] fn logout() { let server = registry::RegistryBuilder::new() .no_configure_token() .credential_provider(&[&build_provider( "test-cred", r#"{"Ok": {"kind": "logout"}}"#, )]) .build(); cargo_process("logout") .replace_crates_io(server.index_url()) .with_stderr_data(str![[r#" {"v":1,"registry":{"index-url":"https://github.com/rust-lang/crates.io-index","name":"crates-io"},"kind":"logout"} "#]]) .run(); } #[cargo_test] fn yank() { let (p, _t) = get_token_test(); p.cargo("yank --version 0.1.0 --registry alternative") .with_stderr_data(str![[r#" [UPDATING] `alternative` index {"v":1,"registry":{"index-url":"[..]","name":"alternative","headers":[..]},"kind":"get","operation":"read"} {"v":1,"registry":{"index-url":"[..]","name":"alternative"},"kind":"get","operation":"yank","name":"foo","vers":"0.1.0"} [YANK] foo@0.1.0 "#]]) .run(); } #[cargo_test] fn owner() { let (p, _t) = get_token_test(); p.cargo("owner --add username --registry alternative") .with_stderr_data(str![[r#" [UPDATING] `alternative` index {"v":1,"registry":{"index-url":"[..]","name":"alternative","headers":[..]},"kind":"get","operation":"read"} {"v":1,"registry":{"index-url":"[..]","name":"alternative"},"kind":"get","operation":"owners","name":"foo"} [OWNER] completed! "#]]) .run(); } #[cargo_test] fn invalid_token_output() { // Error when credential process does not output the expected format for a token. let cred_proj = project() .at("cred_proj") .file("Cargo.toml", &basic_manifest("test-cred", "1.0.0")) .file("src/main.rs", r#"fn main() { print!("a\nb\n"); } "#) .build(); cred_proj.cargo("build").run(); let _server = registry::RegistryBuilder::new() .alternative() .credential_provider(&[ "cargo:token-from-stdout", &toml_bin(&cred_proj, "test-cred"), ]) .no_configure_token() .build(); let p = project() .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) .file("src/lib.rs", "") .build(); p.cargo("publish --no-verify --registry alternative") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [ERROR] credential provider `[..]test-cred[EXE]` failed action `get` Caused by: process `[..]` returned more than one line of output; expected a single token "#]]) .run(); } /// Builds a credential provider that echos the request from cargo to stderr, /// and prints the `response` to stdout. fn build_provider(name: &str, response: &str) -> String { // The credential process to use. let cred_proj = project() .at(name) .file("Cargo.toml", &basic_manifest(name, "1.0.0")) .file( "src/main.rs", &r####" fn main() { println!(r#"{{"v":[1]}}"#); assert_eq!(std::env::args().skip(1).next().unwrap(), "--cargo-plugin"); let mut buffer = String::new(); std::io::stdin().read_line(&mut buffer).unwrap(); eprint!("{}", buffer); use std::io::Write; std::io::stdout().write_all(r###"[RESPONSE]"###.as_bytes()).unwrap(); println!(); } "#### .replace("[RESPONSE]", response), ) .build(); cred_proj.cargo("build").run(); toml_bin(&cred_proj, name) } #[cargo_test] fn not_found() { let registry = registry::RegistryBuilder::new() .no_configure_token() .http_index() .auth_required() .credential_provider(&[&build_provider( "not_found", r#"{"Err": {"kind": "not-found"}}"#, )]) .build(); // should not suggest a _TOKEN environment variable since the cargo:token provider isn't available. cargo_process("install -v foo") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] crates.io index [CREDENTIAL] [..]not_found[..] get crates-io {"v":1[..] [ERROR] no token found, please run `cargo login` "#]]) .run(); } #[cargo_test] fn all_not_found() { let server = registry::RegistryBuilder::new() .no_configure_token() .auth_required() .http_index() .build(); let not_found = build_provider("not_found", r#"{"Err": {"kind": "not-found"}}"#); cargo_util::paths::append( &paths::home().join(".cargo/config.toml"), format!( r#" [registry] global-credential-providers = ["not_found"] [credential-alias] not_found = ["{not_found}"] "#, ) .as_bytes(), ) .unwrap(); // should not suggest a _TOKEN environment variable since the cargo:token provider isn't available. cargo_process("install -v foo") .replace_crates_io(server.index_url()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] crates.io index [CREDENTIAL] [..]not_found[..] get crates-io {"v":1,"registry":{"index-url":"[..]","name":"crates-io","headers":[[..]"WWW-Authenticate: Cargo login_url=/"https://test-registry-login/me/""[..]]},"kind":"get","operation":"read"} [ERROR] no token found, please run `cargo login` "#]]) .run(); } #[cargo_test] fn all_not_supported() { let server = registry::RegistryBuilder::new() .no_configure_token() .auth_required() .http_index() .build(); let not_supported = build_provider("not_supported", r#"{"Err": {"kind": "url-not-supported"}}"#); cargo_util::paths::append( &paths::home().join(".cargo/config.toml"), format!( r#" [registry] global-credential-providers = ["not_supported"] [credential-alias] not_supported = ["{not_supported}"] "#, ) .as_bytes(), ) .unwrap(); cargo_process("install -v foo") .replace_crates_io(server.index_url()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] crates.io index [CREDENTIAL] [..]not_supported[..] get crates-io {"v":1,"registry":{"index-url":"[..]","name":"crates-io","headers":[[..]"WWW-Authenticate: Cargo login_url=/"https://test-registry-login/me/""[..]]},"kind":"get","operation":"read"} [ERROR] no credential providers could handle the request "#]]) .run(); } #[cargo_test] fn multiple_providers() { let server = registry::RegistryBuilder::new() .no_configure_token() .build(); // Set up two credential providers: the first will fail with "UrlNotSupported" // and Cargo should skip it. The second should succeed. let url_not_supported = build_provider( "url_not_supported", r#"{"Err": {"kind": "url-not-supported"}}"#, ); let success_provider = build_provider("success_provider", r#"{"Ok": {"kind": "login"}}"#); cargo_util::paths::append( &paths::home().join(".cargo/config.toml"), format!( r#" [registry] global-credential-providers = ["success_provider", "url_not_supported"] [credential-alias] success_provider = ["{success_provider}"] url_not_supported = ["{url_not_supported}"] "#, ) .as_bytes(), ) .unwrap(); cargo_process("login -v") .with_stdin("abcdefg") .replace_crates_io(server.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [CREDENTIAL] [..]url_not_supported[..] login crates-io {"v":1,"registry":{"index-url":"https://github.com/rust-lang/crates.io-index","name":"crates-io"},"kind":"login","token":"abcdefg","login-url":"[ROOTURL]/api/me"} [CREDENTIAL] [..]success_provider[..] login crates-io {"v":1,"registry":{"index-url":"https://github.com/rust-lang/crates.io-index","name":"crates-io"},"kind":"login","token":"abcdefg","login-url":"[ROOTURL]/api/me"} "#]]) .run(); } #[cargo_test] fn both_token_and_provider() { let server = registry::RegistryBuilder::new() .credential_provider(&["cargo:paseto"]) .build(); cargo_process("login -Z asymmetric-token") .masquerade_as_nightly_cargo(&["asymmetric-token"]) .replace_crates_io(server.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] registry `crates-io` has a token configured in [ROOT]/home/.cargo/credentials.toml that will be ignored because this registry is configured to use credential-provider `cargo:paseto` k3.public[..] "#]]) .run(); } #[cargo_test] fn registry_provider_overrides_global() { let server = registry::RegistryBuilder::new().build(); cargo_util::paths::append( &paths::home().join(".cargo/config.toml"), format!( r#" [registry] global-credential-providers = ["should-not-be-called"] "#, ) .as_bytes(), ) .unwrap(); cargo_process("login -v") .with_stdin("abcdefg") .env("CARGO_REGISTRY_CREDENTIAL_PROVIDER", "cargo:token") .replace_crates_io(server.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [CREDENTIAL] cargo:token login crates-io [LOGIN] token for `crates-io` saved "#]]) .run(); let credentials = std::fs::read_to_string(paths::home().join(".cargo/credentials.toml")).unwrap(); assert_eq!(credentials, "[registry]\ntoken = \"abcdefg\"\n"); } #[cargo_test] fn both_asymmetric_and_token() { let server = registry::RegistryBuilder::new().build(); cargo_util::paths::append( &paths::home().join(".cargo/config.toml"), format!( r#" [registry] token = "foo" secret-key = "bar" "#, ) .as_bytes(), ) .unwrap(); cargo_process("login -Zasymmetric-token -v").with_stdin("abcdefg") .masquerade_as_nightly_cargo(&["asymmetric-token"]) .replace_crates_io(server.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] registry `crates-io` has a `secret_key` configured in [..]config.toml that will be ignored because a `token` is also configured, and the `cargo:token` provider is configured with higher precedence [CREDENTIAL] cargo:token login crates-io [LOGIN] token for `crates-io` saved "#]]) .run(); } #[cargo_test] fn token_caching() { let server = registry::RegistryBuilder::new() .no_configure_token() .no_configure_registry() .token(cargo_test_support::registry::Token::Plaintext( "sekrit".to_string(), )) .alternative() .http_api() .http_index() .build(); // Token should not be re-used if it is expired let expired_provider = build_provider( "expired_provider", r#"{"Ok":{"kind":"get","token":"sekrit","cache":"expires","expiration":0,"operation_independent":true}}"#, ); // Token should not be re-used for a different operation if it is not operation_independent let non_independent_provider = build_provider( "non_independent_provider", r#"{"Ok":{"kind":"get","token":"sekrit","cache":"session","operation_independent":false}}"#, ); let p = project() .file( ".cargo/config.toml", &format!( r#" [registries.alternative] index = "{}" credential-provider = ["{expired_provider}"] "#, server.index_url(), ), ) .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" description = "foo" license = "MIT" homepage = "https://example.com/" "#, ) .file("src/lib.rs", "") .build(); let output = r#"[UPDATING] `alternative` index {"v":1,"registry":{"index-url":"[..]","name":"alternative"},"kind":"get","operation":"read"} [PACKAGING] foo v0.1.0 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] foo v0.1.0 ([ROOT]/foo) {"v":1,"registry":{"index-url":"[..]","name":"alternative"},"kind":"get","operation":"publish","name":"foo","vers":"0.1.0","cksum":"[..]"} [UPLOADED] foo v0.1.0 to registry `alternative` [NOTE] waiting [..] You may press ctrl-c [..] [PUBLISHED] foo v0.1.0 at registry `alternative` "#; // The output should contain two JSON messages from the provider in both cases: // The first because the credential is expired, the second because the provider // indicated that the token was non-operation-independent. p.cargo("publish --registry alternative --no-verify") .with_stderr_data(output) .run(); let output_non_independent = r#"[UPDATING] `alternative` index {"v":1,"registry":{"index-url":"[..]","name":"alternative"},"kind":"get","operation":"read"} [PACKAGING] foo v0.1.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] foo v0.1.1 ([ROOT]/foo) {"v":1,"registry":{"index-url":"[..]","name":"alternative"},"kind":"get","operation":"publish","name":"foo","vers":"0.1.1","cksum":"[..]"} [UPLOADED] foo v0.1.1 to registry `alternative` [NOTE] waiting [..] You may press ctrl-c [..] [PUBLISHED] foo v0.1.1 at registry `alternative` "#; p.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.1" edition = "2015" description = "foo" license = "MIT" homepage = "https://example.com/" "#, ); p.change_file( ".cargo/config.toml", &format!( r#" [registries.alternative] index = "{}" credential-provider = ["{non_independent_provider}"] "#, server.index_url(), ), ); p.cargo("publish --registry alternative --no-verify") .with_stderr_data(output_non_independent) .run(); } #[cargo_test] fn basic_provider() { let cred_proj = project() .at("cred_proj") .file("Cargo.toml", &basic_manifest("test-cred", "1.0.0")) .file("src/main.rs", r#"fn main() { eprintln!("CARGO={:?}", std::env::var("CARGO").ok()); eprintln!("CARGO_REGISTRY_NAME_OPT={:?}", std::env::var("CARGO_REGISTRY_NAME_OPT").ok()); eprintln!("CARGO_REGISTRY_INDEX_URL={:?}", std::env::var("CARGO_REGISTRY_INDEX_URL").ok()); print!("sekrit"); }"#) .build(); cred_proj.cargo("build").run(); let _server = registry::RegistryBuilder::new() .no_configure_token() .credential_provider(&[ "cargo:token-from-stdout", &toml_bin(&cred_proj, "test-cred"), ]) .token(cargo_test_support::registry::Token::Plaintext( "sekrit".to_string(), )) .alternative() .http_api() .auth_required() .build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] version = "0.0.1" registry = "alternative" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("bar", "0.0.1").alternative(true).publish(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `alternative` index [LOCKING] 1 package to latest compatible version CARGO=Some([..]) CARGO_REGISTRY_NAME_OPT=Some("alternative") CARGO_REGISTRY_INDEX_URL=Some("[ROOTURL]/alternative-registry") [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `alternative`) [CHECKING] bar v0.0.1 (registry `alternative`) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn unsupported_version() { let cred_proj = project() .at("new-vers") .file("Cargo.toml", &basic_manifest("new-vers", "1.0.0")) .file( "src/main.rs", &r####" fn main() { println!(r#"{{"v":[998, 999]}}"#); assert_eq!(std::env::args().skip(1).next().unwrap(), "--cargo-plugin"); let mut buffer = String::new(); std::io::stdin().read_line(&mut buffer).unwrap(); std::thread::sleep(std::time::Duration::from_secs(1)); panic!("child process should have been killed before getting here"); } "####, ) .build(); cred_proj.cargo("build").run(); let provider = toml_bin(&cred_proj, "new-vers"); let registry = registry::RegistryBuilder::new() .no_configure_token() .credential_provider(&[&provider]) .build(); cargo_process("login") .with_stdin("abcdefg") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] crates.io index [ERROR] credential provider `[..]` failed action `login` Caused by: credential provider supports protocol versions [998, 999], while Cargo supports [1] "#]]) .run(); } #[cargo_test] fn alias_builtin_warning() { let registry = registry::RegistryBuilder::new() .credential_provider(&[&"cargo:token"]) .build(); cargo_util::paths::append( &paths::home().join(".cargo/config.toml"), format!( r#" [credential-alias] "cargo:token" = ["ignored"] "#, ) .as_bytes(), ) .unwrap(); cargo_process("login") .with_stdin("abcdefg") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] credential-alias `cargo:token` (defined in `[ROOT]/home/.cargo/config.toml`) will be ignored because it would shadow a built-in credential-provider [LOGIN] token for `crates-io` saved "#]]) .run(); } #[cargo_test] fn login_token_from_stdin() { // Test reading a token from stdin, ensuring newlines are trimmed. let registry = registry::RegistryBuilder::new() .no_configure_token() .credential_provider(&[&build_provider("test-cred", r#"{"Ok": {"kind": "login"}}"#)]) .build(); cargo_process("login") .replace_crates_io(registry.index_url()) .with_stdin("abcdefg\n") .with_stderr_data(str![[r#" [UPDATING] crates.io index {"v":1,"registry":{"index-url":"https://github.com/rust-lang/crates.io-index","name":"crates-io"},"kind":"login","token":"abcdefg","login-url":"[ROOTURL]/api/me"} "#]]) .run(); } cargo-0.91.0/tests/testsuite/cross_compile.rs000064400000000000000000001010451046102023000174110ustar 00000000000000//! Tests for cross compiling with --target. //! //! See `cargo_test_support::cross_compile` for more detail. use crate::prelude::*; use cargo_test_support::rustc_host; use cargo_test_support::str; use cargo_test_support::{basic_bin_manifest, basic_manifest, cross_compile, project}; use crate::utils::cross_compile::{ can_run_on_host as cross_compile_can_run_on_host, disabled as cross_compile_disabled, }; #[cargo_test] fn simple_cross() { if cross_compile_disabled() { return; } let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file( "build.rs", &format!( r#" fn main() {{ assert_eq!(std::env::var("TARGET").unwrap(), "{}"); }} "#, cross_compile::alternate() ), ) .file( "src/main.rs", &format!( r#" use std::env; fn main() {{ assert_eq!(env::consts::ARCH, "{}"); }} "#, cross_compile::alternate_arch() ), ) .build(); let target = cross_compile::alternate(); p.cargo("build -v --target").arg(&target).run(); assert!(p.target_bin(target, "foo").is_file()); if cross_compile_can_run_on_host() { p.process(&p.target_bin(target, "foo")).run(); } } #[cargo_test] fn simple_cross_config() { if cross_compile_disabled() { return; } let p = project() .file( ".cargo/config.toml", &format!( r#" [build] target = "{}" "#, cross_compile::alternate() ), ) .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file( "build.rs", &format!( r#" fn main() {{ assert_eq!(std::env::var("TARGET").unwrap(), "{}"); }} "#, cross_compile::alternate() ), ) .file( "src/main.rs", &format!( r#" use std::env; fn main() {{ assert_eq!(env::consts::ARCH, "{}"); }} "#, cross_compile::alternate_arch() ), ) .build(); let target = cross_compile::alternate(); p.cargo("build -v").run(); assert!(p.target_bin(target, "foo").is_file()); if cross_compile_can_run_on_host() { p.process(&p.target_bin(target, "foo")).run(); } } #[cargo_test] fn simple_deps() { if cross_compile_disabled() { return; } let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "../bar" "#, ) .file("src/main.rs", "extern crate bar; fn main() { bar::bar(); }") .build(); let _p2 = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("src/lib.rs", "pub fn bar() {}") .build(); let target = cross_compile::alternate(); p.cargo("build --target").arg(&target).run(); assert!(p.target_bin(target, "foo").is_file()); if cross_compile_can_run_on_host() { p.process(&p.target_bin(target, "foo")).run(); } } /// Always take care of setting these so that /// `cross_compile::alternate()` is the actually-picked target fn per_crate_target_test( default_target: Option<&'static str>, forced_target: Option<&'static str>, arg_target: Option<&'static str>, ) { if cross_compile_disabled() { return; } let p = project() .file( "Cargo.toml", &format!( r#" cargo-features = ["per-package-target"] [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] build = "build.rs" {} {} "#, default_target .map(|t| format!(r#"default-target = "{}""#, t)) .unwrap_or(String::new()), forced_target .map(|t| format!(r#"forced-target = "{}""#, t)) .unwrap_or(String::new()), ), ) .file( "build.rs", &format!( r#" fn main() {{ assert_eq!(std::env::var("TARGET").unwrap(), "{}"); }} "#, cross_compile::alternate() ), ) .file( "src/main.rs", &format!( r#" use std::env; fn main() {{ assert_eq!(env::consts::ARCH, "{}"); }} "#, cross_compile::alternate_arch() ), ) .build(); let mut cmd = p.cargo("build -v"); if let Some(t) = arg_target { cmd.arg("--target").arg(&t); } cmd.masquerade_as_nightly_cargo(&["per-package-target"]) .run(); assert!(p.target_bin(cross_compile::alternate(), "foo").is_file()); if cross_compile_can_run_on_host() { p.process(&p.target_bin(cross_compile::alternate(), "foo")) .run(); } } #[cargo_test] fn per_crate_default_target_is_default() { per_crate_target_test(Some(cross_compile::alternate()), None, None); } #[cargo_test] fn per_crate_default_target_gets_overridden() { per_crate_target_test( Some(cross_compile::unused()), None, Some(cross_compile::alternate()), ); } #[cargo_test] fn per_crate_forced_target_is_default() { per_crate_target_test(None, Some(cross_compile::alternate()), None); } #[cargo_test] fn per_crate_forced_target_does_not_get_overridden() { per_crate_target_test( None, Some(cross_compile::alternate()), Some(cross_compile::unused()), ); } #[cargo_test] fn workspace_with_multiple_targets() { if cross_compile_disabled() { return; } let p = project() .file( "Cargo.toml", r#" [workspace] members = ["native", "cross"] "#, ) .file( "native/Cargo.toml", r#" cargo-features = ["per-package-target"] [package] name = "native" version = "0.0.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file( "native/build.rs", &format!( r#" fn main() {{ assert_eq!(std::env::var("TARGET").unwrap(), "{}"); }} "#, cross_compile::native() ), ) .file( "native/src/main.rs", &format!( r#" use std::env; fn main() {{ assert_eq!(env::consts::ARCH, "{}"); }} "#, cross_compile::native_arch() ), ) .file( "cross/Cargo.toml", &format!( r#" cargo-features = ["per-package-target"] [package] name = "cross" version = "0.0.0" edition = "2015" authors = [] build = "build.rs" default-target = "{}" "#, cross_compile::alternate(), ), ) .file( "cross/build.rs", &format!( r#" fn main() {{ assert_eq!(std::env::var("TARGET").unwrap(), "{}"); }} "#, cross_compile::alternate() ), ) .file( "cross/src/main.rs", &format!( r#" use std::env; fn main() {{ assert_eq!(env::consts::ARCH, "{}"); }} "#, cross_compile::alternate_arch() ), ) .build(); let mut cmd = p.cargo("build -v"); cmd.masquerade_as_nightly_cargo(&["per-package-target"]) .run(); assert!(p.bin("native").is_file()); assert!(p.target_bin(cross_compile::alternate(), "cross").is_file()); p.process(&p.bin("native")).run(); if cross_compile_can_run_on_host() { p.process(&p.target_bin(cross_compile::alternate(), "cross")) .run(); } } #[cargo_test] fn linker() { if cross_compile_disabled() { return; } let target = cross_compile::alternate(); let p = project() .file( ".cargo/config.toml", &format!( r#" [target.{}] linker = "my-linker-tool" "#, target ), ) .file("Cargo.toml", &basic_bin_manifest("foo")) .file( "src/foo.rs", &format!( r#" use std::env; fn main() {{ assert_eq!(env::consts::ARCH, "{}"); }} "#, cross_compile::alternate_arch() ), ) .build(); p.cargo("build -v --target") .arg(&target) .with_status(101) .with_stderr_data(str![[r#" [WARNING] path `src/foo.rs` was erroneously implicitly accepted for binary `foo`, please set bin.path in Cargo.toml [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/foo.rs [..]--crate-type bin --emit=[..]link[..]-C debuginfo=2 [..] -C metadata=[..] --out-dir [ROOT]/foo/target/[ALT_TARGET]/debug/deps --target [ALT_TARGET] -C linker=my-linker-tool -L dependency=[ROOT]/foo/target/[ALT_TARGET]/debug/deps -L dependency=[ROOT]/foo/target/debug/deps` [ERROR] linker `my-linker-tool` not found ... "#]]) .run(); } #[cargo_test] fn cross_tests() { if !cross_compile_can_run_on_host() { return; } let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.0" edition = "2015" [[bin]] name = "bar" "#, ) .file( "src/bin/bar.rs", &format!( r#" #[allow(unused_extern_crates)] extern crate foo; use std::env; fn main() {{ assert_eq!(env::consts::ARCH, "{}"); }} #[test] fn test() {{ main() }} "#, cross_compile::alternate_arch() ), ) .file( "src/lib.rs", &format!( r#" //! ``` //! extern crate foo; //! assert!(true); //! ``` use std::env; pub fn foo() {{ assert_eq!(env::consts::ARCH, "{}"); }} #[test] fn test_foo() {{ foo() }} "#, cross_compile::alternate_arch() ), ) .build(); let target = cross_compile::alternate(); p.cargo("test --target") .arg(&target) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/[ALT_TARGET]/debug/deps/foo-[HASH][EXE]) [RUNNING] unittests src/bin/bar.rs (target/[ALT_TARGET]/debug/deps/bar-[HASH][EXE]) [DOCTEST] foo "#]]) .with_stdout_data(str![[r#" running 1 test test test_foo ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test test ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test src/lib.rs - (line 2) ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn simple_cargo_run() { if !cross_compile_can_run_on_host() { return; } let p = project() .file( "src/main.rs", &format!( r#" use std::env; fn main() {{ assert_eq!(env::consts::ARCH, "{}"); }} "#, cross_compile::alternate_arch() ), ) .build(); let target = cross_compile::alternate(); p.cargo("run --target").arg(&target).run(); } #[cargo_test] fn cross_with_a_build_script() { if cross_compile_disabled() { return; } let target = cross_compile::alternate(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] build = 'build.rs' "#, ) .file( "build.rs", &format!( r#" use std::env; use std::path::PathBuf; fn main() {{ assert_eq!(env::var("TARGET").unwrap(), "{0}"); let mut path = PathBuf::from(env::var_os("OUT_DIR").unwrap()); assert_eq!(path.file_name().unwrap().to_str().unwrap(), "out"); path.pop(); assert!(path.file_name().unwrap().to_str().unwrap() .starts_with("foo-")); path.pop(); assert_eq!(path.file_name().unwrap().to_str().unwrap(), "build"); path.pop(); assert_eq!(path.file_name().unwrap().to_str().unwrap(), "debug"); path.pop(); assert_eq!(path.file_name().unwrap().to_str().unwrap(), "{0}"); path.pop(); assert_eq!(path.file_name().unwrap().to_str().unwrap(), "target"); }} "#, target ), ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build -v --target") .arg(&target) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.0 ([ROOT]/foo) [RUNNING] `rustc [..] build.rs [..] --out-dir [ROOT]/foo/target/debug/build/foo-[HASH] [..] [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc [..] src/main.rs [..] --target [ALT_TARGET] [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn build_script_needed_for_host_and_target() { if cross_compile_disabled() { return; } let target = cross_compile::alternate(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] build = 'build.rs' [dependencies.d1] path = "d1" [build-dependencies.d2] path = "d2" "#, ) .file( "build.rs", r#" #[allow(unused_extern_crates)] extern crate d2; fn main() { d2::d2(); } "#, ) .file( "src/main.rs", " #[allow(unused_extern_crates)] extern crate d1; fn main() { d1::d1(); } ", ) .file( "d1/Cargo.toml", r#" [package] name = "d1" version = "0.0.0" edition = "2015" authors = [] build = 'build.rs' "#, ) .file("d1/src/lib.rs", "pub fn d1() {}") .file( "d1/build.rs", r#" use std::env; fn main() { let target = env::var("TARGET").unwrap(); let root = std::env::current_dir().unwrap(); let root = root.parent().unwrap().join(format!("link-{target}")); println!("cargo::rustc-flags=-L {}", root.display()); } "#, ) .file( "d2/Cargo.toml", r#" [package] name = "d2" version = "0.0.0" edition = "2015" authors = [] [dependencies.d1] path = "../d1" "#, ) .file( "d2/src/lib.rs", " #[allow(unused_extern_crates)] extern crate d1; pub fn d2() { d1::d1(); } ", ) .build(); p.root().join(format!("link-{target}")).mkdir_p(); p.root().join(format!("link-{}", rustc_host())).mkdir_p(); p.cargo("build -v --target") .arg(&target) .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] d1 v0.0.0 ([ROOT]/foo/d1) [RUNNING] `rustc [..] d1/build.rs [..] --out-dir [ROOT]/foo/target/debug/build/d1-[HASH] [..] [RUNNING] `[ROOT]/foo/target/debug/build/d1-[HASH]/build-script-build` [RUNNING] `[ROOT]/foo/target/debug/build/d1-[HASH]/build-script-build` [RUNNING] `rustc [..] d1/src/lib.rs [..] --out-dir [ROOT]/foo/target/debug/deps [..] [RUNNING] `rustc [..] d1/src/lib.rs [..] --out-dir [ROOT]/foo/target/[ALT_TARGET]/debug/deps [..] [COMPILING] d2 v0.0.0 ([ROOT]/foo/d2) [RUNNING] `rustc [..] d2/src/lib.rs [..] --out-dir [ROOT]/foo/target/debug/deps [..]-L [ROOT]/foo/link-[HOST_TARGET]` [COMPILING] foo v0.0.0 ([ROOT]/foo) [RUNNING] `rustc [..] build.rs [..] --out-dir [ROOT]/foo/target/debug/build/foo-[HASH] [..]-L [ROOT]/foo/link-[HOST_TARGET]` [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc [..] src/main.rs [..] --out-dir [ROOT]/foo/target/[ALT_TARGET]/debug/deps --target [ALT_TARGET] [..]-L [ROOT]/foo/link-[ALT_TARGET]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]].unordered()) .run(); } #[cargo_test] fn build_deps_for_the_right_arch() { if cross_compile_disabled() { return; } let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [dependencies.d2] path = "d2" "#, ) .file("src/main.rs", "extern crate d2; fn main() {}") .file("d1/Cargo.toml", &basic_manifest("d1", "0.0.0")) .file("d1/src/lib.rs", "pub fn d1() {}") .file( "d2/Cargo.toml", r#" [package] name = "d2" version = "0.0.0" edition = "2015" authors = [] build = "build.rs" [build-dependencies.d1] path = "../d1" "#, ) .file("d2/build.rs", "extern crate d1; fn main() {}") .file("d2/src/lib.rs", "") .build(); let target = cross_compile::alternate(); p.cargo("build -v --target").arg(&target).run(); } #[cargo_test] fn build_script_only_host() { if cross_compile_disabled() { return; } let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] build = "build.rs" [build-dependencies.d1] path = "d1" "#, ) .file("src/main.rs", "fn main() {}") .file("build.rs", "extern crate d1; fn main() {}") .file( "d1/Cargo.toml", r#" [package] name = "d1" version = "0.0.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file("d1/src/lib.rs", "pub fn d1() {}") .file( "d1/build.rs", r#" use std::env; fn main() { assert!(env::var("OUT_DIR").unwrap().replace("\\", "/") .contains("target/debug/build/d1-"), "bad: {:?}", env::var("OUT_DIR")); } "#, ) .build(); let target = cross_compile::alternate(); p.cargo("build -v --target").arg(&target).run(); } #[cargo_test] fn build_script_with_platform_specific_dependencies() { if cross_compile_disabled() { return; } let target = cross_compile::alternate(); let host = rustc_host(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" [build-dependencies.d1] path = "d1" "#, ) .file( "build.rs", " #[allow(unused_extern_crates)] extern crate d1; fn main() {} ", ) .file("src/lib.rs", "") .file( "d1/Cargo.toml", &format!( r#" [package] name = "d1" version = "0.0.0" edition = "2015" authors = [] [target.{}.dependencies] d2 = {{ path = "../d2" }} "#, host ), ) .file( "d1/src/lib.rs", "#[allow(unused_extern_crates)] extern crate d2;", ) .file("d2/Cargo.toml", &basic_manifest("d2", "0.0.0")) .file("d2/src/lib.rs", "") .build(); p.cargo("build -v --target") .arg(&target) .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] d2 v0.0.0 ([ROOT]/foo/d2) [RUNNING] `rustc [..] d2/src/lib.rs [..]` [COMPILING] d1 v0.0.0 ([ROOT]/foo/d1) [RUNNING] `rustc [..] d1/src/lib.rs [..]` [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..] build.rs [..]` [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc [..] src/lib.rs [..] --target [ALT_TARGET] [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn platform_specific_dependencies_do_not_leak() { if cross_compile_disabled() { return; } let target = cross_compile::alternate(); let host = rustc_host(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" [dependencies.d1] path = "d1" [build-dependencies.d1] path = "d1" "#, ) .file("build.rs", "extern crate d1; fn main() {}") .file("src/lib.rs", "") .file( "d1/Cargo.toml", &format!( r#" [package] name = "d1" version = "0.0.0" edition = "2015" authors = [] [target.{}.dependencies] d2 = {{ path = "../d2" }} "#, host ), ) .file("d1/src/lib.rs", "extern crate d2;") .file("d1/Cargo.toml", &basic_manifest("d1", "0.0.0")) .file("d2/src/lib.rs", "") .build(); p.cargo("build -v --target") .arg(&target) .with_status(101) .with_stderr_data(str![[r#" ... error[E0463]: can't find crate for `d2` ... "#]]) .run(); } #[cargo_test] fn platform_specific_variables_reflected_in_build_scripts() { if cross_compile_disabled() { return; } let target = cross_compile::alternate(); let host = rustc_host(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" [target.{host}.dependencies] d1 = {{ path = "d1" }} [target.{target}.dependencies] d2 = {{ path = "d2" }} "#, host = host, target = target ), ) .file( "build.rs", &format!( r#" use std::env; fn main() {{ let platform = env::var("TARGET").unwrap(); let (expected, not_expected) = match &platform[..] {{ "{host}" => ("DEP_D1_VAL", "DEP_D2_VAL"), "{target}" => ("DEP_D2_VAL", "DEP_D1_VAL"), _ => panic!("unknown platform") }}; env::var(expected).ok() .expect(&format!("missing {{}}", expected)); env::var(not_expected).err() .expect(&format!("found {{}}", not_expected)); }} "#, host = host, target = target ), ) .file("src/lib.rs", "") .file( "d1/Cargo.toml", r#" [package] name = "d1" version = "0.0.0" edition = "2015" authors = [] links = "d1" build = "build.rs" "#, ) .file( "d1/build.rs", r#"fn main() { println!("cargo::metadata=val=1") }"#, ) .file("d1/src/lib.rs", "") .file( "d2/Cargo.toml", r#" [package] name = "d2" version = "0.0.0" edition = "2015" authors = [] links = "d2" build = "build.rs" "#, ) .file( "d2/build.rs", r#"fn main() { println!("cargo::metadata=val=1") }"#, ) .file("d2/src/lib.rs", "") .build(); p.cargo("build -v").run(); p.cargo("build -v --target").arg(&target).run(); } #[cargo_test] #[cfg_attr( target_os = "macos", ignore = "don't have a dylib cross target on macos" )] fn cross_test_dylib() { if cross_compile_disabled() { return; } let target = cross_compile::alternate(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lib] name = "foo" crate-type = ["dylib"] [dependencies.bar] path = "bar" "#, ) .file( "src/lib.rs", r#" extern crate bar as the_bar; pub fn bar() { the_bar::baz(); } #[test] fn foo() { bar(); } "#, ) .file( "tests/test.rs", r#" extern crate foo as the_foo; #[test] fn foo() { the_foo::bar(); } "#, ) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [lib] name = "bar" crate-type = ["dylib"] "#, ) .file( "bar/src/lib.rs", &format!( r#" use std::env; pub fn baz() {{ assert_eq!(env::consts::ARCH, "{}"); }} "#, cross_compile::alternate_arch() ), ) .build(); p.cargo("test --target") .arg(&target) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/[ALT_TARGET]/debug/deps/foo-[HASH][EXE]) [RUNNING] tests/test.rs (target/[ALT_TARGET]/debug/deps/test-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test foo ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test foo ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn doctest_xcompile_linker() { if cross_compile_disabled() { return; } let target = cross_compile::alternate(); let p = project() .file( ".cargo/config.toml", &format!( r#" [target.{}] linker = "my-linker-tool" "#, target ), ) .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) .file( "src/lib.rs", r#" /// ``` /// assert_eq!(1, 1); /// ``` pub fn foo() {} "#, ) .build(); // Fails because `my-linker-tool` doesn't actually exist. p.cargo("test --doc -v --target") .arg(&target) .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..] --out-dir [ROOT]/foo/target/[ALT_TARGET]/debug/deps --target [ALT_TARGET] [..] [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [DOCTEST] foo [RUNNING] `rustdoc [..] src/lib.rs [..] [ERROR] doctest failed, to rerun pass `--doc` "#]]) .run(); } #[cargo_test] fn always_emit_warnings_as_warnings_when_learning_target_info() { if cross_compile_disabled() { return; } let target = "wasm32-unknown-unknown"; if !cross_compile::requires_target_installed(target) { return; } let p = project() .file( "Cargo.toml", r#" [package] name = "foo" edition = "2015" "#, ) .file("src/lib.rs", "") .build(); p.cargo("build -v --target") .env("RUSTFLAGS", "-Awarnings") .arg(target) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]-Awarnings[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } cargo-0.91.0/tests/testsuite/cross_publish.rs000064400000000000000000000066561046102023000174430ustar 00000000000000//! Tests for publishing using the `--target` flag. use std::fs::File; use crate::prelude::*; use crate::utils::cross_compile::disabled as cross_compile_disabled; use cargo_test_support::{cross_compile, project, publish, registry, str}; #[cargo_test] fn simple_cross_package() { if cross_compile_disabled() { return; } let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] license = "MIT" description = "foo" repository = "bar" "#, ) .file( "src/main.rs", &format!( r#" use std::env; fn main() {{ assert_eq!(env::consts::ARCH, "{}"); }} "#, cross_compile::alternate_arch() ), ) .build(); let target = cross_compile::alternate(); p.cargo("package --target") .arg(&target) .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.0 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.0 ([ROOT]/foo) [COMPILING] foo v0.0.0 ([ROOT]/foo/target/package/foo-0.0.0) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Check that the tarball contains the files let f = File::open(&p.root().join("target/package/foo-0.0.0.crate")).unwrap(); publish::validate_crate_contents( f, "foo-0.0.0.crate", &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], (), ); } #[cargo_test] fn publish_with_target() { if cross_compile_disabled() { return; } // `publish` generally requires a remote registry let registry = registry::RegistryBuilder::new().http_api().build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] license = "MIT" description = "foo" repository = "bar" "#, ) .file( "src/main.rs", &format!( r#" use std::env; fn main() {{ assert_eq!(env::consts::ARCH, "{}"); }} "#, cross_compile::alternate_arch() ), ) .build(); let target = cross_compile::alternate(); p.cargo("publish") .replace_crates_io(registry.index_url()) .arg("--target") .arg(&target) .with_stderr_data(str![[r#" [UPDATING] crates.io index [PACKAGING] foo v0.0.0 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.0 ([ROOT]/foo) [COMPILING] foo v0.0.0 ([ROOT]/foo/target/package/foo-0.0.0) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] foo v0.0.0 ([ROOT]/foo) [UPLOADED] foo v0.0.0 to registry `crates-io` [NOTE] waiting for foo v0.0.0 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.0.0 at registry `crates-io` "#]]) .run(); } cargo-0.91.0/tests/testsuite/custom_target.rs000064400000000000000000000174251046102023000174400ustar 00000000000000//! Tests for custom json target specifications. use std::fs; use crate::prelude::*; use cargo_test_support::{basic_manifest, project, str}; const MINIMAL_LIB: &str = r#" #![allow(internal_features)] #![feature(no_core)] #![feature(lang_items)] #![no_core] #[lang = "pointee_sized"] pub trait PointeeSized { // Empty. } #[lang = "meta_sized"] pub trait MetaSized: PointeeSized { // Empty. } #[lang = "sized"] pub trait Sized: MetaSized { // Empty. } #[lang = "copy"] pub trait Copy { // Empty. } "#; const SIMPLE_SPEC: &str = r#" { "llvm-target": "x86_64-unknown-none-gnu", "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", "arch": "x86_64", "target-endian": "little", "target-pointer-width": "64", "os": "none", "linker-flavor": "ld.lld", "linker": "rust-lld", "executables": true } "#; #[cargo_test(nightly, reason = "requires features no_core, lang_items")] fn custom_target_minimal() { let p = project() .file( "src/lib.rs", &" __MINIMAL_LIB__ pub fn foo() -> u32 { 42 } " .replace("__MINIMAL_LIB__", MINIMAL_LIB), ) .file("custom-target.json", SIMPLE_SPEC) .build(); p.cargo("build --lib --target custom-target.json -v").run(); p.cargo("build --lib --target src/../custom-target.json -v") .run(); // Ensure that the correct style of flag is passed to --target with doc tests. p.cargo("test --doc --target src/../custom-target.json -v") .masquerade_as_nightly_cargo(&["no_core", "lang_items"]) .with_stderr_data(str![[r#" [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [DOCTEST] foo [RUNNING] `rustdoc [..]--target [..]foo/custom-target.json[..] "#]]) .run(); } #[cargo_test(nightly, reason = "requires features no_core, lang_items, auto_traits")] fn custom_target_dependency() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = ["author@example.com"] [dependencies] bar = { path = "bar" } "#, ) .file( "src/lib.rs", r#" #![allow(internal_features)] #![feature(no_core)] #![feature(lang_items)] #![feature(auto_traits)] #![no_core] extern crate bar; pub fn foo() -> u32 { bar::bar() } #[lang = "freeze"] unsafe auto trait Freeze {} "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file( "bar/src/lib.rs", &" __MINIMAL_LIB__ pub fn bar() -> u32 { 42 } " .replace("__MINIMAL_LIB__", MINIMAL_LIB), ) .file("custom-target.json", SIMPLE_SPEC) .build(); p.cargo("build --lib --target custom-target.json -v").run(); } #[cargo_test(nightly, reason = "requires features no_core, lang_items")] // This is randomly crashing in lld. See https://github.com/rust-lang/rust/issues/115985 #[cfg_attr(all(windows, target_env = "gnu"), ignore = "windows-gnu lld crashing")] fn custom_bin_target() { let p = project() .file( "src/main.rs", &" #![no_main] __MINIMAL_LIB__ " .replace("__MINIMAL_LIB__", MINIMAL_LIB), ) .file("custom-bin-target.json", SIMPLE_SPEC) .build(); p.cargo("build --target custom-bin-target.json -v").run(); } #[cargo_test(nightly, reason = "requires features no_core, lang_items")] fn changing_spec_rebuilds() { // Changing the .json file will trigger a rebuild. let p = project() .file( "src/lib.rs", &" __MINIMAL_LIB__ pub fn foo() -> u32 { 42 } " .replace("__MINIMAL_LIB__", MINIMAL_LIB), ) .file("custom-target.json", SIMPLE_SPEC) .build(); p.cargo("build --lib --target custom-target.json -v").run(); p.cargo("build --lib --target custom-target.json -v") .with_stderr_data(str![[r#" [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let spec_path = p.root().join("custom-target.json"); let spec = fs::read_to_string(&spec_path).unwrap(); // Some arbitrary change that I hope is safe. let spec = spec.replace('{', "{\n\"vendor\": \"unknown\",\n"); fs::write(&spec_path, spec).unwrap(); p.cargo("build --lib --target custom-target.json -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "requires features no_core, lang_items")] // This is randomly crashing in lld. See https://github.com/rust-lang/rust/issues/115985 #[cfg_attr(all(windows, target_env = "gnu"), ignore = "windows-gnu lld crashing")] fn changing_spec_relearns_crate_types() { // Changing the .json file will invalidate the cache of crate types. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [lib] crate-type = ["cdylib"] "#, ) .file("src/lib.rs", MINIMAL_LIB) .file("custom-target.json", SIMPLE_SPEC) .build(); p.cargo("build --lib --target custom-target.json -v") .with_status(101) .with_stderr_data(str![[r#" [ERROR] cannot produce cdylib for `foo v0.1.0 ([ROOT]/foo)` [..] "#]]) .run(); // Enable dynamic linking. let spec_path = p.root().join("custom-target.json"); let spec = fs::read_to_string(&spec_path).unwrap(); let spec = spec.replace('{', "{\n\"dynamic-linking\": true,\n"); fs::write(&spec_path, spec).unwrap(); p.cargo("build --lib --target custom-target.json -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "requires features no_core, lang_items")] fn custom_target_ignores_filepath() { // Changing the path of the .json file will not trigger a rebuild. let p = project() .file( "src/lib.rs", &" __MINIMAL_LIB__ pub fn foo() -> u32 { 42 } " .replace("__MINIMAL_LIB__", MINIMAL_LIB), ) .file("b/custom-target.json", SIMPLE_SPEC) .file("a/custom-target.json", SIMPLE_SPEC) .build(); // Should build the library the first time. p.cargo("build --lib --target a/custom-target.json") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // But not the second time, even though the path to the custom target is dfferent. p.cargo("build --lib --target b/custom-target.json") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } cargo-0.91.0/tests/testsuite/death.rs000064400000000000000000000062551046102023000156440ustar 00000000000000//! Tests for ctrl-C handling. use std::fs; use std::io::{self, Read}; use std::net::TcpListener; use std::process::{Child, Stdio}; use std::thread; use crate::prelude::*; use cargo_test_support::{project, slow_cpu_multiplier}; #[cargo_test] fn ctrl_c_kills_everyone() { let listener = TcpListener::bind("127.0.0.1:0").unwrap(); let addr = listener.local_addr().unwrap(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", &format!( r#" use std::net::TcpStream; use std::io::Read; fn main() {{ let mut socket = TcpStream::connect("{}").unwrap(); let _ = socket.read(&mut [0; 10]); panic!("that read should never return"); }} "#, addr ), ) .build(); let mut cargo = p.cargo("check").build_command(); cargo .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .env("__CARGO_TEST_SETSID_PLEASE_DONT_USE_ELSEWHERE", "1"); let mut child = cargo.spawn().unwrap(); let mut sock = listener.accept().unwrap().0; ctrl_c(&mut child); assert!(!child.wait().unwrap().success()); match sock.read(&mut [0; 10]) { Ok(n) => assert_eq!(n, 0), Err(e) => assert_eq!(e.kind(), io::ErrorKind::ConnectionReset), } // Ok so what we just did was spawn cargo that spawned a build script, then // we killed cargo in hopes of it killing the build script as well. If all // went well the build script is now dead. On Windows, however, this is // enforced with job objects which means that it may actually be in the // *process* of being torn down at this point. // // Now on Windows we can't completely remove a file until all handles to it // have been closed. Including those that represent running processes. So if // we were to return here then there may still be an open reference to some // file in the build directory. What we want to actually do is wait for the // build script to *complete* exit. Take care of that by blowing away the // build directory here, and panicking if we eventually spin too long // without being able to. for i in 0..10 { match fs::remove_dir_all(&p.root().join("target")) { Ok(()) => return, Err(e) => println!("attempt {}: {}", i, e), } thread::sleep(slow_cpu_multiplier(100)); } panic!( "couldn't remove build directory after a few tries, seems like \ we won't be able to!" ); } #[cfg(unix)] pub fn ctrl_c(child: &mut Child) { let r = unsafe { libc::kill(-(child.id() as i32), libc::SIGINT) }; if r < 0 { panic!("failed to kill: {}", io::Error::last_os_error()); } } #[cfg(windows)] pub fn ctrl_c(child: &mut Child) { child.kill().unwrap(); } cargo-0.91.0/tests/testsuite/dep_info.rs000064400000000000000000000344421046102023000163410ustar 00000000000000//! Tests for dep-info files. This includes the dep-info file Cargo creates in //! the output directory, and the ones stored in the fingerprint. use std::path::Path; use crate::prelude::*; use cargo_test_support::compare::assert_e2e; use cargo_test_support::paths; use cargo_test_support::registry::Package; use cargo_test_support::str; use cargo_test_support::{assert_deps, assert_deps_contains}; use cargo_test_support::{basic_bin_manifest, basic_manifest, main_file, project, rustc_host}; use filetime::FileTime; #[cargo_test] fn build_dep_info() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("build").run(); let depinfo_bin_path = &p.bin("foo").with_extension("d"); assert!(depinfo_bin_path.is_file()); let depinfo = p.read_file(depinfo_bin_path); let bin_path = p.bin("foo"); let src_path = p.root().join("src").join("foo.rs"); if !depinfo.lines().any(|line| { line.starts_with(&format!("{}:", bin_path.display())) && line.contains(src_path.to_str().unwrap()) }) { panic!( "Could not find {:?}: {:?} in {:?}", bin_path, src_path, depinfo_bin_path ); } } #[cargo_test] fn build_dep_info_lib() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [[example]] name = "ex" crate-type = ["lib"] "#, ) .file("build.rs", "fn main() {}") .file("src/lib.rs", "") .file("examples/ex.rs", "") .build(); p.cargo("build --example=ex").run(); assert!(p.example_lib("ex", "lib").with_extension("d").is_file()); } #[cargo_test] fn build_dep_info_rlib() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [[example]] name = "ex" crate-type = ["rlib"] "#, ) .file("src/lib.rs", "") .file("examples/ex.rs", "") .build(); p.cargo("build --example=ex").run(); assert!(p.example_lib("ex", "rlib").with_extension("d").is_file()); } #[cargo_test] fn build_dep_info_dylib() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [[example]] name = "ex" crate-type = ["dylib"] "#, ) .file("src/lib.rs", "") .file("examples/ex.rs", "") .build(); p.cargo("build --example=ex").run(); assert!(p.example_lib("ex", "dylib").with_extension("d").is_file()); } #[cargo_test] fn dep_path_inside_target_has_correct_path() { let p = project() .file("Cargo.toml", &basic_bin_manifest("a")) .file("target/debug/blah", "") .file( "src/main.rs", r#" fn main() { let x = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/target/debug/blah")); } "#, ) .build(); p.cargo("build").run(); let depinfo_path = &p.bin("a").with_extension("d"); assert!(depinfo_path.is_file(), "{:?}", depinfo_path); let depinfo = p.read_file(depinfo_path); let bin_path = p.bin("a"); let target_debug_blah = Path::new("target").join("debug").join("blah"); if !depinfo.lines().any(|line| { line.starts_with(&format!("{}:", bin_path.display())) && line.contains(target_debug_blah.to_str().unwrap()) }) { panic!( "Could not find {:?}: {:?} in {:?}", bin_path, target_debug_blah, depinfo_path ); } } #[cargo_test] fn no_rewrite_if_no_change() { let p = project().file("src/lib.rs", "").build(); p.cargo("build").run(); let dep_info = p.root().join("target/debug/libfoo.d"); let metadata1 = dep_info.metadata().unwrap(); p.cargo("build").run(); let metadata2 = dep_info.metadata().unwrap(); assert_eq!( FileTime::from_last_modification_time(&metadata1), FileTime::from_last_modification_time(&metadata2), ); } #[cargo_test(nightly, reason = "-Z binary-dep-depinfo is unstable")] fn relative_depinfo_paths_ws() { // Test relative dep-info paths in a workspace with --target with // proc-macros and other dependency kinds. Package::new("regdep", "0.1.0") .file("src/lib.rs", "pub fn f() {}") .publish(); Package::new("pmdep", "0.1.0") .file("src/lib.rs", "pub fn f() {}") .publish(); Package::new("bdep", "0.1.0") .file("src/lib.rs", "pub fn f() {}") .publish(); let p = project() /*********** Workspace ***********/ .file( "Cargo.toml", r#" [workspace] members = ["foo"] "#, ) /*********** Main Project ***********/ .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [dependencies] pm = {path = "../pm"} bar = {path = "../bar"} regdep = "0.1" [build-dependencies] bdep = "0.1" bar = {path = "../bar"} "#, ) .file( "foo/src/main.rs", r#" pm::noop!{} fn main() { bar::f(); regdep::f(); } "#, ) .file("foo/build.rs", "fn main() { bdep::f(); }") /*********** Proc Macro ***********/ .file( "pm/Cargo.toml", r#" [package] name = "pm" version = "0.1.0" edition = "2018" [lib] proc-macro = true [dependencies] pmdep = "0.1" "#, ) .file( "pm/src/lib.rs", r#" extern crate proc_macro; use proc_macro::TokenStream; #[proc_macro] pub fn noop(_item: TokenStream) -> TokenStream { pmdep::f(); "".parse().unwrap() } "#, ) /*********** Path Dependency `bar` ***********/ .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn f() {}") .build(); let host = rustc_host(); p.cargo("build -Z binary-dep-depinfo --target") .arg(&host) .masquerade_as_nightly_cargo(&["binary-dep-depinfo"]) .with_stderr_data(str![[r#" ... [COMPILING] foo v0.1.0 ([ROOT]/foo/foo) ... "#]]) .run(); assert_deps_contains( &p, "target/debug/.fingerprint/pm-*/dep-lib-pm", &[(0, "src/lib.rs"), (1, "debug/deps/libpmdep-*.rlib")], ); assert_deps_contains( &p, &format!("target/{}/debug/.fingerprint/foo-*/dep-bin-foo", host), &[ (0, "src/main.rs"), ( 1, &format!( "debug/deps/{}pm-*.{}", paths::get_lib_prefix("proc-macro"), paths::get_lib_extension("proc-macro") ), ), (1, &format!("{}/debug/deps/libbar-*.rlib", host)), (1, &format!("{}/debug/deps/libregdep-*.rlib", host)), ], ); assert_deps_contains( &p, "target/debug/.fingerprint/foo-*/dep-build-script-build-script-build", &[(0, "build.rs"), (1, "debug/deps/libbdep-*.rlib")], ); // Make sure it stays fresh. p.cargo("build -Z binary-dep-depinfo --target") .arg(&host) .masquerade_as_nightly_cargo(&["binary-dep-depinfo"]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "-Z binary-dep-depinfo is unstable")] fn relative_depinfo_paths_no_ws() { // Test relative dep-info paths without a workspace with proc-macros and // other dependency kinds. Package::new("regdep", "0.1.0") .file("src/lib.rs", "pub fn f() {}") .publish(); Package::new("pmdep", "0.1.0") .file("src/lib.rs", "pub fn f() {}") .publish(); Package::new("bdep", "0.1.0") .file("src/lib.rs", "pub fn f() {}") .publish(); let p = project() /*********** Main Project ***********/ .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [dependencies] pm = {path = "pm"} bar = {path = "bar"} regdep = "0.1" [build-dependencies] bdep = "0.1" bar = {path = "bar"} "#, ) .file( "src/main.rs", r#" pm::noop!{} fn main() { bar::f(); regdep::f(); } "#, ) .file("build.rs", "fn main() { bdep::f(); }") /*********** Proc Macro ***********/ .file( "pm/Cargo.toml", r#" [package] name = "pm" version = "0.1.0" edition = "2018" [lib] proc-macro = true [dependencies] pmdep = "0.1" "#, ) .file( "pm/src/lib.rs", r#" extern crate proc_macro; use proc_macro::TokenStream; #[proc_macro] pub fn noop(_item: TokenStream) -> TokenStream { pmdep::f(); "".parse().unwrap() } "#, ) /*********** Path Dependency `bar` ***********/ .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn f() {}") .build(); p.cargo("build -Z binary-dep-depinfo") .masquerade_as_nightly_cargo(&["binary-dep-depinfo"]) .with_stderr_data(str![[r#" ... [COMPILING] foo v0.1.0 ([ROOT]/foo) ... "#]]) .run(); assert_deps_contains( &p, "target/debug/.fingerprint/pm-*/dep-lib-pm", &[(0, "src/lib.rs"), (1, "debug/deps/libpmdep-*.rlib")], ); assert_deps_contains( &p, "target/debug/.fingerprint/foo-*/dep-bin-foo", &[ (0, "src/main.rs"), ( 1, &format!( "debug/deps/{}pm-*.{}", paths::get_lib_prefix("proc-macro"), paths::get_lib_extension("proc-macro") ), ), (1, "debug/deps/libbar-*.rlib"), (1, "debug/deps/libregdep-*.rlib"), ], ); assert_deps_contains( &p, "target/debug/.fingerprint/foo-*/dep-build-script-build-script-build", &[(0, "build.rs"), (1, "debug/deps/libbdep-*.rlib")], ); // Make sure it stays fresh. p.cargo("build -Z binary-dep-depinfo") .masquerade_as_nightly_cargo(&["binary-dep-depinfo"]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn reg_dep_source_not_tracked() { // Make sure source files in dep-info file are not tracked for registry dependencies. Package::new("regdep", "0.1.0") .file("src/lib.rs", "pub fn f() {}") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] regdep = "0.1" "#, ) .file("src/lib.rs", "pub fn f() { regdep::f(); }") .build(); p.cargo("check").run(); assert_deps( &p, "target/debug/.fingerprint/regdep-*/dep-lib-regdep", |info_path, entries| { for (kind, path) in entries { if *kind == 1 { panic!( "Did not expect package root relative path type: {:?} in {:?}", path, info_path ); } } }, ); } #[cargo_test(nightly, reason = "-Z binary-dep-depinfo is unstable")] fn canonical_path() { if !cargo_test_support::symlink_supported() { return; } Package::new("regdep", "0.1.0") .file("src/lib.rs", "pub fn f() {}") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] regdep = "0.1" "#, ) .file("src/lib.rs", "pub fn f() { regdep::f(); }") .build(); let real = p.root().join("real_target"); real.mkdir_p(); p.symlink(real, "target"); p.cargo("check -Z binary-dep-depinfo") .masquerade_as_nightly_cargo(&["binary-dep-depinfo"]) .run(); assert_deps_contains( &p, "target/debug/.fingerprint/foo-*/dep-lib-foo", &[(0, "src/lib.rs"), (1, "debug/deps/libregdep-*.rmeta")], ); } #[cargo_test] fn non_local_build_script() { // Non-local build script information is not included. Package::new("bar", "1.0.0") .file( "build.rs", r#" fn main() { println!("cargo::rerun-if-changed=build.rs"); } "#, ) .file("src/lib.rs", "") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] bar = "1.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build").run(); let contents = p.read_file("target/debug/foo.d"); assert_e2e().eq( &contents, str![[r#" [ROOT]/foo/target/debug/foo[EXE]: [ROOT]/foo/src/main.rs "#]], ); } cargo-0.91.0/tests/testsuite/diagnostics.rs000064400000000000000000000010551046102023000170570ustar 00000000000000use crate::prelude::*; use cargo_test_support::project; use cargo_test_support::str; #[cargo_test] fn dont_panic_on_render() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2021" [[bench.foo]] "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid type: map, expected a sequence --> Cargo.toml:6:3 | 6 | [[bench.foo]] | ^^^^^ | "#]]) .run(); } cargo-0.91.0/tests/testsuite/direct_minimal_versions.rs000064400000000000000000000150451046102023000214640ustar 00000000000000//! Tests for minimal-version resolution. //! //! Note: Some tests are located in the resolver-tests package. use crate::prelude::*; use cargo_test_support::project; use cargo_test_support::registry::Package; use cargo_test_support::str; #[cargo_test] fn simple() { Package::new("dep", "1.0.0").publish(); Package::new("dep", "1.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.1" [dependencies] dep = "1.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("generate-lockfile -Zdirect-minimal-versions") .masquerade_as_nightly_cargo(&["direct-minimal-versions"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package [ADDING] dep v1.0.0 (available: v1.1.0) "#]]) .run(); let lock = p.read_lockfile(); assert!( lock.contains("1.0.0"), "dep minimal version must be present" ); assert!( !lock.contains("1.1.0"), "dep maximimal version cannot be present" ); } #[cargo_test] fn mixed_dependencies() { Package::new("dep", "1.0.0").publish(); Package::new("dep", "1.1.0").publish(); Package::new("dep", "1.2.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.1" [dependencies] dep = "1.0" [dev-dependencies] dep = "1.1" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("generate-lockfile -Zdirect-minimal-versions") .masquerade_as_nightly_cargo(&["direct-minimal-versions"]) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for `dep`. ... required by package `foo v0.0.1 ([ROOT]/foo)` versions that meet the requirements `^1.1` are: 1.1.0 all possible versions conflict with previously selected packages. previously selected package `dep v1.0.0` ... which satisfies dependency `dep = "^1.0"` of package `foo v0.0.1 ([ROOT]/foo)` failed to select a version for `dep` which could resolve this conflict "#]]) .run(); } #[cargo_test] fn yanked() { Package::new("dep", "1.0.0").yanked(true).publish(); Package::new("dep", "1.1.0").publish(); Package::new("dep", "1.2.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.1" [dependencies] dep = "1.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("generate-lockfile -Zdirect-minimal-versions") .masquerade_as_nightly_cargo(&["direct-minimal-versions"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package [ADDING] dep v1.1.0 (available: v1.2.0) "#]]) .run(); let lock = p.read_lockfile(); assert!( lock.contains("1.1.0"), "dep minimal version must be present" ); assert!( !lock.contains("1.0.0"), "yanked minimal version must be skipped" ); assert!( !lock.contains("1.2.0"), "dep maximimal version cannot be present" ); } #[cargo_test] fn indirect() { Package::new("indirect", "2.0.0").publish(); Package::new("indirect", "2.1.0").publish(); Package::new("indirect", "2.2.0").publish(); Package::new("direct", "1.0.0") .dep("indirect", "2.1") .publish(); Package::new("direct", "1.1.0") .dep("indirect", "2.1") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.1" [dependencies] direct = "1.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("generate-lockfile -Zdirect-minimal-versions") .masquerade_as_nightly_cargo(&["direct-minimal-versions"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages [ADDING] direct v1.0.0 (available: v1.1.0) "#]]) .run(); let lock = p.read_lockfile(); assert!( lock.contains("1.0.0"), "direct minimal version must be present" ); assert!( !lock.contains("1.1.0"), "direct maximimal version cannot be present" ); assert!( !lock.contains("2.0.0"), "indirect minimal version cannot be present" ); assert!( !lock.contains("2.1.0"), "indirect minimal version cannot be present" ); assert!( lock.contains("2.2.0"), "indirect maximal version must be present" ); } #[cargo_test] fn indirect_conflict() { Package::new("indirect", "2.0.0").publish(); Package::new("indirect", "2.1.0").publish(); Package::new("indirect", "2.2.0").publish(); Package::new("direct", "1.0.0") .dep("indirect", "2.1") .publish(); Package::new("direct", "1.1.0") .dep("indirect", "2.1") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.1" [dependencies] direct = "1.0" indirect = "2.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("generate-lockfile -Zdirect-minimal-versions") .masquerade_as_nightly_cargo(&["direct-minimal-versions"]) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for `indirect`. ... required by package `direct v1.0.0` ... which satisfies dependency `direct = "^1.0"` of package `foo v0.0.1 ([ROOT]/foo)` versions that meet the requirements `^2.1` are: 2.2.0, 2.1.0 all possible versions conflict with previously selected packages. previously selected package `indirect v2.0.0` ... which satisfies dependency `indirect = "^2.0"` of package `foo v0.0.1 ([ROOT]/foo)` failed to select a version for `indirect` which could resolve this conflict "#]]) .run(); } cargo-0.91.0/tests/testsuite/directory.rs000064400000000000000000000520661046102023000165640ustar 00000000000000//! Tests for directory sources. use std::collections::HashMap; use std::fs; use std::str; use crate::prelude::*; use crate::utils::cargo_process; use cargo_test_support::git; use cargo_test_support::paths; use cargo_test_support::registry::{Package, cksum}; use cargo_test_support::str; use cargo_test_support::{ProjectBuilder, basic_manifest, project, t}; use serde::Serialize; fn setup() { let root = paths::root(); t!(fs::create_dir(&root.join(".cargo"))); t!(fs::write( root.join(".cargo/config.toml"), r#" [source.crates-io] replace-with = 'my-awesome-local-registry' [source.my-awesome-local-registry] directory = 'index' "# )); } struct VendorPackage { p: Option, cksum: Checksum, } #[derive(Serialize)] struct Checksum { package: Option, files: HashMap, } impl VendorPackage { fn new(name: &str) -> VendorPackage { VendorPackage { p: Some(project().at(&format!("index/{}", name))), cksum: Checksum { package: Some(String::new()), files: HashMap::new(), }, } } fn file(&mut self, name: &str, contents: &str) -> &mut VendorPackage { self.p = Some(self.p.take().unwrap().file(name, contents)); self.cksum .files .insert(name.to_string(), cksum(contents.as_bytes())); self } fn disable_checksum(&mut self) -> &mut VendorPackage { self.cksum.package = None; self } fn no_manifest(mut self) -> Self { self.p = self.p.map(|pb| pb.no_manifest()); self } fn build(&mut self) { let p = self.p.take().unwrap(); let json = serde_json::to_string(&self.cksum).unwrap(); let p = p.file(".cargo-checksum.json", &json); let _ = p.build(); } } #[cargo_test] fn simple() { setup(); VendorPackage::new("bar") .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "pub fn bar() {}") .build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file( "src/lib.rs", "extern crate bar; pub fn foo() { bar::bar(); }", ) .build(); p.cargo("check") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.1.0 [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn simple_install() { setup(); VendorPackage::new("foo") .file("src/lib.rs", "pub fn foo() {}") .build(); VendorPackage::new("bar") .file( "Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] [dependencies] foo = "0.0.1" "#, ) .file( "src/main.rs", "extern crate foo; pub fn main() { foo::foo(); }", ) .build(); cargo_process("install bar") .with_stderr_data(str![[r#" [INSTALLING] bar v0.1.0 [LOCKING] 1 package to latest compatible version [COMPILING] foo v0.0.1 [COMPILING] bar v0.1.0 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [..]bar[..] [INSTALLED] package `bar v0.1.0` (executable `bar[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); } #[cargo_test] fn simple_install_fail() { setup(); VendorPackage::new("foo") .file("src/lib.rs", "pub fn foo() {}") .build(); VendorPackage::new("bar") .file( "Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] [dependencies] foo = "0.1.0" baz = "9.8.7" "#, ) .file( "src/main.rs", "extern crate foo; pub fn main() { foo::foo(); }", ) .build(); cargo_process("install bar") .with_status(101) .with_stderr_data(str![[r#" [INSTALLING] bar v0.1.0 [ERROR] failed to compile `bar v0.1.0`, intermediate artifacts can be found at `[..]`. To reuse those artifacts with a future compilation, set the environment variable `CARGO_TARGET_DIR` to that path. Caused by: no matching package found searched package name: `baz` perhaps you meant: bar or foo location searched: directory source `[ROOT]/index` (which is replacing registry `crates-io`) required by package `bar v0.1.0` "#]]) .run(); } #[cargo_test] fn install_without_feature_dep() { setup(); VendorPackage::new("foo") .file("src/lib.rs", "pub fn foo() {}") .build(); VendorPackage::new("bar") .file( "Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] [dependencies] foo = "0.0.1" baz = { version = "9.8.7", optional = true } [features] wantbaz = ["baz"] "#, ) .file( "src/main.rs", "extern crate foo; pub fn main() { foo::foo(); }", ) .build(); cargo_process("install bar") .with_stderr_data(str![[r#" [INSTALLING] bar v0.1.0 [LOCKING] 1 package to latest compatible version [COMPILING] foo v0.0.1 [COMPILING] bar v0.1.0 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [..]bar[..] [INSTALLED] package `bar v0.1.0` (executable `bar[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); } #[cargo_test] fn not_there() { setup(); let _ = project().at("index").build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file( "src/lib.rs", "extern crate bar; pub fn foo() { bar::bar(); }", ) .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no matching package named `bar` found location searched: directory source `[ROOT]/index` (which is replacing registry `crates-io`) required by package `foo v0.1.0 ([ROOT]/foo)` "#]]) .run(); } #[cargo_test] fn multiple() { setup(); VendorPackage::new("bar-0.1.0") .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "pub fn bar() {}") .file(".cargo-checksum", "") .build(); VendorPackage::new("bar-0.2.0") .file("Cargo.toml", &basic_manifest("bar", "0.2.0")) .file("src/lib.rs", "pub fn bar() {}") .file(".cargo-checksum", "") .build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file( "src/lib.rs", "extern crate bar; pub fn foo() { bar::bar(); }", ) .build(); p.cargo("check") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [ADDING] bar v0.1.0 (available: v0.2.0) [CHECKING] bar v0.1.0 [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn crates_io_then_directory() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file( "src/lib.rs", "extern crate bar; pub fn foo() { bar::bar(); }", ) .build(); let cksum = Package::new("bar", "0.1.0") .file("src/lib.rs", "pub fn bar() -> u32 { 0 }") .publish(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.1.0 (registry `dummy-registry`) [CHECKING] bar v0.1.0 [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); setup(); let mut v = VendorPackage::new("bar"); v.file("Cargo.toml", &basic_manifest("bar", "0.1.0")); v.file("src/lib.rs", "pub fn bar() -> u32 { 1 }"); v.cksum.package = Some(cksum); v.build(); p.cargo("check") .with_stderr_data(str![[r#" [CHECKING] bar v0.1.0 [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn crates_io_then_bad_checksum() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .build(); Package::new("bar", "0.1.0").publish(); p.cargo("check").run(); setup(); VendorPackage::new("bar") .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] checksum for `bar v0.1.0` changed between lock files this could be indicative of a few possible errors: * the lock file is corrupt * a replacement source in use (e.g., a mirror) returned a different checksum * the source itself may be corrupt in one way or another unable to verify that `bar v0.1.0` is the same as when the lockfile was generated "#]]) .run(); } #[cargo_test] fn bad_file_checksum() { setup(); VendorPackage::new("bar") .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "") .build(); t!(fs::write( paths::root().join("index/bar/src/lib.rs"), "fn bar() -> u32 { 0 }" )); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [ERROR] the listed checksum of `[ROOT]/index/bar/src/lib.rs` has changed: expected: [..] actual: [..] directory sources are not intended to be edited, if modifications are required then it is recommended that `[patch]` is used with a forked copy of the source "#]]) .run(); } #[cargo_test] fn only_dot_files_ok() { setup(); VendorPackage::new("bar") .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "") .build(); VendorPackage::new("foo") .no_manifest() .file(".bar", "") .build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check").run(); } #[cargo_test] fn random_files_ok() { setup(); VendorPackage::new("bar") .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "") .build(); VendorPackage::new("foo") .no_manifest() .file("bar", "") .file("../test", "") .build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check").run(); } #[cargo_test] fn git_lock_file_doesnt_change() { let git = git::new("git", |p| { p.file("Cargo.toml", &basic_manifest("git", "0.5.0")) .file("src/lib.rs", "") }); VendorPackage::new("git") .file("Cargo.toml", &basic_manifest("git", "0.5.0")) .file("src/lib.rs", "") .disable_checksum() .build(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] git = {{ git = '{0}' }} "#, git.url() ), ) .file("src/lib.rs", "") .build(); p.cargo("check").run(); let lock1 = p.read_lockfile(); let root = paths::root(); t!(fs::create_dir(&root.join(".cargo"))); t!(fs::write( root.join(".cargo/config.toml"), format!( r#" [source.my-git-repo] git = '{}' replace-with = 'my-awesome-local-registry' [source.my-awesome-local-registry] directory = 'index' "#, git.url() ) )); p.cargo("check") .with_stderr_data(str![[r#" [CHECKING] git v0.5.0 ([..]) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let lock2 = p.read_lockfile(); assert_eq!(lock1, lock2, "lock files changed"); } #[cargo_test] fn git_override_requires_lockfile() { VendorPackage::new("git") .file("Cargo.toml", &basic_manifest("git", "0.5.0")) .file("src/lib.rs", "") .disable_checksum() .build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] git = { git = 'https://example.com/' } "#, ) .file("src/lib.rs", "") .build(); let root = paths::root(); t!(fs::create_dir(&root.join(".cargo"))); t!(fs::write( root.join(".cargo/config.toml"), r#" [source.my-git-repo] git = 'https://example.com/' replace-with = 'my-awesome-local-registry' [source.my-awesome-local-registry] directory = 'index' "# )); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to get `git` as a dependency of package `foo v0.0.1 ([ROOT]/foo)` Caused by: failed to load source for dependency `git` Caused by: Unable to update [..] Caused by: the source my-git-repo requires a lock file to be present first before it can be used against vendored source code remove the source replacement configuration, generate a lock file, and then restore the source replacement configuration to continue the build "#]]) .run(); } #[cargo_test] fn workspace_different_locations() { let p = project() .no_manifest() .file( "foo/Cargo.toml", r#" [package] name = 'foo' version = '0.1.0' edition = "2015" [dependencies] baz = "*" "#, ) .file("foo/src/lib.rs", "") .file("foo/vendor/baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("foo/vendor/baz/src/lib.rs", "") .file("foo/vendor/baz/.cargo-checksum.json", "{\"files\":{}}") .file( "bar/Cargo.toml", r#" [package] name = 'bar' version = '0.1.0' edition = "2015" [dependencies] baz = "*" "#, ) .file("bar/src/lib.rs", "") .file( ".cargo/config.toml", r#" [build] target-dir = './target' [source.crates-io] replace-with = 'my-awesome-local-registry' [source.my-awesome-local-registry] directory = 'foo/vendor' "#, ) .build(); p.cargo("check").cwd("foo").run(); p.cargo("check") .cwd("bar") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn version_missing() { setup(); VendorPackage::new("foo") .file("src/lib.rs", "pub fn foo() {}") .build(); VendorPackage::new("bar") .file( "Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] [dependencies] foo = "2" "#, ) .file("src/main.rs", "fn main() {}") .build(); cargo_process("install bar") .with_stderr_data(str![[r#" [INSTALLING] bar v0.1.0 [ERROR] failed to compile [..], intermediate artifacts can be found at `[..]`. To reuse those artifacts with a future compilation, set the environment variable `CARGO_TARGET_DIR` to that path. Caused by: failed to select a version for the requirement `foo = "^2"` candidate versions found which didn't match: 0.0.1 location searched: directory source `[..] (which is replacing registry `[..]`) required by package `bar v0.1.0` perhaps a crate was updated and forgotten to be re-vendored? "#]]) .with_status(101) .run(); } #[cargo_test] fn root_dir_diagnostics() { let p = ProjectBuilder::new(paths::root()) .no_manifest() // we are placing it in a different dir .file( "ws_root/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] "#, ) .file("ws_root/src/lib.rs", "invalid;") .build(); // Crucially, the rustc error message below says `ws_root/...`, i.e. // it is relative to our fake home, not to the workspace root. p.cargo("check") .arg("-Zroot-dir=.") .arg("--manifest-path=ws_root/Cargo.toml") .masquerade_as_nightly_cargo(&["-Zroot-dir"]) .with_status(101) .with_stderr_data(str![[r#" [CHECKING] foo v0.1.0 ([ROOT]/ws_root) [ERROR] [..] --> ws_root/src/lib.rs:1:8 | 1 | invalid; | [..] [ERROR] could not compile `foo` (lib) due to 1 previous error "#]]) .run(); } #[cargo_test] fn root_dir_file_macro() { let p = ProjectBuilder::new(paths::root()) .no_manifest() // we are placing it in a different dir .file( "ws_root/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] "#, ) .file( "ws_root/src/main.rs", r#"fn main() { println!("{}", file!()); }"#, ) .build(); // Crucially, the path is relative to our fake home, not to the workspace root. p.cargo("run") .arg("-Zroot-dir=.") .arg("--manifest-path=ws_root/Cargo.toml") .masquerade_as_nightly_cargo(&["-Zroot-dir"]) .with_stdout_data(str![[r#" ws_root/src/main.rs "#]]) .run(); // Try again with an absolute path for `root-dir`. p.cargo("run") .arg(format!("-Zroot-dir={}", p.root().display())) .arg("--manifest-path=ws_root/Cargo.toml") .masquerade_as_nightly_cargo(&["-Zroot-dir"]) .with_stdout_data(str![[r#" ws_root/src/main.rs "#]]) .run(); } cargo-0.91.0/tests/testsuite/doc.rs000064400000000000000000002545451046102023000153330ustar 00000000000000//! Tests for the `cargo doc` command. use std::fs; use std::str; use crate::prelude::*; use crate::utils::tools; use cargo::core::compiler::RustDocFingerprint; use cargo_test_support::registry::Package; use cargo_test_support::str; use cargo_test_support::{basic_lib_manifest, basic_manifest, git, project}; use cargo_test_support::{rustc_host, symlink_supported}; #[cargo_test] fn simple() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" "#, ) .file("build.rs", "fn main() {}") .file("src/lib.rs", "pub fn foo() {}") .build(); p.cargo("doc") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); assert!(p.root().join("target/doc").is_dir()); assert!(p.root().join("target/doc/foo/index.html").is_file()); } #[cargo_test] fn doc_no_libs() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [[bin]] name = "foo" doc = false "#, ) .file("src/main.rs", "bad code") .build(); p.cargo("doc").run(); } #[cargo_test] fn doc_twice() { let p = project().file("src/lib.rs", "pub fn foo() {}").build(); p.cargo("doc") .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); p.cargo("doc") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); } #[cargo_test] fn doc_deps() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "bar" "#, ) .file("src/lib.rs", "extern crate bar; pub fn foo() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "pub fn bar() {}") .build(); p.cargo("doc") .with_stderr_data( str![[r#" [LOCKING] 1 package to latest compatible version [DOCUMENTING] bar v0.0.1 ([ROOT]/foo/bar) [CHECKING] bar v0.0.1 ([ROOT]/foo/bar) [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]] .unordered(), ) .run(); assert!(p.root().join("target/doc").is_dir()); assert!(p.root().join("target/doc/foo/index.html").is_file()); assert!(p.root().join("target/doc/bar/index.html").is_file()); // Verify that it only emits rmeta for the dependency. assert_eq!(p.glob("target/debug/**/*.rlib").count(), 0); assert_eq!(p.glob("target/debug/deps/libbar-*.rmeta").count(), 1); // Make sure it doesn't recompile. p.cargo("doc") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); assert!(p.root().join("target/doc").is_dir()); assert!(p.root().join("target/doc/foo/index.html").is_file()); assert!(p.root().join("target/doc/bar/index.html").is_file()); } #[cargo_test] fn doc_no_deps() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "bar" "#, ) .file("src/lib.rs", "extern crate bar; pub fn foo() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "pub fn bar() {}") .build(); p.cargo("doc --no-deps") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.0.1 ([ROOT]/foo/bar) [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); assert!(p.root().join("target/doc").is_dir()); assert!(p.root().join("target/doc/foo/index.html").is_file()); assert!(!p.root().join("target/doc/bar/index.html").is_file()); } #[cargo_test] fn doc_only_bin() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "bar" "#, ) .file("src/main.rs", "extern crate bar; pub fn foo() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "pub fn bar() {}") .build(); p.cargo("doc -v").run(); assert!(p.root().join("target/doc").is_dir()); assert!(p.root().join("target/doc/bar/index.html").is_file()); assert!(p.root().join("target/doc/foo/index.html").is_file()); } #[cargo_test] fn doc_multiple_targets_same_name_lib() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo", "bar"] "#, ) .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [lib] name = "foo_lib" "#, ) .file("foo/src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" [lib] name = "foo_lib" "#, ) .file("bar/src/lib.rs", "") .build(); p.cargo("doc --workspace") .with_status(101) .with_stderr_data(str![[r#" [ERROR] document output filename collision The lib `foo_lib` in package `foo v0.1.0 ([ROOT]/foo/foo)` has the same name as the lib `foo_lib` in package `bar v0.1.0 ([ROOT]/foo/bar)`. Only one may be documented at once since they output to the same path. Consider documenting only one, renaming one, or marking one with `doc = false` in Cargo.toml. "#]]) .run(); } #[cargo_test] fn doc_multiple_targets_same_name() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo", "bar"] "#, ) .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [[bin]] name = "foo_lib" path = "src/foo_lib.rs" "#, ) .file("foo/src/foo_lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" [lib] name = "foo_lib" "#, ) .file("bar/src/lib.rs", "") .build(); p.cargo("doc --workspace") .with_stderr_data(str![[r#" [WARNING] output filename collision. The bin target `foo_lib` in package `foo v0.1.0 ([ROOT]/foo/foo)` has the same output filename as the lib target `foo_lib` in package `bar v0.1.0 ([ROOT]/foo/bar)`. Colliding filename is: [ROOT]/foo/target/doc/foo_lib/index.html The targets should have unique names. This is a known bug where multiple crates with the same name use the same path; see . [DOCUMENTING] bar v0.1.0 ([ROOT]/foo/bar) [DOCUMENTING] foo v0.1.0 ([ROOT]/foo/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo_lib/index.html and 1 other file "#]].unordered()) .run(); } #[cargo_test] fn doc_multiple_targets_same_name_bin() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo", "bar"] "#, ) .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" "#, ) .file("foo/src/bin/foo-cli.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" "#, ) .file("bar/src/bin/foo-cli.rs", "") .build(); p.cargo("doc --workspace") .with_status(101) .with_stderr_data(str![[r#" [ERROR] document output filename collision The bin `foo-cli` in package `foo v0.1.0 ([ROOT]/foo/foo)` has the same name as the bin `foo-cli` in package `bar v0.1.0 ([ROOT]/foo/bar)`. Only one may be documented at once since they output to the same path. Consider documenting only one, renaming one, or marking one with `doc = false` in Cargo.toml. "#]]) .run(); } #[cargo_test] fn doc_multiple_targets_same_name_undoced() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo", "bar"] "#, ) .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [[bin]] name = "foo-cli" "#, ) .file("foo/src/foo-cli.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" [[bin]] name = "foo-cli" doc = false "#, ) .file("bar/src/foo-cli.rs", "") .build(); p.cargo("doc --workspace").run(); } #[cargo_test] fn doc_lib_bin_same_name_documents_lib() { let p = project() .file( "src/main.rs", r#" //! Binary documentation extern crate foo; fn main() { foo::foo(); } "#, ) .file( "src/lib.rs", r#" //! Library documentation pub fn foo() {} "#, ) .build(); p.cargo("doc") .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); let doc_html = p.read_file("target/doc/foo/index.html"); assert!(doc_html.contains("Library")); assert!(!doc_html.contains("Binary")); } #[cargo_test] fn doc_lib_bin_same_name_documents_lib_when_requested() { let p = project() .file( "src/main.rs", r#" //! Binary documentation extern crate foo; fn main() { foo::foo(); } "#, ) .file( "src/lib.rs", r#" //! Library documentation pub fn foo() {} "#, ) .build(); p.cargo("doc --lib") .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); let doc_html = p.read_file("target/doc/foo/index.html"); assert!(doc_html.contains("Library")); assert!(!doc_html.contains("Binary")); } #[cargo_test] fn doc_lib_bin_same_name_with_dash() { // Checks `doc` behavior when there is a dash in the package name, and // there is a lib and bin, and the lib name is inferred. let p = project() .file("Cargo.toml", &basic_manifest("foo-bar", "1.0.0")) .file("src/lib.rs", "") .file("src/main.rs", "fn main() {}") .build(); p.cargo("doc") .with_stderr_data(str![[r#" [DOCUMENTING] foo-bar v1.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo_bar/index.html "#]]) .run(); assert!(p.build_dir().join("doc/foo_bar/index.html").exists()); assert!(!p.build_dir().join("doc/foo_bar/fn.main.html").exists()); } #[cargo_test] fn doc_lib_bin_same_name_documents_named_bin_when_requested() { let p = project() .file( "src/main.rs", r#" //! Binary documentation extern crate foo; fn main() { foo::foo(); } "#, ) .file( "src/lib.rs", r#" //! Library documentation pub fn foo() {} "#, ) .build(); p.cargo("doc --bin foo") // The checking/documenting lines are sometimes swapped since they run // concurrently. .with_stderr_data(str![[r#" [WARNING] output filename collision. The bin target `foo` in package `foo v0.0.1 ([ROOT]/foo)` has the same output filename as the lib target `foo` in package `foo v0.0.1 ([ROOT]/foo)`. Colliding filename is: [ROOT]/foo/target/doc/foo/index.html The targets should have unique names. This is a known bug where multiple crates with the same name use the same path; see . [CHECKING] foo v0.0.1 ([ROOT]/foo) [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]].unordered()) .run(); let doc_html = p.read_file("target/doc/foo/index.html"); assert!(!doc_html.contains("Library")); assert!(doc_html.contains("Binary")); } #[cargo_test] fn doc_lib_bin_same_name_documents_bins_when_requested() { let p = project() .file( "src/main.rs", r#" //! Binary documentation extern crate foo; fn main() { foo::foo(); } "#, ) .file( "src/lib.rs", r#" //! Library documentation pub fn foo() {} "#, ) .build(); p.cargo("doc --bins") // The checking/documenting lines are sometimes swapped since they run // concurrently. .with_stderr_data(str![[r#" [WARNING] output filename collision. The bin target `foo` in package `foo v0.0.1 ([ROOT]/foo)` has the same output filename as the lib target `foo` in package `foo v0.0.1 ([ROOT]/foo)`. Colliding filename is: [ROOT]/foo/target/doc/foo/index.html The targets should have unique names. This is a known bug where multiple crates with the same name use the same path; see . [CHECKING] foo v0.0.1 ([ROOT]/foo) [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]].unordered()) .run(); let doc_html = p.read_file("target/doc/foo/index.html"); assert!(!doc_html.contains("Library")); assert!(doc_html.contains("Binary")); } #[cargo_test] fn doc_lib_bin_example_same_name_documents_named_example_when_requested() { let p = project() .file( "src/main.rs", r#" //! Binary documentation extern crate foo; fn main() { foo::foo(); } "#, ) .file( "src/lib.rs", r#" //! Library documentation pub fn foo() {} "#, ) .file( "examples/ex1.rs", r#" //! Example1 documentation pub fn x() { f(); } "#, ) .build(); p.cargo("doc --example ex1") // The checking/documenting lines are sometimes swapped since they run // concurrently. .with_stderr_data( str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/ex1/index.html "#]] .unordered(), ) .run(); let doc_html = p.read_file("target/doc/ex1/index.html"); assert!(!doc_html.contains("Library")); assert!(!doc_html.contains("Binary")); assert!(doc_html.contains("Example1")); } #[cargo_test] fn doc_lib_bin_example_same_name_documents_examples_when_requested() { let p = project() .file( "src/main.rs", r#" //! Binary documentation extern crate foo; fn main() { foo::foo(); } "#, ) .file( "src/lib.rs", r#" //! Library documentation pub fn foo() {} "#, ) .file( "examples/ex1.rs", r#" //! Example1 documentation pub fn example1() { f(); } "#, ) .file( "examples/ex2.rs", r#" //! Example2 documentation pub fn example2() { f(); } "#, ) .build(); p.cargo("doc --examples") // The checking/documenting lines are sometimes swapped since they run // concurrently. .with_stderr_data( str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/ex1/index.html and 1 other file "#]] .unordered(), ) .run(); let example_doc_html_1 = p.read_file("target/doc/ex1/index.html"); let example_doc_html_2 = p.read_file("target/doc/ex2/index.html"); assert!(!example_doc_html_1.contains("Library")); assert!(!example_doc_html_1.contains("Binary")); assert!(!example_doc_html_2.contains("Library")); assert!(!example_doc_html_2.contains("Binary")); assert!(example_doc_html_1.contains("Example1")); assert!(example_doc_html_2.contains("Example2")); } #[cargo_test] fn doc_dash_p() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.a] path = "a" "#, ) .file("src/lib.rs", "extern crate a;") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] [dependencies.b] path = "../b" "#, ) .file("a/src/lib.rs", "extern crate b;") .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) .file("b/src/lib.rs", "") .build(); p.cargo("doc -p a") .with_stderr_data( str![[r#" [LOCKING] 2 packages to latest compatible versions [DOCUMENTING] b v0.0.1 ([ROOT]/foo/b) [CHECKING] b v0.0.1 ([ROOT]/foo/b) [DOCUMENTING] a v0.0.1 ([ROOT]/foo/a) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/a/index.html "#]] .unordered(), ) .run(); } #[cargo_test] fn doc_all_exclude() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "pub fn baz() { break_the_build(); }") .build(); p.cargo("doc --workspace --exclude baz") .with_stderr_data(str![[r#" [DOCUMENTING] bar v0.1.0 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/bar/index.html "#]]) .run(); } #[cargo_test] fn doc_all_exclude_glob() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "pub fn baz() { break_the_build(); }") .build(); p.cargo("doc --workspace --exclude '*z'") .with_stderr_data(str![[r#" [DOCUMENTING] bar v0.1.0 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/bar/index.html "#]]) .run(); } #[cargo_test] fn doc_same_name() { let p = project() .file("src/lib.rs", "") .file("src/bin/main.rs", "fn main() {}") .file("examples/main.rs", "fn main() {}") .file("tests/main.rs", "fn main() {}") .build(); p.cargo("doc").run(); } #[cargo_test(nightly, reason = "no_core, lang_items requires nightly")] fn doc_target() { const TARGET: &str = "arm-unknown-linux-gnueabihf"; let p = project() .file( "src/lib.rs", r#" #![allow(internal_features)] #![feature(no_core, lang_items)] #![no_core] #[lang = "pointee_sized"] trait PointeeSized {} #[lang = "meta_sized"] trait MetaSized: PointeeSized {} #[lang = "sized"] trait Sized: MetaSized {} extern { pub static A: u32; } "#, ) .build(); p.cargo("doc --verbose --target").arg(TARGET).run(); assert!(p.root().join(&format!("target/{}/doc", TARGET)).is_dir()); assert!( p.root() .join(&format!("target/{}/doc/foo/index.html", TARGET)) .is_file() ); } #[cargo_test] fn target_specific_not_documented() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [target.foo.dependencies] a = { path = "a" } "#, ) .file("src/lib.rs", "") .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) .file("a/src/lib.rs", "not rust") .build(); p.cargo("doc").run(); } #[cargo_test] fn output_not_captured() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] a = { path = "a" } "#, ) .file("src/lib.rs", "") .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) .file( "a/src/lib.rs", " /// ``` /// ` /// ``` pub fn foo() {} ", ) .build(); p.cargo("doc") .with_stderr_data(str![[r#" ... [..]unknown start of token: ` ... "#]]) .run(); } #[cargo_test] fn target_specific_documented() { let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [target.foo.dependencies] a = {{ path = "a" }} [target.{}.dependencies] a = {{ path = "a" }} "#, rustc_host() ), ) .file( "src/lib.rs", " extern crate a; /// test pub fn foo() {} ", ) .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) .file( "a/src/lib.rs", " /// test pub fn foo() {} ", ) .build(); p.cargo("doc").run(); } #[cargo_test] fn no_document_build_deps() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [build-dependencies] a = { path = "a" } "#, ) .file("src/lib.rs", "pub fn foo() {}") .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) .file( "a/src/lib.rs", " /// ``` /// ☃ /// ``` pub fn foo() {} ", ) .build(); p.cargo("doc").run(); } #[cargo_test] fn doc_release() { let p = project().file("src/lib.rs", "").build(); p.cargo("check --release").run(); p.cargo("doc --release -v") .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustdoc [..] src/lib.rs [..]` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); } #[cargo_test] fn doc_multiple_deps() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "bar" [dependencies.baz] path = "baz" "#, ) .file("src/lib.rs", "extern crate bar; pub fn foo() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "pub fn bar() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.0.1")) .file("baz/src/lib.rs", "pub fn baz() {}") .build(); p.cargo("doc -p bar -p baz -v").run(); assert!(p.root().join("target/doc").is_dir()); assert!(p.root().join("target/doc/bar/index.html").is_file()); assert!(p.root().join("target/doc/baz/index.html").is_file()); } #[cargo_test] fn features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "bar" [features] foo = ["bar/bar"] "#, ) .file("src/lib.rs", r#"#[cfg(feature = "foo")] pub fn foo() {}"#) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [features] bar = [] "#, ) .file( "bar/build.rs", r#" fn main() { println!("cargo::rustc-cfg=bar"); } "#, ) .file( "bar/src/lib.rs", r#"#[cfg(feature = "bar")] pub fn bar() {}"#, ) .build(); p.cargo("doc --features foo") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [DOCUMENTING] bar v0.0.1 ([ROOT]/foo/bar) [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); assert!(p.root().join("target/doc").is_dir()); assert!(p.root().join("target/doc/foo/fn.foo.html").is_file()); assert!(p.root().join("target/doc/bar/fn.bar.html").is_file()); // Check that turning the feature off will remove the files. p.cargo("doc") .with_stderr_data(str![[r#" [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [DOCUMENTING] bar v0.0.1 ([ROOT]/foo/bar) [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); assert!(!p.root().join("target/doc/foo/fn.foo.html").is_file()); assert!(!p.root().join("target/doc/bar/fn.bar.html").is_file()); // And switching back will rebuild and bring them back. p.cargo("doc --features foo") .with_stderr_data(str![[r#" [DOCUMENTING] bar v0.0.1 ([ROOT]/foo/bar) [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); assert!(p.root().join("target/doc/foo/fn.foo.html").is_file()); assert!(p.root().join("target/doc/bar/fn.bar.html").is_file()); } #[cargo_test] fn rerun_when_dir_removed() { let p = project() .file( "src/lib.rs", r#" /// dox pub fn foo() {} "#, ) .build(); p.cargo("doc").run(); assert!(p.root().join("target/doc/foo/index.html").is_file()); fs::remove_dir_all(p.root().join("target/doc/foo")).unwrap(); p.cargo("doc").run(); assert!(p.root().join("target/doc/foo/index.html").is_file()); } #[cargo_test] fn document_only_lib() { let p = project() .file( "src/lib.rs", r#" /// dox pub fn foo() {} "#, ) .file( "src/bin/bar.rs", r#" /// ``` /// ☃ /// ``` pub fn foo() {} fn main() { foo(); } "#, ) .build(); p.cargo("doc --lib").run(); assert!(p.root().join("target/doc/foo/index.html").is_file()); } #[cargo_test] fn plugins_no_use_target() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lib] proc-macro = true "#, ) .file("src/lib.rs", "") .build(); p.cargo("doc --target=x86_64-unknown-openbsd -v").run(); } #[cargo_test] fn doc_all_workspace() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { path = "bar" } [workspace] "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .build(); // The order in which bar is compiled or documented is not deterministic p.cargo("doc --workspace") .with_stderr_data( str![[r#" [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [DOCUMENTING] bar v0.1.0 ([ROOT]/foo/bar) [DOCUMENTING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/bar/index.html and 1 other file "#]] .unordered(), ) .run(); } #[cargo_test] fn doc_all_workspace_verbose() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { path = "bar" } [workspace] "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .build(); // The order in which bar is compiled or documented is not deterministic p.cargo("doc --workspace -v") .with_stderr_data( str![[r#" [DOCUMENTING] bar v0.1.0 ([ROOT]/foo/bar) [DOCUMENTING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustdoc [..] [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [RUNNING] `rustc [..] [RUNNING] `rustdoc [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/bar/index.html [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]] .unordered(), ) .run(); } #[cargo_test] fn doc_all_virtual_manifest() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "pub fn baz() {}") .build(); // The order in which bar and baz are documented is not guaranteed p.cargo("doc --workspace") .with_stderr_data( str![[r#" [DOCUMENTING] baz v0.1.0 ([ROOT]/foo/baz) [DOCUMENTING] bar v0.1.0 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/bar/index.html and 1 other file "#]] .unordered(), ) .run(); } #[cargo_test] fn doc_virtual_manifest_all_implied() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "pub fn baz() {}") .build(); // The order in which bar and baz are documented is not guaranteed p.cargo("doc") .with_stderr_data( str![[r#" [GENERATED] [ROOT]/foo/target/doc/bar/index.html and 1 other file [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [DOCUMENTING] bar v0.1.0 ([ROOT]/foo/bar) [DOCUMENTING] baz v0.1.0 ([ROOT]/foo/baz) "#]] .unordered(), ) .run(); } #[cargo_test] fn doc_virtual_manifest_one_project() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "pub fn baz() { break_the_build(); }") .build(); p.cargo("doc -p bar") .with_stderr_data(str![[r#" [DOCUMENTING] bar v0.1.0 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/bar/index.html "#]]) .run(); } #[cargo_test] fn doc_virtual_manifest_glob() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() { break_the_build(); }") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "pub fn baz() {}") .build(); p.cargo("doc -p '*z'") .with_stderr_data(str![[r#" [DOCUMENTING] baz v0.1.0 ([ROOT]/foo/baz) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/baz/index.html "#]]) .run(); } #[cargo_test] fn doc_all_member_dependency_same_name() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar"] "#, ) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" [dependencies] bar = "0.1.0" "#, ) .file("bar/src/lib.rs", "pub fn bar() {}") .build(); Package::new("bar", "0.1.0").publish(); p.cargo("doc --workspace") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.1.0 (registry `dummy-registry`) [WARNING] output filename collision. The lib target `bar` in package `bar v0.1.0` has the same output filename as the lib target `bar` in package `bar v0.1.0 ([ROOT]/foo/bar)`. Colliding filename is: [ROOT]/foo/target/doc/bar/index.html The targets should have unique names. This is a known bug where multiple crates with the same name use the same path; see . [DOCUMENTING] bar v0.1.0 [CHECKING] bar v0.1.0 [DOCUMENTING] bar v0.1.0 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/bar/index.html "#]].unordered()) .run(); } #[cargo_test] fn doc_workspace_open_help_message() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo", "bar"] "#, ) .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("foo/src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "") .build(); // The order in which bar is compiled or documented is not deterministic p.cargo("doc --workspace --open") .env("BROWSER", tools::echo()) .with_stderr_data( str![[r#" [DOCUMENTING] foo v0.1.0 ([ROOT]/foo/foo) [DOCUMENTING] bar v0.1.0 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [OPENING] [ROOT]/foo/target/doc/bar/index.html "#]] .unordered(), ) .run(); } #[cargo_test(nightly, reason = "-Zextern-html-root-url is unstable")] fn doc_extern_map_local() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" "#, ) .file("src/lib.rs", "") .file(".cargo/config.toml", "doc.extern-map.std = 'local'") .build(); p.cargo("doc -v --no-deps -Zrustdoc-map --open") .env("BROWSER", tools::echo()) .masquerade_as_nightly_cargo(&["rustdoc-map"]) .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustdoc --edition=2015 --crate-type lib --crate-name foo src/lib.rs [..]--crate-version 0.1.0` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [OPENING] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); } #[cargo_test] fn open_no_doc_crate() { let p = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] [lib] doc = false "#, ) .file("src/lib.rs", "#[cfg(feature)] pub fn f();") .build(); p.cargo("doc --open") .env("BROWSER", "do_not_run_me") .with_status(101) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [ERROR] cannot open specified crate's documentation: no documentation generated "#]]) .run(); } #[cargo_test] fn doc_workspace_open_different_library_and_package_names() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo"] "#, ) .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [lib] name = "foolib" "#, ) .file("foo/src/lib.rs", "") .build(); p.cargo("doc --open") .env("BROWSER", tools::echo()) .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.1.0 ([ROOT]/foo/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [OPENING] [ROOT]/foo/target/doc/foolib/index.html "#]]) .with_stdout_data(str![[r#" [ROOT]/foo/target/doc/foolib/index.html "#]]) .run(); p.change_file( ".cargo/config.toml", &format!( r#" [doc] browser = ["{}", "a"] "#, tools::echo().display().to_string().replace('\\', "\\\\") ), ); // check that the cargo config overrides the browser env var p.cargo("doc --open") .env("BROWSER", "do_not_run_me") .with_stdout_data(str![[r#" a [ROOT]/foo/target/doc/foolib/index.html "#]]) .run(); } #[cargo_test] fn doc_workspace_open_binary() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo"] "#, ) .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [[bin]] name = "foobin" path = "src/main.rs" "#, ) .file("foo/src/main.rs", "") .build(); p.cargo("doc --open") .env("BROWSER", tools::echo()) .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.1.0 ([ROOT]/foo/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [OPENING] [ROOT]/foo/target/doc/foobin/index.html "#]]) .run(); } #[cargo_test] fn doc_workspace_open_binary_and_library() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo"] "#, ) .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [lib] name = "foolib" [[bin]] name = "foobin" path = "src/main.rs" "#, ) .file("foo/src/lib.rs", "") .file("foo/src/main.rs", "") .build(); p.cargo("doc --open") .env("BROWSER", tools::echo()) .with_stderr_data( str![[r#" [DOCUMENTING] foo v0.1.0 ([ROOT]/foo/foo) [CHECKING] foo v0.1.0 ([ROOT]/foo/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [OPENING] [ROOT]/foo/target/doc/foolib/index.html "#]] .unordered(), ) .run(); } #[cargo_test] fn doc_edition() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] edition = "2018" "#, ) .file("src/lib.rs", "") .build(); p.cargo("doc -v") .with_stderr_data(str![[r#" ... [RUNNING] `rustdoc [..]--edition=2018[..] ... "#]]) .run(); p.cargo("test -v") .with_stderr_data(str![[r#" ... [RUNNING] `rustdoc [..]--edition=2018[..] ... "#]]) .run(); } #[cargo_test] fn doc_target_edition() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lib] edition = "2018" "#, ) .file("src/lib.rs", "") .build(); p.cargo("doc -v") .with_stderr_data(str![[r#" ... [RUNNING] `rustdoc [..]--edition=2018[..] ... "#]]) .run(); p.cargo("test -v") .with_stderr_data(str![[r#" ... [RUNNING] `rustdoc [..]--edition=2018[..] ... "#]]) .run(); } // Tests an issue where depending on different versions of the same crate depending on `cfg`s // caused `cargo doc` to fail. #[cargo_test] fn issue_5345() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [target.'cfg(all(windows, target_arch = "x86"))'.dependencies] bar = "0.1" [target.'cfg(not(all(windows, target_arch = "x86")))'.dependencies] bar = "0.2" "#, ) .file("src/lib.rs", "extern crate bar;") .build(); Package::new("bar", "0.1.0").publish(); Package::new("bar", "0.2.0").publish(); foo.cargo("check").run(); foo.cargo("doc").run(); } #[cargo_test] fn doc_private_items() { let foo = project() .file("src/lib.rs", "mod private { fn private_item() {} }") .build(); foo.cargo("doc --document-private-items").run(); assert!(foo.root().join("target/doc").is_dir()); assert!( foo.root() .join("target/doc/foo/private/index.html") .is_file() ); } #[cargo_test] fn doc_private_ws() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] "#, ) .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) .file("a/src/lib.rs", "fn p() {}") .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) .file("b/src/lib.rs", "fn p2() {}") .file("b/src/bin/b-cli.rs", "fn main() {}") .build(); p.cargo("doc --workspace --bins --lib --document-private-items -v") .with_stderr_data( str![[r#" [DOCUMENTING] b v0.0.1 ([ROOT]/foo/b) [CHECKING] b v0.0.1 ([ROOT]/foo/b) [DOCUMENTING] a v0.0.1 ([ROOT]/foo/a) [RUNNING] `rustdoc [..] a/src/lib.rs [..]--document-private-items[..] [RUNNING] `rustc [..] [WARNING] function `p2` is never used ... [RUNNING] `rustdoc [..] b/src/lib.rs [..]--document-private-items[..] [WARNING] `b` (lib) generated 1 warning [RUNNING] `rustdoc [..] b/src/bin/b-cli.rs [..]--document-private-items[..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/a/index.html [GENERATED] [ROOT]/foo/target/doc/b/index.html [GENERATED] [ROOT]/foo/target/doc/b_cli/index.html "#]] .unordered(), ) .run(); } const BAD_INTRA_LINK_LIB: &str = r#" #![deny(rustdoc::broken_intra_doc_links)] /// [bad_link] pub fn foo() {} "#; #[cargo_test] fn doc_cap_lints() { let a = git::new("a", |p| { p.file("Cargo.toml", &basic_lib_manifest("a")) .file("src/lib.rs", BAD_INTRA_LINK_LIB) }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] a = {{ git = '{}' }} "#, a.url() ), ) .file("src/lib.rs", "") .build(); p.cargo("doc") .with_stderr_data( str![[r#" [LOCKING] 1 package to latest compatible version [UPDATING] git repository `[..]` [DOCUMENTING] a v0.5.0 ([..]) [CHECKING] a v0.5.0 ([..]) [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]] .unordered(), ) .run(); p.root().join("target").rm_rf(); p.cargo("doc -vv") .with_stderr_data(str![[r#" ... [WARNING] [..]`bad_link`[..] ... "#]]) .run(); } #[cargo_test] fn doc_message_format() { let p = project().file("src/lib.rs", BAD_INTRA_LINK_LIB).build(); p.cargo("doc --message-format=json") .with_status(101) .with_stdout_data( str![[r##" [ { "manifest_path": "[ROOT]/foo/Cargo.toml", "message": { "$message_type": "diagnostic", "level": "error", "...": "{...}" }, "package_id": "path+[ROOTURL]/foo#0.0.1", "reason": "compiler-message", "target": "{...}" }, "{...}" ] "##]] .is_json() .against_jsonlines(), ) .run(); } #[cargo_test] fn doc_json_artifacts() { // Checks the output of json artifact messages. let p = project() .file("src/lib.rs", "") .file("src/bin/somebin.rs", "fn main() {}") .build(); p.cargo("doc --message-format=json") .with_stdout_data( str![[r#" [ { "executable": null, "features": [], "filenames": [ "[ROOT]/foo/target/debug/deps/libfoo-[HASH].rmeta" ], "fresh": false, "manifest_path": "[ROOT]/foo/Cargo.toml", "package_id": "path+[ROOTURL]/foo#0.0.1", "profile": "{...}", "reason": "compiler-artifact", "target": { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "foo", "src_path": "[ROOT]/foo/src/lib.rs", "test": true } }, { "executable": null, "features": [], "filenames": [ "[ROOT]/foo/target/doc/foo/index.html" ], "fresh": false, "manifest_path": "[ROOT]/foo/Cargo.toml", "package_id": "path+[ROOTURL]/foo#0.0.1", "profile": "{...}", "reason": "compiler-artifact", "target": { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "foo", "src_path": "[ROOT]/foo/src/lib.rs", "test": true } }, { "executable": null, "features": [], "filenames": [ "[ROOT]/foo/target/doc/somebin/index.html" ], "fresh": false, "manifest_path": "[ROOT]/foo/Cargo.toml", "package_id": "path+[ROOTURL]/foo#0.0.1", "profile": "{...}", "reason": "compiler-artifact", "target": { "crate_types": [ "bin" ], "doc": true, "doctest": false, "edition": "2015", "kind": [ "bin" ], "name": "somebin", "src_path": "[ROOT]/foo/src/bin/somebin.rs", "test": true } }, { "reason": "build-finished", "success": true } ] "#]] .is_json() .against_jsonlines() .unordered(), ) .run(); } #[cargo_test] fn short_message_format() { let p = project().file("src/lib.rs", BAD_INTRA_LINK_LIB).build(); p.cargo("doc --message-format=short") .with_status(101) .with_stderr_data(str![[r#" ... src/lib.rs:4:6: [ERROR] [..]`bad_link`[..] ... "#]]) .run(); } #[cargo_test] fn doc_example() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [[example]] crate-type = ["lib"] name = "ex1" doc = true "#, ) .file("src/lib.rs", "pub fn f() {}") .file( "examples/ex1.rs", r#" use foo::f; /// Example pub fn x() { f(); } "#, ) .build(); p.cargo("doc").run(); assert!( p.build_dir() .join("doc") .join("ex1") .join("fn.x.html") .exists() ); } #[cargo_test] fn doc_example_with_deps() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [[example]] crate-type = ["lib"] name = "ex" doc = true [dev-dependencies] a = {path = "a"} b = {path = "b"} "#, ) .file("src/lib.rs", "") .file( "examples/ex.rs", r#" use a::fun; /// Example pub fn x() { fun(); } "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" [dependencies] b = {path = "../b"} "#, ) .file("a/src/fun.rs", "pub fn fun() {}") .file("a/src/lib.rs", "pub mod fun;") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.0.1" edition = "2015" "#, ) .file("b/src/lib.rs", "") .build(); p.cargo("doc --examples").run(); assert!( p.build_dir() .join("doc") .join("ex") .join("fn.x.html") .exists() ); } #[cargo_test] fn bin_private_items() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] "#, ) .file( "src/main.rs", " pub fn foo_pub() {} fn foo_priv() {} struct FooStruct; enum FooEnum {} trait FooTrait {} type FooType = u32; mod foo_mod {} ", ) .build(); p.cargo("doc") .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); assert!(p.root().join("target/doc/foo/index.html").is_file()); assert!(p.root().join("target/doc/foo/fn.foo_pub.html").is_file()); assert!(p.root().join("target/doc/foo/fn.foo_priv.html").is_file()); assert!( p.root() .join("target/doc/foo/struct.FooStruct.html") .is_file() ); assert!(p.root().join("target/doc/foo/enum.FooEnum.html").is_file()); assert!( p.root() .join("target/doc/foo/trait.FooTrait.html") .is_file() ); assert!(p.root().join("target/doc/foo/type.FooType.html").is_file()); assert!(p.root().join("target/doc/foo/foo_mod/index.html").is_file()); } #[cargo_test] fn bin_private_items_deps() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "bar" "#, ) .file( "src/main.rs", " fn foo_priv() {} pub fn foo_pub() {} ", ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file( "bar/src/lib.rs", " #[allow(dead_code)] fn bar_priv() {} pub fn bar_pub() {} ", ) .build(); p.cargo("doc") .with_stderr_data( str![[r#" [LOCKING] 1 package to latest compatible version [DOCUMENTING] bar v0.0.1 ([ROOT]/foo/bar) [CHECKING] bar v0.0.1 ([ROOT]/foo/bar) [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]] .unordered(), ) .run(); assert!(p.root().join("target/doc/foo/index.html").is_file()); assert!(p.root().join("target/doc/foo/fn.foo_pub.html").is_file()); assert!(p.root().join("target/doc/foo/fn.foo_priv.html").is_file()); assert!(p.root().join("target/doc/bar/index.html").is_file()); assert!(p.root().join("target/doc/bar/fn.bar_pub.html").is_file()); assert!(!p.root().join("target/doc/bar/fn.bar_priv.html").exists()); } #[cargo_test] fn crate_versions() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.2.4" edition = "2015" authors = [] "#, ) .file("src/lib.rs", "") .build(); p.cargo("doc -v") .with_stderr_data(str![[r#" [DOCUMENTING] foo v1.2.4 ([ROOT]/foo) [RUNNING] `rustdoc --edition=2015 --crate-type lib --crate-name foo src/lib.rs [..]--crate-version 1.2.4` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); let output_path = p.root().join("target/doc/foo/index.html"); let output_documentation = fs::read_to_string(&output_path).unwrap(); assert!(output_documentation.contains("1.2.4")); } #[cargo_test] fn crate_versions_flag_is_overridden() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.2.4" edition = "2015" authors = [] "#, ) .file("src/lib.rs", "") .build(); let output_documentation = || { let output_path = p.root().join("target/doc/foo/index.html"); fs::read_to_string(&output_path).unwrap() }; let asserts = |html: String| { assert!(!html.contains("1.2.4")); assert!(html.contains("2.0.3")); }; p.cargo("doc") .env("RUSTDOCFLAGS", "--crate-version 2.0.3") .run(); asserts(output_documentation()); p.build_dir().rm_rf(); p.cargo("rustdoc -- --crate-version 2.0.3").run(); asserts(output_documentation()); } #[cargo_test] fn doc_test_in_workspace() { let p = project() .file( "Cargo.toml", r#" [workspace] members = [ "crate-a", "crate-b", ] "#, ) .file( "crate-a/Cargo.toml", r#" [package] name = "crate-a" version = "0.1.0" edition = "2015" "#, ) .file( "crate-a/src/lib.rs", "\ //! ``` //! assert_eq!(1, 1); //! ``` ", ) .file( "crate-b/Cargo.toml", r#" [package] name = "crate-b" version = "0.1.0" edition = "2015" "#, ) .file( "crate-b/src/lib.rs", "\ //! ``` //! assert_eq!(1, 1); //! ``` ", ) .build(); p.cargo("test --doc -vv") .with_stderr_data(str![[r#" ... [DOCTEST] crate_a ... "#]]) .with_stdout_data(str![[r#" running 1 test test crate-a/src/lib.rs - (line 1) ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test crate-b/src/lib.rs - (line 1) ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } /// This is a test for . /// The `file!()` macro inside of an `include!()` should output /// workspace-relative paths, just like it does in other cases. #[cargo_test] fn doc_test_include_file() { let p = project() .file( "Cargo.toml", r#" [workspace] members = [ "child", ] [package] name = "root" version = "0.1.0" edition = "2015" "#, ) .file( "src/lib.rs", r#" /// ``` /// assert_eq!("src/lib.rs", file!().replace("\\", "/")) /// ``` pub mod included { include!(concat!("../", file!(), ".included.rs")); } "#, ) .file( "src/lib.rs.included.rs", r#" /// ``` /// assert_eq!(1, 1) /// ``` pub fn foo() {} "#, ) .file( "child/Cargo.toml", r#" [package] name = "child" version = "0.1.0" edition = "2015" "#, ) .file( "child/src/lib.rs", r#" /// ``` /// assert_eq!("child/src/lib.rs", file!().replace("\\", "/")) /// ``` pub mod included { include!(concat!("../../", file!(), ".included.rs")); } "#, ) .file( "child/src/lib.rs.included.rs", r#" /// ``` /// assert_eq!(1, 1) /// ``` pub fn foo() {} "#, ) .build(); p.cargo("test --workspace --doc -vv -- --test-threads=1") .with_stderr_data(str![[r#" ... [DOCTEST] child ... [DOCTEST] root ... "#]]) .with_stdout_data(str![[r#" running 2 tests test child/src/../../child/src/lib.rs.included.rs - included::foo (line 2) ... ok test child/src/lib.rs - included (line 2) ... ok test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s running 2 tests test src/../src/lib.rs.included.rs - included::foo (line 2) ... ok test src/lib.rs - included (line 2) ... ok test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn doc_fingerprint_is_versioning_consistent() { // Random rustc verbose version let old_rustc_verbose_version = format!( "\ rustc 1.41.1 (f3e1a954d 2020-02-24) binary: rustc commit-hash: f3e1a954d2ead4e2fc197c7da7d71e6c61bad196 commit-date: 2020-02-24 host: {} release: 1.41.1 LLVM version: 9.0 ", rustc_host() ); // Create the dummy project. let dummy_project = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.2.4" edition = "2015" authors = [] "#, ) .file("src/lib.rs", "//! These are the docs!") .build(); dummy_project.cargo("doc").run(); let fingerprint: RustDocFingerprint = serde_json::from_str(&dummy_project.read_file("target/.rustdoc_fingerprint.json")) .expect("JSON Serde fail"); // Check that the fingerprint contains the actual rustc version // which has been used to compile the docs. let output = std::process::Command::new("rustc") .arg("-vV") .output() .expect("Failed to get actual rustc verbose version"); assert_eq!( fingerprint.rustc_vv, (String::from_utf8_lossy(&output.stdout).as_ref()) ); // As the test shows above. Now we have generated the `doc/` folder and inside // the rustdoc fingerprint file is located with the correct rustc version. // So we will remove it and create a new fingerprint with an old rustc version // inside it. We will also place a bogus file inside of the `doc/` folder to ensure // it gets removed as we expect on the next doc compilation. dummy_project.change_file( "target/.rustdoc_fingerprint.json", &old_rustc_verbose_version, ); fs::write( dummy_project.build_dir().join("doc/bogus_file"), String::from("This is a bogus file and should be removed!"), ) .expect("Error writing test bogus file"); // Now if we trigger another compilation, since the fingerprint contains an old version // of rustc, cargo should remove the entire `/doc` folder (including the fingerprint) // and generating another one with the actual version. // It should also remove the bogus file we created above. dummy_project.cargo("doc").run(); assert!(!dummy_project.build_dir().join("doc/bogus_file").exists()); let fingerprint: RustDocFingerprint = serde_json::from_str(&dummy_project.read_file("target/.rustdoc_fingerprint.json")) .expect("JSON Serde fail"); // Check that the fingerprint contains the actual rustc version // which has been used to compile the docs. assert_eq!( fingerprint.rustc_vv, (String::from_utf8_lossy(&output.stdout).as_ref()) ); } #[cargo_test] fn doc_fingerprint_respects_target_paths() { // Random rustc verbose version let old_rustc_verbose_version = format!( "\ rustc 1.41.1 (f3e1a954d 2020-02-24) binary: rustc commit-hash: f3e1a954d2ead4e2fc197c7da7d71e6c61bad196 commit-date: 2020-02-24 host: {} release: 1.41.1 LLVM version: 9.0 ", rustc_host() ); // Create the dummy project. let dummy_project = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.2.4" edition = "2015" authors = [] "#, ) .file("src/lib.rs", "//! These are the docs!") .build(); dummy_project.cargo("doc --target").arg(rustc_host()).run(); let fingerprint: RustDocFingerprint = serde_json::from_str(&dummy_project.read_file("target/.rustdoc_fingerprint.json")) .expect("JSON Serde fail"); // Check that the fingerprint contains the actual rustc version // which has been used to compile the docs. let output = std::process::Command::new("rustc") .arg("-vV") .output() .expect("Failed to get actual rustc verbose version"); assert_eq!( fingerprint.rustc_vv, (String::from_utf8_lossy(&output.stdout).as_ref()) ); // As the test shows above. Now we have generated the `doc/` folder and inside // the rustdoc fingerprint file is located with the correct rustc version. // So we will remove it and create a new fingerprint with an old rustc version // inside it. We will also place a bogus file inside of the `doc/` folder to ensure // it gets removed as we expect on the next doc compilation. dummy_project.change_file( "target/.rustdoc_fingerprint.json", &old_rustc_verbose_version, ); fs::write( dummy_project .build_dir() .join(rustc_host()) .join("doc/bogus_file"), String::from("This is a bogus file and should be removed!"), ) .expect("Error writing test bogus file"); // Now if we trigger another compilation, since the fingerprint contains an old version // of rustc, cargo should remove the entire `/doc` folder (including the fingerprint) // and generating another one with the actual version. // It should also remove the bogus file we created above. dummy_project.cargo("doc --target").arg(rustc_host()).run(); assert!( !dummy_project .build_dir() .join(rustc_host()) .join("doc/bogus_file") .exists() ); let fingerprint: RustDocFingerprint = serde_json::from_str(&dummy_project.read_file("target/.rustdoc_fingerprint.json")) .expect("JSON Serde fail"); // Check that the fingerprint contains the actual rustc version // which has been used to compile the docs. assert_eq!( fingerprint.rustc_vv, (String::from_utf8_lossy(&output.stdout).as_ref()) ); } #[cargo_test] fn doc_fingerprint_unusual_behavior() { // Checks for some unusual circumstances with clearing the doc directory. if !symlink_supported() { return; } let p = project().file("src/lib.rs", "").build(); p.build_dir().mkdir_p(); let real_doc = p.root().join("doc"); real_doc.mkdir_p(); let build_doc = p.build_dir().join("doc"); p.symlink(&real_doc, &build_doc); fs::write(real_doc.join("somefile"), "test").unwrap(); fs::write(real_doc.join(".hidden"), "test").unwrap(); p.cargo("doc").run(); // Make sure for the first run, it does not delete any files and does not // break the symlink. assert!(build_doc.join("somefile").exists()); assert!(real_doc.join("somefile").exists()); assert!(real_doc.join(".hidden").exists()); assert!(real_doc.join("foo/index.html").exists()); // Pretend that the last build was generated by an older version. p.change_file( "target/.rustdoc_fingerprint.json", "{\"rustc_vv\": \"I am old\"}", ); // Change file to trigger a new build. p.change_file("src/lib.rs", "// changed"); p.cargo("doc") .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); // This will delete somefile, but not .hidden. assert!(!real_doc.join("somefile").exists()); assert!(real_doc.join(".hidden").exists()); assert!(real_doc.join("foo/index.html").exists()); // And also check the -Z flag behavior. p.change_file( "target/.rustdoc_fingerprint.json", "{\"rustc_vv\": \"I am old\"}", ); // Change file to trigger a new build. p.change_file("src/lib.rs", "// changed2"); fs::write(real_doc.join("somefile"), "test").unwrap(); p.cargo("doc -Z skip-rustdoc-fingerprint") .masquerade_as_nightly_cargo(&["skip-rustdoc-fingerprint"]) .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); // Should not have deleted anything. assert!(build_doc.join("somefile").exists()); assert!(real_doc.join("somefile").exists()); } #[cargo_test] fn lib_before_bin() { // Checks that the library is documented before the binary. // Previously they were built concurrently, which can cause issues // if the bin has intra-doc links to the lib. let p = project() .file( "src/lib.rs", r#" /// Hi pub fn abc() {} "#, ) .file( "src/bin/somebin.rs", r#" //! See [`foo::abc`] fn main() {} "#, ) .build(); // Run check first. This just helps ensure that the test clearly shows the // order of the rustdoc commands. p.cargo("check").run(); // The order of output here should be deterministic. p.cargo("doc -v") .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustdoc --edition=2015 --crate-type lib --crate-name foo src/lib.rs [..] [RUNNING] `rustdoc --edition=2015 --crate-type bin --crate-name somebin src/bin/somebin.rs [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html [GENERATED] [ROOT]/foo/target/doc/somebin/index.html "#]]) .run(); // And the link should exist. let bin_html = p.read_file("target/doc/somebin/index.html"); assert!(bin_html.contains("../foo/fn.abc.html")); } #[cargo_test] fn doc_lib_false() { // doc = false for a library let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [lib] doc = false [dependencies] bar = {path = "bar"} "#, ) .file("src/lib.rs", "extern crate bar;") .file("src/bin/some-bin.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" [lib] doc = false "#, ) .file("bar/src/lib.rs", "") .build(); p.cargo("doc") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [CHECKING] foo v0.1.0 ([ROOT]/foo) [DOCUMENTING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/some_bin/index.html "#]]) .run(); assert!(!p.build_dir().join("doc/foo").exists()); assert!(!p.build_dir().join("doc/bar").exists()); assert!(p.build_dir().join("doc/some_bin").exists()); } #[cargo_test] fn doc_lib_false_dep() { // doc = false for a dependency // Ensures that the rmeta gets produced let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { path = "bar" } "#, ) .file("src/lib.rs", "extern crate bar;") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" [lib] doc = false "#, ) .file("bar/src/lib.rs", "") .build(); p.cargo("doc") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [DOCUMENTING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); assert!(p.build_dir().join("doc/foo").exists()); assert!(!p.build_dir().join("doc/bar").exists()); } #[cargo_test] fn link_to_private_item() { let main = r#" //! [bar] #[allow(dead_code)] fn bar() {} "#; let p = project().file("src/lib.rs", main).build(); p.cargo("doc") .with_stderr_data(str![[r#" ... [..]documentation for `foo` links to private item `bar` ... "#]]) .run(); // Check that binaries don't emit a private_intra_doc_links warning. fs::rename(p.root().join("src/lib.rs"), p.root().join("src/main.rs")).unwrap(); p.cargo("doc") .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); } #[cargo_test] fn rustdoc_failure_hides_command_line_by_default() { let p = project().file("src/lib.rs", "invalid rust code").build(); // `cargo doc` doesn't print the full command line on failures by default p.cargo("doc") .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [ERROR] expected one of `!` or `::`, found `rust` --> src/lib.rs:1:9 | 1 | invalid rust code | ^^^^ expected one of `!` or `::` [ERROR] could not document `foo` "#]]) .with_status(101) .run(); // ... but it still does so if requested with `--verbose`. p.cargo("doc --verbose") .with_stderr_data(str![[r#" ... Caused by: process didn't exit successfully[..]rustdoc[..] "#]]) .with_status(101) .run(); } #[cargo_test(nightly, reason = "`rustdoc --emit` is unstable")] fn rustdoc_depinfo_gated() { let p = project() .file("Cargo.toml", &basic_lib_manifest("foo")) .file("src/lib.rs", "") .build(); p.cargo("doc -Zrustdoc-depinfo") .with_status(101) .with_stderr_data(str![[r#" [ERROR] the `-Z` flag is only accepted on the nightly channel of Cargo, but this is the `stable` channel See https://doc.rust-lang.org/book/appendix-07-nightly-rust.html for more information about Rust release channels. "#]]) .run(); } #[cargo_test(nightly, reason = "`rustdoc --emit` is unstable")] fn rebuild_tracks_target_src_outside_package_root() { let p = cargo_test_support::project_in("parent") .file( "Cargo.toml", r#" [package] name = "foo" edition = "2015" [lib] path = "../lib.rs" "#, ) .file("../lib.rs", "//! # depinfo-before") .build(); p.cargo("doc -Zrustdoc-depinfo") .masquerade_as_nightly_cargo(&["rustdoc-depinfo"]) .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.0.0 ([ROOT]/parent/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/parent/foo/target/doc/foo/index.html "#]]) .run(); let doc_html = p.read_file("target/doc/foo/index.html"); assert!(doc_html.contains("depinfo-before")); p.change_file("../lib.rs", "//! # depinfo-after"); p.cargo("doc --verbose -Zrustdoc-depinfo") .masquerade_as_nightly_cargo(&["rustdoc-depinfo"]) .with_stderr_data(str![[r#" [DIRTY] foo v0.0.0 ([ROOT]/parent/foo): the file `../lib.rs` has changed ([TIME_DIFF_AFTER_LAST_BUILD]) [DOCUMENTING] foo v0.0.0 ([ROOT]/parent/foo) [RUNNING] `rustdoc [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/parent/foo/target/doc/foo/index.html "#]]) .run(); let doc_html = p.read_file("target/doc/foo/index.html"); assert!(doc_html.contains("depinfo-after")); } #[cargo_test(nightly, reason = "`rustdoc --emit` is unstable")] fn rebuild_tracks_include_str() { let p = cargo_test_support::project_in("parent") .file("Cargo.toml", &basic_lib_manifest("foo")) .file("src/lib.rs", r#"#![doc = include_str!("../../README")]"#) .file("../README", "# depinfo-before") .build(); p.cargo("doc -Zrustdoc-depinfo") .masquerade_as_nightly_cargo(&["rustdoc-depinfo"]) .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.5.0 ([ROOT]/parent/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/parent/foo/target/doc/foo/index.html "#]]) .run(); let doc_html = p.read_file("target/doc/foo/index.html"); assert!(doc_html.contains("depinfo-before")); p.change_file("../README", "# depinfo-after"); p.cargo("doc --verbose -Zrustdoc-depinfo") .masquerade_as_nightly_cargo(&["rustdoc-depinfo"]) .with_stderr_data(str![[r#" [DIRTY] foo v0.5.0 ([ROOT]/parent/foo): the file `src/../../README` has changed ([TIME_DIFF_AFTER_LAST_BUILD]) [DOCUMENTING] foo v0.5.0 ([ROOT]/parent/foo) [RUNNING] `rustdoc [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/parent/foo/target/doc/foo/index.html "#]]) .run(); let doc_html = p.read_file("target/doc/foo/index.html"); assert!(doc_html.contains("depinfo-after")); } #[cargo_test(nightly, reason = "`rustdoc --emit` is unstable")] fn rebuild_tracks_path_attr() { let p = cargo_test_support::project_in("parent") .file("Cargo.toml", &basic_lib_manifest("foo")) .file("src/lib.rs", r#"#[path = "../../bar.rs"] pub mod bar;"#) .file("../bar.rs", "//! # depinfo-before") .build(); p.cargo("doc -Zrustdoc-depinfo") .masquerade_as_nightly_cargo(&["rustdoc-depinfo"]) .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.5.0 ([ROOT]/parent/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/parent/foo/target/doc/foo/index.html "#]]) .run(); let doc_html = p.read_file("target/doc/foo/index.html"); assert!(doc_html.contains("depinfo-before")); p.change_file("../bar.rs", "//! # depinfo-after"); p.cargo("doc --verbose -Zrustdoc-depinfo") .masquerade_as_nightly_cargo(&["rustdoc-depinfo"]) .with_stderr_data(str![[r#" [DIRTY] foo v0.5.0 ([ROOT]/parent/foo): the file `src/../../bar.rs` has changed ([TIME_DIFF_AFTER_LAST_BUILD]) [DOCUMENTING] foo v0.5.0 ([ROOT]/parent/foo) [RUNNING] `rustdoc [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/parent/foo/target/doc/foo/index.html "#]]) .run(); let doc_html = p.read_file("target/doc/foo/index.html"); assert!(doc_html.contains("depinfo-after")); } #[cargo_test(nightly, reason = "`rustdoc --emit` is unstable")] fn rebuild_tracks_env() { let env = "__RUSTDOC_INJECTED"; let p = project() .file("Cargo.toml", &basic_lib_manifest("foo")) .file("src/lib.rs", &format!(r#"#![doc = env!("{env}")]"#)) .build(); p.cargo("doc -Zrustdoc-depinfo") .env(env, "# depinfo-before") .masquerade_as_nightly_cargo(&["rustdoc-depinfo"]) .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); let doc_html = p.read_file("target/doc/foo/index.html"); assert!(doc_html.contains("depinfo-before")); p.cargo("doc --verbose -Zrustdoc-depinfo") .env(env, "# depinfo-after") .masquerade_as_nightly_cargo(&["rustdoc-depinfo"]) .with_stderr_data(str![[r#" [DIRTY] foo v0.5.0 ([ROOT]/foo): the environment variable __RUSTDOC_INJECTED changed [DOCUMENTING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustdoc [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); let doc_html = p.read_file("target/doc/foo/index.html"); assert!(doc_html.contains("depinfo-after")); } #[cargo_test(nightly, reason = "`rustdoc --emit` is unstable")] fn rebuild_tracks_env_in_dep() { let env = "__RUSTDOC_INJECTED"; Package::new("bar", "0.1.0") .file("src/lib.rs", &format!(r#"#![doc = env!("{env}")]"#)) .publish(); let env = "__RUSTDOC_INJECTED"; let p = project() .file( "Cargo.toml", r#" [package] name = "foo" edition = "2015" [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("doc -Zrustdoc-depinfo") .env(env, "# depinfo-before") .masquerade_as_nightly_cargo(&["rustdoc-depinfo"]) .with_stderr_data( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.1.0 (registry `dummy-registry`) [CHECKING] bar v0.1.0 [DOCUMENTING] bar v0.1.0 [DOCUMENTING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]] .unordered(), ) .run(); let doc_html = p.read_file("target/doc/bar/index.html"); assert!(doc_html.contains("depinfo-before")); p.cargo("doc --verbose -Zrustdoc-depinfo") .env(env, "# depinfo-after") .masquerade_as_nightly_cargo(&["rustdoc-depinfo"]) .with_stderr_data( str![[r#" [DIRTY] bar v0.1.0: the environment variable __RUSTDOC_INJECTED changed [DOCUMENTING] bar v0.1.0 [DIRTY] bar v0.1.0: the environment variable __RUSTDOC_INJECTED changed [CHECKING] bar v0.1.0 [RUNNING] `rustc --crate-name bar [..]` [RUNNING] `rustdoc [..]--crate-name bar [..]` [DIRTY] foo v0.0.0 ([ROOT]/foo): the dependency bar was rebuilt [DOCUMENTING] foo v0.0.0 ([ROOT]/foo) [RUNNING] `rustdoc [..]--crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]] .unordered(), ) .run(); let doc_html = p.read_file("target/doc/bar/index.html"); assert!(doc_html.contains("depinfo-after")); } #[cargo_test( nightly, reason = "`rustdoc --emit` is unstable; requires -Zchecksum-hash-algorithm" )] fn rebuild_tracks_checksum() { let p = cargo_test_support::project_in("parent") .file("Cargo.toml", &basic_lib_manifest("foo")) .file("src/lib.rs", r#"#![doc = include_str!("../../README")]"#) .file("../README", "# depinfo-before") .build(); p.cargo("doc -Zrustdoc-depinfo -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["rustdoc-depinfo", "checksum-freshness"]) .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.5.0 ([ROOT]/parent/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/parent/foo/target/doc/foo/index.html "#]]) .run(); let doc_html = p.read_file("target/doc/foo/index.html"); assert!(doc_html.contains("depinfo-before")); p.change_file("../README", "# depinfo-after"); // Change mtime into the future p.root().move_into_the_future(); p.cargo("doc --verbose -Zrustdoc-depinfo -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["rustdoc-depinfo"]) .with_stderr_data(str![[r#" [DIRTY] foo v0.5.0 ([ROOT]/parent/foo): file size changed (16 != 15) for `src/../../README` [DOCUMENTING] foo v0.5.0 ([ROOT]/parent/foo) [RUNNING] `rustdoc [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/parent/foo/target/doc/foo/index.html "#]]) .run(); let doc_html = p.read_file("target/doc/foo/index.html"); assert!(doc_html.contains("depinfo-after")); } cargo-0.91.0/tests/testsuite/docscrape.rs000064400000000000000000000551471046102023000165260ustar 00000000000000//! Tests for the `cargo doc` command with `-Zrustdoc-scrape-examples`. use crate::prelude::*; use cargo_test_support::project; use cargo_test_support::str; #[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] fn basic() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] "#, ) .file("examples/ex.rs", "fn main() { foo::foo(); }") .file("src/lib.rs", "pub fn foo() {}\npub fn bar() { foo(); }") .build(); p.cargo("doc -Zunstable-options -Zrustdoc-scrape-examples") .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [SCRAPING] foo v0.0.1 ([ROOT]/foo) [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); p.cargo("doc -Zunstable-options -Z rustdoc-scrape-examples") .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); let doc_html = p.read_file("target/doc/foo/fn.foo.html"); assert!(doc_html.contains("Examples found in repository")); assert!(!doc_html.contains("More examples")); // Ensure that the reverse-dependency has its sources generated assert!(p.build_dir().join("doc/src/ex/ex.rs.html").exists()); } // This test ensures that even if there is no `[workspace]` in the top-level `Cargo.toml` file, the // dependencies will get their examples scraped and that they appear in the generated documentation. #[cargo_test(nightly, reason = "-Zrustdoc-scrape-examples is unstable")] fn scrape_examples_for_non_workspace_reexports() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2021" authors = [] [dependencies] a = { path = "crates/a" } "#, ) .file("src/lib.rs", "pub use a::*;") // Example .file( "examples/one.rs", r#"use foo::*; fn main() { let foo = Foo::new("yes".into()); foo.maybe(); }"#, ) // `a` crate .file( "crates/a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] "#, ) .file( "crates/a/src/lib.rs", r#" #[derive(Debug)] pub struct Foo { foo: String, yes: bool, } impl Foo { pub fn new(foo: String) -> Self { Self { foo, yes: true } } pub fn maybe(&self) { if self.yes { println!("{}", self.foo) } } }"#, ) .build(); p.cargo("doc -Zunstable-options -Zrustdoc-scrape-examples --no-deps") .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) .with_stderr_data( str![[r#" [LOCKING] 1 package to latest compatible version [CHECKING] a v0.0.1 ([ROOT]/foo/crates/a) [CHECKING] foo v0.0.1 ([ROOT]/foo) [SCRAPING] foo v0.0.1 ([ROOT]/foo) [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]] .unordered(), ) .run(); let doc_html = p.read_file("target/doc/foo/struct.Foo.html"); assert!(doc_html.contains("Examples found in repository")); } #[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] fn avoid_build_script_cycle() { let p = project() // package with build dependency .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] links = "foo" [workspace] members = ["bar"] [build-dependencies] bar = {path = "bar"} "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main(){}") // dependency .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] links = "bar" "#, ) .file("bar/src/lib.rs", "") .file("bar/build.rs", "fn main(){}") .build(); p.cargo("doc --workspace -Zunstable-options -Zrustdoc-scrape-examples") .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) .run(); } #[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] fn complex_reverse_dependencies() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dev-dependencies] a = {path = "a", features = ["feature"]} b = {path = "b"} [workspace] members = ["b"] "#, ) .file("src/lib.rs", "") .file("examples/ex.rs", "fn main() {}") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] [lib] proc-macro = true [dependencies] b = {path = "../b"} [features] feature = [] "#, ) .file("a/src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.0.1" edition = "2015" authors = [] "#, ) .file("b/src/lib.rs", "") .build(); p.cargo("doc --workspace --examples -Zunstable-options -Zrustdoc-scrape-examples") .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) .run(); } #[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] fn crate_with_dash() { let p = project() .file( "Cargo.toml", r#" [package] name = "da-sh" version = "0.0.1" edition = "2015" authors = [] "#, ) .file("src/lib.rs", "pub fn foo() {}") .file("examples/a.rs", "fn main() { da_sh::foo(); }") .build(); p.cargo("doc -Zunstable-options -Zrustdoc-scrape-examples") .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) .run(); let doc_html = p.read_file("target/doc/da_sh/fn.foo.html"); assert!(doc_html.contains("Examples found in repository")); } #[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] fn configure_target() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lib] doc-scrape-examples = true [[bin]] name = "a_bin" doc-scrape-examples = true [[example]] name = "a" doc-scrape-examples = false "#, ) .file( "src/lib.rs", "pub fn foo() {} fn lib_must_appear() { foo(); }", ) .file( "examples/a.rs", "fn example_must_not_appear() { foo::foo(); }", ) .file( "src/bin/a_bin.rs", "fn bin_must_appear() { foo::foo(); } fn main(){}", ) .build(); p.cargo("doc -Zunstable-options -Zrustdoc-scrape-examples") .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) .run(); let doc_html = p.read_file("target/doc/foo/fn.foo.html"); assert!(doc_html.contains("lib_must_appear")); assert!(doc_html.contains("bin_must_appear")); assert!(!doc_html.contains("example_must_not_appear")); } #[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] fn configure_profile_issue_10500() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [profile.dev] panic = "abort" "#, ) .file("examples/ex.rs", "fn main() { foo::foo(); }") .file("src/lib.rs", "pub fn foo() {}\npub fn bar() { foo(); }") .build(); p.cargo("doc -Zunstable-options -Zrustdoc-scrape-examples") .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) .run(); let doc_html = p.read_file("target/doc/foo/fn.foo.html"); assert!(doc_html.contains("Examples found in repository")); } #[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] fn issue_10545() { let p = project() .file( "Cargo.toml", r#" [workspace] resolver = "2" members = ["a", "b"] "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" authors = [] edition = "2021" [features] default = ["foo"] foo = [] "#, ) .file("a/src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.0.1" authors = [] edition = "2021" [lib] proc-macro = true "#, ) .file("b/src/lib.rs", "") .build(); p.cargo("doc --workspace -Zunstable-options -Zrustdoc-scrape-examples") .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) .run(); } #[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] fn cache() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] "#, ) .file("examples/ex.rs", "fn main() { foo::foo(); }") .file("src/lib.rs", "pub fn foo() {}\npub fn bar() { foo(); }") .build(); p.cargo("doc -Zunstable-options -Zrustdoc-scrape-examples") .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [SCRAPING] foo v0.0.1 ([ROOT]/foo) [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); p.cargo("doc -Zunstable-options -Zrustdoc-scrape-examples") .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); } #[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] fn no_fail_bad_lib() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] "#, ) .file("src/lib.rs", "pub fn foo() { CRASH_THE_BUILD() }") .file("examples/ex.rs", "fn main() { foo::foo(); }") .file("examples/ex2.rs", "fn main() { foo::foo(); }") .build(); p.cargo("doc -Zunstable-options -Z rustdoc-scrape-examples") .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [SCRAPING] foo v0.0.1 ([ROOT]/foo) [WARNING] failed to check lib in package `foo` as a prerequisite for scraping examples from: example "ex", example "ex2" Try running with `--verbose` to see the error message. If an example should not be scanned, then consider adding `doc-scrape-examples = false` to its `[[example]]` definition in Cargo.toml [WARNING] `foo` (lib) generated 1 warning [WARNING] failed to scan example "ex" in package `foo` for example code usage Try running with `--verbose` to see the error message. If an example should not be scanned, then consider adding `doc-scrape-examples = false` to its `[[example]]` definition in Cargo.toml [WARNING] `foo` (example "ex") generated 1 warning [WARNING] failed to scan example "ex2" in package `foo` for example code usage Try running with `--verbose` to see the error message. If an example should not be scanned, then consider adding `doc-scrape-examples = false` to its `[[example]]` definition in Cargo.toml [WARNING] `foo` (example "ex2") generated 1 warning [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]].unordered()) .run(); } #[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] fn fail_bad_build_script() { // See rust-lang/cargo#11623 let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() { panic!(\"You shall not pass\")}") .file("examples/ex.rs", "fn main() {}") .build(); // `cargo doc` fails p.cargo("doc") .with_status(101) .with_stderr_data(str![[r#" ... [..]You shall not pass[..] ... "#]]) .run(); // scrape examples should fail whenever `cargo doc` fails. p.cargo("doc -Zunstable-options -Z rustdoc-scrape-examples") .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) .with_status(101) .with_stderr_data(str![[r#" ... [..]You shall not pass[..] ... "#]]) .run(); } #[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] fn no_fail_bad_example() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] "#, ) .file("examples/ex1.rs", "DOES NOT COMPILE") .file("examples/ex2.rs", "fn main() { foo::foo(); }") .file("src/lib.rs", "pub fn foo(){}") .build(); p.cargo("doc -Zunstable-options -Z rustdoc-scrape-examples") .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [SCRAPING] foo v0.0.1 ([ROOT]/foo) [WARNING] failed to scan example "ex1" in package `foo` for example code usage Try running with `--verbose` to see the error message. If an example should not be scanned, then consider adding `doc-scrape-examples = false` to its `[[example]]` definition in Cargo.toml [WARNING] `foo` (example "ex1") generated 1 warning [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); p.cargo("clean").run(); p.cargo("doc -v -Zunstable-options -Z rustdoc-scrape-examples") .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) .with_stderr_data( str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo[..] [SCRAPING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustdoc[..] --crate-name ex1[..] [RUNNING] `rustdoc[..] --crate-name ex2[..] [RUNNING] `rustdoc[..] --crate-name foo[..] [ERROR] expected one of `!` or `::`, found `NOT` --> examples/ex1.rs:1:6 | 1 | DOES NOT COMPILE | ^^^ expected one of `!` or `::` [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]] .unordered(), ) .run(); let doc_html = p.read_file("target/doc/foo/fn.foo.html"); assert!(doc_html.contains("Examples found in repository")); } #[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] fn no_scrape_with_dev_deps() { // Tests that a crate with dev-dependencies does not have its examples // scraped unless explicitly prompted to check them. See // `UnitGenerator::create_docscrape_proposals` for details on why. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dev-dependencies] a = {path = "a"} "#, ) .file("src/lib.rs", "") .file("examples/ex.rs", "fn main() { a::f(); }") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] "#, ) .file("a/src/lib.rs", "pub fn f() {}") .build(); // If --examples is not provided, then the example is not scanned, and a warning // should be raised. p.cargo("doc -Zunstable-options -Z rustdoc-scrape-examples") .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [WARNING] Rustdoc did not scrape the following examples because they require dev-dependencies: ex If you want Rustdoc to scrape these examples, then add `doc-scrape-examples = true` to the [[example]] target configuration of at least one example. [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); // If --examples is provided, then the example is scanned. p.cargo("doc --examples -Zunstable-options -Z rustdoc-scrape-examples") .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) .with_stderr_data( str![[r#" [CHECKING] a v0.0.1 ([ROOT]/foo/a) [CHECKING] foo v0.0.1 ([ROOT]/foo) [DOCUMENTING] a v0.0.1 ([ROOT]/foo/a) [SCRAPING] foo v0.0.1 ([ROOT]/foo) [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/ex/index.html "#]] .unordered(), ) .run(); } #[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] fn use_dev_deps_if_explicitly_enabled() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [[example]] name = "ex" doc-scrape-examples = true [dev-dependencies] a = {path = "a"} "#, ) .file("src/lib.rs", "") .file("examples/ex.rs", "fn main() { a::f(); }") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] "#, ) .file("a/src/lib.rs", "pub fn f() {}") .build(); // If --examples is not provided, then the example is never scanned. p.cargo("doc -Zunstable-options -Z rustdoc-scrape-examples") .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) .with_stderr_data( str![[r#" [LOCKING] 1 package to latest compatible version [CHECKING] a v0.0.1 ([ROOT]/foo/a) [CHECKING] foo v0.0.1 ([ROOT]/foo) [SCRAPING] foo v0.0.1 ([ROOT]/foo) [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [..] [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]] .unordered(), ) .run(); } #[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] fn only_scrape_documented_targets() { // package bar has doc = false and should not be eligible for documtation. let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [lib] doc = false [workspace] members = ["foo"] [dependencies] foo = {{ path = "foo" }} "# ), ) .file("src/lib.rs", "") .file("examples/ex.rs", "pub fn main() { foo::foo(); }") .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] "#, ) .file("foo/src/lib.rs", "pub fn foo() {}") .build(); p.cargo("doc --workspace -Zunstable-options -Zrustdoc-scrape-examples") .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) .run(); let doc_html = p.read_file("target/doc/foo/fn.foo.html"); let example_found = doc_html.contains("Examples found in repository"); assert!(!example_found); } #[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] fn issue_11496() { let p = project() .file( "Cargo.toml", r#" [package] name = "repro" version = "0.1.0" edition = "2021" [lib] proc-macro = true "#, ) .file("src/lib.rs", "") .file("examples/ex.rs", "fn main(){}") .build(); p.cargo("doc -Zunstable-options -Zrustdoc-scrape-examples") .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) .run(); } cargo-0.91.0/tests/testsuite/edition.rs000064400000000000000000000166501046102023000162120ustar 00000000000000//! Tests for edition setting. use crate::prelude::*; use cargo::core::Edition; use cargo_test_support::{basic_lib_manifest, project, str}; #[cargo_test] fn edition_works_for_build_script() { let p = project() .file( "Cargo.toml", r#" [package] name = 'foo' version = '0.1.0' edition = '2018' [build-dependencies] a = { path = 'a' } "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { a::foo(); } "#, ) .file("a/Cargo.toml", &basic_lib_manifest("a")) .file("a/src/lib.rs", "pub fn foo() {}") .build(); p.cargo("check -v").run(); } #[cargo_test] fn edition_unstable_gated() { // During the period where a new edition is coming up, but not yet stable, // this test will verify that it cannot be used on stable. If there is no // next edition, it does nothing. let next = match Edition::LATEST_UNSTABLE { Some(next) => next, None => { eprintln!("Next edition is currently not available, skipping test."); return; } }; let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "{}" "#, next ), ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(format!( "\ [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: feature `edition{next}` is required The package requires the Cargo feature called `edition{next}`, but that feature is not stabilized in this version of Cargo (1.[..]). Consider trying a newer version of Cargo (this may require the nightly release). See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#edition-{next} for more information about the status of this feature. ")) .run(); } #[cargo_test(nightly, reason = "fundamentally always nightly")] fn edition_unstable() { // During the period where a new edition is coming up, but not yet stable, // this test will verify that it can be used with `cargo-features`. If // there is no next edition, it does nothing. let next = match Edition::LATEST_UNSTABLE { Some(next) => next, None => { eprintln!("Next edition is currently not available, skipping test."); return; } }; let p = project() .file( "Cargo.toml", &format!( r#" cargo-features = ["edition{next}"] [package] name = "foo" version = "0.1.0" edition = "{next}" "#, next = next ), ) .file("src/lib.rs", "") .build(); p.cargo("check") .masquerade_as_nightly_cargo(&["always_nightly"]) .with_stderr_data(str![[r#" [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn unset_edition_with_unset_rust_version() { let p = project() .file( "Cargo.toml", r#" [package] name = 'foo' version = '0.1.0' "#, ) .file("src/lib.rs", "") .build(); p.cargo("check -v") .with_stderr_data(str![[r#" [WARNING] no edition set: defaulting to the 2015 edition while the latest is [..] [CHECKING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc [..] --edition=2015 [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn unset_edition_works_with_no_newer_compatible_edition() { let p = project() .file( "Cargo.toml", r#" [package] name = 'foo' version = '0.1.0' rust-version = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check -v") .with_stderr_data(str![[r#" [CHECKING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc [..] --edition=2015 [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn unset_edition_works_on_old_msrv() { let p = project() .file( "Cargo.toml", r#" [package] name = 'foo' version = '0.1.0' rust-version = "1.50" # contains 2018 edition "#, ) .file("src/lib.rs", "") .build(); p.cargo("check -v") .with_stderr_data(str![[r#" [WARNING] no edition set: defaulting to the 2015 edition while 2018 is compatible with `rust-version` [CHECKING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc [..] --edition=2015 [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn future_edition_is_gated() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" edition = "future" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: feature `unstable-editions` is required The package requires the Cargo feature called `unstable-editions`, but that feature is not stabilized in this version of Cargo ([..]). Consider trying a newer version of Cargo (this may require the nightly release). See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#unstable-editions for more information about the status of this feature. "#]]) .run(); // Repeat on nightly. p.cargo("check") .masquerade_as_nightly_cargo(&["unstable-editions"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: feature `unstable-editions` is required The package requires the Cargo feature called `unstable-editions`, but that feature is not stabilized in this version of Cargo ([..]). Consider adding `cargo-features = ["unstable-editions"]` to the top of Cargo.toml (above the [package] table) to tell Cargo you are opting in to use this unstable feature. See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#unstable-editions for more information about the status of this feature. "#]]) .run(); } #[cargo_test(nightly, reason = "future edition is always unstable")] fn future_edition_works() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["unstable-editions"] [package] name = "foo" edition = "future" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .masquerade_as_nightly_cargo(&["unstable-editions"]) .with_stderr_data(str![[r#" [CHECKING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } cargo-0.91.0/tests/testsuite/error.rs000064400000000000000000000010351046102023000156770ustar 00000000000000//! General error tests that don't belong anywhere else. use crate::prelude::*; use crate::utils::cargo_process; #[cargo_test] fn internal_error() { cargo_process("init") .env("__CARGO_TEST_INTERNAL_ERROR", "1") .with_status(101) .with_stderr_data(format!( "\ [ERROR] internal error test [NOTE] this is an unexpected cargo internal error [NOTE] we would appreciate a bug report: https://github.com/rust-lang/cargo/issues/ [NOTE] cargo {} ", cargo::version() )) .run(); } cargo-0.91.0/tests/testsuite/feature_unification.rs000064400000000000000000001115121046102023000205730ustar 00000000000000//! Tests for workspace feature unification. use crate::prelude::*; use crate::utils::cargo_process; use cargo_test_support::{ basic_manifest, compare::assert_e2e, project, registry::{Dependency, Package}, str, }; #[cargo_test] fn workspace_feature_unification() { let p = project() .file( ".cargo/config.toml", r#" [resolver] feature-unification = "workspace" "#, ) .file( "Cargo.toml", r#" [workspace] resolver = "2" members = ["common", "a", "b"] "#, ) .file( "common/Cargo.toml", r#" [package] name = "common" version = "0.1.0" edition = "2021" [features] a = [] b = [] "#, ) .file( "common/src/lib.rs", r#" #[cfg(not(all(feature = "a", feature = "b")))] compile_error!("features were not unified"); "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2021" [dependencies] common = { path = "../common", features = ["a"] } "#, ) .file("a/src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" edition = "2021" [dependencies] common = { path = "../common", features = ["b"] } "#, ) .file("b/src/lib.rs", "") .build(); p.cargo("check -p common") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .with_stderr_data(str![[r#" [CHECKING] common v0.1.0 ([ROOT]/foo/common) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check -p a") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .with_stderr_data(str![[r#" [CHECKING] a v0.1.0 ([ROOT]/foo/a) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check -p b") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .with_stderr_data(str![[r#" [CHECKING] b v0.1.0 ([ROOT]/foo/b) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn package_feature_unification() { Package::new("outside", "0.1.0") .feature("a", &[]) .feature("b", &[]) .file( "src/lib.rs", r#" #[cfg(all(feature = "a", feature = "b"))] compile_error!("features were unified"); #[cfg(feature = "a")] pub fn a() {} #[cfg(feature = "b")] pub fn b() {} "#, ) .publish(); let p = project() .file( ".cargo/config.toml", r#" [resolver] feature-unification = "package" "#, ) .file( "Cargo.toml", r#" [workspace] resolver = "2" members = ["common", "a", "b"] "#, ) .file( "common/Cargo.toml", r#" [package] name = "common" version = "0.1.0" edition = "2021" [features] a = [] b = [] "#, ) .file( "common/src/lib.rs", r#" #[cfg(all(feature = "a", feature = "b"))] compile_error!("features were unified"); #[cfg(feature = "a")] pub fn a() {} #[cfg(feature = "b")] pub fn b() {} "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2021" [dependencies] common = { path = "../common", features = ["a"] } outside = { version = "0.1.0", features = ["a"] } "#, ) .file("a/src/lib.rs", "pub use common::a;") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" edition = "2021" [dependencies] common = { path = "../common", features = ["b"] } outside = { version = "0.1.0", features = ["b"] } "#, ) .file("b/src/lib.rs", "pub use common::b;") .build(); p.cargo("check -p common") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [CHECKING] common v0.1.0 ([ROOT]/foo/common) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check -p a") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .with_stderr_data( str![[r#" [DOWNLOADING] crates ... [DOWNLOADED] outside v0.1.0 (registry `dummy-registry`) [CHECKING] outside v0.1.0 [CHECKING] common v0.1.0 ([ROOT]/foo/common) [CHECKING] a v0.1.0 ([ROOT]/foo/a) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); p.cargo("check -p b") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .with_stderr_data( str![[r#" [CHECKING] outside v0.1.0 [CHECKING] common v0.1.0 ([ROOT]/foo/common) [CHECKING] b v0.1.0 ([ROOT]/foo/b) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); p.cargo("check -p a -p b") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .with_stderr_data( str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); p.cargo("check") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Sanity check that compilation without package feature unification does not work p.cargo("check -p a -p b") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "selected") .with_status(101) .with_stderr_contains("[ERROR] features were unified") .run(); } #[cargo_test] fn package_feature_unification_default_features() { let p = project() .file( ".cargo/config.toml", r#" [resolver] feature-unification = "package" "#, ) .file( "Cargo.toml", r#" [workspace] resolver = "2" members = ["common", "a", "b"] "#, ) .file( "common/Cargo.toml", r#" [package] name = "common" version = "0.1.0" edition = "2021" [features] default = ["a"] a = [] b = [] "#, ) .file( "common/src/lib.rs", r#" #[cfg(all(feature = "a", feature = "b"))] compile_error!("features were unified"); #[cfg(feature = "a")] pub fn a() {} #[cfg(feature = "b")] pub fn b() {} "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2021" [dependencies] common = { path = "../common" } "#, ) .file("a/src/lib.rs", "pub use common::a;") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" edition = "2021" [dependencies] common = { path = "../common", features = ["b"], default-features = false } "#, ) .file("b/src/lib.rs", "pub use common::b;") .build(); p.cargo("check -p common") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .with_stderr_data(str![[r#" [CHECKING] common v0.1.0 ([ROOT]/foo/common) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check -p a") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .with_stderr_data(str![[r#" [CHECKING] a v0.1.0 ([ROOT]/foo/a) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check -p b") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .with_stderr_data(str![[r#" [CHECKING] common v0.1.0 ([ROOT]/foo/common) [CHECKING] b v0.1.0 ([ROOT]/foo/b) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .with_stderr_data( str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn package_feature_unification_cli_features() { Package::new("outside", "0.1.0") .feature("a", &[]) .feature("b", &[]) .file( "src/lib.rs", r#" #[cfg(all(feature = "a", feature = "b"))] compile_error!("features were unified"); #[cfg(feature = "a")] pub fn a() {} #[cfg(feature = "b")] pub fn b() {} "#, ) .publish(); let p = project() .file( ".cargo/config.toml", r#" [resolver] feature-unification = "package" "#, ) .file( "Cargo.toml", r#" [workspace] resolver = "2" members = ["common", "a", "b"] "#, ) .file( "common/Cargo.toml", r#" [package] name = "common" version = "0.1.0" edition = "2021" [features] a = [] b = [] "#, ) .file( "common/src/lib.rs", r#" #[cfg(all(feature = "a", feature = "b"))] compile_error!("features were unified"); #[cfg(feature = "a")] pub fn a() {} #[cfg(feature = "b")] pub fn b() {} "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2021" [dependencies] common = { path = "../common" } outside = "0.1.0" [features] a = ["common/a", "outside/a"] "#, ) .file("a/src/lib.rs", "pub use common::a;") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" edition = "2021" [dependencies] common = { path = "../common", features = ["b"] } outside = "0.1.0" [features] b = ["common/b", "outside/b"] "#, ) .file("b/src/lib.rs", "pub use common::b;") .build(); p.cargo("check -p a -p b -F a,b") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .with_stderr_data( str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [CHECKING] common v0.1.0 ([ROOT]/foo/common) [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] outside v0.1.0 (registry `dummy-registry`) [CHECKING] outside v0.1.0 [CHECKING] b v0.1.0 ([ROOT]/foo/b) [CHECKING] a v0.1.0 ([ROOT]/foo/a) "#]] .unordered(), ) .run(); p.cargo("check --workspace --exclude common -F a,b") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .with_stderr_data( str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); p.cargo("check -p a -p b -F a/a,b/b") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .with_stderr_data( str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); p.cargo("check -p a -p b -F a,b,c") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] none of the selected packages contains this feature: c selected packages: a, b "#]]) .run(); p.cargo("check -p a -F b") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] the package 'a' does not contain this feature: b [HELP] packages with the missing feature: common, b "#]]) .run(); p.cargo("check -p a -F a/a,common/b") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .with_status(101) .with_stderr_contains("[ERROR] features were unified") .run(); p.cargo("check -p a -F a/a,outside/b") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .with_status(101) .with_stderr_contains("[ERROR] features were unified") .run(); // Sanity check that compilation without package feature unification does not work p.cargo("check -p a -p b -F a,b") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "selected") .with_status(101) .with_stderr_contains("[ERROR] features were unified") .run(); } #[cargo_test] fn package_feature_unification_weak_dependencies() { let p = project() .file( ".cargo/config.toml", r#" [resolver] feature-unification = "package" "#, ) .file( "Cargo.toml", r#" [workspace] resolver = "2" members = ["common", "a", "b"] "#, ) .file( "common/Cargo.toml", r#" [package] name = "common" version = "0.1.0" edition = "2021" [features] a = [] b = [] "#, ) .file( "common/src/lib.rs", r#" #[cfg(all(feature = "a", feature = "b"))] compile_error!("features were unified"); #[cfg(feature = "a")] pub fn a() {} #[cfg(feature = "b")] pub fn b() {} "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2021" [dependencies] common = { path = "../common", optional = true } [features] default = ["dep:common", "common?/a"] "#, ) .file("a/src/lib.rs", "pub use common::a;") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" edition = "2021" [dependencies] common = { path = "../common", optional = true } [features] default = ["dep:common", "common?/b"] "#, ) .file("b/src/lib.rs", "pub use common::b;") .build(); p.cargo("check -p a -p b") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .with_stderr_data( str![[r#" [CHECKING] common v0.1.0 ([ROOT]/foo/common) [CHECKING] a v0.1.0 ([ROOT]/foo/a) [CHECKING] b v0.1.0 ([ROOT]/foo/b) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); p.cargo("check") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .with_stderr_data(str![[r#" [CHECKING] common v0.1.0 ([ROOT]/foo/common) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Sanity check that compilation without package feature unification does not work p.cargo("check -p a -p b") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "selected") .with_status(101) .with_stderr_contains("[ERROR] features were unified") .run(); } #[cargo_test] fn feature_unification_cargo_tree() { Package::new("outside", "0.1.0") .feature("a", &[]) .feature("b", &[]) .file( "src/lib.rs", r#" #[cfg(all(feature = "a", feature = "b"))] compile_error!("features were unified"); #[cfg(feature = "a")] pub fn a() {} #[cfg(feature = "b")] pub fn b() {} "#, ) .publish(); let p = project() .file( "Cargo.toml", r#" [workspace] resolver = "2" members = ["common", "a", "b"] "#, ) .file( "common/Cargo.toml", r#" [package] name = "common" version = "0.1.0" edition = "2021" [features] a = [] b = [] "#, ) .file( "common/src/lib.rs", r#" #[cfg(all(feature = "a", feature = "b"))] compile_error!("features were unified"); #[cfg(feature = "a")] pub fn a() {} #[cfg(feature = "b")] pub fn b() {} "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2021" [dependencies] common = { path = "../common", features = ["a"] } outside = { version = "0.1.0", features = ["a"] } "#, ) .file("a/src/lib.rs", "pub use common::a;") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" edition = "2021" [dependencies] common = { path = "../common", features = ["b"] } outside = { version = "0.1.0", features = ["b"] } "#, ) .file("b/src/lib.rs", "pub use common::b;") .build(); p.cargo("tree -e features") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "selected") .with_stdout_data(str![[r#" a v0.1.0 ([ROOT]/foo/a) ├── common feature "a" │ └── common v0.1.0 ([ROOT]/foo/common) ├── common feature "default" (command-line) │ └── common v0.1.0 ([ROOT]/foo/common) ├── outside feature "a" │ └── outside v0.1.0 └── outside feature "default" └── outside v0.1.0 b v0.1.0 ([ROOT]/foo/b) ├── common feature "b" │ └── common v0.1.0 ([ROOT]/foo/common) ├── common feature "default" (command-line) (*) ├── outside feature "b" │ └── outside v0.1.0 └── outside feature "default" (*) common v0.1.0 ([ROOT]/foo/common) "#]]) .run(); p.cargo("tree -e features") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "workspace") .with_stdout_data(str![[r#" a v0.1.0 ([ROOT]/foo/a) ├── common feature "a" │ └── common v0.1.0 ([ROOT]/foo/common) ├── common feature "default" (command-line) │ └── common v0.1.0 ([ROOT]/foo/common) ├── outside feature "a" │ └── outside v0.1.0 └── outside feature "default" └── outside v0.1.0 b v0.1.0 ([ROOT]/foo/b) ├── common feature "b" │ └── common v0.1.0 ([ROOT]/foo/common) ├── common feature "default" (command-line) (*) ├── outside feature "b" │ └── outside v0.1.0 └── outside feature "default" (*) common v0.1.0 ([ROOT]/foo/common) "#]]) .run(); p.cargo("tree -e features") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "package") .with_stdout_data(str![[r#" common v0.1.0 ([ROOT]/foo/common) a v0.1.0 ([ROOT]/foo/a) ├── common feature "a" │ └── common v0.1.0 ([ROOT]/foo/common) ├── common feature "default" │ └── common v0.1.0 ([ROOT]/foo/common) ├── outside feature "a" │ └── outside v0.1.0 └── outside feature "default" └── outside v0.1.0 b v0.1.0 ([ROOT]/foo/b) ├── common feature "b" │ └── common v0.1.0 ([ROOT]/foo/common) ├── common feature "default" │ └── common v0.1.0 ([ROOT]/foo/common) ├── outside feature "b" │ └── outside v0.1.0 └── outside feature "default" └── outside v0.1.0 "#]]) .run(); } #[cargo_test] fn cargo_install_ignores_config() { let p = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2021" [dependencies] common = { path = "common", features = ["a"] } [workspace] members = ["common", "b"] "#, ) .file("src/main.rs", "fn main() {}") .file( "common/Cargo.toml", r#" [package] name = "common" version = "0.1.0" edition = "2021" [features] a = [] b = [] "#, ) .file( "common/src/lib.rs", r#" #[cfg(all(feature = "a", feature = "b"))] compile_error!("features should not be unified"); "#, ) .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" edition = "2021" [dependencies] common = { path = "../common", features = ["b"] } "#, ) .file("b/src/lib.rs", "") .build(); cargo_process("install --path") .arg(p.root()) .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "workspace") .with_stderr_data(str![[r#" [INSTALLING] a v0.1.0 ([ROOT]/foo) [COMPILING] common v0.1.0 ([ROOT]/foo/common) [COMPILING] a v0.1.0 ([ROOT]/foo) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/a[EXE] [INSTALLED] package `a v0.1.0 ([ROOT]/foo)` (executable `a[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); cargo_process("install --path") .arg(p.root()) .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "package") .with_stderr_data(str![[r#" [INSTALLING] a v0.1.0 ([ROOT]/foo) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [REPLACING] [ROOT]/home/.cargo/bin/a[EXE] [REPLACED] package `a v0.1.0 ([ROOT]/foo)` with `a v0.1.0 ([ROOT]/foo)` (executable `a[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); } #[cargo_test] fn unstable_config_on_stable() { let p = project() .file( "Cargo.toml", r#" [workspace] resolver = "2" members = ["bar"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "") .build(); p.cargo("check") .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "workspace") .with_stderr_data(str![[r#" [WARNING] ignoring `resolver.feature-unification` without `-Zfeature-unification` [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn cargo_fix_works() { let p = project() .file( "Cargo.toml", r#" # Before project [ project ] # After project header # After project header line name = "foo" edition = "2021" # After project table "#, ) .file("src/lib.rs", "") .build(); p.cargo("fix --edition --allow-no-vcs") .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "package") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .with_stderr_data(str![[r#" [MIGRATING] Cargo.toml from 2021 edition to 2024 [FIXED] Cargo.toml (1 fix) [CHECKING] foo v0.0.0 ([ROOT]/foo) [MIGRATING] src/lib.rs from 2021 edition to 2024 [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert_e2e().eq( p.read_file("Cargo.toml"), str![[r#" # Before project [ package ] # After project header # After project header line name = "foo" edition = "2021" # After project table "#]], ); } #[cargo_test] fn edition_v2_resolver_report() { // Show a report if the V2 resolver shows differences. Package::new("common", "1.0.0") .feature("f1", &[]) .feature("dev-feat", &[]) .add_dep(Dependency::new("opt_dep", "1.0").optional(true)) .publish(); Package::new("opt_dep", "1.0.0").publish(); Package::new("bar", "1.0.0") .add_dep( Dependency::new("common", "1.0") .target("cfg(whatever)") .enable_features(&["f1"]), ) .publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar"] [package] name = "foo" version = "0.1.0" edition = "2018" [dependencies] common = "1.0" bar = "1.0" [build-dependencies] common = { version = "1.0", features = ["opt_dep"] } [dev-dependencies] common = { version="1.0", features=["dev-feat"] } "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2018" "#, ) .file("bar/src/lib.rs", "") .build(); p.cargo("fix --edition --allow-no-vcs --workspace") .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "package") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .with_status(101) .with_stderr_data( str![[r#" [MIGRATING] Cargo.toml from 2018 edition to 2021 [UPDATING] `dummy-registry` index [LOCKING] 3 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] common v1.0.0 (registry `dummy-registry`) [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [DOWNLOADED] opt_dep v1.0.0 (registry `dummy-registry`) [MIGRATING] bar/Cargo.toml from 2018 edition to 2021 [ERROR] cannot fix edition when using `feature-unification = "package"`. "#]] .unordered(), ) .run(); } #[cargo_test] fn feature_unification_of_cli_features_within_workspace() { let p = project() .file( "Cargo.toml", r#" [workspace] resolver = "2" members = ["parent", "child", "grandchild"] "#, ) .file( "grandchild/Cargo.toml", r#" [package] name = "grandchild" version = "0.1.0" edition = "2021" [features] a = [] "#, ) .file( "grandchild/src/lib.rs", r#" #[cfg(feature = "a")] pub fn a() {} "#, ) .file( "child/Cargo.toml", r#" [package] name = "child" version = "0.1.0" edition = "2021" [dependencies] grandchild = { path = "../grandchild" } "#, ) .file("child/src/lib.rs", "pub use grandchild::*;") .file( "parent/Cargo.toml", r#" [package] name = "parent" version = "0.1.0" edition = "2021" [dependencies] child = { path = "../child" } "#, ) .file("parent/src/lib.rs", "pub use child::a;") .build(); p.cargo("check -p parent -F grandchild/a") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "package") .with_status(101) .with_stderr_data(str![[r#" [ERROR] the package 'parent' does not contain this feature: grandchild/a "#]]) .run(); p.cargo("check -p parent -F grandchild/a") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "workspace") .with_status(101) .with_stderr_data(str![[r#" [ERROR] the package 'parent' does not contain this feature: grandchild/a "#]]) .run(); p.cargo("check -p parent -F grandchild/a") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "selected") .with_status(101) .with_stderr_data(str![[r#" [ERROR] the package 'parent' does not contain this feature: grandchild/a "#]]) .run(); p.cargo("check -p child -F grandchild/a") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "package") .with_stderr_data(str![[r#" [CHECKING] grandchild v0.1.0 ([ROOT]/foo/grandchild) [CHECKING] child v0.1.0 ([ROOT]/foo/child) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check -p child -F grandchild/a") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "workspace") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check -p child -F grandchild/a") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "selected") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check -F grandchild/a") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "package") .with_stderr_data(str![[r#" [CHECKING] parent v0.1.0 ([ROOT]/foo/parent) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check -F grandchild/a") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "workspace") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check -F grandchild/a") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "selected") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check -F grandchild/a --workspace --exclude grandchild") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "package") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check -F grandchild/a --workspace --exclude grandchild") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "workspace") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check -F grandchild/a --workspace --exclude grandchild") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "selected") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check -F grandchild/a --workspace --exclude grandchild --exclude child") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "package") .with_status(101) .with_stderr_data(str![[r#" [ERROR] the package 'parent' does not contain this feature: grandchild/a "#]]) .run(); p.cargo("check -F grandchild/a --workspace --exclude grandchild --exclude child") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "workspace") .with_status(101) .with_stderr_data(str![[r#" [ERROR] the package 'parent' does not contain this feature: grandchild/a "#]]) .run(); p.cargo("check -F grandchild/a --workspace --exclude grandchild --exclude child") .arg("-Zfeature-unification") .masquerade_as_nightly_cargo(&["feature-unification"]) .env("CARGO_RESOLVER_FEATURE_UNIFICATION", "selected") .with_status(101) .with_stderr_data(str![[r#" [ERROR] the package 'parent' does not contain this feature: grandchild/a "#]]) .run(); } cargo-0.91.0/tests/testsuite/features.rs000064400000000000000000001634611046102023000164000ustar 00000000000000//! Tests for `[features]` table. use crate::prelude::*; use cargo_test_support::registry::{Dependency, Package}; use cargo_test_support::str; use cargo_test_support::{basic_manifest, project}; #[cargo_test] fn feature_activates_missing_feature() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] bar = ["baz"] "#, ) .file("src/main.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: feature `bar` includes `baz` which is neither a dependency nor another feature [HELP] a feature with a similar name exists: `bar` "#]]) .run(); } #[cargo_test] fn feature_activates_typoed_feature() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] bar = ["baz"] jaz = [] "#, ) .file("src/main.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: feature `bar` includes `baz` which is neither a dependency nor another feature [HELP] a feature with a similar name exists: `bar` "#]]) .run(); } #[cargo_test] fn empty_feature_name() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] "" = [] "#, ) .file("src/main.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] feature name cannot be empty --> Cargo.toml:9:17 | 9 | "" = [] | ^^ | "#]]) .run(); } #[cargo_test] fn same_name() { // Feature with the same name as a dependency. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] bar = ["baz"] baz = [] [dependencies.bar] path = "bar" "#, ) .file("src/main.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "1.0.0")) .file("bar/src/lib.rs", "") .build(); p.cargo("tree -f") .arg("{p} [{f}]") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version "#]]) .with_stdout_data(str![[r#" foo v0.0.1 ([ROOT]/foo) [] └── bar v1.0.0 ([ROOT]/foo/bar) [] "#]]) .run(); p.cargo("tree --features bar -f") .arg("{p} [{f}]") .with_stderr_data("") .with_stdout_data(str![[r#" foo v0.0.1 ([ROOT]/foo) [bar,baz] └── bar v1.0.0 ([ROOT]/foo/bar) [] "#]]) .run(); } #[cargo_test] fn feature_activates_required_dependency() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] bar = ["baz"] [dependencies.baz] path = "foo" "#, ) .file("src/main.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: feature `bar` includes `baz`, but `baz` is not an optional dependency A non-optional dependency of the same name is defined; consider adding `optional = true` to its definition. "#]]) .run(); } #[cargo_test] fn dependency_activates_missing_feature() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "bar" features = ["bar"] "#, ) .file("src/main.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to select a version for `bar`. ... required by package `foo v0.0.1 ([ROOT]/foo)` versions that meet the requirements `*` are: 0.0.1 package `foo` depends on `bar` with feature `bar` but `bar` does not have that feature. failed to select a version for `bar` which could resolve this conflict "#]]) .run(); p.change_file("Cargo.toml", &basic_manifest("foo", "0.0.1")); p.cargo("check --features test") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package `foo v0.0.1 ([ROOT]/foo)` does not have the feature `test` "#]]) .run(); } #[cargo_test] fn dependency_activates_typoed_feature() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "bar" features = ["bar"] "#, ) .file("src/main.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [features] baz = [] "#, ) .file("bar/src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to select a version for `bar`. ... required by package `foo v0.0.1 ([ROOT]/foo)` versions that meet the requirements `*` are: 0.0.1 package `foo` depends on `bar` with feature `bar` but `bar` does not have that feature. package `bar` does have feature `baz` failed to select a version for `bar` which could resolve this conflict "#]]) .run(); } #[cargo_test] fn optional_dev_dependency() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dev-dependencies.bar] path = "bar" optional = true "#, ) .file("src/main.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: dev-dependencies are not allowed to be optional: `bar` "#]]) .run(); } #[cargo_test] fn feature_activates_missing_dep_feature() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] foo = ["bar/baz"] "#, ) .file("src/main.rs", "") .build(); p.cargo("check --features foo") .with_status(101) .with_stderr_data(str![[r#" [ERROR] feature `foo` includes `bar/baz`, but `bar` is not a dependency --> Cargo.toml:9:23 | 9 | foo = ["bar/baz"] | ^^^^^^^^^^^ | [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` "#]]) .run(); } #[cargo_test] fn feature_activates_feature_inside_feature() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] foo = ["bar/baz"] bar = [] "#, ) .file("src/main.rs", "") .build(); p.cargo("check --features foo") .with_status(101) .with_stderr_data(str![[r#" [ERROR] feature `foo` includes `bar/baz`, but `bar` is not a dependency --> Cargo.toml:9:23 | 9 | foo = ["bar/baz"] | ^^^^^^^^^^^ | [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` "#]]) .run(); } #[cargo_test] fn dependency_activates_dep_feature() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "bar" features = ["foo/bar"] "#, ) .file("src/main.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "") .build(); p.cargo("check --features foo") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: feature `foo/bar` in dependency `bar` is not allowed to contain slashes If you want to enable features [..] "#]]) .run(); } #[cargo_test] fn cli_activates_required_dependency() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "bar" "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "") .build(); p.cargo("check --features bar") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [ERROR] package `foo v0.0.1 ([ROOT]/foo)` does not have feature `bar` [HELP] a depednency with that name exists but it is required dependency and only optional dependencies can be used as features. "#]]) .with_status(101) .run(); } #[cargo_test] fn dependency_activates_required_dependency() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "bar" features = ["baz"] "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [dependencies.baz] path = "baz" "#, ) .file("bar/src/lib.rs", "") .file("bar/baz/Cargo.toml", &basic_manifest("baz", "0.0.1")) .file("bar/baz/src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [ERROR] failed to select a version for `bar`. ... required by package `foo v0.0.1 ([ROOT]/foo)` versions that meet the requirements `*` are: 0.0.1 package `foo` depends on `bar` with feature `baz` but `bar` does not have that feature. A required dependency with that name exists, but only optional dependencies can be used as features. failed to select a version for `bar` which could resolve this conflict "#]]) .with_status(101) .run(); } #[cargo_test] fn no_transitive_dep_feature_requirement() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.derived] path = "derived" [features] default = ["derived/bar/qux"] "#, ) .file( "src/main.rs", r#" extern crate derived; fn main() { derived::test(); } "#, ) .file( "derived/Cargo.toml", r#" [package] name = "derived" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "../bar" "#, ) .file("derived/src/lib.rs", "extern crate bar; pub use bar::test;") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [features] qux = [] "#, ) .file( "bar/src/lib.rs", r#" #[cfg(feature = "qux")] pub fn test() { print!("test"); } "#, ) .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: multiple slashes in feature `derived/bar/qux` (included by feature `default`) are not allowed "#]]) .run(); } #[cargo_test] fn no_feature_doesnt_build() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "bar" optional = true "#, ) .file( "src/main.rs", r#" #[cfg(feature = "bar")] extern crate bar; #[cfg(feature = "bar")] fn main() { bar::bar(); println!("bar") } #[cfg(not(feature = "bar"))] fn main() {} "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "pub fn bar() {}") .build(); p.cargo("build") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.process(&p.bin("foo")).with_stdout_data("").run(); let expected = if cfg!(target_os = "windows") && cfg!(target_env = "msvc") { str![[r#" [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [RUNNING] `rustc --crate-name bar [..]` [DIRTY] foo v0.0.1 ([ROOT]/foo): the list of features changed [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] } else { str![[r#" [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [RUNNING] `rustc --crate-name bar [..]` [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] }; p.cargo("build --features bar -v") .with_stderr_data(expected) .run(); p.process(&p.bin("foo")) .with_stdout_data(str![[r#" bar "#]]) .run(); } #[cargo_test] fn default_feature_pulled_in() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] default = ["bar"] [dependencies.bar] path = "bar" optional = true "#, ) .file( "src/main.rs", r#" #[cfg(feature = "bar")] extern crate bar; #[cfg(feature = "bar")] fn main() { bar::bar(); println!("bar") } #[cfg(not(feature = "bar"))] fn main() {} "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "pub fn bar() {}") .build(); p.cargo("build") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.process(&p.bin("foo")) .with_stdout_data(str![[r#" bar "#]]) .run(); let expected = if cfg!(target_os = "windows") && cfg!(target_env = "msvc") { str![[r#" [DIRTY] foo v0.0.1 ([ROOT]/foo): the list of features changed [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] } else { str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] }; p.cargo("build --no-default-features -v") .with_stderr_data(expected) .run(); p.process(&p.bin("foo")).with_stdout_data("").run(); } #[cargo_test] fn cyclic_feature() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] default = ["default"] "#, ) .file("src/main.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] cyclic feature dependency: feature `default` depends on itself "#]]) .run(); } #[cargo_test] fn cyclic_feature2() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] foo = ["bar"] bar = ["foo"] "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn groups_on_groups_on_groups() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] default = ["f1"] f1 = ["f2", "bar"] f2 = ["f3", "f4"] f3 = ["f5", "f6", "baz"] f4 = ["f5", "f7"] f5 = ["f6"] f6 = ["f7"] f7 = ["bar"] [dependencies.bar] path = "bar" optional = true [dependencies.baz] path = "baz" optional = true "#, ) .file( "src/main.rs", r#" #[allow(unused_extern_crates)] extern crate bar; #[allow(unused_extern_crates)] extern crate baz; fn main() {} "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "pub fn bar() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.0.1")) .file("baz/src/lib.rs", "pub fn baz() {}") .build(); p.cargo("check") .with_stderr_data( str![[r#" [LOCKING] 2 packages to latest compatible versions [CHECKING] bar v0.0.1 ([ROOT]/foo/bar) [CHECKING] baz v0.0.1 ([ROOT]/foo/baz) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn many_cli_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "bar" optional = true [dependencies.baz] path = "baz" optional = true "#, ) .file( "src/main.rs", r#" #[allow(unused_extern_crates)] extern crate bar; #[allow(unused_extern_crates)] extern crate baz; fn main() {} "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "pub fn bar() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.0.1")) .file("baz/src/lib.rs", "pub fn baz() {}") .build(); p.cargo("check --features") .arg("bar baz") .with_stderr_data( str![[r#" [LOCKING] 2 packages to latest compatible versions [CHECKING] bar v0.0.1 ([ROOT]/foo/bar) [CHECKING] baz v0.0.1 ([ROOT]/foo/baz) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn union_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.d1] path = "d1" features = ["f1"] [dependencies.d2] path = "d2" features = ["f2"] "#, ) .file( "src/main.rs", r#" #[allow(unused_extern_crates)] extern crate d1; extern crate d2; fn main() { d2::f1(); d2::f2(); } "#, ) .file( "d1/Cargo.toml", r#" [package] name = "d1" version = "0.0.1" edition = "2015" authors = [] [features] f1 = ["d2"] [dependencies.d2] path = "../d2" features = ["f1"] optional = true "#, ) .file("d1/src/lib.rs", "") .file( "d2/Cargo.toml", r#" [package] name = "d2" version = "0.0.1" edition = "2015" authors = [] [features] f1 = [] f2 = [] "#, ) .file( "d2/src/lib.rs", r#" #[cfg(feature = "f1")] pub fn f1() {} #[cfg(feature = "f2")] pub fn f2() {} "#, ) .build(); p.cargo("check") .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [CHECKING] d2 v0.0.1 ([ROOT]/foo/d2) [CHECKING] d1 v0.0.1 ([ROOT]/foo/d1) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn many_features_no_rebuilds() { let p = project() .file( "Cargo.toml", r#" [package] name = "b" version = "0.1.0" edition = "2015" authors = [] [dependencies.a] path = "a" features = ["fall"] "#, ) .file("src/main.rs", "fn main() {}") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" authors = [] [features] ftest = [] ftest2 = [] fall = ["ftest", "ftest2"] "#, ) .file("a/src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [CHECKING] a v0.1.0 ([ROOT]/foo/a) [CHECKING] b v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.root().move_into_the_past(); p.cargo("check -v") .with_stderr_data(str![[r#" [FRESH] a v0.1.0 ([ROOT]/foo/a) [FRESH] b v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } // Tests that all cmd lines work with `--features ""` #[cargo_test] fn empty_features() { let p = project().file("src/main.rs", "fn main() {}").build(); p.cargo("check --features").arg("").run(); } // Tests that all cmd lines work with `--features ""` #[cargo_test] fn transitive_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] foo = ["bar/baz"] [dependencies.bar] path = "bar" "#, ) .file("src/main.rs", "extern crate bar; fn main() { bar::baz(); }") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [features] baz = [] "#, ) .file( "bar/src/lib.rs", r#"#[cfg(feature = "baz")] pub fn baz() {}"#, ) .build(); p.cargo("check --features foo").run(); } #[cargo_test] fn everything_in_the_lockfile() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] f1 = ["d1/f1"] f2 = ["d2"] [dependencies.d1] path = "d1" [dependencies.d2] path = "d2" optional = true [dependencies.d3] path = "d3" optional = true "#, ) .file("src/main.rs", "fn main() {}") .file( "d1/Cargo.toml", r#" [package] name = "d1" version = "0.0.1" edition = "2015" authors = [] [features] f1 = [] "#, ) .file("d1/src/lib.rs", "") .file("d2/Cargo.toml", &basic_manifest("d2", "0.0.2")) .file("d2/src/lib.rs", "") .file( "d3/Cargo.toml", r#" [package] name = "d3" version = "0.0.3" edition = "2015" authors = [] [features] f3 = [] "#, ) .file("d3/src/lib.rs", "") .build(); p.cargo("fetch").run(); let lockfile = p.read_lockfile(); assert!( lockfile.contains(r#"name = "d1""#), "d1 not found\n{}", lockfile ); assert!( lockfile.contains(r#"name = "d2""#), "d2 not found\n{}", lockfile ); assert!( lockfile.contains(r#"name = "d3""#), "d3 not found\n{}", lockfile ); } #[cargo_test] fn no_rebuild_when_frobbing_default_feature() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [dependencies] a = { path = "a" } b = { path = "b" } "#, ) .file("src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" edition = "2015" authors = [] [dependencies] a = { path = "../a", features = ["f1"], default-features = false } "#, ) .file("b/src/lib.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" authors = [] [features] default = ["f1"] f1 = [] "#, ) .file("a/src/lib.rs", "") .build(); p.cargo("check").run(); p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn unions_work_with_no_default_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [dependencies] a = { path = "a" } b = { path = "b" } "#, ) .file("src/lib.rs", "extern crate a; pub fn foo() { a::a(); }") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" edition = "2015" authors = [] [dependencies] a = { path = "../a", features = [], default-features = false } "#, ) .file("b/src/lib.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" authors = [] [features] default = ["f1"] f1 = [] "#, ) .file("a/src/lib.rs", r#"#[cfg(feature = "f1")] pub fn a() {}"#) .build(); p.cargo("check").run(); p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn optional_and_dev_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "test" version = "0.1.0" edition = "2015" authors = [] [dependencies] foo = { path = "foo", optional = true } [dev-dependencies] foo = { path = "foo" } "#, ) .file("src/lib.rs", "") .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("foo/src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [CHECKING] test v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn activating_feature_activates_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "test" version = "0.1.0" edition = "2015" authors = [] [dependencies] foo = { path = "foo", optional = true } [features] a = ["foo/a"] "#, ) .file( "src/lib.rs", "extern crate foo; pub fn bar() { foo::bar(); }", ) .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [features] a = [] "#, ) .file("foo/src/lib.rs", r#"#[cfg(feature = "a")] pub fn bar() {}"#) .build(); p.cargo("check --features a -v").run(); } #[cargo_test] fn activating_feature_does_not_activate_transitive_dev_dependency() { let p = project() .no_manifest() .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.0.0" edition = "2021" [features] f = ["b/f"] [dependencies] b = { path = "../b" } "#, ) .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.0.0" edition = "2021" [features] f = ["c/f"] [dev-dependencies] c = { path = "../c" } "#, ) .file( "c/Cargo.toml", r#" [package] name = "c" version = "0.0.0" edition = "2021" [features] f = [] "#, ) .file("a/src/lib.rs", "") .file("b/src/lib.rs", "") .file("c/src/lib.rs", "compile_error!") .build(); p.cargo("check --manifest-path a/Cargo.toml --features f") .run(); } #[cargo_test] fn dep_feature_in_cmd_line() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.derived] path = "derived" "#, ) .file( "src/main.rs", r#" extern crate derived; fn main() { derived::test(); } "#, ) .file( "derived/Cargo.toml", r#" [package] name = "derived" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "../bar" [features] default = [] derived-feat = ["bar/some-feat"] "#, ) .file("derived/src/lib.rs", "extern crate bar; pub use bar::test;") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [features] some-feat = [] "#, ) .file( "bar/src/lib.rs", r#" #[cfg(feature = "some-feat")] pub fn test() { print!("test"); } "#, ) .build(); // The foo project requires that feature "some-feat" in "bar" is enabled. // Building without any features enabled should fail: p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" ... error[E0432]: unresolved import `bar::test` ... "#]]) .run(); // We should be able to enable the feature "derived-feat", which enables "some-feat", // on the command line. The feature is enabled, thus building should be successful: p.cargo("check --features derived/derived-feat").run(); // Trying to enable features of transitive dependencies is an error p.cargo("check --features bar/some-feat") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package `foo v0.0.1 ([ROOT]/foo)` does not have a dependency named `bar` "#]]) .run(); // Hierarchical feature specification should still be disallowed p.cargo("check --features derived/bar/some-feat") .with_status(101) .with_stderr_data(str![[r#" [ERROR] multiple slashes in feature `derived/bar/some-feat` is not allowed "#]]) .run(); } #[cargo_test] fn all_features_flag_enables_all_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] foo = [] bar = [] [dependencies.baz] path = "baz" optional = true "#, ) .file( "src/main.rs", r#" #[cfg(feature = "foo")] pub fn foo() {} #[cfg(feature = "bar")] pub fn bar() { extern crate baz; baz::baz(); } fn main() { foo(); bar(); } "#, ) .file("baz/Cargo.toml", &basic_manifest("baz", "0.0.1")) .file("baz/src/lib.rs", "pub fn baz() {}") .build(); p.cargo("check --all-features").run(); } #[cargo_test] fn many_cli_features_comma_delimited() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "bar" optional = true [dependencies.baz] path = "baz" optional = true "#, ) .file( "src/main.rs", r#" #[allow(unused_extern_crates)] extern crate bar; #[allow(unused_extern_crates)] extern crate baz; fn main() {} "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "pub fn bar() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.0.1")) .file("baz/src/lib.rs", "pub fn baz() {}") .build(); p.cargo("check --features bar,baz") .with_stderr_data( str![[r#" [LOCKING] 2 packages to latest compatible versions [CHECKING] bar v0.0.1 ([ROOT]/foo/bar) [CHECKING] baz v0.0.1 ([ROOT]/foo/baz) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn many_cli_features_comma_and_space_delimited() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "bar" optional = true [dependencies.baz] path = "baz" optional = true [dependencies.bam] path = "bam" optional = true [dependencies.bap] path = "bap" optional = true "#, ) .file( "src/main.rs", r#" #[allow(unused_extern_crates)] extern crate bar; #[allow(unused_extern_crates)] extern crate baz; #[allow(unused_extern_crates)] extern crate bam; #[allow(unused_extern_crates)] extern crate bap; fn main() {} "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "pub fn bar() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.0.1")) .file("baz/src/lib.rs", "pub fn baz() {}") .file("bam/Cargo.toml", &basic_manifest("bam", "0.0.1")) .file("bam/src/lib.rs", "pub fn bam() {}") .file("bap/Cargo.toml", &basic_manifest("bap", "0.0.1")) .file("bap/src/lib.rs", "pub fn bap() {}") .build(); p.cargo("check --features") .arg("bar,baz bam bap") .with_stderr_data( str![[r#" [LOCKING] 4 packages to latest compatible versions [CHECKING] bam v0.0.1 ([ROOT]/foo/bam) [CHECKING] bap v0.0.1 ([ROOT]/foo/bap) [CHECKING] bar v0.0.1 ([ROOT]/foo/bar) [CHECKING] baz v0.0.1 ([ROOT]/foo/baz) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn only_dep_is_optional() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] foo = ['bar'] [dependencies] bar = { version = "0.1", optional = true } [dev-dependencies] bar = "0.1" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check").run(); } #[cargo_test] fn all_features_all_crates() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [workspace] members = ['bar'] "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [features] foo = [] "#, ) .file("bar/src/main.rs", "#[cfg(feature = \"foo\")] fn main() {}") .build(); p.cargo("check --all-features --workspace").run(); } #[cargo_test] fn feature_off_dylib() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar"] [package] name = "foo" version = "0.0.1" edition = "2015" [lib] crate-type = ["dylib"] [features] f1 = [] "#, ) .file( "src/lib.rs", r#" pub fn hello() -> &'static str { if cfg!(feature = "f1") { "f1" } else { "no f1" } } "#, ) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" [dependencies] foo = { path = ".." } "#, ) .file( "bar/src/main.rs", r#" extern crate foo; fn main() { assert_eq!(foo::hello(), "no f1"); } "#, ) .build(); // Build the dylib with `f1` feature. p.cargo("check --features f1").run(); // Check that building without `f1` uses a dylib without `f1`. p.cargo("run -p bar").run(); } #[cargo_test] fn warn_if_default_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "bar" optional = true [features] default-features = ["bar"] "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "pub fn bar() {}") .build(); p.cargo("check").with_stderr_data(str![[r#" [WARNING] `default-features = [".."]` was found in [features]. Did you mean to use `default = [".."]`? [LOCKING] 1 package to latest compatible version [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn no_feature_for_non_optional_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = { path = "bar" } "#, ) .file( "src/main.rs", r#" #[cfg(not(feature = "bar"))] fn main() { } "#, ) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [features] a = [] "#, ) .file("bar/src/lib.rs", "pub fn bar() {}") .build(); p.cargo("check --features bar/a").run(); } #[cargo_test] fn features_option_given_twice() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] a = [] b = [] "#, ) .file( "src/main.rs", r#" #[cfg(all(feature = "a", feature = "b"))] fn main() {} "#, ) .build(); p.cargo("check --features a --features b").run(); } #[cargo_test] fn multi_multi_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] a = [] b = [] c = [] "#, ) .file( "src/main.rs", r#" #[cfg(all(feature = "a", feature = "b", feature = "c"))] fn main() {} "#, ) .build(); p.cargo("check --features a --features").arg("b c").run(); } #[cargo_test] fn cli_parse_ok() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] a = [] "#, ) .file( "src/main.rs", r#" #[cfg(feature = "a")] fn main() { assert_eq!(std::env::args().nth(1).unwrap(), "b"); } "#, ) .build(); p.cargo("run --features a b").run(); } #[cargo_test] fn all_features_virtual_ws() { // What happens with `--all-features` in the root of a virtual workspace. // Some of this behavior is a little strange (member dependencies also // have all features enabled, one might expect `f4` to be disabled). let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2018" [dependencies] b = {path="../b", optional=true} [features] default = ["f1"] f1 = [] f2 = [] "#, ) .file( "a/src/main.rs", r#" fn main() { if cfg!(feature="f1") { println!("f1"); } if cfg!(feature="f2") { println!("f2"); } #[cfg(feature="b")] b::f(); } "#, ) .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" edition = "2015" [features] default = ["f3"] f3 = [] f4 = [] "#, ) .file( "b/src/lib.rs", r#" pub fn f() { if cfg!(feature="f3") { println!("f3"); } if cfg!(feature="f4") { println!("f4"); } } "#, ) .build(); p.cargo("run") .with_stdout_data(str![[r#" f1 "#]]) .run(); p.cargo("run --all-features") .with_stdout_data(str![[r#" f1 f2 f3 f4 "#]]) .run(); // In `a`, it behaves differently. :( p.cargo("run --all-features") .cwd("a") .with_stdout_data(str![[r#" f1 f2 f3 "#]]) .run(); } #[cargo_test] fn slash_optional_enables() { // --features dep/feat will enable `dep` and set its feature. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] dep = {path="dep", optional=true} "#, ) .file( "src/lib.rs", r#" #[cfg(not(feature="dep"))] compile_error!("dep not set"); "#, ) .file( "dep/Cargo.toml", r#" [package] name = "dep" version = "0.1.0" edition = "2015" [features] feat = [] "#, ) .file( "dep/src/lib.rs", r#" #[cfg(not(feature="feat"))] compile_error!("feat not set"); "#, ) .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" ... [ERROR] dep not set ... "#]]) .run(); p.cargo("check --features dep/feat").run(); } #[cargo_test] fn registry_summary_order_doesnt_matter() { // Checks for an issue where the resolver depended on the order of entries // in the registry summary. If there was a non-optional dev-dependency // that appeared before an optional normal dependency, then the resolver // would not activate the optional dependency with a pkg/featname feature // syntax. Package::new("dep", "0.1.0") .feature("feat1", &[]) .file( "src/lib.rs", r#" #[cfg(feature="feat1")] pub fn work() { println!("it works"); } "#, ) .publish(); Package::new("bar", "0.1.0") .feature("bar_feat", &["dep/feat1"]) .add_dep(Dependency::new("dep", "0.1.0").dev()) .add_dep(Dependency::new("dep", "0.1.0").optional(true)) .file( "src/lib.rs", r#" // This will fail to compile without `dep` optional dep activated. extern crate dep; pub fn doit() { dep::work(); } "#, ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [dependencies] bar = { version="0.1", features = ["bar_feat"] } "#, ) .file( "src/main.rs", r#" fn main() { bar::doit(); } "#, ) .build(); p.cargo("run") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] dep v0.1.0 (registry `dummy-registry`) [DOWNLOADED] bar v0.1.0 (registry `dummy-registry`) [COMPILING] dep v0.1.0 [COMPILING] bar v0.1.0 [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .with_stdout_data(str![[r#" it works "#]]) .run(); } #[cargo_test] fn nonexistent_required_features() { Package::new("required_dependency", "0.1.0") .feature("simple", &[]) .publish(); Package::new("optional_dependency", "0.2.0") .feature("optional", &[]) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [features] existing = [] fancy = ["optional_dependency"] [dependencies] required_dependency = { version = "0.1", optional = false} optional_dependency = { version = "0.2", optional = true} [[example]] name = "ololo" required-features = ["not_present", "existing", "fancy", "required_dependency/not_existing", "required_dependency/simple", "optional_dependency/optional", "not_specified_dependency/some_feature"] "#, ) .file("src/main.rs", "fn main() {}") .file("examples/ololo.rs", "fn main() {}") .build(); p.cargo("check --examples").with_stderr_data(str![[r#" ... [WARNING] invalid feature `not_present` in required-features of target `ololo`: `not_present` is not present in [features] section [WARNING] invalid feature `required_dependency/not_existing` in required-features of target `ololo`: feature `not_existing` does not exist in package `required_dependency v0.1.0` [WARNING] invalid feature `not_specified_dependency/some_feature` in required-features of target `ololo`: dependency `not_specified_dependency` does not exist ... "#]]).run(); } #[cargo_test] fn invalid_feature_names_error() { // Errors for more restricted feature syntax. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [features] # Invalid start character. "+foo" = [] "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid character `+` in feature name: `+foo`, the first character must be a Unicode XID start character or digit (most letters or `_` or `0` to `9`) --> Cargo.toml:9:17 | 9 | "+foo" = [] | ^^^^^^ | "#]]) .run(); p.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [features] # Invalid continue character. "a&b" = [] "#, ); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid character `&` in feature name: `a&b`, characters must be Unicode XID characters, '-', `+`, or `.` (numbers, `+`, `-`, `_`, `.`, or most letters) --> Cargo.toml:9:13 | 9 | "a&b" = [] | ^^^^^ | "#]]) .run(); } #[cargo_test] fn invalid_feature_name_slash_error() { // Errors for more restricted feature syntax. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [features] "foo/bar" = [] "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid character `/` in feature name: `foo/bar`, feature name is not allowed to contain slashes --> Cargo.toml:8:17 | 8 | "foo/bar" = [] | ^^^^^^^^^ | "#]]) .run(); } cargo-0.91.0/tests/testsuite/features2.rs000064400000000000000000002246771046102023000164710ustar 00000000000000//! Tests for the new feature resolver. use std::fs::File; use crate::prelude::*; use crate::utils::cargo_process; use crate::utils::cross_compile::disabled as cross_compile_disabled; use cargo_test_support::cross_compile::alternate; use cargo_test_support::paths; use cargo_test_support::publish::validate_crate_contents; use cargo_test_support::registry::{Dependency, Package}; use cargo_test_support::str; use cargo_test_support::{Project, basic_manifest, project, rustc_host}; /// Switches Cargo.toml to use `resolver = "2"`. pub fn switch_to_resolver_2(p: &Project) { let mut manifest = p.read_file("Cargo.toml"); if manifest.contains("resolver =") { panic!("did not expect manifest to already contain a resolver setting"); } if let Some(index) = manifest.find("[workspace]\n") { manifest.insert_str(index + 12, "resolver = \"2\"\n"); } else if let Some(index) = manifest.find("[package]\n") { manifest.insert_str(index + 10, "resolver = \"2\"\n"); } else { panic!("expected [package] or [workspace] in manifest"); } p.change_file("Cargo.toml", &manifest); } #[cargo_test] fn inactivate_targets() { // Basic test of `itarget`. A shared dependency where an inactive [target] // changes the features. Package::new("common", "1.0.0") .feature("f1", &[]) .file( "src/lib.rs", r#" #[cfg(feature = "f1")] compile_error!("f1 should not activate"); "#, ) .publish(); Package::new("bar", "1.0.0") .add_dep( Dependency::new("common", "1.0") .target("cfg(whatever)") .enable_features(&["f1"]), ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] common = "1.0" bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data( str![[r#" ... [ERROR] f1 should not activate ... "#]] .unordered(), ) .run(); switch_to_resolver_2(&p); p.cargo("check").run(); } #[cargo_test] fn inactive_target_optional() { // Activating optional [target] dependencies for inactivate target. Package::new("common", "1.0.0") .feature("f1", &[]) .feature("f2", &[]) .feature("f3", &[]) .feature("f4", &[]) .file( "src/lib.rs", r#" pub fn f() { if cfg!(feature="f1") { println!("f1"); } if cfg!(feature="f2") { println!("f2"); } if cfg!(feature="f3") { println!("f3"); } if cfg!(feature="f4") { println!("f4"); } } "#, ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [dependencies] common = "1.0" [target.'cfg(whatever)'.dependencies] dep1 = {path='dep1', optional=true} dep2 = {path='dep2', optional=true, features=["f3"]} common = {version="1.0", optional=true, features=["f4"]} [features] foo1 = ["dep1/f2"] foo2 = ["dep2"] "#, ) .file( "src/main.rs", r#" fn main() { if cfg!(feature="foo1") { println!("foo1"); } if cfg!(feature="foo2") { println!("foo2"); } if cfg!(feature="dep1") { println!("dep1"); } if cfg!(feature="dep2") { println!("dep2"); } if cfg!(feature="common") { println!("common"); } common::f(); } "#, ) .file( "dep1/Cargo.toml", r#" [package] name = "dep1" version = "0.1.0" edition = "2015" [dependencies] common = {version="1.0", features=["f1"]} [features] f2 = ["common/f2"] "#, ) .file( "dep1/src/lib.rs", r#"compile_error!("dep1 should not build");"#, ) .file( "dep2/Cargo.toml", r#" [package] name = "dep2" version = "0.1.0" edition = "2015" [dependencies] common = "1.0" [features] f3 = ["common/f3"] "#, ) .file( "dep2/src/lib.rs", r#"compile_error!("dep2 should not build");"#, ) .build(); p.cargo("run --all-features") .with_stdout_data(str![[r#" foo1 foo2 dep1 dep2 common f1 f2 f3 f4 "#]]) .run(); p.cargo("run --features dep1") .with_stdout_data(str![[r#" dep1 f1 "#]]) .run(); p.cargo("run --features foo1") .with_stdout_data(str![[r#" foo1 dep1 f1 f2 "#]]) .run(); p.cargo("run --features dep2") .with_stdout_data(str![[r#" dep2 f3 "#]]) .run(); p.cargo("run --features common") .with_stdout_data(str![[r#" common f4 "#]]) .run(); switch_to_resolver_2(&p); p.cargo("run --all-features") .with_stdout_data(str![[r#" foo1 foo2 dep1 dep2 common "#]]) .run(); p.cargo("run --features dep1") .with_stdout_data(str![[r#" dep1 "#]]) .run(); p.cargo("run --features foo1") .with_stdout_data(str![[r#" foo1 "#]]) .run(); p.cargo("run --features dep2") .with_stdout_data(str![[r#" dep2 "#]]) .run(); p.cargo("run --features common") .with_stdout_data(str![[r#" common "#]]) .run(); } #[cargo_test] fn itarget_proc_macro() { // itarget inside a proc-macro while cross-compiling if cross_compile_disabled() { return; } Package::new("hostdep", "1.0.0").publish(); Package::new("pm", "1.0.0") .proc_macro(true) .target_dep("hostdep", "1.0", rustc_host()) .file("src/lib.rs", "extern crate hostdep;") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] pm = "1.0" "#, ) .file("src/lib.rs", "") .build(); // Old behavior p.cargo("check").run(); p.cargo("check --target").arg(alternate()).run(); // New behavior switch_to_resolver_2(&p); p.cargo("check").run(); p.cargo("check --target").arg(alternate()).run(); // For good measure, just make sure things don't break. p.cargo("check --target").arg(alternate()).run(); } #[cargo_test] fn decouple_host_deps() { // Basic test for `host_dep` decouple. Package::new("common", "1.0.0") .feature("f1", &[]) .file( "src/lib.rs", r#" #[cfg(feature = "f1")] pub fn foo() {} #[cfg(not(feature = "f1"))] pub fn bar() {} "#, ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [build-dependencies] common = {version="1.0", features=["f1"]} [dependencies] common = "1.0" "#, ) .file( "build.rs", r#" use common::foo; fn main() {} "#, ) .file("src/lib.rs", "use common::bar;") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" ... error[E0432]: unresolved import `common::bar` ... "#]]) .run(); switch_to_resolver_2(&p); p.cargo("check").run(); } #[cargo_test] fn decouple_host_deps_nested() { // `host_dep` decouple of transitive dependencies. Package::new("common", "1.0.0") .feature("f1", &[]) .file( "src/lib.rs", r#" #[cfg(feature = "f1")] pub fn foo() {} #[cfg(not(feature = "f1"))] pub fn bar() {} "#, ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [build-dependencies] bdep = {path="bdep"} [dependencies] common = "1.0" "#, ) .file( "build.rs", r#" use bdep::foo; fn main() {} "#, ) .file("src/lib.rs", "use common::bar;") .file( "bdep/Cargo.toml", r#" [package] name = "bdep" version = "0.1.0" edition = "2018" [dependencies] common = {version="1.0", features=["f1"]} "#, ) .file("bdep/src/lib.rs", "pub use common::foo;") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" ... error[E0432]: unresolved import `common::bar` ... "#]]) .run(); switch_to_resolver_2(&p); p.cargo("check").run(); } #[cargo_test] fn decouple_dev_deps() { // Basic test for `dev_dep` decouple. Package::new("common", "1.0.0") .feature("f1", &[]) .feature("f2", &[]) .file( "src/lib.rs", r#" // const ensures it uses the correct dependency at *build time* // compared to *link time*. #[cfg(all(feature="f1", not(feature="f2")))] pub const X: u32 = 1; #[cfg(all(feature="f1", feature="f2"))] pub const X: u32 = 3; pub fn foo() -> u32 { let mut res = 0; if cfg!(feature = "f1") { res |= 1; } if cfg!(feature = "f2") { res |= 2; } res } "#, ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [dependencies] common = {version="1.0", features=["f1"]} [dev-dependencies] common = {version="1.0", features=["f2"]} "#, ) .file( "src/main.rs", r#" fn main() { let expected: u32 = std::env::args().skip(1).next().unwrap().parse().unwrap(); assert_eq!(foo::foo(), expected); assert_eq!(foo::build_time(), expected); assert_eq!(common::foo(), expected); assert_eq!(common::X, expected); } #[test] fn test_bin() { assert_eq!(foo::foo(), 3); assert_eq!(common::foo(), 3); assert_eq!(common::X, 3); assert_eq!(foo::build_time(), 3); } "#, ) .file( "src/lib.rs", r#" pub fn foo() -> u32 { common::foo() } pub fn build_time() -> u32 { common::X } #[test] fn test_lib() { assert_eq!(foo(), 3); assert_eq!(common::foo(), 3); assert_eq!(common::X, 3); } "#, ) .file( "tests/t1.rs", r#" #[test] fn test_t1() { assert_eq!(foo::foo(), 3); assert_eq!(common::foo(), 3); assert_eq!(common::X, 3); assert_eq!(foo::build_time(), 3); } #[test] fn test_main() { // Features are unified for main when run with `cargo test`, // even with the new resolver. let s = std::process::Command::new("target/debug/foo") .arg("3") .status().unwrap(); assert!(s.success()); } "#, ) .build(); // Old behavior p.cargo("run 3").run(); p.cargo("test").run(); // New behavior switch_to_resolver_2(&p); p.cargo("run 1").run(); p.cargo("test").run(); } #[cargo_test] fn build_script_runtime_features() { // Check that the CARGO_FEATURE_* environment variable is set correctly. // // This has a common dependency between build/normal/dev-deps, and it // queries which features it was built with in different circumstances. Package::new("common", "1.0.0") .feature("normal", &[]) .feature("dev", &[]) .feature("build", &[]) .file( "build.rs", r#" fn is_set(name: &str) -> bool { std::env::var(name) == Ok("1".to_string()) } fn main() { let mut res = 0; if is_set("CARGO_FEATURE_NORMAL") { res |= 1; } if is_set("CARGO_FEATURE_DEV") { res |= 2; } if is_set("CARGO_FEATURE_BUILD") { res |= 4; } println!("cargo::rustc-cfg=RunCustomBuild=\"{}\"", res); let mut res = 0; if cfg!(feature = "normal") { res |= 1; } if cfg!(feature = "dev") { res |= 2; } if cfg!(feature = "build") { res |= 4; } println!("cargo::rustc-cfg=CustomBuild=\"{}\"", res); } "#, ) .file( "src/lib.rs", r#" pub fn foo() -> u32 { let mut res = 0; if cfg!(feature = "normal") { res |= 1; } if cfg!(feature = "dev") { res |= 2; } if cfg!(feature = "build") { res |= 4; } res } pub fn build_time() -> u32 { #[cfg(RunCustomBuild="1")] return 1; #[cfg(RunCustomBuild="3")] return 3; #[cfg(RunCustomBuild="4")] return 4; #[cfg(RunCustomBuild="5")] return 5; #[cfg(RunCustomBuild="7")] return 7; } "#, ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [build-dependencies] common = {version="1.0", features=["build"]} [dependencies] common = {version="1.0", features=["normal"]} [dev-dependencies] common = {version="1.0", features=["dev"]} "#, ) .file( "build.rs", r#" fn main() { assert_eq!(common::foo(), common::build_time()); println!("cargo::rustc-cfg=from_build=\"{}\"", common::foo()); } "#, ) .file( "src/lib.rs", r#" pub fn foo() -> u32 { common::foo() } pub fn build_time() -> u32 { common::build_time() } #[test] fn test_lib() { assert_eq!(common::foo(), common::build_time()); assert_eq!(common::foo(), std::env::var("CARGO_FEATURE_EXPECT").unwrap().parse().unwrap()); } "#, ) .file( "src/main.rs", r#" fn main() { assert_eq!(common::foo(), common::build_time()); assert_eq!(common::foo(), std::env::var("CARGO_FEATURE_EXPECT").unwrap().parse().unwrap()); } #[test] fn test_bin() { assert_eq!(common::foo(), common::build_time()); assert_eq!(common::foo(), std::env::var("CARGO_FEATURE_EXPECT").unwrap().parse().unwrap()); } "#, ) .file( "tests/t1.rs", r#" #[test] fn test_t1() { assert_eq!(common::foo(), common::build_time()); assert_eq!(common::foo(), std::env::var("CARGO_FEATURE_EXPECT").unwrap().parse().unwrap()); } #[test] fn test_main() { // Features are unified for main when run with `cargo test`, // even with the new resolver. let s = std::process::Command::new("target/debug/foo") .status().unwrap(); assert!(s.success()); } "#, ) .build(); // Old way, unifies all 3. p.cargo("run").env("CARGO_FEATURE_EXPECT", "7").run(); p.cargo("test").env("CARGO_FEATURE_EXPECT", "7").run(); // New behavior. switch_to_resolver_2(&p); // normal + build unify p.cargo("run").env("CARGO_FEATURE_EXPECT", "1").run(); // dev_deps are still unified with `cargo test` p.cargo("test").env("CARGO_FEATURE_EXPECT", "3").run(); } #[cargo_test] fn cyclical_dev_dep() { // Check how a cyclical dev-dependency will work. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [features] dev = [] [dev-dependencies] foo = { path = '.', features = ["dev"] } "#, ) .file( "src/lib.rs", r#" pub fn assert_dev(enabled: bool) { assert_eq!(enabled, cfg!(feature="dev")); } #[test] fn test_in_lib() { assert_dev(true); } "#, ) .file( "src/main.rs", r#" fn main() { let expected: bool = std::env::args().skip(1).next().unwrap().parse().unwrap(); foo::assert_dev(expected); } "#, ) .file( "tests/t1.rs", r#" #[test] fn integration_links() { foo::assert_dev(true); // The lib linked with main.rs will also be unified. let s = std::process::Command::new("target/debug/foo") .arg("true") .status().unwrap(); assert!(s.success()); } "#, ) .build(); // Old way unifies features. p.cargo("run true").run(); // dev feature should always be enabled in tests. p.cargo("test").run(); // New behavior. switch_to_resolver_2(&p); // Should decouple main. p.cargo("run false").run(); // And this should be no different. p.cargo("test").run(); } #[cargo_test] fn all_feature_opts() { // All feature options at once. Package::new("common", "1.0.0") .feature("normal", &[]) .feature("build", &[]) .feature("dev", &[]) .feature("itarget", &[]) .file( "src/lib.rs", r#" pub fn feats() -> u32 { let mut res = 0; if cfg!(feature="normal") { res |= 1; } if cfg!(feature="build") { res |= 2; } if cfg!(feature="dev") { res |= 4; } if cfg!(feature="itarget") { res |= 8; } res } "#, ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [dependencies] common = {version = "1.0", features=["normal"]} [dev-dependencies] common = {version = "1.0", features=["dev"]} [build-dependencies] common = {version = "1.0", features=["build"]} [target.'cfg(whatever)'.dependencies] common = {version = "1.0", features=["itarget"]} "#, ) .file( "src/main.rs", r#" fn main() { expect(); } fn expect() { let expected: u32 = std::env::var("EXPECTED_FEATS").unwrap().parse().unwrap(); assert_eq!(expected, common::feats()); } #[test] fn from_test() { expect(); } "#, ) .build(); p.cargo("run").env("EXPECTED_FEATS", "15").run(); p.cargo("test").env("EXPECTED_FEATS", "15").run(); // New behavior. switch_to_resolver_2(&p); // Only normal feature. p.cargo("run").env("EXPECTED_FEATS", "1").run(); // only normal+dev p.cargo("test").env("EXPECTED_FEATS", "5").run(); } #[cargo_test] fn required_features_host_dep() { // Check that required-features handles build-dependencies correctly. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [[bin]] name = "x" required-features = ["bdep/f1"] [build-dependencies] bdep = {path="bdep"} "#, ) .file("build.rs", "fn main() {}") .file( "src/bin/x.rs", r#" fn main() {} "#, ) .file( "bdep/Cargo.toml", r#" [package] name = "bdep" version = "0.1.0" edition = "2015" [features] f1 = [] "#, ) .file("bdep/src/lib.rs", "") .build(); p.cargo("run") .with_status(101) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [ERROR] target `x` in package `foo` requires the features: `bdep/f1` Consider enabling them by passing, e.g., `--features="bdep/f1"` "#]]) .run(); // New behavior. switch_to_resolver_2(&p); p.cargo("run --features bdep/f1").run(); } #[cargo_test] fn disabled_shared_host_dep() { // Check for situation where an optional dep of a shared dep is enabled in // a normal dependency, but disabled in an optional one. The unit tree is: // foo // ├── foo build.rs // | └── common (BUILD dependency, NO FEATURES) // └── common (Normal dependency, default features) // └── somedep Package::new("somedep", "1.0.0") .file( "src/lib.rs", r#" pub fn f() { println!("hello from somedep"); } "#, ) .publish(); Package::new("common", "1.0.0") .feature("default", &["somedep"]) .add_dep(Dependency::new("somedep", "1.0").optional(true)) .file( "src/lib.rs", r#" pub fn check_somedep() -> bool { #[cfg(feature="somedep")] { extern crate somedep; somedep::f(); true } #[cfg(not(feature="somedep"))] { println!("no somedep"); false } } "#, ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" edition = "2018" resolver = "2" [dependencies] common = "1.0" [build-dependencies] common = {version = "1.0", default-features = false} "#, ) .file( "src/main.rs", "fn main() { assert!(common::check_somedep()); }", ) .file( "build.rs", "fn main() { assert!(!common::check_somedep()); }", ) .build(); p.cargo("run -v") .with_stdout_data(str![[r#" hello from somedep "#]]) .run(); } #[cargo_test] fn required_features_inactive_dep() { // required-features with an inactivated dep. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" resolver = "2" [target.'cfg(whatever)'.dependencies] bar = {path="bar"} [[bin]] name = "foo" required-features = ["feat1"] [features] feat1 = [] "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check --features=feat1") .with_stderr_data(str![[r#" [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn decouple_proc_macro() { // proc macro features are not shared Package::new("common", "1.0.0") .feature("somefeat", &[]) .file( "src/lib.rs", r#" pub const fn foo() -> bool { cfg!(feature="somefeat") } #[cfg(feature="somefeat")] pub const FEAT_ONLY_CONST: bool = true; "#, ) .publish(); Package::new("pm", "1.0.0") .proc_macro(true) .feature_dep("common", "1.0", &["somefeat"]) .file( "src/lib.rs", r#" extern crate proc_macro; extern crate common; #[proc_macro] pub fn foo(input: proc_macro::TokenStream) -> proc_macro::TokenStream { assert!(common::foo()); "".parse().unwrap() } "#, ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" edition = "2018" [dependencies] pm = "1.0" common = "1.0" "#, ) .file( "src/lib.rs", r#" //! Test with docs. //! //! ```rust //! pm::foo!{} //! fn main() { //! let expected = std::env::var_os("TEST_EXPECTS_ENABLED").is_some(); //! assert_eq!(expected, common::foo(), "common is wrong"); //! } //! ``` "#, ) .file( "src/main.rs", r#" pm::foo!{} fn main() { println!("it is {}", common::foo()); } "#, ) .build(); p.cargo("run") .env("TEST_EXPECTS_ENABLED", "1") .with_stdout_data(str![[r#" it is true "#]]) .run(); // Make sure the test is fallible. p.cargo("test --doc") .with_status(101) .with_stdout_data("...\n[..]common is wrong[..]\n...") .run(); p.cargo("test --doc").env("TEST_EXPECTS_ENABLED", "1").run(); p.cargo("doc").run(); assert!( p.build_dir() .join("doc/common/constant.FEAT_ONLY_CONST.html") .exists() ); // cargo doc should clean in-between runs, but it doesn't, and leaves stale files. // https://github.com/rust-lang/cargo/issues/6783 (same for removed items) p.build_dir().join("doc").rm_rf(); // New behavior. switch_to_resolver_2(&p); p.cargo("run") .with_stdout_data(str![[r#" it is false "#]]) .run(); p.cargo("test --doc").run(); p.cargo("doc").run(); assert!( !p.build_dir() .join("doc/common/constant.FEAT_ONLY_CONST.html") .exists() ); } #[cargo_test] fn proc_macro_ws() { // Checks for bug with proc-macro in a workspace with dependency (shouldn't panic). // // Note, debuginfo is explicitly requested here to preserve the intent of this non-regression // test: that will disable the debuginfo build dependencies optimization. Otherwise, it would // initially trigger when the crates are built independently, but rebuild them with debuginfo // when it sees the shared build/runtime dependency when checking the complete workspace. let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo", "pm"] resolver = "2" [profile.dev.build-override] debug = true "#, ) .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [features] feat1 = [] "#, ) .file("foo/src/lib.rs", "") .file( "pm/Cargo.toml", r#" [package] name = "pm" version = "0.1.0" edition = "2015" [lib] proc-macro = true [dependencies] foo = { path = "../foo", features=["feat1"] } "#, ) .file("pm/src/lib.rs", "") .build(); p.cargo("check -p pm -v") .with_stderr_data(str![[r#" ... [RUNNING] `rustc --crate-name foo [..]--cfg[..]feat1[..]` ... "#]]) .run(); // This may be surprising that `foo` doesn't get built separately. It is // because pm might have other units (binaries, tests, etc.), and so the // feature resolver must assume that normal deps get unified with it. This // is related to the bigger issue where the features selected in a // workspace depend on which packages are selected. p.cargo("check --workspace -v") .with_stderr_data(str![[r#" [FRESH] foo v0.1.0 ([ROOT]/foo/foo) [FRESH] pm v0.1.0 ([ROOT]/foo/pm) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Selecting just foo will build without unification. p.cargo("check -p foo -v") // Make sure `foo` is built without feat1 .with_stderr_line_without( &["[RUNNING] `rustc --crate-name foo --edition=2015"], &["--cfg[..]feat1"], ) .run(); } #[cargo_test] fn has_dev_dep_for_test() { // Check for a bug where the decision on whether or not "dev dependencies" // should be used did not consider `check --profile=test`. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dev-dependencies] dep = { path = 'dep', features = ['f1'] } "#, ) .file( "src/lib.rs", r#" #[test] fn t1() { dep::f(); } "#, ) .file( "dep/Cargo.toml", r#" [package] name = "dep" version = "0.1.0" edition = "2015" [features] f1 = [] "#, ) .file( "dep/src/lib.rs", r#" #[cfg(feature = "f1")] pub fn f() {} "#, ) .build(); p.cargo("check -v") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [CHECKING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check -v --profile=test") .with_stderr_data(str![[r#" [CHECKING] dep v0.1.0 ([ROOT]/foo/dep) [RUNNING] `rustc --crate-name dep [..]` [CHECKING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // New resolver should not be any different. switch_to_resolver_2(&p); p.cargo("check -v --profile=test") .with_stderr_data(str![[r#" [FRESH] dep v0.1.0 ([ROOT]/foo/dep) [FRESH] foo v0.1.0 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn build_dep_activated() { // Build dependencies always match the host for [target.*.build-dependencies]. if cross_compile_disabled() { return; } Package::new("somedep", "1.0.0") .file("src/lib.rs", "") .publish(); Package::new("targetdep", "1.0.0").publish(); Package::new("hostdep", "1.0.0") // Check that "for_host" is sticky. .target_dep("somedep", "1.0", rustc_host()) .feature("feat1", &[]) .file( "src/lib.rs", r#" extern crate somedep; #[cfg(not(feature="feat1"))] compile_error!{"feat1 missing"} "#, ) .publish(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2015" # This should never be selected. [target.'{}'.build-dependencies] targetdep = "1.0" [target.'{}'.build-dependencies] hostdep = {{version="1.0", features=["feat1"]}} "#, alternate(), rustc_host() ), ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .build(); p.cargo("check").run(); p.cargo("check --target").arg(alternate()).run(); // New behavior. switch_to_resolver_2(&p); p.cargo("check").run(); p.cargo("check --target").arg(alternate()).run(); } #[cargo_test] fn resolver_bad_setting() { // Unknown setting in `resolver` let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" resolver = "foo" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: `resolver` setting `foo` is not valid, valid options are "1", "2" or "3" "#]]) .run(); } #[cargo_test] fn resolver_original() { // resolver="1" uses old unification behavior. Package::new("common", "1.0.0") .feature("f1", &[]) .file( "src/lib.rs", r#" #[cfg(feature = "f1")] compile_error!("f1 should not activate"); "#, ) .publish(); Package::new("bar", "1.0.0") .add_dep( Dependency::new("common", "1.0") .target("cfg(whatever)") .enable_features(&["f1"]), ) .publish(); let manifest = |resolver| { format!( r#" [package] name = "foo" version = "0.1.0" edition = "2015" resolver = "{}" [dependencies] common = "1.0" bar = "1.0" "#, resolver ) }; let p = project() .file("Cargo.toml", &manifest("1")) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data( str![[r#" ... [ERROR] f1 should not activate ... "#]] .unordered(), ) .run(); p.change_file("Cargo.toml", &manifest("2")); p.cargo("check").run(); } #[cargo_test] fn resolver_not_both() { // Can't specify resolver in both workspace and package. let p = project() .file( "Cargo.toml", r#" [workspace] resolver = "2" [package] name = "foo" version = "0.1.0" edition = "2015" resolver = "2" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: cannot specify `resolver` field in both `[workspace]` and `[package]` "#]]) .run(); } #[cargo_test] fn resolver_ws_member() { // Can't specify `resolver` in a ws member. let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a"] "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" resolver = "2" "#, ) .file("a/src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [WARNING] resolver for the non root package will be ignored, specify resolver at the workspace root: package: [ROOT]/foo/a/Cargo.toml workspace: [ROOT]/foo/Cargo.toml [CHECKING] a v0.1.0 ([ROOT]/foo/a) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn edition_2021_workspace_member() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a"] "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2021" "#, ) .file("a/src/lib.rs", "") .build(); p.cargo("check").with_stderr_data(str![[r#" [WARNING] virtual workspace defaulting to `resolver = "1"` despite one or more workspace members being on edition 2021 which implies `resolver = "2"` [NOTE] to keep the current resolver, specify `workspace.resolver = "1"` in the workspace root's manifest [NOTE] to use the edition 2021 resolver, specify `workspace.resolver = "2"` in the workspace root's manifest [NOTE] for more details see https://doc.rust-lang.org/cargo/reference/resolver.html#resolver-versions [CHECKING] a v0.1.0 ([ROOT]/foo/a) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn resolver_ws_root_and_member() { // Check when specified in both ws root and member. let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a"] resolver = "2" "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" resolver = "2" "#, ) .file("a/src/lib.rs", "") .build(); // Ignores if they are the same. p.cargo("check") .with_stderr_data(str![[r#" [CHECKING] a v0.1.0 ([ROOT]/foo/a) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn resolver_enables_new_features() { // resolver="2" enables all the things. Package::new("common", "1.0.0") .feature("normal", &[]) .feature("build", &[]) .feature("dev", &[]) .feature("itarget", &[]) .file( "src/lib.rs", r#" pub fn feats() -> u32 { let mut res = 0; if cfg!(feature="normal") { res |= 1; } if cfg!(feature="build") { res |= 2; } if cfg!(feature="dev") { res |= 4; } if cfg!(feature="itarget") { res |= 8; } res } "#, ) .publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] resolver = "2" "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2018" [dependencies] common = {version = "1.0", features=["normal"]} [dev-dependencies] common = {version = "1.0", features=["dev"]} [build-dependencies] common = {version = "1.0", features=["build"]} [target.'cfg(whatever)'.dependencies] common = {version = "1.0", features=["itarget"]} "#, ) .file( "a/src/main.rs", r#" fn main() { expect(); } fn expect() { let expected: u32 = std::env::var("EXPECTED_FEATS").unwrap().parse().unwrap(); assert_eq!(expected, common::feats()); } #[test] fn from_test() { expect(); } "#, ) .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" edition = "2015" [features] ping = [] "#, ) .file( "b/src/main.rs", r#" fn main() { if cfg!(feature="ping") { println!("pong"); } } "#, ) .build(); // Only normal. p.cargo("run --bin a") .env("EXPECTED_FEATS", "1") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] common v1.0.0 (registry `dummy-registry`) [COMPILING] common v1.0.0 [COMPILING] a v0.1.0 ([ROOT]/foo/a) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/a[EXE]` "#]]) .run(); // only normal+dev p.cargo("test").cwd("a").env("EXPECTED_FEATS", "5").run(); // Can specify features of packages from a different directory. p.cargo("run -p b --features=ping") .cwd("a") .with_stdout_data(str![[r#" pong "#]]) .run(); } #[cargo_test] fn install_resolve_behavior() { // install honors the resolver behavior. Package::new("common", "1.0.0") .feature("f1", &[]) .file( "src/lib.rs", r#" #[cfg(feature = "f1")] compile_error!("f1 should not activate"); "#, ) .publish(); Package::new("bar", "1.0.0").dep("common", "1.0").publish(); Package::new("foo", "1.0.0") .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" edition = "2015" resolver = "2" [target.'cfg(whatever)'.dependencies] common = {version="1.0", features=["f1"]} [dependencies] bar = "1.0" "#, ) .file("src/main.rs", "fn main() {}") .publish(); cargo_process("install foo").run(); } #[cargo_test] fn package_includes_resolve_behavior() { // `cargo package` will inherit the correct resolve behavior. let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a"] resolver = "2" "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" authors = ["Zzz"] description = "foo" license = "MIT" homepage = "https://example.com/" "#, ) .file("a/src/lib.rs", "") .build(); p.cargo("package").cwd("a").run(); let rewritten_toml = str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "a" version = "0.1.0" authors = ["Zzz"] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" homepage = "https://example.com/" readme = false license = "MIT" resolver = "2" [lib] name = "a" path = "src/lib.rs" "##]]; let f = File::open(&p.root().join("target/package/a-0.1.0.crate")).unwrap(); validate_crate_contents( f, "a-0.1.0.crate", &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs", "Cargo.lock"], [("Cargo.toml", rewritten_toml)], ); } #[cargo_test] fn tree_all() { // `cargo tree` with the new feature resolver. Package::new("log", "0.4.8").feature("serde", &[]).publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" resolver = "2" [target.'cfg(whatever)'.dependencies] log = {version="*", features=["serde"]} "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree --target=all") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── log v0.4.8 "#]]) .run(); } #[cargo_test] fn shared_dep_same_but_dependencies() { // Checks for a bug of nondeterminism. This scenario creates a shared // dependency `dep` which needs to be built twice (once as normal, and // once as a build dep). However, in both cases the flags to `dep` are the // same, the only difference is what it links to. The normal dependency // should link to `subdep` with the feature disabled, and the build // dependency should link to it with it enabled. Crucially, the `--target` // flag should not be specified, otherwise Unit.kind would be different // and avoid the collision, and this bug won't manifest. let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bin1", "bin2"] resolver = "2" "#, ) .file( "bin1/Cargo.toml", r#" [package] name = "bin1" version = "0.1.0" edition = "2015" [dependencies] dep = { path = "../dep" } "#, ) .file("bin1/src/main.rs", "fn main() { dep::feat_func(); }") .file( "bin2/Cargo.toml", r#" [package] name = "bin2" version = "0.1.0" edition = "2015" [build-dependencies] dep = { path = "../dep" } subdep = { path = "../subdep", features = ["feat"] } "#, ) .file("bin2/build.rs", "fn main() { dep::feat_func(); }") .file("bin2/src/main.rs", "fn main() {}") .file( "dep/Cargo.toml", r#" [package] name = "dep" version = "0.1.0" edition = "2015" [dependencies] subdep = { path = "../subdep" } "#, ) .file( "dep/src/lib.rs", "pub fn feat_func() { subdep::feat_func(); }", ) .file( "subdep/Cargo.toml", r#" [package] name = "subdep" version = "0.1.0" edition = "2015" [features] feat = [] "#, ) .file( "subdep/src/lib.rs", r#" pub fn feat_func() { #[cfg(feature = "feat")] println!("cargo::warning=feat: enabled"); #[cfg(not(feature = "feat"))] println!("cargo::warning=feat: not enabled"); } "#, ) .build(); p.cargo("build --bin bin1 --bin bin2") // unordered because bin1 and bin2 build at the same time .with_stderr_data( str![[r#" [COMPILING] subdep v0.1.0 ([ROOT]/foo/subdep) [COMPILING] dep v0.1.0 ([ROOT]/foo/dep) [COMPILING] bin1 v0.1.0 ([ROOT]/foo/bin1) [COMPILING] bin2 v0.1.0 ([ROOT]/foo/bin2) [WARNING] bin2@0.1.0: feat: enabled [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); p.process(p.bin("bin1")) .with_stdout_data(str![[r#" cargo::warning=feat: not enabled "#]]) .run(); // Make sure everything stays cached. p.cargo("build -v --bin bin1 --bin bin2") .with_stderr_data( str![[r#" [FRESH] subdep v0.1.0 ([ROOT]/foo/subdep) [FRESH] dep v0.1.0 ([ROOT]/foo/dep) [FRESH] bin1 v0.1.0 ([ROOT]/foo/bin1) [WARNING] bin2@0.1.0: feat: enabled [FRESH] bin2 v0.1.0 ([ROOT]/foo/bin2) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn test_proc_macro() { // Running `cargo test` on a proc-macro, with a shared dependency that has // different features. // // There was a bug where `shared` was built twice (once with feature "B" // and once without), and both copies linked into the unit test. This // would cause a type failure when used in an intermediate dependency // (the-macro-support). let p = project() .file( "Cargo.toml", r#" [package] name = "runtime" version = "0.1.0" edition = "2015" resolver = "2" [dependencies] the-macro = { path = "the-macro", features = ['a'] } [build-dependencies] shared = { path = "shared", features = ['b'] } "#, ) .file("src/lib.rs", "") .file( "the-macro/Cargo.toml", r#" [package] name = "the-macro" version = "0.1.0" edition = "2015" [lib] proc-macro = true test = false [dependencies] the-macro-support = { path = "../the-macro-support" } shared = { path = "../shared" } [dev-dependencies] runtime = { path = ".." } [features] a = [] "#, ) .file( "the-macro/src/lib.rs", " fn _test() { the_macro_support::foo(shared::Foo); } ", ) .file( "the-macro-support/Cargo.toml", r#" [package] name = "the-macro-support" version = "0.1.0" edition = "2015" [dependencies] shared = { path = "../shared" } "#, ) .file( "the-macro-support/src/lib.rs", " pub fn foo(_: shared::Foo) {} ", ) .file( "shared/Cargo.toml", r#" [package] name = "shared" version = "0.1.0" edition = "2015" [features] b = [] "#, ) .file("shared/src/lib.rs", "pub struct Foo;") .build(); p.cargo("test --manifest-path the-macro/Cargo.toml").run(); } #[cargo_test] fn doc_optional() { // Checks for a bug where `cargo doc` was failing with an inactive target // that enables a shared optional dependency. Package::new("spin", "1.0.0").publish(); Package::new("bar", "1.0.0") .add_dep(Dependency::new("spin", "1.0").optional(true)) .publish(); // The enabler package enables the `spin` feature, which we don't want. Package::new("enabler", "1.0.0") .feature_dep("bar", "1.0", &["spin"]) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" resolver = "2" [target.'cfg(whatever)'.dependencies] enabler = "1.0" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("doc") .with_stderr_data( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 3 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] spin v1.0.0 (registry `dummy-registry`) [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [DOCUMENTING] bar v1.0.0 [CHECKING] bar v1.0.0 [DOCUMENTING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]] .unordered(), ) .run(); } #[cargo_test] fn minimal_download() { // Various checks that it only downloads the minimum set of dependencies // needed in various situations. // // This checks several permutations of the different // host_dep/dev_dep/itarget settings. These 3 are planned to be stabilized // together, so there isn't much need to be concerned about how the behave // independently. However, there are some cases where they do behave // independently. Specifically: // // * `cargo test` forces dev_dep decoupling to be disabled. // * `cargo tree --target=all` forces ignore_inactive_targets off and decouple_dev_deps off. // * `cargo tree --target=all -e normal` forces ignore_inactive_targets off. // // However, `cargo tree` is a little weird because it downloads everything // anyways. // // So to summarize the different permutations: // // dev_dep | host_dep | itarget | Notes // --------|----------|---------|---------------------------- // | | | -Zfeatures=compare (new resolver should behave same as old) // | | ✓ | This scenario should not happen. // | ✓ | | `cargo tree --target=all -Zfeatures=all`† // | ✓ | ✓ | `cargo test` // ✓ | | | This scenario should not happen. // ✓ | | ✓ | This scenario should not happen. // ✓ | ✓ | | `cargo tree --target=all -e normal -Z features=all`† // ✓ | ✓ | ✓ | A normal build. // // † — However, `cargo tree` downloads everything. Package::new("normal", "1.0.0").publish(); Package::new("normal_pm", "1.0.0").publish(); Package::new("normal_opt", "1.0.0").publish(); Package::new("dev_dep", "1.0.0").publish(); Package::new("dev_dep_pm", "1.0.0").publish(); Package::new("build_dep", "1.0.0").publish(); Package::new("build_dep_pm", "1.0.0").publish(); Package::new("build_dep_opt", "1.0.0").publish(); Package::new("itarget_normal", "1.0.0").publish(); Package::new("itarget_normal_pm", "1.0.0").publish(); Package::new("itarget_dev_dep", "1.0.0").publish(); Package::new("itarget_dev_dep_pm", "1.0.0").publish(); Package::new("itarget_build_dep", "1.0.0").publish(); Package::new("itarget_build_dep_pm", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] normal = "1.0" normal_pm = "1.0" normal_opt = { version = "1.0", optional = true } [dev-dependencies] dev_dep = "1.0" dev_dep_pm = "1.0" [build-dependencies] build_dep = "1.0" build_dep_pm = "1.0" build_dep_opt = { version = "1.0", optional = true } [target.'cfg(whatever)'.dependencies] itarget_normal = "1.0" itarget_normal_pm = "1.0" [target.'cfg(whatever)'.dev-dependencies] itarget_dev_dep = "1.0" itarget_dev_dep_pm = "1.0" [target.'cfg(whatever)'.build-dependencies] itarget_build_dep = "1.0" itarget_build_dep_pm = "1.0" "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .build(); let clear = || { paths::cargo_home().join("registry/cache").rm_rf(); paths::cargo_home().join("registry/src").rm_rf(); p.build_dir().rm_rf(); }; // none // Should be the same as `-Zfeatures=all` p.cargo("check -Zfeatures=compare") .masquerade_as_nightly_cargo(&["features=compare"]) .with_stderr_data( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 14 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] normal_pm v1.0.0 (registry `dummy-registry`) [DOWNLOADED] normal v1.0.0 (registry `dummy-registry`) [DOWNLOADED] build_dep_pm v1.0.0 (registry `dummy-registry`) [DOWNLOADED] build_dep v1.0.0 (registry `dummy-registry`) [COMPILING] build_dep v1.0.0 [COMPILING] build_dep_pm v1.0.0 [CHECKING] normal_pm v1.0.0 [CHECKING] normal v1.0.0 [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); clear(); // New behavior switch_to_resolver_2(&p); // all p.cargo("check") .with_stderr_data( str![[r#" [DOWNLOADING] crates ... [DOWNLOADED] normal_pm v1.0.0 (registry `dummy-registry`) [DOWNLOADED] normal v1.0.0 (registry `dummy-registry`) [DOWNLOADED] build_dep_pm v1.0.0 (registry `dummy-registry`) [DOWNLOADED] build_dep v1.0.0 (registry `dummy-registry`) [COMPILING] build_dep_pm v1.0.0 [COMPILING] build_dep v1.0.0 [CHECKING] normal_pm v1.0.0 [CHECKING] normal v1.0.0 [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); clear(); // This disables decouple_dev_deps. p.cargo("test --no-run") .with_stderr_data( str![[r#" [DOWNLOADING] crates ... [DOWNLOADED] normal_pm v1.0.0 (registry `dummy-registry`) [DOWNLOADED] normal v1.0.0 (registry `dummy-registry`) [DOWNLOADED] dev_dep_pm v1.0.0 (registry `dummy-registry`) [DOWNLOADED] dev_dep v1.0.0 (registry `dummy-registry`) [DOWNLOADED] build_dep_pm v1.0.0 (registry `dummy-registry`) [DOWNLOADED] build_dep v1.0.0 (registry `dummy-registry`) [COMPILING] build_dep_pm v1.0.0 [COMPILING] build_dep v1.0.0 [COMPILING] normal_pm v1.0.0 [COMPILING] normal v1.0.0 [COMPILING] dev_dep v1.0.0 [COMPILING] dev_dep_pm v1.0.0 [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [EXECUTABLE] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) "#]] .unordered(), ) .run(); clear(); // This disables itarget, but leaves decouple_dev_deps enabled. p.cargo("tree -e normal --target=all") .with_stderr_data( str![[r#" [DOWNLOADING] crates ... [DOWNLOADED] normal_pm v1.0.0 (registry `dummy-registry`) [DOWNLOADED] normal v1.0.0 (registry `dummy-registry`) [DOWNLOADED] itarget_normal_pm v1.0.0 (registry `dummy-registry`) [DOWNLOADED] itarget_normal v1.0.0 (registry `dummy-registry`) [DOWNLOADED] itarget_build_dep_pm v1.0.0 (registry `dummy-registry`) [DOWNLOADED] itarget_build_dep v1.0.0 (registry `dummy-registry`) [DOWNLOADED] build_dep_pm v1.0.0 (registry `dummy-registry`) [DOWNLOADED] build_dep v1.0.0 (registry `dummy-registry`) "#]] .unordered(), ) .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── itarget_normal v1.0.0 ├── itarget_normal_pm v1.0.0 ├── normal v1.0.0 └── normal_pm v1.0.0 "#]]) .run(); clear(); // This disables itarget and decouple_dev_deps. p.cargo("tree --target=all") .with_stderr_data( str![[r#" [DOWNLOADING] crates ... [DOWNLOADED] normal_pm v1.0.0 (registry `dummy-registry`) [DOWNLOADED] normal v1.0.0 (registry `dummy-registry`) [DOWNLOADED] itarget_normal_pm v1.0.0 (registry `dummy-registry`) [DOWNLOADED] itarget_normal v1.0.0 (registry `dummy-registry`) [DOWNLOADED] itarget_dev_dep_pm v1.0.0 (registry `dummy-registry`) [DOWNLOADED] itarget_dev_dep v1.0.0 (registry `dummy-registry`) [DOWNLOADED] itarget_build_dep_pm v1.0.0 (registry `dummy-registry`) [DOWNLOADED] itarget_build_dep v1.0.0 (registry `dummy-registry`) [DOWNLOADED] dev_dep_pm v1.0.0 (registry `dummy-registry`) [DOWNLOADED] dev_dep v1.0.0 (registry `dummy-registry`) [DOWNLOADED] build_dep_pm v1.0.0 (registry `dummy-registry`) [DOWNLOADED] build_dep v1.0.0 (registry `dummy-registry`) "#]] .unordered(), ) .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── itarget_normal v1.0.0 ├── itarget_normal_pm v1.0.0 ├── normal v1.0.0 └── normal_pm v1.0.0 [build-dependencies] ├── build_dep v1.0.0 ├── build_dep_pm v1.0.0 ├── itarget_build_dep v1.0.0 └── itarget_build_dep_pm v1.0.0 [dev-dependencies] ├── dev_dep v1.0.0 ├── dev_dep_pm v1.0.0 ├── itarget_dev_dep v1.0.0 └── itarget_dev_dep_pm v1.0.0 "#]]) .run(); clear(); } #[cargo_test] fn pm_with_int_shared() { // This is a somewhat complex scenario of a proc-macro in a workspace with // an integration test where the proc-macro is used for other things, and // *everything* is built at once (`--workspace --all-targets // --all-features`). There was a bug where the UnitFor settings were being // incorrectly computed based on the order that the graph was traversed. // // There are some uncertainties about exactly how proc-macros should behave // with `--workspace`, see https://github.com/rust-lang/cargo/issues/8312. // // This uses a const-eval hack to do compile-time feature checking. let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo", "pm", "shared"] resolver = "2" "#, ) .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [dependencies] pm = { path = "../pm" } shared = { path = "../shared", features = ["norm-feat"] } "#, ) .file( "foo/src/lib.rs", r#" // foo->shared always has both features set const _CHECK: [(); 0] = [(); 0-!(shared::FEATS==3) as usize]; "#, ) .file( "pm/Cargo.toml", r#" [package] name = "pm" version = "0.1.0" edition = "2015" [lib] proc-macro = true [dependencies] shared = { path = "../shared", features = ["host-feat"] } "#, ) .file( "pm/src/lib.rs", r#" // pm->shared always has just host const _CHECK: [(); 0] = [(); 0-!(shared::FEATS==1) as usize]; "#, ) .file( "pm/tests/pm_test.rs", r#" // integration test gets both set const _CHECK: [(); 0] = [(); 0-!(shared::FEATS==3) as usize]; "#, ) .file( "shared/Cargo.toml", r#" [package] name = "shared" version = "0.1.0" edition = "2015" [features] norm-feat = [] host-feat = [] "#, ) .file( "shared/src/lib.rs", r#" pub const FEATS: u32 = { if cfg!(feature="norm-feat") && cfg!(feature="host-feat") { 3 } else if cfg!(feature="norm-feat") { 2 } else if cfg!(feature="host-feat") { 1 } else { 0 } }; "#, ) .build(); p.cargo("build --workspace --all-targets --all-features -v") .with_stderr_data( str![[r#" [COMPILING] shared v0.1.0 ([ROOT]/foo/shared) [RUNNING] `rustc --crate-name shared [..]--crate-type lib [..]` [RUNNING] `rustc --crate-name shared [..]--crate-type lib [..]` [RUNNING] `rustc --crate-name shared [..]--test[..]` [COMPILING] pm v0.1.0 ([ROOT]/foo/pm) [RUNNING] `rustc --crate-name pm [..]--crate-type proc-macro[..]` [RUNNING] `rustc --crate-name pm [..]--test[..]` [COMPILING] foo v0.1.0 ([ROOT]/foo/foo) [RUNNING] `rustc --crate-name foo [..]--crate-type lib [..]` [RUNNING] `rustc --crate-name foo [..]--test[..]` [RUNNING] `rustc --crate-name pm_test [..]--test[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); // And again, should stay fresh. p.cargo("build --workspace --all-targets --all-features -v") .with_stderr_data( str![[r#" [FRESH] pm v0.1.0 ([ROOT]/foo/pm) [FRESH] foo v0.1.0 ([ROOT]/foo/foo) [FRESH] shared v0.1.0 ([ROOT]/foo/shared) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn doc_proc_macro() { // Checks for a bug when documenting a proc-macro with a dependency. The // doc unit builder was not carrying the "for host" setting through the // dependencies, and the `pm-dep` dependency was causing a panic because // it was looking for target features instead of host features. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" resolver = "2" [dependencies] pm = { path = "pm" } "#, ) .file("src/lib.rs", "") .file( "pm/Cargo.toml", r#" [package] name = "pm" version = "0.1.0" edition = "2015" [lib] proc-macro = true [dependencies] pm-dep = { path = "../pm-dep" } "#, ) .file("pm/src/lib.rs", "") .file("pm-dep/Cargo.toml", &basic_manifest("pm-dep", "0.1.0")) .file("pm-dep/src/lib.rs", "") .build(); // Unfortunately this cannot check the output because what it prints is // nondeterministic. Sometimes it says "Compiling pm-dep" and sometimes // "Checking pm-dep". This is because it is both building it and checking // it in parallel (building so it can build the proc-macro, and checking // so rustdoc can load it). p.cargo("doc").run(); } #[cargo_test] fn edition_2021_default_2() { // edition = 2021 defaults to v2 resolver. Package::new("common", "1.0.0") .feature("f1", &[]) .file("src/lib.rs", "") .publish(); Package::new("bar", "1.0.0") .add_dep( Dependency::new("common", "1.0") .target("cfg(whatever)") .enable_features(&["f1"]), ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] common = "1.0" bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); // First without edition. p.cargo("tree -f") .arg("{p} feats:{f}") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) feats: ├── bar v1.0.0 feats: └── common v1.0.0 feats:f1 "#]]) .run(); p.change_file( "Cargo.toml", r#" cargo-features = ["edition2021"] [package] name = "foo" version = "0.1.0" edition = "2021" [dependencies] common = "1.0" bar = "1.0" "#, ); // Importantly, this does not include `f1` on `common`. p.cargo("tree -f") .arg("{p} feats:{f}") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) feats: ├── bar v1.0.0 feats: └── common v1.0.0 feats: "#]]) .run(); } #[cargo_test] fn all_features_merges_with_features() { Package::new("dep", "0.1.0") .feature("feat1", &[]) .file( "src/lib.rs", r#" #[cfg(feature="feat1")] pub fn work() { println!("it works"); } "#, ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [features] a = [] [dependencies] dep = "0.1" [[example]] name = "ex" required-features = ["a", "dep/feat1"] "#, ) .file( "examples/ex.rs", r#" fn main() { dep::work(); } "#, ) .file("src/lib.rs", "") .build(); p.cargo("run --example ex --all-features --features dep/feat1") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] dep v0.1.0 (registry `dummy-registry`) [COMPILING] dep v0.1.0 [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/examples/ex[EXE]` "#]]) .with_stdout_data(str![[r#" it works "#]]) .run(); switch_to_resolver_2(&p); p.cargo("run --example ex --all-features --features dep/feat1") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/examples/ex[EXE]` "#]]) .with_stdout_data(str![[r#" it works "#]]) .run(); } #[cargo_test] fn dep_with_optional_host_deps_activated() { // To prevent regression like rust-lang/cargo#11330 let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2021" [dependencies] serde = { path = "serde", features = ["derive", "build"] } "#, ) .file("src/lib.rs", "") .file( "serde/Cargo.toml", r#" [package] name = "serde" version = "0.1.0" edition = "2021" [dependencies] serde_derive = { path = "../serde_derive", optional = true } [build-dependencies] serde_build = { path = "../serde_build", optional = true } [features] derive = ["dep:serde_derive"] build = ["dep:serde_build"] "#, ) .file("serde/src/lib.rs", "") .file("serde/build.rs", "fn main() {}") .file( "serde_derive/Cargo.toml", r#" [package] name = "serde_derive" version = "0.1.0" edition = "2021" [lib] proc-macro = true "#, ) .file("serde_derive/src/lib.rs", "") .file( "serde_build/Cargo.toml", &basic_manifest("serde_build", "0.1.0"), ) .file("serde_build/src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [LOCKING] 3 packages to latest compatible versions [COMPILING] serde_build v0.1.0 ([ROOT]/foo/serde_build) [COMPILING] serde_derive v0.1.0 ([ROOT]/foo/serde_derive) [COMPILING] serde v0.1.0 ([ROOT]/foo/serde) [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn dont_unify_proc_macro_example_from_dependency() { // See https://github.com/rust-lang/cargo/issues/13726 let p = project() .file( "Cargo.toml", r#" [package] name = "foo" edition = "2021" [dependencies] pm_helper = { path = "pm_helper" } "#, ) .file("src/lib.rs", "") .file( "pm_helper/Cargo.toml", r#" [package] name = "pm_helper" [[example]] name = "pm" proc-macro = true crate-type = ["proc-macro"] "#, ) .file("pm_helper/src/lib.rs", "") .file("pm_helper/examples/pm.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [CHECKING] pm_helper v0.0.0 ([ROOT]/foo/pm_helper) [CHECKING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } cargo-0.91.0/tests/testsuite/features_namespaced.rs000064400000000000000000001043571046102023000205570ustar 00000000000000//! Tests for namespaced features. use crate::prelude::*; use cargo_test_support::registry::{Dependency, Package, RegistryBuilder}; use cargo_test_support::str; use cargo_test_support::{project, publish}; use super::features2::switch_to_resolver_2; #[cargo_test] fn dependency_with_crate_syntax() { // Registry dependency uses dep: syntax. Package::new("baz", "1.0.0").publish(); Package::new("bar", "1.0.0") .add_dep(Dependency::new("baz", "1.0").optional(true)) .feature("feat", &["dep:baz"]) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = {version="1.0", features=["feat"]} "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] baz v1.0.0 (registry `dummy-registry`) [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [CHECKING] baz v1.0.0 [CHECKING] bar v1.0.0 [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn namespaced_invalid_feature() { // Specifies a feature that doesn't exist. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] bar = ["baz"] "#, ) .file("src/main.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: feature `bar` includes `baz` which is neither a dependency nor another feature [HELP] a feature with a similar name exists: `bar` "#]]) .run(); } #[cargo_test] fn namespaced_invalid_dependency() { // Specifies a dep:name that doesn't exist. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [features] bar = ["dep:baz"] "#, ) .file("src/main.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: feature `bar` includes `dep:baz`, but `baz` is not listed as a dependency "#]]) .run(); } #[cargo_test] fn namespaced_non_optional_dependency() { // Specifies a dep:name for a dependency that is not optional. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [features] bar = ["dep:baz"] [dependencies] baz = "0.1" "#, ) .file("src/main.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: feature `bar` includes `dep:baz`, but `baz` is not an optional dependency A non-optional dependency of the same name is defined; consider adding `optional = true` to its definition. "#]]) .run(); } #[cargo_test] fn namespaced_implicit_feature() { // Backwards-compatible with old syntax. Package::new("baz", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [features] bar = ["baz"] [dependencies] baz = { version = "0.1", optional = true } "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check --features baz") .with_stderr_data(str![[r#" [DOWNLOADING] crates ... [DOWNLOADED] baz v0.1.0 (registry `dummy-registry`) [CHECKING] baz v0.1.0 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn namespaced_shadowed_dep() { // An optional dependency is not listed in the features table, and its // implicit feature is overridden. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [features] baz = [] [dependencies] baz = { version = "0.1", optional = true } "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: optional dependency `baz` is not included in any feature Make sure that `dep:baz` is included in one of features in the [features] table. "#]]) .run(); } #[cargo_test] fn namespaced_shadowed_non_optional() { // Able to specify a feature with the same name as a required dependency. Package::new("baz", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [features] baz = [] [dependencies] baz = "0.1" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check").run(); } #[cargo_test] fn namespaced_implicit_non_optional() { // Includes a non-optional dependency in [features] table. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [features] bar = ["baz"] [dependencies] baz = "0.1" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: feature `bar` includes `baz`, but `baz` is not an optional dependency A non-optional dependency of the same name is defined; consider adding `optional = true` to its definition. "#]]) .run(); } #[cargo_test] fn namespaced_same_name() { // Explicitly listing an optional dependency in the [features] table. Package::new("baz", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [features] baz = ["dep:baz"] [dependencies] baz = { version = "0.1", optional = true } "#, ) .file( "src/main.rs", r#" fn main() { if cfg!(feature="baz") { println!("baz"); } } "#, ) .build(); p.cargo("run") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .with_stdout_data("") .run(); p.cargo("run --features baz") .with_stderr_data(str![[r#" [DOWNLOADING] crates ... [DOWNLOADED] baz v0.1.0 (registry `dummy-registry`) [COMPILING] baz v0.1.0 [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .with_stdout_data(str![[r#" baz "#]]) .run(); } #[cargo_test] fn no_implicit_feature() { // Using `dep:` will not create an implicit feature. Package::new("regex", "1.0.0").publish(); Package::new("lazy_static", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] regex = { version = "1.0", optional = true } lazy_static = { version = "1.0", optional = true } [features] regex = ["dep:regex", "dep:lazy_static"] "#, ) .file( "src/main.rs", r#" fn main() { if cfg!(feature = "regex") { println!("regex"); } #[allow(unexpected_cfgs)] if cfg!(feature = "lazy_static") { println!("lazy_static"); } } "#, ) .build(); p.cargo("run") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .with_stdout_data("") .run(); p.cargo("run --features regex") .with_stderr_data( str![[r#" [DOWNLOADING] crates ... [DOWNLOADED] regex v1.0.0 (registry `dummy-registry`) [DOWNLOADED] lazy_static v1.0.0 (registry `dummy-registry`) [COMPILING] regex v1.0.0 [COMPILING] lazy_static v1.0.0 [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]] .unordered(), ) .with_stdout_data(str![[r#" regex "#]]) .run(); p.cargo("run --features lazy_static") .with_stderr_data(str![[r#" [ERROR] package `foo v0.1.0 ([ROOT]/foo)` does not have feature `lazy_static` [HELP] an optional dependency with that name exists, but the `features` table includes it with the "dep:" syntax so it does not have an implicit feature with that name Dependency `lazy_static` would be enabled by these features: - `regex` "#]]) .with_status(101) .run(); } #[cargo_test] fn crate_syntax_bad_name() { // "dep:bar" = [] Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { version="1.0", optional=true } [features] "dep:bar" = [] "#, ) .file("src/lib.rs", "") .build(); p.cargo("check --features dep:bar") .with_status(101) .with_stderr_data(str![[r#" [ERROR] feature named `dep:bar` is not allowed to start with `dep:` --> Cargo.toml:11:17 | 11 | "dep:bar" = [] | ^^^^^^^^^ | "#]]) .run(); } #[cargo_test] fn crate_syntax_in_dep() { // features = ["dep:baz"] Package::new("baz", "1.0.0").publish(); Package::new("bar", "1.0.0") .add_dep(Dependency::new("baz", "1.0").optional(true)) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { version = "1.0", features = ["dep:baz"] } "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: feature `dep:baz` in dependency `bar` is not allowed to use explicit `dep:` syntax If you want to enable an optional dependency, specify the name of the optional dependency without the `dep:` prefix, or specify a feature from the dependency's `[features]` table that enables the optional dependency. "#]]) .run(); } #[cargo_test] fn crate_syntax_cli() { // --features dep:bar Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { version = "1.0", optional=true } "#, ) .file("src/lib.rs", "") .build(); p.cargo("check --features dep:bar") .with_status(101) .with_stderr_data(str![[r#" [ERROR] feature `dep:bar` is not allowed to use explicit `dep:` syntax "#]]) .run(); switch_to_resolver_2(&p); p.cargo("check --features dep:bar") .with_status(101) .with_stderr_data(str![[r#" [ERROR] feature `dep:bar` is not allowed to use explicit `dep:` syntax "#]]) .run(); } #[cargo_test] fn crate_required_features() { // required-features = ["dep:bar"] Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { version = "1.0", optional=true } [[bin]] name = "foo" required-features = ["dep:bar"] "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [ERROR] invalid feature `dep:bar` in required-features of target `foo`: `dep:` prefixed feature values are not allowed in required-features "#]]) .run(); } #[cargo_test] fn json_exposed() { // Checks that the implicit dep: values are exposed in JSON. Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { version = "1.0", optional=true } "#, ) .file("src/lib.rs", "") .build(); p.cargo("metadata --no-deps") .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [], "categories": [], "default_run": null, "dependencies": "{...}", "description": null, "documentation": null, "edition": "2015", "features": { "bar": [ "dep:bar" ] }, "homepage": null, "id": "path+[ROOTURL]/foo#0.1.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/Cargo.toml", "metadata": null, "name": "foo", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": "{...}", "version": "0.1.0" } ], "resolve": null, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo#0.1.0" ], "workspace_members": [ "path+[ROOTURL]/foo#0.1.0" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); } #[cargo_test] fn crate_feature_with_explicit() { // crate_name/feat_name syntax where crate_name already has a feature defined. // NOTE: I don't know if this is actually ideal behavior. Package::new("bar", "1.0.0") .feature("bar_feat", &[]) .file( "src/lib.rs", r#" #[cfg(not(feature="bar_feat"))] compile_error!("bar_feat is not enabled"); "#, ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { version="1.0", optional = true } [features] f1 = ["bar/bar_feat"] bar = ["dep:bar", "f2"] f2 = [] "#, ) .file( "src/lib.rs", r#" #[cfg(not(feature="bar"))] compile_error!("bar should be enabled"); #[cfg(not(feature="f2"))] compile_error!("f2 should be enabled"); "#, ) .build(); p.cargo("check --features f1") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [CHECKING] bar v1.0.0 [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn optional_explicit_without_crate() { // "feat" syntax when there is no implicit "feat" feature because it is // explicitly listed elsewhere. Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { version = "1.0", optional = true } [features] feat1 = ["dep:bar"] feat2 = ["bar"] "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: feature `feat2` includes `bar`, but `bar` is an optional dependency without an implicit feature Use `dep:bar` to enable the dependency. "#]]) .run(); } #[cargo_test] fn tree() { Package::new("baz", "1.0.0").publish(); Package::new("bar", "1.0.0") .add_dep(Dependency::new("baz", "1.0").optional(true)) .feature("feat1", &["dep:baz"]) .feature("feat2", &[]) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { version = "1.0", features = ["feat1"], optional=true } [features] a = ["bar/feat2"] bar = ["dep:bar"] "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree -e features") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) "#]]) .run(); p.cargo("tree -e features --features a") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── bar feature "default" │ └── bar v1.0.0 │ └── baz feature "default" │ └── baz v1.0.0 └── bar feature "feat1" └── bar v1.0.0 (*) "#]]) .run(); p.cargo("tree -e features --features a -i bar") .with_stdout_data(str![[r#" bar v1.0.0 ├── bar feature "default" │ └── foo v0.1.0 ([ROOT]/foo) │ ├── foo feature "a" (command-line) │ ├── foo feature "bar" │ │ └── foo feature "a" (command-line) │ └── foo feature "default" (command-line) ├── bar feature "feat1" │ └── foo v0.1.0 ([ROOT]/foo) (*) └── bar feature "feat2" └── foo feature "a" (command-line) "#]]) .run(); p.cargo("tree -e features --features bar") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── bar feature "default" │ └── bar v1.0.0 │ └── baz feature "default" │ └── baz v1.0.0 └── bar feature "feat1" └── bar v1.0.0 (*) "#]]) .run(); p.cargo("tree -e features --features bar -i bar") .with_stdout_data(str![[r#" bar v1.0.0 ├── bar feature "default" │ └── foo v0.1.0 ([ROOT]/foo) │ ├── foo feature "bar" (command-line) │ └── foo feature "default" (command-line) └── bar feature "feat1" └── foo v0.1.0 ([ROOT]/foo) (*) "#]]) .run(); } #[cargo_test] fn tree_no_implicit() { // tree without an implicit feature Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { version = "1.0", optional=true } [features] a = ["dep:bar"] "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree -e features") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) "#]]) .run(); p.cargo("tree -e features --all-features") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── bar feature "default" └── bar v1.0.0 "#]]) .run(); p.cargo("tree -e features -i bar --all-features") .with_stdout_data(str![[r#" bar v1.0.0 └── bar feature "default" └── foo v0.1.0 ([ROOT]/foo) ├── foo feature "a" (command-line) └── foo feature "default" (command-line) "#]]) .run(); } #[cargo_test] fn publish_no_implicit() { let registry = RegistryBuilder::new().http_api().http_index().build(); // Does not include implicit features or dep: syntax on publish. Package::new("opt-dep1", "1.0.0").publish(); Package::new("opt-dep2", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" description = "foo" license = "MIT" homepage = "https://example.com/" [dependencies] opt-dep1 = { version = "1.0", optional = true } opt-dep2 = { version = "1.0", optional = true } [features] feat = ["opt-dep1"] "#, ) .file("src/lib.rs", "") .build(); p.cargo("publish --no-verify") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [PACKAGING] foo v0.1.0 ([ROOT]/foo) [UPDATING] crates.io index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] foo v0.1.0 ([ROOT]/foo) [UPLOADED] foo v0.1.0 to registry `crates-io` [NOTE] waiting for foo v0.1.0 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.1.0 at registry `crates-io` "#]]) .run(); publish::validate_upload_with_contents( r#" { "authors": [], "badges": {}, "categories": [], "deps": [ { "default_features": true, "features": [], "kind": "normal", "name": "opt-dep1", "optional": true, "target": null, "version_req": "^1.0" }, { "default_features": true, "features": [], "kind": "normal", "name": "opt-dep2", "optional": true, "target": null, "version_req": "^1.0" } ], "description": "foo", "documentation": null, "features": { "feat": ["opt-dep1"] }, "homepage": "https://example.com/", "keywords": [], "license": "MIT", "license_file": null, "links": null, "name": "foo", "readme": null, "readme_file": null, "repository": null, "rust_version": null, "vers": "0.1.0" } "#, "foo-0.1.0.crate", &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs", "Cargo.lock"], [( "Cargo.toml", str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "foo" version = "0.1.0" build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" homepage = "https://example.com/" readme = false license = "MIT" [features] feat = ["opt-dep1"] [lib] name = "foo" path = "src/lib.rs" [dependencies.opt-dep1] version = "1.0" optional = true [dependencies.opt-dep2] version = "1.0" optional = true "##]], )], ); } #[cargo_test] fn publish() { let registry = RegistryBuilder::new().http_api().http_index().build(); // Publish behavior with explicit dep: syntax. Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" description = "foo" license = "MIT" homepage = "https://example.com/" [dependencies] bar = { version = "1.0", optional = true } [features] feat1 = [] feat2 = ["dep:bar"] feat3 = ["feat2"] "#, ) .file("src/lib.rs", "") .build(); p.cargo("publish") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [PACKAGING] foo v0.1.0 ([ROOT]/foo) [UPDATING] crates.io index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.1.0 ([ROOT]/foo) [COMPILING] foo v0.1.0 ([ROOT]/foo/target/package/foo-0.1.0) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] foo v0.1.0 ([ROOT]/foo) [UPLOADED] foo v0.1.0 to registry `crates-io` [NOTE] waiting for foo v0.1.0 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.1.0 at registry `crates-io` "#]]) .run(); publish::validate_upload_with_contents( r#" { "authors": [], "badges": {}, "categories": [], "deps": [ { "default_features": true, "features": [], "kind": "normal", "name": "bar", "optional": true, "target": null, "version_req": "^1.0" } ], "description": "foo", "documentation": null, "features": { "feat1": [], "feat2": ["dep:bar"], "feat3": ["feat2"] }, "homepage": "https://example.com/", "keywords": [], "license": "MIT", "license_file": null, "links": null, "name": "foo", "readme": null, "readme_file": null, "repository": null, "rust_version": null, "vers": "0.1.0" } "#, "foo-0.1.0.crate", &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs", "Cargo.lock"], [( "Cargo.toml", str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "foo" version = "0.1.0" build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" homepage = "https://example.com/" readme = false license = "MIT" [features] feat1 = [] feat2 = ["dep:bar"] feat3 = ["feat2"] [lib] name = "foo" path = "src/lib.rs" [dependencies.bar] version = "1.0" optional = true "##]], )], ); } #[cargo_test] fn namespaced_feature_together() { // Check for an error when `dep:` is used with `/` Package::new("bar", "1.0.0") .feature("bar-feat", &[]) .publish(); // Non-optional shouldn't have extra err. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" [features] f1 = ["dep:bar/bar-feat"] "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: feature `f1` includes `dep:bar/bar-feat` with both `dep:` and `/` To fix this, remove the `dep:` prefix. "#]]) .run(); // Weak dependency shouldn't have extra err. p.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = {version = "1.0", optional = true } [features] f1 = ["dep:bar?/bar-feat"] "#, ); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: feature `f1` includes `dep:bar?/bar-feat` with both `dep:` and `/` To fix this, remove the `dep:` prefix. "#]]) .run(); // If dep: is already specified, shouldn't have extra err. p.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = {version = "1.0", optional = true } [features] f1 = ["dep:bar", "dep:bar/bar-feat"] "#, ); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: feature `f1` includes `dep:bar/bar-feat` with both `dep:` and `/` To fix this, remove the `dep:` prefix. "#]]) .run(); // Only when the other 3 cases aren't true should it give some extra help. p.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = {version = "1.0", optional = true } [features] f1 = ["dep:bar/bar-feat"] "#, ); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: feature `f1` includes `dep:bar/bar-feat` with both `dep:` and `/` To fix this, remove the `dep:` prefix. If the intent is to avoid creating an implicit feature `bar` for an optional dependency, then consider replacing this with two values: "dep:bar", "bar/bar-feat" "#]]) .run(); } #[cargo_test] fn dep_feature_when_hidden() { // Checks for behavior with dep:bar and bar/feat syntax when there is no // `bar` feature. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { path = "bar", optional = true } [features] f1 = ["dep:bar"] f2 = ["bar/bar_feat"] "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" [features] bar_feat = [] "#, ) .file("bar/src/lib.rs", "") .build(); p.cargo("tree -f") .arg("{p} features={f}") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) features= "#]]) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version "#]]) .run(); p.cargo("tree -F f1 -f") .arg("{p} features={f}") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) features=f1 └── bar v0.1.0 ([ROOT]/foo/bar) features= "#]]) .with_stderr_data("") .run(); p.cargo("tree -F f2 -f") .arg("{p} features={f}") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) features=f2 └── bar v0.1.0 ([ROOT]/foo/bar) features=bar_feat "#]]) .with_stderr_data("") .run(); p.cargo("tree --all-features -f") .arg("{p} features={f}") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) features=f1,f2 └── bar v0.1.0 ([ROOT]/foo/bar) features=bar_feat "#]]) .with_stderr_data("") .run(); } cargo-0.91.0/tests/testsuite/fetch.rs000064400000000000000000000072041046102023000156430ustar 00000000000000//! Tests for the `cargo fetch` command. use crate::prelude::*; use crate::utils::cross_compile::disabled as cross_compile_disabled; use cargo_test_support::registry::Package; use cargo_test_support::rustc_host; use cargo_test_support::{basic_manifest, cross_compile, project, str}; #[cargo_test] fn no_deps() { let p = project() .file("src/main.rs", "mod a; fn main() {}") .file("src/a.rs", "") .build(); p.cargo("fetch").with_stderr_data("").run(); } #[cargo_test] fn fetch_all_platform_dependencies_when_no_target_is_given() { if cross_compile_disabled() { return; } Package::new("d1", "1.2.3") .file("Cargo.toml", &basic_manifest("d1", "1.2.3")) .file("src/lib.rs", "") .publish(); Package::new("d2", "0.1.2") .file("Cargo.toml", &basic_manifest("d2", "0.1.2")) .file("src/lib.rs", "") .publish(); let target = cross_compile::alternate(); let host = rustc_host(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [target.{host}.dependencies] d1 = "1.2.3" [target.{target}.dependencies] d2 = "0.1.2" "#, host = host, target = target ), ) .file("src/lib.rs", "") .build(); p.cargo("fetch") .with_stderr_data(str![[r#" ... [DOWNLOADED] d1 v1.2.3 (registry `dummy-registry`) [DOWNLOADED] d2 v0.1.2 (registry `dummy-registry`) ... "#]]) .run(); } #[cargo_test] fn fetch_platform_specific_dependencies() { if cross_compile_disabled() { return; } Package::new("d1", "1.2.3") .file("Cargo.toml", &basic_manifest("d1", "1.2.3")) .file("src/lib.rs", "") .publish(); Package::new("d2", "0.1.2") .file("Cargo.toml", &basic_manifest("d2", "0.1.2")) .file("src/lib.rs", "") .publish(); let target = cross_compile::alternate(); let host = rustc_host(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [target.{host}.dependencies] d1 = "1.2.3" [target.{target}.dependencies] d2 = "0.1.2" "#, host = host, target = target ), ) .file("src/lib.rs", "") .build(); p.cargo("fetch --target") .arg(&host) .with_stderr_contains("[DOWNLOADED] d1 v1.2.3 [..]") .with_stderr_does_not_contain("[DOWNLOADED] d2 v0.1.2 [..]") .run(); p.cargo("fetch --target") .arg(&target) .with_stderr_contains("[DOWNLOADED] d2 v0.1.2[..]") .with_stderr_does_not_contain("[DOWNLOADED] d1 v1.2.3 [..]") .run(); } #[cargo_test] fn fetch_warning() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" edition = "2015" misspelled = "wut" "#, ) .file("src/lib.rs", "") .build(); p.cargo("fetch") .with_stderr_data(str![[r#" [WARNING] unused manifest key: package.misspelled "#]]) .run(); } cargo-0.91.0/tests/testsuite/fix.rs000064400000000000000000002550521046102023000153460ustar 00000000000000//! Tests for the `cargo fix` command. use crate::prelude::*; use crate::utils::tools; use cargo::core::Edition; use cargo_test_support::compare::assert_e2e; use cargo_test_support::git::{self, init}; use cargo_test_support::paths; use cargo_test_support::registry::{Dependency, Package}; use cargo_test_support::str; use cargo_test_support::{basic_manifest, is_nightly, project}; #[cargo_test] fn do_not_fix_broken_builds() { let p = project() .file( "src/lib.rs", r#" pub fn foo() { let mut x = 3; let _ = x; } pub fn foo2() { let _x: u32 = "a"; } "#, ) .build(); p.cargo("fix --allow-no-vcs") .env("__CARGO_FIX_YOLO", "1") .with_status(101) .with_stderr_data(str![[r#" ... [ERROR] could not compile `foo` (lib) due to 1 previous error; 1 warning emitted ... "#]]) .run(); assert!(p.read_file("src/lib.rs").contains("let mut x = 3;")); } #[cargo_test] fn fix_broken_if_requested() { let p = project() .file( "src/lib.rs", r#" fn foo(a: &u32) -> u32 { a + 1 } pub fn bar() { foo(1); } "#, ) .build(); p.cargo("fix --allow-no-vcs --broken-code") .env("__CARGO_FIX_YOLO", "1") .run(); } #[cargo_test] fn fix_path_deps() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { path = 'bar' } [workspace] "#, ) .file( "src/lib.rs", r#" extern crate bar; pub fn foo() -> u32 { let mut x = 3; x } "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file( "bar/src/lib.rs", r#" pub fn foo() -> u32 { let mut x = 3; x } "#, ) .build(); p.cargo("fix --allow-no-vcs -p foo -p bar") .env("__CARGO_FIX_YOLO", "1") .with_stdout_data("") .with_stderr_data( str![[r#" [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [FIXED] bar/src/lib.rs (1 fix) [CHECKING] foo v0.1.0 ([ROOT]/foo) [FIXED] src/lib.rs (1 fix) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn do_not_fix_non_relevant_deps() { let p = project() .no_manifest() .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { path = '../bar' } [workspace] "#, ) .file("foo/src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file( "bar/src/lib.rs", r#" pub fn foo() -> u32 { let mut x = 3; x } "#, ) .build(); p.cargo("fix --allow-no-vcs") .env("__CARGO_FIX_YOLO", "1") .cwd("foo") .run(); assert!(p.read_file("bar/src/lib.rs").contains("mut")); } #[cargo_test] fn prepare_for_2018() { let p = project() .file( "src/lib.rs", r#" #![allow(unused)] mod foo { pub const FOO: &str = "fooo"; } mod bar { use ::foo::FOO; } fn main() { let x = ::foo::FOO; } "#, ) .build(); p.cargo("fix --edition --allow-no-vcs") .with_stderr_data(str![[r#" [MIGRATING] Cargo.toml from 2015 edition to 2018 [CHECKING] foo v0.0.1 ([ROOT]/foo) [MIGRATING] src/lib.rs from 2015 edition to 2018 [FIXED] src/lib.rs (2 fixes) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .with_stdout_data("") .run(); println!("{}", p.read_file("src/lib.rs")); assert!(p.read_file("src/lib.rs").contains("use crate::foo::FOO;")); assert!( p.read_file("src/lib.rs") .contains("let x = crate::foo::FOO;") ); } #[cargo_test] fn fix_tests_with_edition() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" "#, ) .file( "src/lib.rs", r#" #![allow(ellipsis_inclusive_range_patterns)] pub fn foo() {} #[cfg(test)] mod tests { #[test] fn it_works() { f(); } fn f() -> bool { let x = 123; match x { 0...100 => true, _ => false, } } } "#, ) .build(); p.cargo("fix --edition --allow-no-vcs") .with_stderr_data(str![[r#" [MIGRATING] Cargo.toml from 2018 edition to 2021 [CHECKING] foo v0.1.0 ([ROOT]/foo) [MIGRATING] src/lib.rs from 2018 edition to 2021 [FIXED] src/lib.rs (1 fix) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .with_stdout_data("") .run(); // Check that the test is fixed. assert!(p.read_file("src/lib.rs").contains(r#"0..=100 => true,"#)); } #[cargo_test] fn fix_tests_with_edition_idioms() { let p = project() .file( "Cargo.toml", r#" [package] name = 'foo' version = '0.1.0' edition = '2018' "#, ) .file( "src/lib.rs", r#" pub fn foo() {} #[cfg(test)] mod tests { #[test] fn it_works() { f(); } use std::any::Any; pub fn f() { let _x: Box = Box::new(3); } } "#, ) .build(); p.cargo("fix --edition-idioms --allow-no-vcs") .with_stderr_data(str![[r#" [CHECKING] foo v0.1.0 ([ROOT]/foo) [FIXED] src/lib.rs (1 fix) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .with_stdout_data("") .run(); // Check that the test is fixed. assert!(p.read_file("src/lib.rs").contains("Box")); } #[cargo_test] fn local_paths() { let p = project() .file( "src/lib.rs", r#" use test::foo; mod test { pub fn foo() {} } pub fn f() { foo(); } "#, ) .build(); p.cargo("fix --edition --allow-no-vcs") .with_stderr_data(str![[r#" [MIGRATING] Cargo.toml from 2015 edition to 2018 [CHECKING] foo v0.0.1 ([ROOT]/foo) [MIGRATING] src/lib.rs from 2015 edition to 2018 [FIXED] src/lib.rs (1 fix) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .with_stdout_data("") .run(); println!("{}", p.read_file("src/lib.rs")); assert!(p.read_file("src/lib.rs").contains("use crate::test::foo;")); } #[cargo_test] fn upgrade_extern_crate() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = '2018' [workspace] [dependencies] bar = { path = 'bar' } "#, ) .file( "src/lib.rs", r#" #![warn(rust_2018_idioms)] extern crate bar; use bar::bar; pub fn foo() { ::bar::bar(); bar(); } "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .build(); p.cargo("fix --allow-no-vcs") .env("__CARGO_FIX_YOLO", "1") .with_stderr_data(str![[r#" [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [CHECKING] foo v0.1.0 ([ROOT]/foo) [FIXED] src/lib.rs (1 fix) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .with_stdout_data("") .run(); println!("{}", p.read_file("src/lib.rs")); assert!(!p.read_file("src/lib.rs").contains("extern crate")); } #[cargo_test] fn specify_rustflags() { let p = project() .file( "src/lib.rs", r#" #![allow(unused)] mod foo { pub const FOO: &str = "fooo"; } fn main() { let x = ::foo::FOO; } "#, ) .build(); p.cargo("fix --edition --allow-no-vcs") .env("RUSTFLAGS", "-C linker=cc") .with_stderr_data(str![[r#" [MIGRATING] Cargo.toml from 2015 edition to 2018 [CHECKING] foo v0.0.1 ([ROOT]/foo) [MIGRATING] src/lib.rs from 2015 edition to 2018 [FIXED] src/lib.rs (1 fix) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .with_stdout_data("") .run(); } #[cargo_test] fn no_changes_necessary() { let p = project().file("src/lib.rs", "").build(); p.cargo("fix --allow-no-vcs") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .with_stdout_data("") .run(); } #[cargo_test] fn fixes_extra_mut() { let p = project() .file( "src/lib.rs", r#" pub fn foo() -> u32 { let mut x = 3; x } "#, ) .build(); p.cargo("fix --allow-no-vcs") .env("__CARGO_FIX_YOLO", "1") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [FIXED] src/lib.rs (1 fix) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .with_stdout_data("") .run(); } #[cargo_test] fn fixes_two_missing_ampersands() { let p = project() .file( "src/lib.rs", r#" pub fn foo() -> u32 { let mut x = 3; let mut y = 3; x + y } "#, ) .build(); p.cargo("fix --allow-no-vcs") .env("__CARGO_FIX_YOLO", "1") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [FIXED] src/lib.rs (2 fixes) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .with_stdout_data("") .run(); } #[cargo_test] fn tricky() { let p = project() .file( "src/lib.rs", r#" pub fn foo() -> u32 { let mut x = 3; let mut y = 3; x + y } "#, ) .build(); p.cargo("fix --allow-no-vcs") .env("__CARGO_FIX_YOLO", "1") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [FIXED] src/lib.rs (2 fixes) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .with_stdout_data("") .run(); } #[cargo_test] fn preserve_line_endings() { let p = project() .file( "src/lib.rs", "fn add(a: &u32) -> u32 { a + 1 }\r\n\ pub fn foo() -> u32 { let mut x = 3; add(&x) }\r\n\ ", ) .build(); p.cargo("fix --allow-no-vcs") .env("__CARGO_FIX_YOLO", "1") .run(); assert!(p.read_file("src/lib.rs").contains("\r\n")); } #[cargo_test] fn fix_deny_warnings() { let p = project() .file( "src/lib.rs", "#![deny(warnings)] pub fn foo() { let mut x = 3; let _ = x; } ", ) .build(); p.cargo("fix --allow-no-vcs") .env("__CARGO_FIX_YOLO", "1") .run(); } #[cargo_test] fn fix_deny_warnings_but_not_others() { let p = project() .file( "src/lib.rs", " #![deny(unused_mut)] pub fn foo() -> u32 { let mut x = 3; x } pub fn bar() { #[allow(unused_mut)] let mut _y = 4; } ", ) .build(); p.cargo("fix --allow-no-vcs") .env("__CARGO_FIX_YOLO", "1") .run(); assert!(!p.read_file("src/lib.rs").contains("let mut x = 3;")); assert!(p.read_file("src/lib.rs").contains("let mut _y = 4;")); } #[cargo_test] fn fix_two_files() { let p = project() .file( "src/lib.rs", " pub mod bar; pub fn foo() -> u32 { let mut x = 3; x } ", ) .file( "src/bar.rs", " pub fn foo() -> u32 { let mut x = 3; x } ", ) .build(); p.cargo("fix --allow-no-vcs") .env("__CARGO_FIX_YOLO", "1") .with_stderr_data( str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [FIXED] src/bar.rs (1 fix) [FIXED] src/lib.rs (1 fix) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); assert!(!p.read_file("src/lib.rs").contains("let mut x = 3;")); assert!(!p.read_file("src/bar.rs").contains("let mut x = 3;")); } #[cargo_test] fn fixes_missing_ampersand() { let p = project() .file("src/main.rs", "fn main() { let mut x = 3; let _ = x; }") .file( "src/lib.rs", r#" pub fn foo() { let mut x = 3; let _ = x; } #[test] pub fn foo2() { let mut x = 3; let _ = x; } "#, ) .file( "tests/a.rs", r#" #[test] pub fn foo() { let mut x = 3; let _ = x; } "#, ) .file("examples/foo.rs", "fn main() { let mut x = 3; let _ = x; }") .file("build.rs", "fn main() { let mut x = 3; let _ = x; }") .build(); p.cargo("fix --all-targets --allow-no-vcs") .env("__CARGO_FIX_YOLO", "1") .with_stdout_data("") // Don't assert number of fixes for `src/lib.rs`, as we don't know if we're // fixing it once or twice! We run this all concurrently, and if we // compile (and fix) in `--test` mode first, we get two fixes. Otherwise // we'll fix one non-test thing, and then fix another one later in // test mode. .with_stderr_data( str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FIXED] build.rs (1 fix) [FIXED] src/lib.rs ([..]fix[..]) [FIXED] src/main.rs (1 fix) [FIXED] examples/foo.rs (1 fix) [FIXED] tests/a.rs (1 fix) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s ... "#]] .unordered(), ) .run(); p.cargo("check").run(); p.cargo("test").run(); } #[cargo_test] fn fix_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [features] bar = [] [workspace] "#, ) .file( "src/lib.rs", r#" #[cfg(feature = "bar")] pub fn foo() -> u32 { let mut x = 3; x } "#, ) .build(); p.cargo("fix --allow-no-vcs").run(); p.cargo("check").run(); p.cargo("fix --features bar --allow-no-vcs").run(); p.cargo("check --features bar").run(); } #[cargo_test] fn shows_warnings() { let p = project() .file( "src/lib.rs", "#[deprecated] fn bar() {} pub fn foo() { let _ = bar(); }", ) .build(); p.cargo("fix --allow-no-vcs") .with_stderr_data(str![[r#" ... [WARNING] use of deprecated function `bar` ... "#]]) .run(); } #[cargo_test] fn warns_if_no_vcs_detected() { let p = project().file("src/lib.rs", "pub fn foo() {}").build(); p.cargo("fix") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no VCS found for this package and `cargo fix` can potentially perform destructive changes; if you'd like to suppress this error pass `--allow-no-vcs` "#]]) .run(); p.cargo("fix --allow-no-vcs").run(); } #[cargo_test] fn warns_about_dirty_working_directory() { let p = git::new("foo", |p| p.file("src/lib.rs", "pub fn foo() {}")); p.change_file("src/lib.rs", ""); p.cargo("fix") .with_status(101) .with_stderr_data(str![[r#" [ERROR] the working directory of this package has uncommitted changes, and `cargo fix` can potentially perform destructive changes; if you'd like to suppress this error pass `--allow-dirty`, or commit the changes to these files: * src/lib.rs (dirty) "#]]) .run(); p.cargo("fix --allow-dirty").run(); } #[cargo_test] fn warns_about_staged_working_directory() { let (p, repo) = git::new_repo("foo", |p| p.file("src/lib.rs", "pub fn foo() {}")); p.change_file("src/lib.rs", "pub fn bar() {}"); git::add(&repo); p.cargo("fix") .with_status(101) .with_stderr_data(str![[r#" [ERROR] the working directory of this package has uncommitted changes, and `cargo fix` can potentially perform destructive changes; if you'd like to suppress this error pass `--allow-dirty`, or commit the changes to these files: * src/lib.rs (staged) "#]]) .run(); p.cargo("fix --allow-staged").run(); } #[cargo_test] fn errors_about_untracked_files() { let mut git_project = project().at("foo"); git_project = git_project.file("src/lib.rs", "pub fn foo() {}"); let p = git_project.build(); let _ = init(&p.root()); p.cargo("fix") .with_status(101) .with_stderr_data(str![[r#" [ERROR] the working directory of this package has uncommitted changes, and `cargo fix` can potentially perform destructive changes; if you'd like to suppress this error pass `--allow-dirty`, or commit the changes to these files: * Cargo.toml (dirty) * src/ (dirty) "#]]) .run(); p.cargo("fix --allow-dirty").run(); } #[cargo_test] fn does_not_warn_about_clean_working_directory() { let p = git::new("foo", |p| p.file("src/lib.rs", "pub fn foo() {}")); p.cargo("fix").run(); } #[cargo_test] fn does_not_warn_about_dirty_ignored_files() { let p = git::new("foo", |p| { p.file("src/lib.rs", "pub fn foo() {}") .file(".gitignore", "bar\n") }); p.change_file("bar", ""); p.cargo("fix").run(); } #[cargo_test] fn do_not_fix_tests_by_default() { let p = project() .file("src/lib.rs", "pub fn foo() { let mut x = 3; let _ = x; }") .file("tests/foo.rs", "pub fn foo() { let mut x = 3; let _ = x; }") .build(); p.cargo("fix --allow-no-vcs") .env("__CARGO_FIX_YOLO", "1") .run(); assert!(!p.read_file("src/lib.rs").contains("let mut x")); assert!(p.read_file("tests/foo.rs").contains("let mut x")); } #[cargo_test] fn prepare_for_unstable() { // During the period where a new edition is coming up, but not yet stable, // this test will verify that it cannot be migrated to on stable. If there // is no next edition, it does nothing. let next = match Edition::LATEST_UNSTABLE { Some(next) => next, None => { eprintln!("Next edition is currently not available, skipping test."); return; } }; let latest_stable = Edition::LATEST_STABLE; let prev = latest_stable.previous().unwrap(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "{}" "#, latest_stable ), ) .file("src/lib.rs", "") .build(); // -j1 to make the error more deterministic (otherwise there can be // multiple errors since they run in parallel). p.cargo("fix --edition --allow-no-vcs -j1") .with_stderr_data(&format!("\ [CHECKING] foo v0.1.0 ([ROOT]/foo) [WARNING] `src/lib.rs` is on the latest edition, but trying to migrate to edition {next}. Edition {next} is unstable and not allowed in this release, consider trying the nightly release channel. If you are trying to migrate from the previous edition ({prev}), the process requires following these steps: 1. Start with `edition = \"{prev}\"` in `Cargo.toml` 2. Run `cargo fix --edition` 3. Modify `Cargo.toml` to set `edition = \"{latest_stable}\"` 4. Run `cargo build` or `cargo test` to verify the fixes worked More details may be found at https://doc.rust-lang.org/edition-guide/editions/transitioning-an-existing-project-to-a-new-edition.html [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s ", next=next, latest_stable=latest_stable, prev=prev)) .run(); if !is_nightly() { // The rest of this test is fundamentally always nightly. return; } p.cargo("fix --edition --allow-no-vcs") .masquerade_as_nightly_cargo(&["always_nightly"]) .with_stderr_data(&format!( "\ [MIGRATING] Cargo.toml from {latest_stable} edition to {next} [CHECKING] foo v0.1.0 ([ROOT]/foo) [MIGRATING] src/lib.rs from {latest_stable} edition to {next} [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s ", latest_stable = latest_stable, next = next, )) .run(); } #[cargo_test] fn prepare_for_latest_stable() { // This is the stable counterpart of prepare_for_unstable. let latest_stable = Edition::LATEST_STABLE; let previous = latest_stable.previous().unwrap(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = 'foo' version = '0.1.0' edition = '{}' "#, previous ), ) .file("src/lib.rs", "") .build(); p.cargo("fix --edition --allow-no-vcs") .with_stderr_data(&format!( "\ [MIGRATING] Cargo.toml from {previous} edition to {latest_stable} [CHECKING] foo v0.1.0 ([ROOT]/foo) [MIGRATING] src/lib.rs from {previous} edition to {latest_stable} [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s ", )) .run(); } #[cargo_test(nightly, reason = "fundamentally always nightly")] fn prepare_for_already_on_latest_unstable() { // During the period where a new edition is coming up, but not yet stable, // this test will check what happens if you are already on the latest. If // there is no next edition, it does nothing. let next_edition = match Edition::LATEST_UNSTABLE { Some(next) => next, None => { eprintln!("Next edition is currently not available, skipping test."); return; } }; let p = project() .file( "Cargo.toml", &format!( r#" cargo-features = ["edition{}"] [package] name = 'foo' version = '0.1.0' edition = '{}' "#, next_edition, next_edition ), ) .file("src/lib.rs", "") .build(); p.cargo("fix --edition --allow-no-vcs") .masquerade_as_nightly_cargo(&["always_nightly"]) .with_stderr_data(&format!( "\ [CHECKING] foo v0.1.0 ([ROOT]/foo) [WARNING] `src/lib.rs` is already on the latest edition ({next_edition}), unable to migrate further ... ", next_edition = next_edition )) .run(); } #[cargo_test] fn prepare_for_already_on_latest_stable() { // Stable counterpart of prepare_for_already_on_latest_unstable. if Edition::LATEST_UNSTABLE.is_some() { eprintln!("This test cannot run while the latest edition is unstable, skipping."); return; } let latest_stable = Edition::LATEST_STABLE; let p = project() .file( "Cargo.toml", &format!( r#" [package] name = 'foo' version = '0.1.0' edition = '{}' "#, latest_stable ), ) .file("src/lib.rs", "") .build(); p.cargo("fix --edition --allow-no-vcs") .with_stderr_contains("[CHECKING] foo [..]") .with_stderr_contains(&format!( "\ [WARNING] `src/lib.rs` is already on the latest edition ({latest_stable}), unable to migrate further ", latest_stable = latest_stable )) .run(); } #[cargo_test] fn fix_overlapping() { let p = project() .file( "src/lib.rs", r#" pub fn foo() {} pub struct A; pub mod bar { pub fn baz() { ::foo::<::A>(); } } "#, ) .build(); p.cargo("fix --allow-no-vcs --edition --lib") .with_stderr_data(str![[r#" [MIGRATING] Cargo.toml from 2015 edition to 2018 [CHECKING] foo v0.0.1 ([ROOT]/foo) [MIGRATING] src/lib.rs from 2015 edition to 2018 [FIXED] src/lib.rs (2 fixes) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let contents = p.read_file("src/lib.rs"); println!("{}", contents); assert!(contents.contains("crate::foo::()")); } #[cargo_test] fn fix_idioms() { let p = project() .file( "Cargo.toml", r#" [package] name = 'foo' version = '0.1.0' edition = '2018' "#, ) .file( "src/lib.rs", r#" use std::any::Any; pub fn foo() { let _x: Box = Box::new(3); } "#, ) .build(); p.cargo("fix --edition-idioms --allow-no-vcs") .with_stderr_data(str![[r#" [CHECKING] foo v0.1.0 ([ROOT]/foo) [FIXED] src/lib.rs (1 fix) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert!(p.read_file("src/lib.rs").contains("Box")); } #[cargo_test] fn idioms_2015_ok() { let p = project().file("src/lib.rs", "").build(); p.cargo("fix --edition-idioms --allow-no-vcs").run(); } #[cargo_test] fn shows_warnings_on_second_run_without_changes() { let p = project() .file( "src/lib.rs", r#" #[deprecated] fn bar() {} pub fn foo() { let _ = bar(); } "#, ) .build(); p.cargo("fix --allow-no-vcs") .with_stderr_data(str![[r#" ... [WARNING] use of deprecated function `bar` ... "#]]) .run(); p.cargo("fix --allow-no-vcs") .with_stderr_data(str![[r#" ... [WARNING] use of deprecated function `bar` ... "#]]) .run(); } #[cargo_test] fn shows_warnings_on_second_run_without_changes_on_multiple_targets() { let p = project() .file( "src/lib.rs", r#" #[deprecated] fn bar() {} pub fn foo() { let _ = bar(); } "#, ) .file( "src/main.rs", r#" #[deprecated] fn bar() {} fn main() { let _ = bar(); } "#, ) .file( "tests/foo.rs", r#" #[deprecated] fn bar() {} #[test] fn foo_test() { let _ = bar(); } "#, ) .file( "tests/bar.rs", r#" #[deprecated] fn bar() {} #[test] fn foo_test() { let _ = bar(); } "#, ) .file( "examples/fooxample.rs", r#" #[deprecated] fn bar() {} fn main() { let _ = bar(); } "#, ) .build(); p.cargo("fix --allow-no-vcs --all-targets") .with_stderr_data( str![[r#" ... --> src/lib.rs:6:29 ... --> src/main.rs:6:29 ... --> examples/fooxample.rs:6:29 ... --> tests/foo.rs:7:29 ... --> tests/bar.rs:7:29 ... "#]] .unordered(), ) .run(); p.cargo("fix --allow-no-vcs --all-targets") .with_stderr_data( str![[r#" ... --> src/lib.rs:6:29 ... --> src/main.rs:6:29 ... --> examples/fooxample.rs:6:29 ... --> tests/bar.rs:7:29 ... --> tests/foo.rs:7:29 ... "#]] .unordered(), ) .run(); } #[cargo_test] fn doesnt_rebuild_dependencies() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { path = 'bar' } [workspace] "#, ) .file("src/lib.rs", "extern crate bar;") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "") .build(); p.cargo("fix --allow-no-vcs -p foo") .env("__CARGO_FIX_YOLO", "1") .with_stdout_data("") .with_stderr_data(str![[r#" [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("fix --allow-no-vcs -p foo") .env("__CARGO_FIX_YOLO", "1") .with_stdout_data("") .with_stderr_data(str![[r#" [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn does_not_crash_with_rustc_wrapper() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" "#, ) .file("src/lib.rs", "") .build(); p.cargo("fix --allow-no-vcs") .env("RUSTC_WRAPPER", tools::echo_wrapper()) .run(); p.build_dir().rm_rf(); p.cargo("fix --allow-no-vcs --verbose") .env("RUSTC_WORKSPACE_WRAPPER", tools::echo_wrapper()) .run(); } #[cargo_test] fn uses_workspace_wrapper_and_primary_wrapper_override() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" "#, ) .file("src/lib.rs", "") .build(); p.cargo("fix --allow-no-vcs --verbose") .env("RUSTC_WORKSPACE_WRAPPER", tools::echo_wrapper()) .with_stderr_data(str![[r#" ... WRAPPER CALLED: rustc src/lib.rs --crate-name foo [..] ... "#]]) .run(); } #[cargo_test] fn only_warn_for_relevant_crates() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] a = { path = 'a' } "#, ) .file("src/lib.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" "#, ) .file( "a/src/lib.rs", " pub fn foo() {} pub mod bar { use foo; pub fn baz() { foo() } } ", ) .build(); p.cargo("fix --allow-no-vcs --edition") .with_stderr_data(str![[r#" [MIGRATING] Cargo.toml from 2015 edition to 2018 [LOCKING] 1 package to latest compatible version [CHECKING] a v0.1.0 ([ROOT]/foo/a) [CHECKING] foo v0.1.0 ([ROOT]/foo) [MIGRATING] src/lib.rs from 2015 edition to 2018 [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn fix_to_broken_code() { let p = project() .file( "foo/Cargo.toml", r#" [package] name = 'foo' version = '0.1.0' edition = "2015" [workspace] "#, ) .file( "foo/src/main.rs", r#" use std::env; use std::fs; use std::io::Write; use std::path::{Path, PathBuf}; use std::process::{self, Command}; fn main() { // Ignore calls to things like --print=file-names and compiling build.rs. // Also compatible for rustc invocations with `@path` argfile. let is_lib_rs = env::args_os() .map(PathBuf::from) .flat_map(|p| if let Some(p) = p.to_str().unwrap_or_default().strip_prefix("@") { fs::read_to_string(p).unwrap().lines().map(PathBuf::from).collect() } else { vec![p] }) .any(|l| l == Path::new("src/lib.rs")); if is_lib_rs { let path = PathBuf::from(env::var_os("OUT_DIR").unwrap()); let path = path.join("foo"); if path.exists() { panic!() } else { fs::File::create(&path).unwrap(); } } let status = Command::new("rustc") .args(env::args().skip(1)) .status() .expect("failed to run rustc"); process::exit(status.code().unwrap_or(2)); } "#, ) .file( "bar/Cargo.toml", r#" [package] name = 'bar' version = '0.1.0' edition = "2015" [workspace] "#, ) .file("bar/build.rs", "fn main() {}") .file("bar/src/lib.rs", "pub fn foo() { let mut x = 3; let _ = x; }") .build(); // Build our rustc shim p.cargo("build").cwd("foo").run(); // Attempt to fix code, but our shim will always fail the second compile p.cargo("fix --allow-no-vcs --broken-code") .cwd("bar") .env("RUSTC", p.root().join("foo/target/debug/foo")) .with_stderr_data(str![[r#" ... [WARNING] failed to automatically apply fixes suggested by rustc to crate `bar` ... "#]]) .run(); assert_e2e().eq( p.read_file("bar/src/lib.rs"), str!["pub fn foo() { let x = 3; let _ = x; }"], ); } #[cargo_test] fn fix_with_common() { let p = project() .file("src/lib.rs", "") .file( "tests/t1.rs", "mod common; #[test] fn t1() { common::try(); }", ) .file( "tests/t2.rs", "mod common; #[test] fn t2() { common::try(); }", ) .file("tests/common/mod.rs", "pub fn try() {}") .build(); p.cargo("fix --edition --allow-no-vcs").run(); assert_e2e().eq( p.read_file("tests/common/mod.rs"), str!["pub fn r#try() {}"], ); } #[cargo_test] fn fix_in_existing_repo_weird_ignore() { // Check that ignore doesn't ignore the repo itself. let p = git::new("foo", |project| { project .file("src/lib.rs", "") .file(".gitignore", "foo\ninner\nCargo.lock\ntarget\n") .file("inner/file", "") }); p.cargo("fix").run(); // This is questionable about whether it is the right behavior. It should // probably be checking if any source file for the current project is // ignored. p.cargo("fix") .cwd("inner") .with_stderr_data(str![[r#" [ERROR] no VCS found for this package and `cargo fix` can potentially perform destructive changes; if you'd like to suppress this error pass `--allow-no-vcs` "#]]) .with_status(101) .run(); p.cargo("fix").cwd("src").run(); } #[cargo_test] fn fix_color_message() { // Check that color appears in diagnostics. let p = project() .file("src/lib.rs", "std::compile_error!{\"color test\"}") .build(); p.cargo("fix --allow-no-vcs --color=always") .with_stderr_data( "\ ... [..]\x1b[[..] ... ", ) .with_status(101) .run(); p.cargo("fix --allow-no-vcs --color=never") .with_stderr_data(str![[r#" ... [ERROR] color test ... "#]]) .with_stderr_does_not_contain("[..]\x1b[[..]") .with_status(101) .run(); } #[cargo_test] fn edition_v2_resolver_report() { // Show a report if the V2 resolver shows differences. Package::new("common", "1.0.0") .feature("f1", &[]) .feature("dev-feat", &[]) .add_dep(Dependency::new("opt_dep", "1.0").optional(true)) .publish(); Package::new("opt_dep", "1.0.0").publish(); Package::new("bar", "1.0.0") .add_dep( Dependency::new("common", "1.0") .target("cfg(whatever)") .enable_features(&["f1"]), ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [dependencies] common = "1.0" bar = "1.0" [build-dependencies] common = { version = "1.0", features = ["opt_dep"] } [dev-dependencies] common = { version="1.0", features=["dev-feat"] } "#, ) .file("src/lib.rs", "") .build(); p.cargo("fix --edition --allow-no-vcs") .with_stderr_data(str![[r#" [MIGRATING] Cargo.toml from 2018 edition to 2021 [UPDATING] `dummy-registry` index [LOCKING] 3 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] common v1.0.0 (registry `dummy-registry`) [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [DOWNLOADED] opt_dep v1.0.0 (registry `dummy-registry`) [NOTE] Switching to Edition 2021 will enable the use of the version 2 feature resolver in Cargo. This may cause some dependencies to be built with fewer features enabled than previously. More information about the resolver changes may be found at https://doc.rust-lang.org/nightly/edition-guide/rust-2021/default-cargo-resolver.html When building the following dependencies, the given features will no longer be used: common v1.0.0 removed features: dev-feat, f1, opt_dep common v1.0.0 (as host dependency) removed features: dev-feat, f1 The following differences only apply when building with dev-dependencies: common v1.0.0 removed features: f1, opt_dep [CHECKING] opt_dep v1.0.0 [CHECKING] common v1.0.0 [CHECKING] bar v1.0.0 [CHECKING] foo v0.1.0 ([ROOT]/foo) [MIGRATING] src/lib.rs from 2018 edition to 2021 [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]].unordered()) .run(); } #[cargo_test] fn rustfix_handles_multi_spans() { // Checks that rustfix handles a single diagnostic with multiple // suggestion spans (non_fmt_panic in this case). let p = project() .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) .file( "src/lib.rs", r#" pub fn foo() { panic!(format!("hey")); } "#, ) .build(); p.cargo("fix --allow-no-vcs").run(); assert!(p.read_file("src/lib.rs").contains(r#"panic!("hey");"#)); } #[cargo_test] fn fix_edition_2021() { // Can migrate 2021, even when lints are allowed. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" "#, ) .file( "src/lib.rs", r#" #![allow(ellipsis_inclusive_range_patterns)] pub fn f() -> bool { let x = 123; match x { 0...100 => true, _ => false, } } "#, ) .build(); p.cargo("fix --edition --allow-no-vcs") .with_stderr_data(str![[r#" [MIGRATING] Cargo.toml from 2018 edition to 2021 [CHECKING] foo v0.1.0 ([ROOT]/foo) [MIGRATING] src/lib.rs from 2018 edition to 2021 [FIXED] src/lib.rs (1 fix) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert!(p.read_file("src/lib.rs").contains(r#"0..=100 => true,"#)); } #[cargo_test] fn fix_shared_cross_workspace() { // Fixing a file that is shared between multiple packages in the same workspace. // Make sure two processes don't try to fix the same file at the same time. let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo", "bar"] "#, ) .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("foo/src/lib.rs", "pub mod shared;") // This will fix both unused and bare trait. .file("foo/src/shared.rs", "pub fn fixme(x: Box<&Fn() -> ()>) {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file( "bar/src/lib.rs", r#" #[path="../../foo/src/shared.rs"] pub mod shared; "#, ) .build(); // The output here can be either of these two, depending on who runs first: // [FIXED] bar/src/../../foo/src/shared.rs (2 fixes) // [FIXED] foo/src/shared.rs (2 fixes) p.cargo("fix --allow-no-vcs") .env("__CARGO_FIX_YOLO", "1") .with_stderr_data( str![[r#" [CHECKING] foo v0.1.0 ([ROOT]/foo/foo) [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [FIXED] [..]foo/src/shared.rs (2 fixes) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); assert_e2e().eq( &p.read_file("foo/src/shared.rs"), str!["pub fn fixme(_x: Box<&dyn Fn() -> ()>) {}"], ); } #[cargo_test] fn abnormal_exit() { // rustc fails unexpectedly after applying fixes, should show some error information. // // This works with a proc-macro that runs twice: // - First run (collect diagnostics pass): writes a file, exits normally. // - Second run (verify diagnostics work): it detects the presence of the // file, removes the file, and aborts the process. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] pm = {path="pm"} "#, ) .file( "src/lib.rs", r#" pub fn f() { let mut x = 1; pm::crashme!(); } "#, ) .file( "pm/Cargo.toml", r#" [package] name = "pm" version = "0.1.0" edition = "2018" [lib] proc-macro = true "#, ) .file( "pm/src/lib.rs", r#" use proc_macro::TokenStream; #[proc_macro] pub fn crashme(_input: TokenStream) -> TokenStream { // Use a file to succeed on the first pass, and fail on the second. let p = std::env::var_os("ONCE_PATH").unwrap(); let check_path = std::path::Path::new(&p); if check_path.exists() { eprintln!("I'm not a diagnostic."); std::fs::remove_file(check_path).unwrap(); std::process::abort(); } else { std::fs::write(check_path, "").unwrap(); "".parse().unwrap() } } "#, ) .build(); p.cargo("fix --lib --allow-no-vcs") .env( "ONCE_PATH", paths::root().join("proc-macro-run-once").to_str().unwrap(), ) // "signal: 6, SIGABRT: process abort signal" on some platforms .with_stderr_data(str![[r#" ... [WARNING] failed to automatically apply fixes suggested by rustc to crate `foo` ... I'm not a diagnostic. rustc exited abnormally: [..] Original diagnostics will follow. ... "#]]) .run(); } #[cargo_test] fn fix_with_run_cargo_in_proc_macros() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [lib] proc-macro = true "#, ) .file( "src/lib.rs", r#" use proc_macro::*; #[proc_macro] pub fn foo(_input: TokenStream) -> TokenStream { let output = std::process::Command::new(env!("CARGO")) .args(&["metadata", "--format-version=1"]) .output() .unwrap(); eprintln!("{}", std::str::from_utf8(&output.stderr).unwrap()); println!("{}", std::str::from_utf8(&output.stdout).unwrap()); "".parse().unwrap() } "#, ) .file( "src/bin/main.rs", r#" use foo::foo; fn main() { foo!("bar") } "#, ) .build(); p.cargo("fix --allow-no-vcs") .with_stderr_does_not_contain("error: could not find .rs file in rustc args") .run(); } #[cargo_test] fn non_edition_lint_migration() { // Migrating to a new edition where a non-edition lint causes problems. let p = project() .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) .file( "src/lib.rs", r#" // This is only used in a test. // To be correct, this should be gated on #[cfg(test)], but // sometimes people don't do that. If the unused_imports // lint removes this, then the unittest will fail to compile. use std::str::from_utf8; pub mod foo { pub const FOO: &[u8] = &[102, 111, 111]; } #[test] fn example() { assert_eq!( from_utf8(::foo::FOO), Ok("foo") ); } "#, ) .build(); // Check that it complains about an unused import. p.cargo("check --lib") .with_stderr_data(str![[r#" ... [..]use std::str::from_utf8; ... = [NOTE] `#[warn(unused_imports)]` on by default ... "#]]) .run(); p.cargo("fix --edition --allow-no-vcs").run(); let contents = p.read_file("src/lib.rs"); // Check it does not remove the "unused" import. assert!(contents.contains("use std::str::from_utf8;")); // Check that it made the edition migration. assert!(contents.contains("from_utf8(crate::foo::FOO)")); } #[cargo_test] fn fix_in_dependency() { // Tests what happens if rustc emits a suggestion to modify a file from a // dependency in cargo's home directory. This should never happen, and // indicates a bug in rustc. However, there are several known bugs in // rustc where it does this (often involving macros), so `cargo fix` has a // guard that says if the suggestion points to some location in CARGO_HOME // to not apply it. // // See https://github.com/rust-lang/cargo/issues/9857 for some other // examples. // // This test uses a simulated rustc which replays a suggestion via a JSON // message that points into CARGO_HOME. This does not use the real rustc // because as the bugs are fixed in the real rustc, that would cause this // test to stop working. Package::new("bar", "1.0.0") .file( "src/lib.rs", r#" #[macro_export] macro_rules! m { ($i:tt) => { let $i = 1; }; } "#, ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" "#, ) .file( "src/lib.rs", r#" pub fn foo() { bar::m!(abc); } "#, ) .build(); p.cargo("fetch").run(); // The path in CARGO_HOME. let bar_path = std::fs::read_dir(paths::home().join(".cargo/registry/src")) .unwrap() .next() .unwrap() .unwrap() .path(); // Since this is a substitution into a Rust string (representing a JSON // string), deal with backslashes like on Windows. let bar_path_str = bar_path.to_str().unwrap().replace("\\", "/"); // This is a fake rustc that will emit a JSON message when the `foo` crate // builds that tells cargo to modify a file it shouldn't. let rustc = project() .at("rustc-replay") .file("Cargo.toml", &basic_manifest("rustc-replay", "1.0.0")) .file("src/main.rs", &r##" fn main() { let pkg_name = match std::env::var("CARGO_PKG_NAME") { Ok(pkg_name) => pkg_name, Err(_) => { let r = std::process::Command::new("rustc") .args(std::env::args_os().skip(1)) .status(); std::process::exit(r.unwrap().code().unwrap_or(2)); } }; if pkg_name == "foo" { eprintln!("{}", r#"{ "$message_type": "diagnostic", "message": "unused variable: `abc`", "code": { "code": "unused_variables", "explanation": null }, "level": "warning", "spans": [ { "file_name": "__BAR_PATH__/bar-1.0.0/src/lib.rs", "byte_start": 127, "byte_end": 129, "line_start": 5, "line_end": 5, "column_start": 29, "column_end": 31, "is_primary": true, "text": [ { "text": " let $i = 1;", "highlight_start": 29, "highlight_end": 31 } ], "label": null, "suggested_replacement": null, "suggestion_applicability": null, "expansion": null } ], "children": [ { "message": "`#[warn(unused_variables)]` on by default", "code": null, "level": "note", "spans": [], "children": [], "rendered": null }, { "message": "if this is intentional, prefix it with an underscore", "code": null, "level": "help", "spans": [ { "file_name": "__BAR_PATH__/bar-1.0.0/src/lib.rs", "byte_start": 127, "byte_end": 129, "line_start": 5, "line_end": 5, "column_start": 29, "column_end": 31, "is_primary": true, "text": [ { "text": " let $i = 1;", "highlight_start": 29, "highlight_end": 31 } ], "label": null, "suggested_replacement": "_abc", "suggestion_applicability": "MachineApplicable", "expansion": null } ], "children": [], "rendered": null } ], "rendered": "warning: unused variable: `abc`\n --> __BAR_PATH__/bar-1.0.0/src/lib.rs:5:29\n |\n5 | let $i = 1;\n | ^^ help: if this is intentional, prefix it with an underscore: `_abc`\n |\n = note: `#[warn(unused_variables)]` on by default\n\n" }"#.replace("\n", "")); } } "##.replace("__BAR_PATH__", &bar_path_str)) .build(); rustc.cargo("build").run(); let rustc_bin = rustc.bin("rustc-replay"); // The output here should not say `Fixed`. // // It is OK to compare the full diagnostic output here because the text is // hard-coded in rustc-replay. Normally tests should not be checking the // compiler output. p.cargo("fix --lib --allow-no-vcs") .env("RUSTC", &rustc_bin) .with_stderr_data(str![[r#" [CHECKING] bar v1.0.0 [CHECKING] foo v0.1.0 ([ROOT]/foo) [WARNING] unused variable: `abc` --> [ROOT]/home/.cargo/registry/src/-[HASH]/bar-1.0.0/src/lib.rs:5:29 | 5 | let $i = 1; | ^^ [HELP] if this is intentional, prefix it with an underscore: `_abc` | = [NOTE] `#[warn(unused_variables)]` on by default [WARNING] `foo` (lib) generated 1 warning (run `cargo fix --lib -p foo` to apply 1 suggestion) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn fix_in_rust_src() { // Tests what happens if rustc emits a suggestion to modify the standard // library in rust source. This should never happen, and indicates a bug in // rustc. However, there are several known bugs in rustc where it does this // (often involving macros), so `cargo fix` has a guard that says if the // suggestion points to rust source under sysroot to not apply it. // // See https://github.com/rust-lang/cargo/issues/9857 for some other // examples. // // This test uses a simulated rustc which replays a suggestion via a JSON // message that points into rust-src. This does not use the real rustc // because as the bugs are fixed in the real rustc, that would cause this // test to stop working. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" edition = "2021" "#, ) .file( "src/lib.rs", r#" pub fn bug_report(w: &mut W) -> std::fmt::Result { if true { writeln!(w, "`;?` here ->")?; } else { writeln!(w, "but not here") } Ok(()) } "#, ) .build(); p.cargo("fetch").run(); // Since this is a substitution into a Rust string (representing a JSON // string), deal with backslashes like on Windows. let sysroot = paths::sysroot().replace("\\", "/"); // This is a fake rustc that will emit a JSON message when the `foo` crate // builds that tells cargo to modify a file it shouldn't. let rustc = project() .at("rustc-replay") .file("Cargo.toml", &basic_manifest("rustc-replay", "1.0.0")) .file("src/main.rs", &r##" fn main() { let pkg_name = match std::env::var("CARGO_PKG_NAME") { Ok(pkg_name) => pkg_name, Err(_) => { let r = std::process::Command::new("rustc") .args(std::env::args_os().skip(1)) .status(); std::process::exit(r.unwrap().code().unwrap_or(2)); } }; if pkg_name == "foo" { eprintln!("{}", r#"{ "$message_type": "diagnostic", "message": "mismatched types", "code": { "code": "E0308", "explanation": "Expected type did not match the received type.\n\nErroneous code examples:\n\n```compile_fail,E0308\nfn plus_one(x: i32) -> i32 {\n x + 1\n}\n\nplus_one(\"Not a number\");\n// ^^^^^^^^^^^^^^ expected `i32`, found `&str`\n\nif \"Not a bool\" {\n// ^^^^^^^^^^^^ expected `bool`, found `&str`\n}\n\nlet x: f32 = \"Not a float\";\n// --- ^^^^^^^^^^^^^ expected `f32`, found `&str`\n// |\n// expected due to this\n```\n\nThis error occurs when an expression was used in a place where the compiler\nexpected an expression of a different type. It can occur in several cases, the\nmost common being when calling a function and passing an argument which has a\ndifferent type than the matching type in the function declaration.\n" }, "level": "error", "spans": [ { "file_name": "__SYSROOT__/lib/rustlib/src/rust/library/core/src/macros/mod.rs", "byte_start": 23568, "byte_end": 23617, "line_start": 670, "line_end": 670, "column_start": 9, "column_end": 58, "is_primary": true, "text": [ { "text": " $dst.write_fmt($crate::format_args_nl!($($arg)*))", "highlight_start": 9, "highlight_end": 58 } ], "label": "expected `()`, found `Result<(), Error>`", "suggested_replacement": null, "suggestion_applicability": null, "expansion": { "span": { "file_name": "lib.rs", "byte_start": 144, "byte_end": 171, "line_start": 5, "line_end": 5, "column_start": 9, "column_end": 36, "is_primary": false, "text": [ { "text": " writeln!(w, \"but not here\")", "highlight_start": 9, "highlight_end": 36 } ], "label": null, "suggested_replacement": null, "suggestion_applicability": null, "expansion": null }, "macro_decl_name": "writeln!", "def_site_span": { "file_name": "__SYSROOT__/lib/rustlib/src/rust/library/core/src/macros/mod.rs", "byte_start": 23434, "byte_end": 23454, "line_start": 665, "line_end": 665, "column_start": 1, "column_end": 21, "is_primary": false, "text": [ { "text": "macro_rules! writeln {", "highlight_start": 1, "highlight_end": 21 } ], "label": null, "suggested_replacement": null, "suggestion_applicability": null, "expansion": null } } }, { "file_name": "lib.rs", "byte_start": 75, "byte_end": 177, "line_start": 2, "line_end": 6, "column_start": 5, "column_end": 6, "is_primary": false, "text": [ { "text": " if true {", "highlight_start": 5, "highlight_end": 14 }, { "text": " writeln!(w, \"`;?` here ->\")?;", "highlight_start": 1, "highlight_end": 38 }, { "text": " } else {", "highlight_start": 1, "highlight_end": 13 }, { "text": " writeln!(w, \"but not here\")", "highlight_start": 1, "highlight_end": 36 }, { "text": " }", "highlight_start": 1, "highlight_end": 6 } ], "label": "expected this to be `()`", "suggested_replacement": null, "suggestion_applicability": null, "expansion": null } ], "children": [ { "message": "use the `?` operator to extract the `Result<(), std::fmt::Error>` value, propagating a `Result::Err` value to the caller", "code": null, "level": "help", "spans": [ { "file_name": "__SYSROOT__/lib/rustlib/src/rust/library/core/src/macros/mod.rs", "byte_start": 23617, "byte_end": 23617, "line_start": 670, "line_end": 670, "column_start": 58, "column_end": 58, "is_primary": true, "text": [ { "text": " $dst.write_fmt($crate::format_args_nl!($($arg)*))", "highlight_start": 58, "highlight_end": 58 } ], "label": null, "suggested_replacement": "?", "suggestion_applicability": "HasPlaceholders", "expansion": { "span": { "file_name": "lib.rs", "byte_start": 144, "byte_end": 171, "line_start": 5, "line_end": 5, "column_start": 9, "column_end": 36, "is_primary": false, "text": [ { "text": " writeln!(w, \"but not here\")", "highlight_start": 9, "highlight_end": 36 } ], "label": null, "suggested_replacement": null, "suggestion_applicability": null, "expansion": null }, "macro_decl_name": "writeln!", "def_site_span": { "file_name": "__SYSROOT__/lib/rustlib/src/rust/library/core/src/macros/mod.rs", "byte_start": 23434, "byte_end": 23454, "line_start": 665, "line_end": 665, "column_start": 1, "column_end": 21, "is_primary": false, "text": [ { "text": "macro_rules! writeln {", "highlight_start": 1, "highlight_end": 21 } ], "label": null, "suggested_replacement": null, "suggestion_applicability": null, "expansion": null } } } ], "children": [], "rendered": null } ], "rendered": "error[E0308]: mismatched types\n --> lib.rs:5:9\n |\n2 | / if true {\n3 | | writeln!(w, \"`;?` here ->\")?;\n4 | | } else {\n5 | | writeln!(w, \"but not here\")\n | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `Result<(), Error>`\n6 | | }\n | |_____- expected this to be `()`\n |\n = note: expected unit type `()`\n found enum `Result<(), std::fmt::Error>`\n = note: this error originates in the macro `writeln` (in Nightly builds, run with -Z macro-backtrace for more info)\nhelp: consider using a semicolon here\n |\n6 | };\n | +\nhelp: you might have meant to return this value\n |\n5 | return writeln!(w, \"but not here\");\n | ++++++ +\nhelp: use the `?` operator to extract the `Result<(), std::fmt::Error>` value, propagating a `Result::Err` value to the caller\n --> __SYSROOT__/lib/rustlib/src/rust/library/core/src/macros/mod.rs:670:58\n |\n67| $dst.write_fmt($crate::format_args_nl!($($arg)*))?\n | +\n\n" }"#.replace("\n", "")); std::process::exit(2); } } "##.replace("__SYSROOT__", &sysroot)) .build(); rustc.cargo("build").run(); let rustc_bin = rustc.bin("rustc-replay"); // The output here should not say `Fixed`. // // It is OK to compare the full diagnostic output here because the text is // hard-coded in rustc-replay. Normally tests should not be checking the // compiler output. p.cargo("fix --lib --allow-no-vcs --broken-code") .env("__CARGO_FIX_YOLO", "1") .env("RUSTC", &rustc_bin) .with_status(101) .with_stderr_data(str![[r#" [CHECKING] foo v0.0.0 ([ROOT]/foo) error[E0308]: mismatched types --> lib.rs:5:9 | 2 | / if true { 3 | | writeln!(w, "`;?` here ->")?; 4 | | } else { 5 | | writeln!(w, "but not here") | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `Result<(), Error>` 6 | | } | |_____- expected this to be `()` | = [NOTE] expected unit type `()` found enum `Result<(), std::fmt::Error>` = [NOTE] this error originates in the macro `writeln` (in Nightly builds, run with -Z macro-backtrace for more info) [HELP] consider using a semicolon here | 6 | }; | + [HELP] you might have meant to return this value | 5 | return writeln!(w, "but not here"); | ++++++ + [HELP] use the `?` operator to extract the `Result<(), std::fmt::Error>` value, propagating a `Result::Err` value to the caller --> [..]/lib/rustlib/src/rust/library/core/src/macros/mod.rs:670:58 | 67| $dst.write_fmt($crate::format_args_nl!($($arg)*))? | + [ERROR] could not compile `foo` (lib) due to 1 previous error "#]]) .run(); } // See #[cargo_test] fn fix_only_once_for_duplicates() { let p = project() .file( "src/main.rs", r#" macro_rules! foo { () => { let x = Box::new(1); std::mem::forget(&x); }; } fn main() { foo!(); foo!(); } "#, ) .build(); p.cargo("fix --allow-no-vcs") .env("__CARGO_FIX_YOLO", "1") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [FIXED] src/main.rs (1 fix) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert_e2e().eq( p.read_file("src/main.rs"), str![[r#" macro_rules! foo { () => { let x = Box::new(1); let _ = &x; }; } fn main() { foo!(); foo!(); } "#]], ); } #[cargo_test] fn migrate_project_to_package() { let p = project() .file( "Cargo.toml", r#" # Before project [ project ] # After project header # After project header line name = "foo" edition = "2021" # After project table "#, ) .file("src/lib.rs", "") .build(); p.cargo("fix --edition --allow-no-vcs") .with_stderr_data(str![[r#" [MIGRATING] Cargo.toml from 2021 edition to 2024 [FIXED] Cargo.toml (1 fix) [CHECKING] foo v0.0.0 ([ROOT]/foo) [MIGRATING] src/lib.rs from 2021 edition to 2024 [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert_e2e().eq( p.read_file("Cargo.toml"), str![[r#" # Before project [ package ] # After project header # After project header line name = "foo" edition = "2021" # After project table "#]], ); } #[cargo_test] fn migrate_removes_project() { let p = project() .file( "Cargo.toml", r#" # Before package [ package ] # After package header # After package header line name = "foo" edition = "2021" # After package table # Before project [ project ] # After project header # After project header line name = "foo" edition = "2021" # After project table "#, ) .file("src/lib.rs", "") .build(); p.cargo("fix --edition --allow-no-vcs") .with_stderr_data(str![[r#" [MIGRATING] Cargo.toml from 2021 edition to 2024 [FIXED] Cargo.toml (1 fix) [CHECKING] foo v0.0.0 ([ROOT]/foo) [MIGRATING] src/lib.rs from 2021 edition to 2024 [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert_e2e().eq( p.read_file("Cargo.toml"), str![[r#" # Before package [ package ] # After package header # After package header line name = "foo" edition = "2021" # After project table "#]], ); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn migrate_removes_project_for_script() { let p = project() .file( "foo.rs", r#" --- # Before package [ package ] # After package header # After package header line name = "foo" edition = "2021" # After package table # Before project [ project ] # After project header # After project header line name = "foo" edition = "2021" # After project table --- fn main() { } "#, ) .build(); p.cargo("-Zscript fix --edition --allow-no-vcs --manifest-path foo.rs") .masquerade_as_nightly_cargo(&["script"]) .with_stderr_data(str![[r#" [MIGRATING] foo.rs from 2021 edition to 2024 [FIXED] foo.rs (1 fix) [CHECKING] foo v0.0.0 ([ROOT]/foo/foo.rs) [MIGRATING] foo.rs from 2021 edition to 2024 [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert_e2e().eq( p.read_file("foo.rs"), str![[r#" --- # Before package [ package ] # After package header # After package header line name = "foo" edition = "2021" # After project table --- fn main() { } "#]], ); } #[cargo_test] fn migrate_rename_underscore_fields() { let p = project() .file( "Cargo.toml", r#" [workspace.dependencies] # Before default_features a = {path = "a", default_features = false} # After default_features value # After default_features line [package] name = "foo" edition = "2021" [lib] name = "foo" # Before crate_type crate_type = ["staticlib", "dylib"] # After crate_type value # After crate_type line [[example]] name = "ex" path = "examples/ex.rs" # Before crate_type crate_type = ["proc-macro"] # After crate_type value # After crate_type line # Before dev_dependencies [ dev_dependencies ] # After dev_dependencies header # After dev_dependencies line a = {path = "a", default_features = false} # After dev_dependencies table # Before build_dependencies [ build_dependencies ] # After build_dependencies header # After build_dependencies line a = {path = "a", default_features = false} # After build_dependencies table # Before dev_dependencies [ target.'cfg(any())'.dev_dependencies ] # After dev_dependencies header # After dev_dependencies line a = {path = "a", default_features = false} # After dev_dependencies table # Before build_dependencies [ target.'cfg(any())'.build_dependencies ] # After build_dependencies header # After build_dependencies line a = {path = "a", default_features = false} # After build_dependencies table "#, ) .file("src/lib.rs", "") .file( "examples/ex.rs", r#" fn main() { println!("ex"); } "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" "#, ) .file("a/src/lib.rs", "") .build(); p.cargo("fix --edition --allow-no-vcs") .with_stderr_data(str![[r#" [MIGRATING] Cargo.toml from 2021 edition to 2024 [FIXED] Cargo.toml (11 fixes) [CHECKING] a v0.0.1 ([ROOT]/foo/a) [CHECKING] foo v0.0.0 ([ROOT]/foo) [MIGRATING] src/lib.rs from 2021 edition to 2024 [MIGRATING] examples/ex.rs from 2021 edition to 2024 [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert_e2e().eq( p.read_file("Cargo.toml"), str![[r#" [workspace.dependencies] # Before default_features a = {path = "a", default-features = false} # After default_features value # After default_features line [package] name = "foo" edition = "2021" [lib] name = "foo" # Before crate_type crate-type = ["staticlib", "dylib"] # After crate_type value # After crate_type line [[example]] name = "ex" path = "examples/ex.rs" # Before crate_type crate-type = ["proc-macro"] # After crate_type value # After crate_type line # Before dev_dependencies [ dev-dependencies ] # After dev_dependencies header # After dev_dependencies line a = {path = "a", default-features = false} # After dev_dependencies table # Before build_dependencies [ build-dependencies ] # After build_dependencies header # After build_dependencies line a = {path = "a", default-features = false} # After build_dependencies table # Before dev_dependencies [ target.'cfg(any())'.dev-dependencies ] # After dev_dependencies header # After dev_dependencies line a = {path = "a", default-features = false} # After dev_dependencies table # Before build_dependencies [ target.'cfg(any())'.build-dependencies ] # After build_dependencies header # After build_dependencies line a = {path = "a", default-features = false} # After build_dependencies table "#]], ); } #[cargo_test] fn migrate_rename_underscore_fields_in_virtual_manifest() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo"] resolver = "2" [workspace.dependencies] # Before default_features a = {path = "a", default_features = false} # After default_features value # After default_features line "#, ) .file( "foo/Cargo.toml", r#" [package] name = "foo" edition = "2021" "#, ) .file("foo/src/lib.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" "#, ) .file("a/src/lib.rs", "") .build(); p.cargo("fix --edition --allow-no-vcs") .with_stderr_data(str![[r#" [MIGRATING] Cargo.toml from 2021 edition to 2024 [FIXED] Cargo.toml (1 fix) [MIGRATING] foo/Cargo.toml from 2021 edition to 2024 [CHECKING] foo v0.0.0 ([ROOT]/foo/foo) [MIGRATING] foo/src/lib.rs from 2021 edition to 2024 [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert_e2e().eq( p.read_file("Cargo.toml"), str![[r#" [workspace] members = ["foo"] resolver = "2" [workspace.dependencies] # Before default_features a = {path = "a", default-features = false} # After default_features value # After default_features line "#]], ); assert_e2e().eq( p.read_file("foo/Cargo.toml"), str![[r#" [package] name = "foo" edition = "2021" "#]], ); } #[cargo_test] fn remove_ignored_default_features() { Package::new("dep_simple", "0.1.0").publish(); Package::new("dep_df_true", "0.1.0").publish(); Package::new("dep_df_false", "0.1.0").publish(); let pkg_default = r#" [package] name = "pkg_default" version = "0.1.0" edition = "2021" [dependencies] dep_simple = { workspace = true } dep_df_true = { workspace = true } dep_df_false = { workspace = true } [build-dependencies] dep_simple = { workspace = true } dep_df_true = { workspace = true } dep_df_false = { workspace = true } [target.'cfg(target_os = "linux")'.dependencies] dep_simple = { workspace = true } dep_df_true = { workspace = true } dep_df_false = { workspace = true } "#; let pkg_df_true = r#" [package] name = "pkg_df_true" version = "0.1.0" edition = "2021" [dependencies] dep_simple = { workspace = true, default-features = true } dep_df_true = { workspace = true, default-features = true } dep_df_false = { workspace = true, default-features = true } [build-dependencies] dep_simple = { workspace = true, default-features = true } dep_df_true = { workspace = true, default-features = true } dep_df_false = { workspace = true, default-features = true } [target.'cfg(target_os = "linux")'.dependencies] dep_simple = { workspace = true, default-features = true } dep_df_true = { workspace = true, default-features = true } dep_df_false = { workspace = true, default-features = true } "#; let pkg_df_false = r#" [package] name = "pkg_df_false" version = "0.1.0" edition = "2021" [dependencies] dep_simple = { workspace = true, default-features = false } dep_df_true = { workspace = true, default-features = false } dep_df_false = { workspace = true, default-features = false } [build-dependencies] dep_simple = { workspace = true, default-features = false } dep_df_true = { workspace = true, default-features = false } dep_df_false = { workspace = true, default-features = false } [target.'cfg(target_os = "linux")'.dependencies] dep_simple = { workspace = true, default-features = false } dep_df_true = { workspace = true, default-features = false } dep_df_false = { workspace = true, default-features = false } "#; let p = project() .file( "Cargo.toml", r#" [workspace] members = ["pkg_default", "pkg_df_true", "pkg_df_false"] resolver = "2" [workspace.dependencies] dep_simple = "0.1.0" dep_df_true = { version = "0.1.0", default-features = true } dep_df_false = { version = "0.1.0", default-features = false } "#, ) .file("pkg_default/Cargo.toml", pkg_default) .file("pkg_default/src/lib.rs", "") .file("pkg_df_true/Cargo.toml", pkg_df_true) .file("pkg_df_true/src/lib.rs", "") .file("pkg_df_false/Cargo.toml", pkg_df_false) .file("pkg_df_false/src/lib.rs", "") .build(); p.cargo("fix --all --edition --allow-no-vcs") .with_stderr_data( str![[r#" [MIGRATING] pkg_default/Cargo.toml from 2021 edition to 2024 [MIGRATING] pkg_df_true/Cargo.toml from 2021 edition to 2024 [MIGRATING] pkg_df_false/Cargo.toml from 2021 edition to 2024 [FIXED] pkg_df_false/Cargo.toml (6 fixes) [UPDATING] `dummy-registry` index [LOCKING] 3 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] dep_simple v0.1.0 (registry `dummy-registry`) [DOWNLOADED] dep_df_true v0.1.0 (registry `dummy-registry`) [DOWNLOADED] dep_df_false v0.1.0 (registry `dummy-registry`) [CHECKING] dep_df_true v0.1.0 [CHECKING] dep_df_false v0.1.0 [CHECKING] dep_simple v0.1.0 [CHECKING] pkg_df_true v0.1.0 ([ROOT]/foo/pkg_df_true) [CHECKING] pkg_df_false v0.1.0 ([ROOT]/foo/pkg_df_false) [CHECKING] pkg_default v0.1.0 ([ROOT]/foo/pkg_default) [MIGRATING] pkg_df_false/src/lib.rs from 2021 edition to 2024 [MIGRATING] pkg_df_true/src/lib.rs from 2021 edition to 2024 [MIGRATING] pkg_default/src/lib.rs from 2021 edition to 2024 [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); assert_e2e().eq(p.read_file("pkg_default/Cargo.toml"), pkg_default); assert_e2e().eq(p.read_file("pkg_df_true/Cargo.toml"), pkg_df_true); assert_e2e().eq( p.read_file("pkg_df_false/Cargo.toml"), str![[r#" [package] name = "pkg_df_false" version = "0.1.0" edition = "2021" [dependencies] dep_simple = { workspace = true} dep_df_true = { workspace = true} dep_df_false = { workspace = true, default-features = false } [build-dependencies] dep_simple = { workspace = true} dep_df_true = { workspace = true} dep_df_false = { workspace = true, default-features = false } [target.'cfg(target_os = "linux")'.dependencies] dep_simple = { workspace = true} dep_df_true = { workspace = true} dep_df_false = { workspace = true, default-features = false } "#]], ); } #[cargo_test] fn fix_edition_skips_old_editions() { // Checks that -Zfix-edition will skip things that are not 2024. let p = project() .file( "Cargo.toml", r#"[workspace] members = ["e2021", "e2024"] resolver = "3" "#, ) .file( "e2021/Cargo.toml", r#" [package] name = "e2021" edition = "2021" "#, ) .file("e2021/src/lib.rs", "") .file( "e2024/Cargo.toml", r#" [package] name = "e2024" edition = "2024" "#, ) .file("e2024/src/lib.rs", "") .build(); // Doing the whole workspace should skip since there is a 2021 in the mix. p.cargo("fix -Zfix-edition=start=2024 -v") .masquerade_as_nightly_cargo(&["fix-edition"]) .with_stderr_data(str![[r#" [SKIPPING] not all packages are at edition 2024 "#]]) .run(); // Same with `end`. p.cargo("fix -Zfix-edition=end=2024,future -v") .masquerade_as_nightly_cargo(&["fix-edition"]) .with_stderr_data(str![[r#" [SKIPPING] not all packages are at edition 2024 "#]]) .run(); // Doing an individual package at the correct edition should check it. p.cargo("fix -Zfix-edition=start=2024 -p e2024") .masquerade_as_nightly_cargo(&["fix-edition"]) .with_stderr_data(str![[r#" [CHECKING] e2024 v0.0.0 ([ROOT]/foo/e2024) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "future edition is always unstable")] fn fix_edition_future() { // Checks that the -Zfix-edition can work for the future. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" edition = "2024""#, ) .file("src/lib.rs", "") .build(); p.cargo("fix -Zfix-edition=end=2024,future") .masquerade_as_nightly_cargo(&["fix-edition"]) .with_stderr_data(str![[r#" [MIGRATING] Cargo.toml from 2024 edition to future [CHECKING] foo v0.0.0 ([ROOT]/foo) [MIGRATING] src/lib.rs from 2024 edition to future [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s Updated edition to future [CHECKING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert_e2e().eq( p.read_file("Cargo.toml"), str![[r#" cargo-features = ["unstable-editions"] [package] name = "foo" edition = "future" "#]], ); } cargo-0.91.0/tests/testsuite/fix_n_times.rs000064400000000000000000000413261046102023000170610ustar 00000000000000//! Tests for the `cargo fix` command, specifically targeting the logic around //! running rustc multiple times to apply and verify fixes. //! //! These tests use a replacement of rustc ("rustc-fix-shim") which emits JSON //! messages based on what the test is exercising. It uses an environment //! variable `RUSTC_FIX_SHIM_SEQUENCE` which determines how it should behave //! based on how many times `rustc` has run. It keeps track of how many times //! rustc has run in a local file. //! //! For example, a sequence of `[Step::OneFix, Step::Error]` will emit one //! suggested fix the first time `rustc` is run, and then the next time it is //! run it will generate an error. //! //! The [`expect_fix_runs_rustc_n_times`] function handles setting everything //! up, and verifying the results. use std::path::PathBuf; use std::sync::{Mutex, OnceLock}; use crate::prelude::*; use crate::utils::tools; use cargo_test_support::{Execs, basic_manifest, paths, project, str}; /// The action that the `rustc` shim should take in the current sequence of /// events. #[derive(Clone, Copy, PartialEq)] #[repr(u8)] enum Step { /// Exits with success with no messages. SuccessNoOutput = b'0', /// Emits one suggested fix. /// /// The suggested fix involves updating the number of the first line /// comment which starts as `// fix-count 0`. OneFix = b'1', /// Emits two suggested fixes which overlap, which rustfix can only apply /// one of them, and fails for the other. /// /// The suggested fix is the same as `Step::OneFix`, it just shows up /// twice. TwoFixOverlapping = b'2', /// Generates a warning without a suggestion. Warning = b'w', /// Generates an error message with no suggestion. Error = b'e', /// Emits one suggested fix and an error. OneFixError = b'f', } /// Verifies `cargo fix` behavior based on the given sequence of behaviors for /// `rustc`. /// /// - `sequence` is the sequence of behaviors for each call to `rustc`. /// If rustc is called more often than the number of steps, then it will panic. /// - `extra_execs` a callback that allows extra customization of the [`Execs`]. /// - `expected_stderr` is the expected output from cargo. /// - `expected_lib_rs` is the expected contents of `src/lib.rs` after the /// fixes have been applied. The file starts out with the content `// /// fix-count 0`, and the number increases based on which suggestions are /// applied. fn expect_fix_runs_rustc_n_times( sequence: &[Step], extra_execs: impl FnOnce(&mut Execs), expected_stderr: impl IntoData, expected_lib_rs: &str, ) { let rustc = rustc_for_cargo_fix(); let p = project().file("src/lib.rs", "// fix-count 0").build(); let sequence_vec: Vec<_> = sequence.iter().map(|x| *x as u8).collect(); let sequence_str = std::str::from_utf8(&sequence_vec).unwrap(); let mut execs = p.cargo("fix --allow-no-vcs --lib"); execs .env("RUSTC", &rustc) .env("RUSTC_FIX_SHIM_SEQUENCE", sequence_str) .with_stderr_data(expected_stderr); extra_execs(&mut execs); execs.run(); let lib_rs = p.read_file("src/lib.rs"); assert_eq!(expected_lib_rs, lib_rs); let count: usize = p.read_file("rustc-fix-shim-count").parse().unwrap(); assert_eq!(sequence.len(), count); } /// Returns the path to the rustc replacement executable. fn rustc_for_cargo_fix() -> PathBuf { static FIX_SHIM: OnceLock>> = OnceLock::new(); let mut lock = FIX_SHIM.get_or_init(|| Default::default()).lock().unwrap(); if let Some(path) = &*lock { return path.clone(); } let p = project() .at(paths::global_root().join("rustc-fix-shim")) .file("Cargo.toml", &basic_manifest("rustc-fix-shim", "1.0.0")) .file( "src/main.rs", r##" fn main() { if std::env::var_os("CARGO_PKG_NAME").is_none() { // Handle things like rustc -Vv let r = std::process::Command::new("rustc") .args(std::env::args_os().skip(1)) .status(); std::process::exit(r.unwrap().code().unwrap_or(2)); } // Keep track of which step in the sequence that needs to run. let successful_count = std::fs::read_to_string("rustc-fix-shim-count") .map(|c| c.parse().unwrap()) .unwrap_or(0); std::fs::write("rustc-fix-shim-count", format!("{}", successful_count + 1)).unwrap(); // The sequence tells us which behavior we should have. let seq = std::env::var("RUSTC_FIX_SHIM_SEQUENCE").unwrap(); if successful_count >= seq.len() { panic!("rustc called too many times count={}, \ make sure to update the Step sequence", successful_count); } match seq.as_bytes()[successful_count] { b'0' => return, b'1' => { output_suggestion(successful_count + 1); } b'2' => { output_suggestion(successful_count + 1); output_suggestion(successful_count + 2); } b'w' => { output_message("warning", successful_count + 1); } b'e' => { output_message("error", successful_count + 1); std::process::exit(1); } b'f' => { output_suggestion(successful_count + 1); output_message("error", successful_count + 2); std::process::exit(1); } _ => panic!("unexpected sequence"), } } fn output_suggestion(count: usize) { let json = format!( r#"{{ "$message_type": "diagnostic", "message": "rustc fix shim comment {count}", "code": null, "level": "warning", "spans": [ {{ "file_name": "src/lib.rs", "byte_start": 13, "byte_end": 14, "line_start": 1, "line_end": 1, "column_start": 14, "column_end": 15, "is_primary": true, "text": [ {{ "text": "// fix-count 0", "highlight_start": 14, "highlight_end": 15 }} ], "label": "increase this number", "suggested_replacement": null, "suggestion_applicability": null, "expansion": null }} ], "children": [ {{ "message": "update the number here", "code": null, "level": "help", "spans": [ {{ "file_name": "src/lib.rs", "byte_start": 13, "byte_end": 14, "line_start": 1, "line_end": 1, "column_start": 14, "column_end": 15, "is_primary": true, "text": [ {{ "text": "// fix-count 0", "highlight_start": 14, "highlight_end": 15 }} ], "label": null, "suggested_replacement": "{count}", "suggestion_applicability": "MachineApplicable", "expansion": null }} ], "children": [], "rendered": null }} ], "rendered": "rustc fix shim comment {count}" }}"#, ) .replace("\n", ""); eprintln!("{json}"); } fn output_message(level: &str, count: usize) { let json = format!( r#"{{ "$message_type": "diagnostic", "message": "rustc fix shim {level} count={count}", "code": null, "level": "{level}", "spans": [ {{ "file_name": "src/lib.rs", "byte_start": 0, "byte_end": 0, "line_start": 1, "line_end": 1, "column_start": 1, "column_end": 1, "is_primary": true, "text": [ {{ "text": "// fix-count 0", "highlight_start": 1, "highlight_end": 4 }} ], "label": "forced error", "suggested_replacement": null, "suggestion_applicability": null, "expansion": null }} ], "children": [], "rendered": "rustc fix shim {level} count={count}" }}"#, ) .replace("\n", ""); eprintln!("{json}"); } "##, ) .build(); p.cargo("build").run(); let path = p.bin("rustc-fix-shim"); *lock = Some(path.clone()); path } #[cargo_test] fn fix_no_suggestions() { // No suggested fixes. expect_fix_runs_rustc_n_times( &[Step::SuccessNoOutput], |_execs| {}, str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], "// fix-count 0", ); } #[cargo_test] fn fix_one_suggestion() { // One suggested fix, with a successful verification, no output. expect_fix_runs_rustc_n_times( &[Step::OneFix, Step::SuccessNoOutput], |_execs| {}, str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [FIXED] src/lib.rs (1 fix) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], "// fix-count 1", ); } #[cargo_test] fn fix_one_overlapping() { // Two suggested fixes, where one fails, then the next step returns no suggestions. expect_fix_runs_rustc_n_times( &[Step::TwoFixOverlapping, Step::SuccessNoOutput], |_execs| {}, str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [FIXED] src/lib.rs (1 fix) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], "// fix-count 2", ); } #[cargo_test] fn fix_overlapping_max() { // Rustc repeatedly spits out suggestions that overlap, which should hit // the limit of 4 attempts. It should show the output from the 5th attempt. expect_fix_runs_rustc_n_times( &[ Step::TwoFixOverlapping, Step::TwoFixOverlapping, Step::TwoFixOverlapping, Step::TwoFixOverlapping, Step::TwoFixOverlapping, ], |_execs| {}, str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [WARNING] error applying suggestions to `src/lib.rs` The full error message was: > cannot replace slice of data that was already replaced This likely indicates a bug in either rustc or cargo itself, and we would appreciate a bug report! You're likely to see a number of compiler warnings after this message which cargo attempted to fix but failed. If you could open an issue at https://github.com/rust-lang/rust/issues quoting the full output of this command we'd be very appreciative! Note that you may be able to make some more progress in the near-term fixing code with the `--broken-code` flag [FIXED] src/lib.rs (4 fixes) rustc fix shim comment 5 rustc fix shim comment 6 [WARNING] `foo` (lib) generated 2 warnings (run `cargo fix --lib -p foo` to apply 2 suggestions) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], "// fix-count 5", ); } #[cargo_test] fn fix_verification_failed() { // One suggested fix, with an error in the verification step. // This should cause `cargo fix` to back out the changes. expect_fix_runs_rustc_n_times( &[Step::OneFix, Step::Error], |_execs| {}, str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [WARNING] failed to automatically apply fixes suggested by rustc to crate `foo` after fixes were automatically applied the compiler reported errors within these files: * src/lib.rs This likely indicates a bug in either rustc or cargo itself, and we would appreciate a bug report! You're likely to see a number of compiler warnings after this message which cargo attempted to fix but failed. If you could open an issue at https://github.com/rust-lang/rust/issues quoting the full output of this command we'd be very appreciative! Note that you may be able to make some more progress in the near-term fixing code with the `--broken-code` flag The following errors were reported: rustc fix shim error count=2 Original diagnostics will follow. rustc fix shim comment 1 [WARNING] `foo` (lib) generated 1 warning (run `cargo fix --lib -p foo` to apply 1 suggestion) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], "// fix-count 0", ); } #[cargo_test] fn fix_verification_failed_clippy() { // This is the same as `fix_verification_failed_clippy`, except it checks // the error message has the customization for the clippy URL and // subcommand. expect_fix_runs_rustc_n_times( &[Step::OneFix, Step::Error], |execs| { execs.env("RUSTC_WORKSPACE_WRAPPER", tools::wrapped_clippy_driver()); }, str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [WARNING] failed to automatically apply fixes suggested by rustc to crate `foo` after fixes were automatically applied the compiler reported errors within these files: * src/lib.rs This likely indicates a bug in either rustc or cargo itself, and we would appreciate a bug report! You're likely to see a number of compiler warnings after this message which cargo attempted to fix but failed. If you could open an issue at https://github.com/rust-lang/rust-clippy/issues quoting the full output of this command we'd be very appreciative! Note that you may be able to make some more progress in the near-term fixing code with the `--broken-code` flag The following errors were reported: rustc fix shim error count=2 Original diagnostics will follow. rustc fix shim comment 1 [WARNING] `foo` (lib) generated 1 warning (run `cargo clippy --fix --lib -p foo` to apply 1 suggestion) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], "// fix-count 0", ); } #[cargo_test] fn warnings() { // Only emits warnings. expect_fix_runs_rustc_n_times( &[Step::Warning], |_execs| {}, str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) rustc fix shim warning count=1 [WARNING] `foo` (lib) generated 1 warning [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], "// fix-count 0", ); } #[cargo_test] fn starts_with_error() { // The source code doesn't compile to start with. expect_fix_runs_rustc_n_times( &[Step::Error], |execs| { execs.with_status(101); }, str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) rustc fix shim error count=1 [ERROR] could not compile `foo` (lib) due to 1 previous error "#]], "// fix-count 0", ); } #[cargo_test] fn broken_code_no_suggestions() { // --broken-code with no suggestions expect_fix_runs_rustc_n_times( &[Step::Error], |execs| { execs.arg("--broken-code").with_status(101); }, str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) rustc fix shim error count=1 [ERROR] could not compile `foo` (lib) due to 1 previous error "#]], "// fix-count 0", ); } #[cargo_test] fn broken_code_one_suggestion() { // --broken-code where there is an error and a suggestion. expect_fix_runs_rustc_n_times( &[Step::OneFixError, Step::Error], |execs| { execs.arg("--broken-code").with_status(101); }, str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [WARNING] failed to automatically apply fixes suggested by rustc to crate `foo` after fixes were automatically applied the compiler reported errors within these files: * src/lib.rs This likely indicates a bug in either rustc or cargo itself, and we would appreciate a bug report! You're likely to see a number of compiler warnings after this message which cargo attempted to fix but failed. If you could open an issue at https://github.com/rust-lang/rust/issues quoting the full output of this command we'd be very appreciative! Note that you may be able to make some more progress in the near-term fixing code with the `--broken-code` flag The following errors were reported: rustc fix shim error count=2 Original diagnostics will follow. rustc fix shim comment 1 rustc fix shim error count=2 [WARNING] `foo` (lib) generated 1 warning [ERROR] could not compile `foo` (lib) due to 1 previous error; 1 warning emitted "#]], "// fix-count 1", ); } cargo-0.91.0/tests/testsuite/freshness.rs000064400000000000000000002566341046102023000165670ustar 00000000000000//! Tests for fingerprinting (rebuild detection). use std::fs::{self, OpenOptions}; use std::io; use std::io::prelude::*; use std::net::TcpListener; use std::path::{Path, PathBuf}; use std::process::Stdio; use std::thread; use std::time::SystemTime; use crate::prelude::*; use cargo_test_support::paths; use cargo_test_support::registry::Package; use cargo_test_support::{ basic_lib_manifest, basic_manifest, is_coarse_mtime, project, rustc_host, rustc_host_env, sleep_ms, str, }; use filetime::FileTime; use super::death; #[cargo_test] fn modifying_and_moving() { let p = project() .file("src/main.rs", "mod a; fn main() {}") .file("src/a.rs", "") .build(); p.cargo("build") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.root().move_into_the_past(); p.root().join("target").move_into_the_past(); p.change_file("src/a.rs", "#[allow(unused)]fn main() {}"); p.cargo("build -v") .with_stderr_data(str![[r#" [DIRTY] foo v0.0.1 ([ROOT]/foo): the file `src/a.rs` has changed ([TIME_DIFF_AFTER_LAST_BUILD]) [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); fs::rename(&p.root().join("src/a.rs"), &p.root().join("src/b.rs")).unwrap(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) error[E0583]: file not found for module `a` ... [ERROR] could not compile `foo` (bin "foo") due to 1 previous error "#]]) .run(); } #[cargo_test] fn modify_only_some_files() { let p = project() .file("src/lib.rs", "mod a;") .file("src/a.rs", "") .file("src/main.rs", "mod b; fn main() {}") .file("src/b.rs", "") .file("tests/test.rs", "") .build(); p.cargo("build") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("test").run(); sleep_ms(1000); assert!(p.bin("foo").is_file()); let lib = p.root().join("src/lib.rs"); p.change_file("src/lib.rs", "invalid rust code"); p.change_file("src/b.rs", "#[allow(unused)]fn foo() {}"); lib.move_into_the_past(); // Make sure the binary is rebuilt, not the lib p.cargo("build -v") .with_stderr_data(str![[r#" [DIRTY] foo v0.0.1 ([ROOT]/foo): the file `src/b.rs` has changed ([TIME_DIFF_AFTER_LAST_BUILD]) [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert!(p.bin("foo").is_file()); } #[cargo_test] fn rebuild_sub_package_then_while_package() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.1" edition = "2015" [dependencies.a] path = "a" [dependencies.b] path = "b" "#, ) .file("src/lib.rs", "extern crate a; extern crate b;") .file( "a/Cargo.toml", r#" [package] name = "a" authors = [] version = "0.0.1" edition = "2015" [dependencies.b] path = "../b" "#, ) .file("a/src/lib.rs", "extern crate b;") .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) .file("b/src/lib.rs", "") .build(); p.cargo("build") .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] b v0.0.1 ([ROOT]/foo/b) [COMPILING] a v0.0.1 ([ROOT]/foo/a) [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); if is_coarse_mtime() { sleep_ms(1000); } p.change_file("b/src/lib.rs", "pub fn b() {}"); p.cargo("build -pb -v") .with_stderr_data(str![[r#" [DIRTY] b v0.0.1 ([ROOT]/foo/b): the file `b/src/lib.rs` has changed ([TIME_DIFF_AFTER_LAST_BUILD]) [COMPILING] b v0.0.1 ([ROOT]/foo/b) [RUNNING] `rustc --crate-name b [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.change_file( "src/lib.rs", "extern crate a; extern crate b; pub fn toplevel() {}", ); p.cargo("build -v") .with_stderr_data(str![[r#" [FRESH] b v0.0.1 ([ROOT]/foo/b) [DIRTY] a v0.0.1 ([ROOT]/foo/a): the dependency b was rebuilt ([TIME_DIFF_AFTER_LAST_BUILD]) [COMPILING] a v0.0.1 ([ROOT]/foo/a) [RUNNING] `rustc --crate-name a [..] [DIRTY] foo v0.0.1 ([ROOT]/foo): the dependency b was rebuilt ([TIME_DIFF_AFTER_LAST_BUILD]) [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] src/lib.rs [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn changing_lib_features_caches_targets() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.1" edition = "2015" [features] foo = [] "#, ) .file("src/lib.rs", "") .build(); p.cargo("build") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build --features foo") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); /* Targets should be cached from the first build */ p.cargo("build") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build --features foo") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn changing_profiles_caches_targets() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.1" edition = "2015" [profile.dev] panic = "abort" "#, ) .file("src/lib.rs", "") .build(); p.cargo("build") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("test") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) [DOCTEST] foo "#]]) .run(); /* Targets should be cached from the first build */ p.cargo("build") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("test foo") .with_stderr_data(str![[r#" [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) "#]]) .run(); } #[cargo_test] fn changing_bin_paths_common_target_features_caches_targets() { // Make sure dep_cache crate is built once per feature let p = project() .no_manifest() .file( ".cargo/config.toml", r#" [build] target-dir = "./target" "#, ) .file( "dep_crate/Cargo.toml", r#" [package] name = "dep_crate" version = "0.0.1" edition = "2015" authors = [] [features] ftest = [] "#, ) .file( "dep_crate/src/lib.rs", r#" #[cfg(feature = "ftest")] pub fn yo() { println!("ftest on") } #[cfg(not(feature = "ftest"))] pub fn yo() { println!("ftest off") } "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] [dependencies] dep_crate = {path = "../dep_crate", features = []} "#, ) .file("a/src/lib.rs", "") .file( "a/src/main.rs", r#" extern crate dep_crate; use dep_crate::yo; fn main() { yo(); } "#, ) .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.0.1" edition = "2015" authors = [] [dependencies] dep_crate = {path = "../dep_crate", features = ["ftest"]} "#, ) .file("b/src/lib.rs", "") .file( "b/src/main.rs", r#" extern crate dep_crate; use dep_crate::yo; fn main() { yo(); } "#, ) .build(); /* Build and rebuild a/. Ensure dep_crate only builds once */ p.cargo("run") .cwd("a") .with_stdout_data(str![[r#" ftest off "#]]) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] dep_crate v0.0.1 ([ROOT]/foo/dep_crate) [COMPILING] a v0.0.1 ([ROOT]/foo/a) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/./target/debug/a[EXE]` "#]]) .run(); p.cargo("clean -p a").cwd("a").run(); p.cargo("run") .cwd("a") .with_stdout_data(str![[r#" ftest off "#]]) .with_stderr_data(str![[r#" [COMPILING] a v0.0.1 ([ROOT]/foo/a) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/./target/debug/a[EXE]` "#]]) .run(); /* Build and rebuild b/. Ensure dep_crate only builds once */ p.cargo("run") .cwd("b") .with_stdout_data(str![[r#" ftest on "#]]) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] dep_crate v0.0.1 ([ROOT]/foo/dep_crate) [COMPILING] b v0.0.1 ([ROOT]/foo/b) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/./target/debug/b[EXE]` "#]]) .run(); p.cargo("clean -p b").cwd("b").run(); p.cargo("run") .cwd("b") .with_stdout_data(str![[r#" ftest on "#]]) .with_stderr_data(str![[r#" [COMPILING] b v0.0.1 ([ROOT]/foo/b) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/./target/debug/b[EXE]` "#]]) .run(); /* Build a/ package again. If we cache different feature dep builds correctly, * this should not cause a rebuild of dep_crate */ p.cargo("clean -p a").cwd("a").run(); p.cargo("run") .cwd("a") .with_stdout_data(str![[r#" ftest off "#]]) .with_stderr_data(str![[r#" [COMPILING] a v0.0.1 ([ROOT]/foo/a) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/./target/debug/a[EXE]` "#]]) .run(); /* Build b/ package again. If we cache different feature dep builds correctly, * this should not cause a rebuild */ p.cargo("clean -p b").cwd("b").run(); p.cargo("run") .cwd("b") .with_stdout_data(str![[r#" ftest on "#]]) .with_stderr_data(str![[r#" [COMPILING] b v0.0.1 ([ROOT]/foo/b) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/./target/debug/b[EXE]` "#]]) .run(); } #[cargo_test] fn changing_bin_features_caches_targets() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.1" edition = "2015" [features] foo = [] "#, ) .file( "src/main.rs", r#" fn main() { let msg = if cfg!(feature = "foo") { "feature on" } else { "feature off" }; println!("{}", msg); } "#, ) .build(); p.cargo("build") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.rename_run("foo", "off1") .with_stdout_data(str![[r#" feature off "#]]) .run(); p.cargo("build --features foo") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.rename_run("foo", "on1") .with_stdout_data(str![[r#" feature on "#]]) .run(); /* Targets should be cached from the first build */ let mut e = p.cargo("build -v"); // MSVC does not include hash in binary filename, so it gets recompiled. if cfg!(target_env = "msvc") { e.with_stderr_data(str![[r#" [DIRTY] foo v0.0.1 ([ROOT]/foo): the list of features changed [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); } else { e.with_stderr_data(str![[r#" [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); } e.run(); p.rename_run("foo", "off2") .with_stdout_data(str![[r#" feature off "#]]) .run(); let mut e = p.cargo("build --features foo -v"); if cfg!(target_env = "msvc") { e.with_stderr_data(str![[r#" [DIRTY] foo v0.0.1 ([ROOT]/foo): the list of features changed [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); } else { e.with_stderr_data(str![[r#" [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); } e.run(); p.rename_run("foo", "on2") .with_stdout_data(str![[r#" feature on "#]]) .run(); } #[cargo_test] fn rebuild_tests_if_lib_changes() { let p = project() .file("src/lib.rs", "pub fn foo() {}") .file("tests/foo-test.rs", "extern crate foo;") .build(); p.cargo("build").run(); p.cargo("test").run(); sleep_ms(1000); p.change_file("src/lib.rs", ""); p.cargo("build").run(); p.cargo("test -v --test foo-test") .with_stderr_data(str![[r#" [DIRTY] foo v0.0.1 ([ROOT]/foo): the dependency foo was rebuilt ([TIME_DIFF_AFTER_LAST_BUILD]) [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo_test [..]` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/debug/deps/foo_test-[HASH][EXE]` "#]]) .run(); } #[cargo_test] fn no_rebuild_transitive_target_deps() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] a = { path = "a" } [dev-dependencies] b = { path = "b" } "#, ) .file("src/lib.rs", "") .file("tests/foo.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] [target.foo.dependencies] c = { path = "../c" } "#, ) .file("a/src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.0.1" edition = "2015" authors = [] [dependencies] c = { path = "../c" } "#, ) .file("b/src/lib.rs", "") .file("c/Cargo.toml", &basic_manifest("c", "0.0.1")) .file("c/src/lib.rs", "") .build(); p.cargo("build").run(); p.cargo("test --no-run") .with_stderr_data(str![[r#" [COMPILING] c v0.0.1 ([ROOT]/foo/c) [COMPILING] b v0.0.1 ([ROOT]/foo/b) [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [EXECUTABLE] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) [EXECUTABLE] tests/foo.rs (target/debug/deps/foo-[HASH][EXE]) "#]]) .run(); } #[cargo_test] fn rerun_if_changed_in_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] a = { path = "a" } "#, ) .file("src/lib.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" "#, ) .file( "a/build.rs", r#" fn main() { println!("cargo::rerun-if-changed=build.rs"); } "#, ) .file("a/src/lib.rs", "") .build(); p.cargo("build").run(); p.cargo("build") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn same_build_dir_cached_packages() { let p = project() .no_manifest() .file( "a1/Cargo.toml", r#" [package] name = "a1" version = "0.0.1" edition = "2015" authors = [] [dependencies] b = { path = "../b" } "#, ) .file("a1/src/lib.rs", "") .file( "a2/Cargo.toml", r#" [package] name = "a2" version = "0.0.1" edition = "2015" authors = [] [dependencies] b = { path = "../b" } "#, ) .file("a2/src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.0.1" edition = "2015" authors = [] [dependencies] c = { path = "../c" } "#, ) .file("b/src/lib.rs", "") .file( "c/Cargo.toml", r#" [package] name = "c" version = "0.0.1" edition = "2015" authors = [] [dependencies] d = { path = "../d" } "#, ) .file("c/src/lib.rs", "") .file("d/Cargo.toml", &basic_manifest("d", "0.0.1")) .file("d/src/lib.rs", "") .file( ".cargo/config.toml", r#" [build] target-dir = "./target" "#, ) .build(); p.cargo("build") .cwd("a1") .with_stderr_data(str![[r#" [LOCKING] 3 packages to latest compatible versions [COMPILING] d v0.0.1 ([ROOT]/foo/d) [COMPILING] c v0.0.1 ([ROOT]/foo/c) [COMPILING] b v0.0.1 ([ROOT]/foo/b) [COMPILING] a1 v0.0.1 ([ROOT]/foo/a1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build") .cwd("a2") .with_stderr_data(str![[r#" [LOCKING] 3 packages to latest compatible versions [COMPILING] a2 v0.0.1 ([ROOT]/foo/a2) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn no_rebuild_if_build_artifacts_move_backwards_in_time() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] a = { path = "a" } "#, ) .file("src/lib.rs", "") .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) .file("a/src/lib.rs", "") .build(); p.cargo("build").run(); p.root().move_into_the_past(); p.cargo("build") .with_stdout_data(str![]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn rebuild_if_build_artifacts_move_forward_in_time() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] a = { path = "a" } "#, ) .file("src/lib.rs", "") .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) .file("a/src/lib.rs", "") .build(); p.cargo("build").run(); p.root().move_into_the_future(); p.cargo("build") .env("CARGO_LOG", "") .with_stdout_data(str![]) .with_stderr_data(str![[r#" [COMPILING] a v0.0.1 ([ROOT]/foo/a) [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn rebuild_if_environment_changes() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" description = "old desc" version = "0.0.1" edition = "2015" authors = [] "#, ) .file( "src/main.rs", r#" fn main() { println!("{}", env!("CARGO_PKG_DESCRIPTION")); } "#, ) .build(); p.cargo("run") .with_stdout_data(str![[r#" old desc "#]]) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .run(); p.change_file( "Cargo.toml", r#" [package] name = "foo" description = "new desc" version = "0.0.1" edition = "2015" authors = [] "#, ); p.cargo("run -v") .with_stdout_data(str![[r#" new desc "#]]) .with_stderr_data(str![[r#" [DIRTY] foo v0.0.1 ([ROOT]/foo): the environment variable CARGO_PKG_DESCRIPTION changed [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .run(); } #[cargo_test] fn no_rebuild_when_rename_dir() { let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [workspace] [dependencies] foo = { path = "foo" } "#, ) .file("src/_unused.rs", "") .file("build.rs", "fn main() {}") .file("foo/Cargo.toml", &basic_manifest("foo", "0.0.1")) .file("foo/src/lib.rs", "") .file("foo/build.rs", "fn main() {}") .build(); // make sure the most recently modified file is `src/lib.rs`, not // `Cargo.toml`, to expose a historical bug where we forgot to strip the // `Cargo.toml` path from looking for the package root. cargo_test_support::sleep_ms(100); fs::write(p.root().join("src/lib.rs"), "").unwrap(); p.cargo("build").run(); let mut new = p.root(); new.pop(); new.push("bar"); fs::rename(p.root(), &new).unwrap(); p.cargo("build") .cwd(&new) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn unused_optional_dep() { Package::new("registry1", "0.1.0").publish(); Package::new("registry2", "0.1.0").publish(); Package::new("registry3", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "p" authors = [] version = "0.1.0" edition = "2015" [dependencies] bar = { path = "bar" } baz = { path = "baz" } registry1 = "*" "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.1" edition = "2015" authors = [] [dev-dependencies] registry2 = "*" "#, ) .file("bar/src/lib.rs", "") .file( "baz/Cargo.toml", r#" [package] name = "baz" version = "0.1.1" edition = "2015" authors = [] [dependencies] registry3 = { version = "*", optional = true } "#, ) .file("baz/src/lib.rs", "") .build(); p.cargo("build").run(); p.cargo("build") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn path_dev_dep_registry_updates() { Package::new("registry1", "0.1.0").publish(); Package::new("registry2", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "p" authors = [] version = "0.1.0" edition = "2015" [dependencies] bar = { path = "bar" } "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.1" edition = "2015" authors = [] [dependencies] registry1 = "*" [dev-dependencies] baz = { path = "../baz"} "#, ) .file("bar/src/lib.rs", "") .file( "baz/Cargo.toml", r#" [package] name = "baz" version = "0.1.1" edition = "2015" authors = [] [dependencies] registry2 = "*" "#, ) .file("baz/src/lib.rs", "") .build(); p.cargo("build").run(); p.cargo("build") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn change_panic_mode() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ['bar', 'baz'] [profile.dev] panic = 'abort' "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1")) .file("bar/src/lib.rs", "") .file( "baz/Cargo.toml", r#" [package] name = "baz" version = "0.1.1" edition = "2015" authors = [] [lib] proc-macro = true [dependencies] bar = { path = '../bar' } "#, ) .file("baz/src/lib.rs", "extern crate bar;") .build(); p.cargo("build -p bar").run(); p.cargo("build -p baz").run(); } #[cargo_test] fn dont_rebuild_based_on_plugins() { let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.1.1" edition = "2015" [workspace] members = ['baz'] [dependencies] proc-macro-thing = { path = 'proc-macro-thing' } "#, ) .file("src/lib.rs", "") .file( "proc-macro-thing/Cargo.toml", r#" [package] name = "proc-macro-thing" version = "0.1.1" edition = "2015" [lib] proc-macro = true [dependencies] qux = { path = '../qux' } "#, ) .file("proc-macro-thing/src/lib.rs", "") .file( "baz/Cargo.toml", r#" [package] name = "baz" version = "0.1.1" edition = "2015" [dependencies] qux = { path = '../qux' } "#, ) .file("baz/src/main.rs", "fn main() {}") .file("qux/Cargo.toml", &basic_manifest("qux", "0.1.1")) .file("qux/src/lib.rs", "") .build(); p.cargo("build").run(); p.cargo("build -p baz").run(); p.cargo("build") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -p bar") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn reuse_workspace_lib() { let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.1.1" edition = "2015" [workspace] [dependencies] baz = { path = 'baz' } "#, ) .file("src/lib.rs", "") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.1")) .file("baz/src/lib.rs", "") .build(); p.cargo("build").run(); p.cargo("test -p baz -v --no-run") .with_stderr_data(str![[r#" [COMPILING] baz v0.1.1 ([ROOT]/foo/baz) [RUNNING] `rustc --crate-name baz [..] [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [EXECUTABLE] `[ROOT]/foo/target/debug/deps/baz-[HASH][EXE]` "#]]) .run(); } #[cargo_test] fn reuse_shared_build_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] shared = {path = "shared"} [workspace] members = ["shared", "bar"] "#, ) .file("src/main.rs", "fn main() {}") .file("shared/Cargo.toml", &basic_manifest("shared", "0.0.1")) .file("shared/src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" [build-dependencies] shared = { path = "../shared" } "#, ) .file("bar/src/lib.rs", "") .file("bar/build.rs", "fn main() {}") .build(); p.cargo("build --workspace").run(); // This should not recompile! p.cargo("build -p foo -v") .with_stderr_data(str![[r#" [FRESH] shared v0.0.1 ([ROOT]/foo/shared) [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn changing_rustflags_is_cached() { let p = project().file("src/lib.rs", "").build(); // This isn't ever cached, we always have to recompile p.cargo("build") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -v") .env("RUSTFLAGS", "-C linker=cc") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -v") .with_stderr_data(str![[r#" [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -v") .env("RUSTFLAGS", "-C linker=cc") .with_stderr_data(str![[r#" [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn changing_rustc_extra_flags_is_cached() { let p = project().file("src/lib.rs", "").build(); // This isn't ever cached, we always have to recompile p.cargo("rustc") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("rustc -v -- -C linker=cc") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("rustc -v") .with_stderr_data(str![[r#" [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("rustc -v -- -C linker=cc") .with_stderr_data(str![[r#" [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn update_dependency_mtime_does_not_rebuild() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = { path = "bar" } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "") .build(); p.cargo("build -Z mtime-on-use") .masquerade_as_nightly_cargo(&["mtime-on-use"]) .env("RUSTFLAGS", "-C linker=cc") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // This does not make new files, but it does update the mtime of the dependency. p.cargo("build -p bar -Z mtime-on-use") .masquerade_as_nightly_cargo(&["mtime-on-use"]) .env("RUSTFLAGS", "-C linker=cc") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // This should not recompile! p.cargo("build -Z mtime-on-use") .masquerade_as_nightly_cargo(&["mtime-on-use"]) .env("RUSTFLAGS", "-C linker=cc") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } fn fingerprint_cleaner(mut dir: PathBuf, timestamp: filetime::FileTime) { // Cargo is experimenting with letting outside projects develop some // limited forms of GC for target_dir. This is one of the forms. // Specifically, Cargo is updating the mtime of a file in // target/profile/.fingerprint each time it uses the fingerprint. // So a cleaner can remove files associated with a fingerprint // if all the files in the fingerprint's folder are older then a time stamp without // effecting any builds that happened since that time stamp. let mut cleaned = false; dir.push(".fingerprint"); for fing in fs::read_dir(&dir).unwrap() { let fing = fing.unwrap(); let outdated = |f: io::Result| { filetime::FileTime::from_last_modification_time(&f.unwrap().metadata().unwrap()) <= timestamp }; if fs::read_dir(fing.path()).unwrap().all(outdated) { fs::remove_dir_all(fing.path()).unwrap(); println!("remove: {:?}", fing.path()); // a real cleaner would remove the big files in deps and build as well // but fingerprint is sufficient for our tests cleaned = true; } else { } } assert!( cleaned, "called fingerprint_cleaner, but there was nothing to remove" ); } #[cargo_test] fn fingerprint_cleaner_does_not_rebuild() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = { path = "bar" } [features] a = [] "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "") .build(); p.cargo("build -Z mtime-on-use") .masquerade_as_nightly_cargo(&["mtime-on-use"]) .run(); p.cargo("build -Z mtime-on-use --features a") .masquerade_as_nightly_cargo(&["mtime-on-use"]) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); if is_coarse_mtime() { sleep_ms(1000); } let timestamp = filetime::FileTime::from_system_time(SystemTime::now()); if is_coarse_mtime() { sleep_ms(1000); } // This does not make new files, but it does update the mtime. p.cargo("build -Z mtime-on-use --features a") .masquerade_as_nightly_cargo(&["mtime-on-use"]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); fingerprint_cleaner(p.target_debug_dir(), timestamp); // This should not recompile! p.cargo("build -Z mtime-on-use --features a") .masquerade_as_nightly_cargo(&["mtime-on-use"]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // But this should be cleaned and so need a rebuild p.cargo("build -Z mtime-on-use") .masquerade_as_nightly_cargo(&["mtime-on-use"]) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn reuse_panic_build_dep_test() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [build-dependencies] bar = { path = "bar" } [dev-dependencies] bar = { path = "bar" } [profile.dev] panic = "abort" "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "") .build(); // Check that `bar` is not built twice. It is only needed once (without `panic`). p.cargo("test --lib --no-run -v") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [RUNNING] `rustc --crate-name bar [..] [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name build_script_build [..] [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc --crate-name foo [..] src/lib.rs [..]--test[..] [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [EXECUTABLE] `[ROOT]/foo/target/debug/deps/foo-[HASH][EXE]` "#]]) .run(); } #[cargo_test] fn reuse_panic_pm() { // foo(panic) -> bar(panic) // somepm(nopanic) -> bar(nopanic) let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = { path = "bar" } somepm = { path = "somepm" } [profile.dev] panic = "abort" "#, ) .file("src/lib.rs", "extern crate bar;") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "") .file( "somepm/Cargo.toml", r#" [package] name = "somepm" version = "0.0.1" edition = "2015" [lib] proc-macro = true [dependencies] bar = { path = "../bar" } "#, ) .file("somepm/src/lib.rs", "extern crate bar;") .build(); // bar is built once without panic (for proc-macro) and once with (for the // normal dependency). p.cargo("build -v") .with_stderr_data( str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [RUNNING] `rustc --crate-name bar [..] -C panic=abort [..] [RUNNING] `rustc --crate-name bar [..] [COMPILING] somepm v0.0.1 ([ROOT]/foo/somepm) [RUNNING] `rustc --crate-name somepm [..] [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] -C panic=abort [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn bust_patched_dep() { Package::new("registry1", "0.1.0").publish(); Package::new("registry2", "0.1.0") .dep("registry1", "0.1.0") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] registry2 = "0.1.0" [patch.crates-io] registry1 = { path = "reg1new" } "#, ) .file("src/lib.rs", "") .file("reg1new/Cargo.toml", &basic_manifest("registry1", "0.1.0")) .file("reg1new/src/lib.rs", "") .build(); p.cargo("build").run(); if is_coarse_mtime() { sleep_ms(1000); } p.change_file("reg1new/src/lib.rs", "// modified"); if is_coarse_mtime() { sleep_ms(1000); } p.cargo("build -v").with_stderr_data(str![[r#" [DIRTY] registry1 v0.1.0 ([ROOT]/foo/reg1new): the file `reg1new/src/lib.rs` has changed ([TIME_DIFF_AFTER_LAST_BUILD]) [COMPILING] registry1 v0.1.0 ([ROOT]/foo/reg1new) [RUNNING] `rustc --crate-name registry1 [..] [DIRTY] registry2 v0.1.0: the dependency registry1 was rebuilt [COMPILING] registry2 v0.1.0 [RUNNING] `rustc --crate-name registry2 [..] [DIRTY] foo v0.0.1 ([ROOT]/foo): the dependency registry2 was rebuilt [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); p.cargo("build -v") .with_stderr_data(str![[r#" [FRESH] registry1 v0.1.0 ([ROOT]/foo/reg1new) [FRESH] registry2 v0.1.0 [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn rebuild_on_mid_build_file_modification() { let server = TcpListener::bind("127.0.0.1:0").unwrap(); let addr = server.local_addr().unwrap(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["root", "proc_macro_dep"] "#, ) .file( "root/Cargo.toml", r#" [package] name = "root" version = "0.1.0" edition = "2015" authors = [] [dependencies] proc_macro_dep = { path = "../proc_macro_dep" } "#, ) .file( "root/src/lib.rs", r#" #[macro_use] extern crate proc_macro_dep; #[derive(Noop)] pub struct X; "#, ) .file( "proc_macro_dep/Cargo.toml", r#" [package] name = "proc_macro_dep" version = "0.1.0" edition = "2015" authors = [] [lib] proc-macro = true "#, ) .file( "proc_macro_dep/src/lib.rs", &format!( r#" extern crate proc_macro; use std::io::Read; use std::net::TcpStream; use proc_macro::TokenStream; #[proc_macro_derive(Noop)] pub fn noop(_input: TokenStream) -> TokenStream {{ let mut stream = TcpStream::connect("{}").unwrap(); let mut v = Vec::new(); stream.read_to_end(&mut v).unwrap(); "".parse().unwrap() }} "#, addr ), ) .build(); let root = p.root(); let t = thread::spawn(move || { let socket = server.accept().unwrap().0; sleep_ms(1000); let mut file = OpenOptions::new() .write(true) .append(true) .open(root.join("root/src/lib.rs")) .unwrap(); writeln!(file, "// modified").expect("Failed to append to root sources"); drop(file); drop(socket); drop(server.accept().unwrap()); }); p.cargo("build") .with_stderr_data(str![[r#" [COMPILING] proc_macro_dep v0.1.0 ([ROOT]/foo/proc_macro_dep) [COMPILING] root v0.1.0 ([ROOT]/foo/root) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -v").with_stderr_data(str![[r#" [FRESH] proc_macro_dep v0.1.0 ([ROOT]/foo/proc_macro_dep) [DIRTY] root v0.1.0 ([ROOT]/foo/root): the file `root/src/lib.rs` has changed ([TIME_DIFF_AFTER_LAST_BUILD]) [COMPILING] root v0.1.0 ([ROOT]/foo/root) [RUNNING] `rustc --crate-name root [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); t.join().ok().unwrap(); } #[cargo_test] fn dirty_both_lib_and_test() { // This tests that all artifacts that depend on the results of a build // script will get rebuilt when the build script reruns, even for separate // commands. It does the following: // // 1. Project "foo" has a build script which will compile a small // staticlib to link against. Normally this would use the `cc` crate, // but here we just use rustc to avoid the `cc` dependency. // 2. Build the library. // 3. Build the unit test. The staticlib intentionally has a bad value. // 4. Rewrite the staticlib with the correct value. // 5. Build the library again. // 6. Build the unit test. This should recompile. let slib = |n| { format!( r#" #[no_mangle] pub extern "C" fn doit() -> i32 {{ return {}; }} "#, n ) }; let p = project() .file( "src/lib.rs", r#" extern "C" { fn doit() -> i32; } #[test] fn t1() { assert_eq!(unsafe { doit() }, 1, "doit assert failure"); } "#, ) .file( "build.rs", r#" use std::env; use std::path::PathBuf; use std::process::Command; fn main() { let rustc = env::var_os("RUSTC").unwrap(); let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); assert!( Command::new(rustc) .args(&[ "--crate-type=staticlib", "--out-dir", out_dir.to_str().unwrap(), "slib.rs" ]) .status() .unwrap() .success(), "slib build failed" ); println!("cargo::rustc-link-lib=slib"); println!("cargo::rustc-link-search={}", out_dir.display()); } "#, ) .file("slib.rs", &slib(2)) .build(); p.cargo("build").run(); // 2 != 1 p.cargo("test --lib") .with_status(101) .with_stdout_data("...\n[..]doit assert failure[..]\n...") .run(); if is_coarse_mtime() { // #5918 sleep_ms(1000); } // Fix the mistake. p.change_file("slib.rs", &slib(1)); p.cargo("build").run(); // This should recompile with the new static lib, and the test should pass. p.cargo("test --lib").run(); } #[cargo_test] fn script_fails_stay_dirty() { // Check if a script is aborted (such as hitting Ctrl-C) that it will re-run. // Steps: // 1. Build to establish fingerprints. // 2. Make a change that triggers the build script to re-run. Abort the // script while it is running. // 3. Run the build again and make sure it re-runs the script. let p = project() .file( "build.rs", r#" mod helper; fn main() { println!("cargo::rerun-if-changed=build.rs"); helper::doit(); } "#, ) .file("helper.rs", "pub fn doit() {}") .file("src/lib.rs", "") .build(); p.cargo("build").run(); if is_coarse_mtime() { sleep_ms(1000); } p.change_file("helper.rs", r#"pub fn doit() {panic!("Crash!");}"#); p.cargo("build") .with_stderr_data("...\n[..]Crash![..]\n...") .with_status(101) .run(); // There was a bug where this second call would be "fresh". p.cargo("build") .with_stderr_data("...\n[..]Crash![..]\n...") .with_status(101) .run(); } #[cargo_test] fn simulated_docker_deps_stay_cached() { // Test what happens in docker where the nanoseconds are zeroed out. Package::new("regdep", "1.0.0").publish(); Package::new("regdep_old_style", "1.0.0") .file("build.rs", "fn main() {}") .file("src/lib.rs", "") .publish(); Package::new("regdep_env", "1.0.0") .file( "build.rs", r#" fn main() { println!("cargo::rerun-if-env-changed=SOMEVAR"); } "#, ) .file("src/lib.rs", "") .publish(); Package::new("regdep_rerun", "1.0.0") .file( "build.rs", r#" fn main() { println!("cargo::rerun-if-changed=build.rs"); } "#, ) .file("src/lib.rs", "") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] pathdep = { path = "pathdep" } regdep = "1.0" regdep_old_style = "1.0" regdep_env = "1.0" regdep_rerun = "1.0" "#, ) .file( "src/lib.rs", " extern crate pathdep; extern crate regdep; extern crate regdep_old_style; extern crate regdep_env; extern crate regdep_rerun; ", ) .file("build.rs", "fn main() {}") .file("pathdep/Cargo.toml", &basic_manifest("pathdep", "1.0.0")) .file("pathdep/src/lib.rs", "") .build(); p.cargo("build").run(); let already_zero = { // This happens on HFS with 1-second timestamp resolution, // or other filesystems where it just so happens to write exactly on a // 1-second boundary. let metadata = fs::metadata(p.root().join("src/lib.rs")).unwrap(); let mtime = FileTime::from_last_modification_time(&metadata); mtime.nanoseconds() == 0 }; // Recursively remove `nanoseconds` from every path. fn zeropath(path: &Path) { for entry in walkdir::WalkDir::new(path) .into_iter() .filter_map(|e| e.ok()) { let metadata = fs::metadata(entry.path()).unwrap(); let mtime = metadata.modified().unwrap(); let mtime_duration = mtime.duration_since(SystemTime::UNIX_EPOCH).unwrap(); let trunc_mtime = FileTime::from_unix_time(mtime_duration.as_secs() as i64, 0); let atime = metadata.accessed().unwrap(); let atime_duration = atime.duration_since(SystemTime::UNIX_EPOCH).unwrap(); let trunc_atime = FileTime::from_unix_time(atime_duration.as_secs() as i64, 0); if let Err(e) = filetime::set_file_times(entry.path(), trunc_atime, trunc_mtime) { // Windows doesn't allow changing filetimes on some things // (directories, other random things I'm not sure why). Just // ignore them. if e.kind() == std::io::ErrorKind::PermissionDenied { println!("PermissionDenied filetime on {:?}", entry.path()); } else { panic!("FileTime error on {:?}: {:?}", entry.path(), e); } } } } zeropath(&p.root()); zeropath(&paths::home()); if already_zero { println!("already zero"); // If it was already truncated, then everything stays fresh. p.cargo("build -v") .with_stderr_data( str![[r#" [FRESH] pathdep [..] [FRESH] regdep [..] [FRESH] regdep_env [..] [FRESH] regdep_old_style [..] [FRESH] regdep_rerun [..] [FRESH] foo [..] [FINISHED] [..] "#]] .unordered(), ) .run(); } else { println!("not already zero"); // It is not ideal that `foo` gets recompiled, but that is the current // behavior. Currently mtimes are ignored for registry deps. // // Note that this behavior is due to the fact that `foo` has a build // script in "old" mode where it doesn't print `rerun-if-*`. In this // mode we use `Precalculated` to fingerprint a path dependency, where // `Precalculated` is an opaque string which has the most recent mtime // in it. It differs between builds because one has nsec=0 and the other // likely has a nonzero nsec. Hence, the rebuild. p.cargo("build -v") .with_stderr_data( str![[r#" [FRESH] regdep [..] [FRESH] pathdep [..] [FRESH] regdep_env [..] [DIRTY] foo v0.1.0 ([ROOT]/foo): the precalculated components changed [COMPILING] foo v0.1.0 ([ROOT]/foo) [FRESH] regdep_rerun [..] [FRESH] regdep_old_style [..] [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } } #[cargo_test] fn metadata_change_invalidates() { // (key, value, value-updated, env-var-name) let scenarios = [ ( "description", r#""foo""#, r#""foo_updated""#, "CARGO_PKG_DESCRIPTION", ), ( "homepage", r#""foo""#, r#""foo_updated""#, "CARGO_PKG_HOMEPAGE", ), ( "repository", r#""foo""#, r#""foo_updated""#, "CARGO_PKG_REPOSITORY", ), ( "license", r#""foo""#, r#""foo_updated""#, "CARGO_PKG_LICENSE", ), ( "license-file", r#""foo""#, r#""foo_updated""#, "CARGO_PKG_LICENSE_FILE", ), ( "authors", r#"["foo"]"#, r#"["foo_updated"]"#, "CARGO_PKG_AUTHORS", ), ( "rust-version", r#""1.0.0""#, r#""1.0.1""#, "CARGO_PKG_RUST_VERSION", ), ("readme", r#""foo""#, r#""foo_updated""#, "CARGO_PKG_README"), ]; let base_cargo_toml = r#" [package] name = "foo" version = "0.1.0" edition = "2015" "#; let p = project().build(); for (key, value, value_updated, env_var) in scenarios { p.change_file("Cargo.toml", base_cargo_toml); p.change_file( "src/main.rs", &format!( r#" fn main() {{ let output = env!("{env_var}"); println!("{{output}}"); }} "# ), ); // Compile the first time p.cargo("build").run(); // Update the manifest, rebuild, and verify the build was invalided p.change_file("Cargo.toml", &format!("{base_cargo_toml}\n{key} = {value}")); p.cargo("build -v") .with_stderr_data(format!( r#"[DIRTY] foo v0.1.0 ([ROOT]/foo): the environment variable {env_var} changed [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "# )) .run(); // Remove references to the metadata and rebuild p.change_file( "src/main.rs", r#" fn main() { println!("foo"); } "#, ); p.cargo("build").run(); // Update the manifest value and verify the build is NOT invalidated. p.change_file( "Cargo.toml", &format!("{base_cargo_toml}\n{key} = {value_updated}"), ); p.cargo("build -v") .with_stderr_data(str![[r#" [FRESH] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } } #[cargo_test] fn edition_change_invalidates() { const MANIFEST: &str = r#" [package] name = "foo" version = "0.1.0" "#; let p = project() .file("Cargo.toml", MANIFEST) .file("src/lib.rs", "") .build(); p.cargo("build").run(); p.change_file("Cargo.toml", &format!("{}edition = \"2018\"", MANIFEST)); p.cargo("build") .with_stderr_data(str![[r#" [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.change_file( "Cargo.toml", &format!( r#"{}edition = "2018" [lib] edition = "2015" "#, MANIFEST ), ); p.cargo("build") .with_stderr_data(str![[r#" [WARNING] `edition` is set on library `foo` which is deprecated [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -v") .with_stderr_data(str![[r#" [WARNING] `edition` is set on library `foo` which is deprecated [FRESH] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert_eq!(p.glob("target/debug/deps/libfoo-*.rlib").count(), 1); } #[cargo_test] fn rename_with_path_deps() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies] a = { path = 'a' } "#, ) .file("src/lib.rs", "extern crate a; pub fn foo() { a::foo(); }") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.5.0" edition = "2015" authors = [] [dependencies] b = { path = 'b' } "#, ) .file("a/src/lib.rs", "extern crate b; pub fn foo() { b::foo() }") .file( "a/b/Cargo.toml", r#" [package] name = "b" version = "0.5.0" edition = "2015" authors = [] "#, ) .file("a/b/src/lib.rs", "pub fn foo() { }"); let p = p.build(); p.cargo("build").run(); // Now rename the root directory and rerun `cargo run`. Not only should we // not build anything but we also shouldn't crash. let mut new = p.root(); new.pop(); new.push("foo2"); fs::rename(p.root(), &new).unwrap(); p.cargo("build") .cwd(&new) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn move_target_directory_with_path_deps() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies] a = { path = "a" } "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.5.0" edition = "2015" authors = [] "#, ) .file("src/lib.rs", "extern crate a; pub use a::print_msg;") .file( "a/build.rs", r###" use std::env; use std::fs; use std::path::Path; fn main() { println!("cargo::rerun-if-changed=build.rs"); let out_dir = env::var("OUT_DIR").unwrap(); let dest_path = Path::new(&out_dir).join("hello.rs"); fs::write(&dest_path, r#" pub fn message() -> &'static str { "Hello, World!" } "#).unwrap(); } "###, ) .file( "a/src/lib.rs", r#" include!(concat!(env!("OUT_DIR"), "/hello.rs")); pub fn print_msg() { message(); } "#, ); let p = p.build(); let mut parent = p.root(); parent.pop(); p.cargo("build").run(); let new_target = p.root().join("target2"); fs::rename(p.root().join("target"), &new_target).unwrap(); p.cargo("build") .env("CARGO_TARGET_DIR", &new_target) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn rerun_if_changes() { let p = project() .file( "build.rs", r#" fn main() { println!("cargo::rerun-if-env-changed=FOO"); if std::env::var("FOO").is_ok() { println!("cargo::rerun-if-env-changed=BAR"); } } "#, ) .file("src/lib.rs", "") .build(); p.cargo("build").run(); p.cargo("build") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -v") .env("FOO", "1") .with_stderr_data(str![[r#" [DIRTY] foo v0.0.1 ([ROOT]/foo): the env variable FOO changed [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build") .env("FOO", "1") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -v") .env("FOO", "1") .env("BAR", "1") .with_stderr_data(str![[r#" [DIRTY] foo v0.0.1 ([ROOT]/foo): the env variable BAR changed [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build") .env("FOO", "1") .env("BAR", "1") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -v") .env("BAR", "2") .with_stderr_data(str![[r#" [DIRTY] foo v0.0.1 ([ROOT]/foo): the env variable FOO changed [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build") .env("BAR", "2") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn channel_shares_filenames() { // Test that different "nightly" releases use the same output filename. // Create separate rustc binaries to emulate running different toolchains. let nightly1 = format!( "\ rustc 1.44.0-nightly (38114ff16 2020-03-21) binary: rustc commit-hash: 38114ff16e7856f98b2b4be7ab4cd29b38bed59a commit-date: 2020-03-21 host: {} release: 1.44.0-nightly LLVM version: 9.0 ", rustc_host() ); let nightly2 = format!( "\ rustc 1.44.0-nightly (a5b09d354 2020-03-31) binary: rustc commit-hash: a5b09d35473615e7142f5570f5c5fad0caf68bd2 commit-date: 2020-03-31 host: {} release: 1.44.0-nightly LLVM version: 9.0 ", rustc_host() ); let beta1 = format!( "\ rustc 1.43.0-beta.3 (4c587bbda 2020-03-25) binary: rustc commit-hash: 4c587bbda04ab55aaf56feab11dfdfe387a85d7a commit-date: 2020-03-25 host: {} release: 1.43.0-beta.3 LLVM version: 9.0 ", rustc_host() ); let beta2 = format!( "\ rustc 1.42.0-beta.5 (4e1c5f0e9 2020-02-28) binary: rustc commit-hash: 4e1c5f0e9769a588b91c977e3d81e140209ef3a2 commit-date: 2020-02-28 host: {} release: 1.42.0-beta.5 LLVM version: 9.0 ", rustc_host() ); let stable1 = format!( "\ rustc 1.42.0 (b8cedc004 2020-03-09) binary: rustc commit-hash: b8cedc00407a4c56a3bda1ed605c6fc166655447 commit-date: 2020-03-09 host: {} release: 1.42.0 LLVM version: 9.0 ", rustc_host() ); let stable2 = format!( "\ rustc 1.41.1 (f3e1a954d 2020-02-24) binary: rustc commit-hash: f3e1a954d2ead4e2fc197c7da7d71e6c61bad196 commit-date: 2020-02-24 host: {} release: 1.41.1 LLVM version: 9.0 ", rustc_host() ); let compiler = project() .at("compiler") .file("Cargo.toml", &basic_manifest("compiler", "0.1.0")) .file( "src/main.rs", r#" fn main() { if std::env::args_os().any(|a| a == "-vV") { print!("{}", env!("FUNKY_VERSION_TEST")); return; } let mut cmd = std::process::Command::new("rustc"); cmd.args(std::env::args_os().skip(1)); assert!(cmd.status().unwrap().success()); } "#, ) .build(); let makeit = |version, vv| { // Force a rebuild. compiler.target_debug_dir().join("deps").rm_rf(); compiler.cargo("build").env("FUNKY_VERSION_TEST", vv).run(); fs::rename(compiler.bin("compiler"), compiler.bin(version)).unwrap(); }; makeit("nightly1", nightly1); makeit("nightly2", nightly2); makeit("beta1", beta1); makeit("beta2", beta2); makeit("stable1", stable1); makeit("stable2", stable2); // Run `cargo check` with different rustc versions to observe its behavior. let p = project().file("src/lib.rs", "").build(); // Runs `cargo check` and returns the rmeta filename created. // Checks that the freshness matches the given value. let check = |version, fresh| -> String { let output = p .cargo("check --message-format=json") .env("RUSTC", compiler.bin(version)) .run(); // Collect the filenames generated. let mut artifacts: Vec<_> = std::str::from_utf8(&output.stdout) .unwrap() .lines() .filter_map(|line| { let value: serde_json::Value = serde_json::from_str(line).unwrap(); if value["reason"].as_str().unwrap() == "compiler-artifact" { assert_eq!(value["fresh"].as_bool().unwrap(), fresh); let filenames = value["filenames"].as_array().unwrap(); assert_eq!(filenames.len(), 1); Some(filenames[0].to_string()) } else { None } }) .collect(); // Should only generate one rmeta file. assert_eq!(artifacts.len(), 1); artifacts.pop().unwrap() }; let nightly1_name = check("nightly1", false); assert_eq!(check("nightly1", true), nightly1_name); assert_eq!(check("nightly2", false), nightly1_name); // same as before assert_eq!(check("nightly2", true), nightly1_name); // Should rebuild going back to nightly1. assert_eq!(check("nightly1", false), nightly1_name); let beta1_name = check("beta1", false); assert_ne!(beta1_name, nightly1_name); assert_eq!(check("beta1", true), beta1_name); assert_eq!(check("beta2", false), beta1_name); // same as before assert_eq!(check("beta2", true), beta1_name); // Should rebuild going back to beta1. assert_eq!(check("beta1", false), beta1_name); let stable1_name = check("stable1", false); assert_ne!(stable1_name, nightly1_name); assert_ne!(stable1_name, beta1_name); let stable2_name = check("stable2", false); assert_ne!(stable1_name, stable2_name); // Check everything is fresh. assert_eq!(check("stable1", true), stable1_name); assert_eq!(check("stable2", true), stable2_name); assert_eq!(check("beta1", true), beta1_name); assert_eq!(check("nightly1", true), nightly1_name); } #[cargo_test] fn linking_interrupted() { // Interrupt during the linking phase shouldn't leave test executable as "fresh". // This is used to detect when linking starts, then to pause the linker so // that the test can kill cargo. let link_listener = TcpListener::bind("127.0.0.1:0").unwrap(); let link_addr = link_listener.local_addr().unwrap(); // This is used to detect when rustc exits. let rustc_listener = TcpListener::bind("127.0.0.1:0").unwrap(); let rustc_addr = rustc_listener.local_addr().unwrap(); // Create a linker that we can interrupt. let linker = project() .at("linker") .file("Cargo.toml", &basic_manifest("linker", "1.0.0")) .file( "src/main.rs", &r#" fn main() { // Figure out the output filename. let output = match std::env::args().find(|a| a.starts_with("/OUT:")) { Some(s) => s[5..].to_string(), None => { let mut args = std::env::args(); loop { if args.next().unwrap() == "-o" { break; } } args.next().unwrap() } }; std::fs::remove_file(&output).unwrap(); std::fs::write(&output, "").unwrap(); // Tell the test that we are ready to be interrupted. let mut socket = std::net::TcpStream::connect("__ADDR__").unwrap(); // Wait for the test to kill us. std::thread::sleep(std::time::Duration::new(60, 0)); } "# .replace("__ADDR__", &link_addr.to_string()), ) .build(); linker.cargo("build").run(); // Create a wrapper around rustc that will tell us when rustc is finished. let rustc = project() .at("rustc-waiter") .file("Cargo.toml", &basic_manifest("rustc-waiter", "1.0.0")) .file( "src/main.rs", &r#" fn main() { let mut conn = None; // Check for a normal build (not -vV or --print). if std::env::args().any(|arg| arg == "t1") { // Tell the test that rustc has started. conn = Some(std::net::TcpStream::connect("__ADDR__").unwrap()); } let status = std::process::Command::new("rustc") .args(std::env::args().skip(1)) .status() .expect("rustc to run"); std::process::exit(status.code().unwrap_or(1)); } "# .replace("__ADDR__", &rustc_addr.to_string()), ) .build(); rustc.cargo("build").run(); // Build it once so that the fingerprint gets saved to disk. let p = project() .file("src/lib.rs", "") .file("tests/t1.rs", "") .build(); p.cargo("test --test t1 --no-run").run(); // Make a change, start a build, then interrupt it. p.change_file("src/lib.rs", "// modified"); let linker_env = format!("CARGO_TARGET_{}_LINKER", rustc_host_env()); // NOTE: This assumes that the paths to the linker or rustc are not in the // fingerprint. But maybe they should be? let mut cmd = p .cargo("test --test t1 --no-run") .env(&linker_env, linker.bin("linker")) .env("RUSTC", rustc.bin("rustc-waiter")) .build_command(); let mut child = cmd .stdout(Stdio::null()) .stderr(Stdio::null()) .env("__CARGO_TEST_SETSID_PLEASE_DONT_USE_ELSEWHERE", "1") .spawn() .unwrap(); // Wait for rustc to start. let mut rustc_conn = rustc_listener.accept().unwrap().0; // Wait for linking to start. drop(link_listener.accept().unwrap()); // Interrupt the child. death::ctrl_c(&mut child); assert!(!child.wait().unwrap().success()); // Wait for rustc to exit. If we don't wait, then the command below could // start while rustc is still being torn down. let mut buf = [0]; drop(rustc_conn.read_exact(&mut buf)); // Build again, shouldn't be fresh. p.cargo("test --test t1 -v") .with_stderr_data(str![[r#" [DIRTY] foo v0.0.1 ([ROOT]/foo): the config settings changed [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] [RUNNING] `rustc --crate-name t1 [..] [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/debug/deps/t1-[HASH][EXE]` "#]]) .run(); } #[cargo_test] #[cfg_attr( not(all(target_arch = "x86_64", target_os = "windows", target_env = "msvc")), ignore )] fn lld_is_fresh() { // Check for bug when using lld linker that it remains fresh with dylib. let p = project() .file( ".cargo/config.toml", r#" [target.x86_64-pc-windows-msvc] linker = "rust-lld" "#, ) .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [lib] crate-type = ["dylib"] "#, ) .file("src/lib.rs", "") .build(); p.cargo("build").run(); p.cargo("build -v") .with_stderr_data(str![[r#" [FRESH] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn env_in_code_causes_rebuild() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" "#, ) .file( "src/main.rs", r#" fn main() { println!("{:?}", option_env!("FOO")); println!("{:?}", option_env!("FOO\nBAR")); } "#, ) .build(); p.cargo("build").env_remove("FOO").run(); p.cargo("build") .env_remove("FOO") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -v") .env("FOO", "bar") .with_stderr_data(str![[r#" [DIRTY] foo v0.1.0 ([ROOT]/foo): the environment variable FOO changed [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build") .env("FOO", "bar") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -v") .env("FOO", "baz") .with_stderr_data(str![[r#" [DIRTY] foo v0.1.0 ([ROOT]/foo): the environment variable FOO changed [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build") .env("FOO", "baz") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -v") .env_remove("FOO") .with_stderr_data(str![[r#" [DIRTY] foo v0.1.0 ([ROOT]/foo): the environment variable FOO changed [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build") .env_remove("FOO") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let interesting = " #!$\nabc\r\\\t\u{8}\r\n"; p.cargo("build").env("FOO", interesting).run(); p.cargo("build") .env("FOO", interesting) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build").env("FOO\nBAR", interesting).run(); p.cargo("build") .env("FOO\nBAR", interesting) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn env_build_script_no_rebuild() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" "#, ) .file( "build.rs", r#" fn main() { println!("cargo::rustc-env=FOO=bar"); } "#, ) .file( "src/main.rs", r#" fn main() { println!("{:?}", env!("FOO")); } "#, ) .build(); p.cargo("build").run(); p.cargo("build") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn cargo_env_changes() { // Checks that changes to the env var CARGO in the dep-info file triggers // a rebuild. let p = project() .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) .file( "src/main.rs", r#" fn main() { println!("{:?}", env!("CARGO")); } "#, ) .build(); let cargo_exe = crate::utils::cargo_exe(); let other_cargo_path = p.root().join(cargo_exe.file_name().unwrap()); std::fs::hard_link(&cargo_exe, &other_cargo_path).unwrap(); let other_cargo = || { let mut pb = cargo_test_support::process(&other_cargo_path); pb.cwd(p.root()); cargo_test_support::execs().with_process_builder(pb) }; p.cargo("check").run(); other_cargo() .arg("check") .arg("-v") .with_stderr_data(str![[r#" [DIRTY] foo v1.0.0 ([ROOT]/foo): the environment variable CARGO changed [CHECKING] foo v1.0.0 ([ROOT]/foo) [RUNNING] `rustc [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // And just to confirm that without using env! it doesn't rebuild. p.change_file("src/main.rs", "fn main() {}"); p.cargo("check") .with_stderr_data(str![[r#" [CHECKING] foo v1.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); other_cargo() .arg("check") .arg("-v") .with_stderr_data(str![[r#" [FRESH] foo v1.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn changing_linker() { // Changing linker should rebuild. let p = project().file("src/main.rs", "fn main() {}").build(); p.cargo("build").run(); let linker_env = format!("CARGO_TARGET_{}_LINKER", rustc_host_env()); p.cargo("build --verbose") .env(&linker_env, "nonexistent-linker") .with_status(101) .with_stderr_data(str![[r#" [DIRTY] foo v0.0.1 ([ROOT]/foo): the config settings changed [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] -C linker=nonexistent-linker [..]` [ERROR] linker `nonexistent-linker` not found ... "#]]) .run(); } #[cargo_test] fn verify_source_before_recompile() { Package::new("bar", "0.1.0") .file("src/lib.rs", "") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("vendor --respect-source-config").run(); p.change_file( ".cargo/config.toml", r#" [source.crates-io] replace-with = 'vendor' [source.vendor] directory = 'vendor' "#, ); // Sanity check: vendoring works correctly. p.cargo("check --verbose") .with_stderr_data(str![[r#" [CHECKING] bar v0.1.0 [RUNNING] `rustc --crate-name bar [..] [ROOT]/foo/vendor/bar/src/lib.rs [..] [CHECKING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] src/lib.rs [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Now modify vendored crate. p.change_file( "vendor/bar/src/lib.rs", r#"compile_error!("You shall not pass!");"#, ); // Should ignore modified sources without any recompile. p.cargo("check --verbose") .with_stderr_data(str![[r#" [FRESH] bar v0.1.0 [FRESH] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Add a `RUSTFLAGS` to trigger a recompile. // // Cargo should refuse to build because of checksum verification failure. // Cargo shouldn't recompile dependency `bar`. p.cargo("check --verbose") .env("RUSTFLAGS", "-W warnings") .with_status(101) .with_stderr_data(str![[r#" [ERROR] the listed checksum of `[ROOT]/foo/vendor/bar/src/lib.rs` has changed: expected: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 actual: 66e843918c1d4ea8231af814f9f958958808249d4407de01114acb730ecd9bdf directory sources are not intended to be edited, if modifications are required then it is recommended that `[patch]` is used with a forked copy of the source "#]]) .run(); } #[cargo_test] fn skip_mtime_check_in_selected_cargo_home_subdirs() { let p = project() .at("cargo_home/registry/foo") .file("Cargo.toml", &basic_lib_manifest("foo")) .file("src/lib.rs", "") .build(); let project_root = p.root(); let cargo_home = project_root.parent().unwrap().parent().unwrap(); p.cargo("check -v") .env("CARGO_HOME", &cargo_home) .with_stderr_data(str![[r#" [CHECKING] foo v0.5.0 ([ROOT]/cargo_home/registry/foo) [RUNNING] `rustc --crate-name foo [..] src/lib.rs [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.change_file("src/lib.rs", "illegal syntax"); p.cargo("check -v") .env("CARGO_HOME", &cargo_home) .with_stderr_data(str![[r#" [FRESH] foo v0.5.0 ([ROOT]/cargo_home/registry/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn use_mtime_cache_in_cargo_home() { let p = project() .at("cargo_home/foo") .file("Cargo.toml", &basic_lib_manifest("foo")) .file("src/lib.rs", "") .build(); let project_root = p.root(); let cargo_home = project_root.parent().unwrap(); p.cargo("check -v") .env("CARGO_HOME", &cargo_home) .with_stderr_data(str![[r#" [CHECKING] foo v0.5.0 ([ROOT]/cargo_home/foo) [RUNNING] `rustc --crate-name foo [..] src/lib.rs [..] src/lib.rs [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.change_file("src/lib.rs", "illegal syntax"); p.cargo("check -v") .env("CARGO_HOME", &cargo_home) .with_status(101) .with_stderr_data(str![[r#" [DIRTY] foo v0.5.0 ([ROOT]/cargo_home/foo): the file `src/lib.rs` has changed ([TIME_DIFF_AFTER_LAST_BUILD]) [CHECKING] foo v0.5.0 ([ROOT]/cargo_home/foo) [RUNNING] `rustc --crate-name foo [..] src/lib.rs [..] ... [ERROR] could not compile `foo` (lib) due to 1 previous error ... "#]]) .run(); } cargo-0.91.0/tests/testsuite/freshness_checksum.rs000064400000000000000000002612421046102023000204400ustar 00000000000000//! Tests for checksum-based fingerprinting (rebuild detection). use std::fs::{self, OpenOptions}; use std::io::prelude::*; use std::net::TcpListener; use std::process::Stdio; use std::thread; use crate::prelude::*; use cargo_test_support::assert_deps_contains; use cargo_test_support::registry::Package; use cargo_test_support::{ basic_lib_manifest, basic_manifest, project, rustc_host, rustc_host_env, str, }; use super::death; #[cargo_test] fn non_nightly_fails() { let p = project().file("src/main.rs", "fn main() {}").build(); p.cargo("build -Zchecksum-freshness") .with_stderr_data(str![[r#" [ERROR] the `-Z` flag is only accepted on the nightly channel of Cargo, but this is the `stable` channel See https://doc.rust-lang.org/book/appendix-07-nightly-rust.html for more information about Rust release channels. "#]]) .with_status(101) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn checksum_actually_uses_checksum() { let p = project() .file("src/main.rs", "mod a; fn main() {}") .file("src/a.rs", "") .build(); p.cargo("check -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.root().move_into_the_future(); p.cargo("check -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn checksum_build_compatible_with_mtime_build() { let p = project() .file("src/main.rs", "mod a; fn main() {}") .file("src/a.rs", "") .build(); p.cargo("check -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn same_size_different_content() { let p = project() .file("src/main.rs", "mod a; fn main() {}") .file("src/a.rs", "") .build(); p.cargo("check -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.change_file("src/main.rs", "mod a;fn main() { }"); p.cargo("check -v -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [DIRTY] foo v0.0.1 ([ROOT]/foo): the file `src/main.rs` has changed (checksum didn't match, blake3=26aa07e1adab787246f9d333be65d2eb78dd5fd0fee834ba7a769098b4b651bc != blake3=fc1a42e376d9c148227c13de41b77143f6b5b8132d2b204b63cdbc9326848894) [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test( nightly, reason = "-Zbinary-dep-depinfo is unstable, also requires -Zchecksum-hash-algorithm" )] fn binary_depinfo_correctly_encoded() { Package::new("regdep", "0.1.0") .file("src/lib.rs", "pub fn f() {}") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [dependencies] regdep = "0.1" bar = {path = "./bar"} "#, ) .file( "src/main.rs", r#" fn main() { regdep::f(); bar::f(); } "#, ) /*********** Path Dependency `bar` ***********/ .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn f() {}") .build(); let host = rustc_host(); p.cargo("build -Zbinary-dep-depinfo -Zchecksum-freshness --target") .arg(&host) .masquerade_as_nightly_cargo(&["binary-dep-depinfo", "checksum-freshness"]) .with_stderr_data(str![[r#" ... [COMPILING] foo v0.1.0 ([ROOT]/foo) ... "#]]) .run(); assert_deps_contains( &p, &format!("target/{}/debug/.fingerprint/foo-*/dep-bin-foo", host), &[ (0, "src/main.rs"), (1, &format!("{}/debug/deps/libbar-*.rlib", host)), (1, &format!("{}/debug/deps/libregdep-*.rlib", host)), ], ); // Make sure it stays fresh. p.cargo("build -Zbinary-dep-depinfo -Zchecksum-freshness --target") .arg(&host) .masquerade_as_nightly_cargo(&["binary-dep-depinfo", "checksum-freshness"]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn modifying_and_moving() { let p = project() .file("src/main.rs", "mod a; fn main() {}") .file("src/a.rs", "") .build(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.root().move_into_the_past(); p.root().join("target").move_into_the_past(); p.change_file("src/a.rs", "#[allow(unused)]fn main() {}"); p.cargo("build -Zchecksum-freshness -v") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [DIRTY] foo v0.0.1 ([ROOT]/foo): file size changed (0 != 28) for `src/a.rs` [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); fs::rename(&p.root().join("src/a.rs"), &p.root().join("src/b.rs")).unwrap(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) error[E0583]: file not found for module `a` ... [ERROR] could not compile `foo` (bin "foo") due to 1 previous error "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn rebuild_sub_package_then_while_package() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.1" edition = "2015" [dependencies.a] path = "a" [dependencies.b] path = "b" "#, ) .file("src/lib.rs", "extern crate a; extern crate b;") .file( "a/Cargo.toml", r#" [package] name = "a" authors = [] version = "0.0.1" edition = "2015" [dependencies.b] path = "../b" "#, ) .file("a/src/lib.rs", "extern crate b;") .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) .file("b/src/lib.rs", "") .build(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] b v0.0.1 ([ROOT]/foo/b) [COMPILING] a v0.0.1 ([ROOT]/foo/a) [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.change_file("b/src/lib.rs", "pub fn b() {}"); p.cargo("build -Zchecksum-freshness -pb -v") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [DIRTY] b v0.0.1 ([ROOT]/foo/b): file size changed (0 != 13) for `b/src/lib.rs` [COMPILING] b v0.0.1 ([ROOT]/foo/b) [RUNNING] `rustc --crate-name b [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.change_file( "src/lib.rs", "extern crate a; extern crate b; pub fn toplevel() {}", ); p.cargo("build -Zchecksum-freshness -v") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [FRESH] b v0.0.1 ([ROOT]/foo/b) [DIRTY] a v0.0.1 ([ROOT]/foo/a): the dependency b was rebuilt ([TIME_DIFF_AFTER_LAST_BUILD]) [COMPILING] a v0.0.1 ([ROOT]/foo/a) [RUNNING] `rustc --crate-name a [..] [DIRTY] foo v0.0.1 ([ROOT]/foo): the dependency b was rebuilt ([TIME_DIFF_AFTER_LAST_BUILD]) [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] src/lib.rs [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn changing_lib_features_caches_targets() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.1" edition = "2015" [features] foo = [] "#, ) .file("src/lib.rs", "") .build(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -Zchecksum-freshness --features foo") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); /* Targets should be cached from the first build */ p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -Zchecksum-freshness --features foo") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn changing_profiles_caches_targets() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.1" edition = "2015" [profile.dev] panic = "abort" "#, ) .file("src/lib.rs", "") .build(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("test -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) [DOCTEST] foo "#]]) .run(); /* Targets should be cached from the first build */ p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("test -Zchecksum-freshness foo") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn changing_bin_paths_common_target_features_caches_targets() { // Make sure dep_cache crate is built once per feature let p = project() .no_manifest() .file( ".cargo/config.toml", r#" [build] target-dir = "./target" "#, ) .file( "dep_crate/Cargo.toml", r#" [package] name = "dep_crate" version = "0.0.1" edition = "2015" authors = [] [features] ftest = [] "#, ) .file( "dep_crate/src/lib.rs", r#" #[cfg(feature = "ftest")] pub fn yo() { println!("ftest on") } #[cfg(not(feature = "ftest"))] pub fn yo() { println!("ftest off") } "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] [dependencies] dep_crate = {path = "../dep_crate", features = []} "#, ) .file("a/src/lib.rs", "") .file( "a/src/main.rs", r#" extern crate dep_crate; use dep_crate::yo; fn main() { yo(); } "#, ) .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.0.1" edition = "2015" authors = [] [dependencies] dep_crate = {path = "../dep_crate", features = ["ftest"]} "#, ) .file("b/src/lib.rs", "") .file( "b/src/main.rs", r#" extern crate dep_crate; use dep_crate::yo; fn main() { yo(); } "#, ) .build(); /* Build and rebuild a/. Ensure dep_crate only builds once */ p.cargo("run -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .cwd("a") .with_stdout_data(str![[r#" ftest off "#]]) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] dep_crate v0.0.1 ([ROOT]/foo/dep_crate) [COMPILING] a v0.0.1 ([ROOT]/foo/a) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/./target/debug/a[EXE]` "#]]) .run(); p.cargo("clean -p a").cwd("a").run(); p.cargo("run -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .cwd("a") .with_stdout_data(str![[r#" ftest off "#]]) .with_stderr_data(str![[r#" [COMPILING] a v0.0.1 ([ROOT]/foo/a) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/./target/debug/a[EXE]` "#]]) .run(); /* Build and rebuild b/. Ensure dep_crate only builds once */ p.cargo("run -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .cwd("b") .with_stdout_data(str![[r#" ftest on "#]]) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] dep_crate v0.0.1 ([ROOT]/foo/dep_crate) [COMPILING] b v0.0.1 ([ROOT]/foo/b) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/./target/debug/b[EXE]` "#]]) .run(); p.cargo("clean -p b").cwd("b").run(); p.cargo("run -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .cwd("b") .with_stdout_data(str![[r#" ftest on "#]]) .with_stderr_data(str![[r#" [COMPILING] b v0.0.1 ([ROOT]/foo/b) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/./target/debug/b[EXE]` "#]]) .run(); /* Build a/ package again. If we cache different feature dep builds correctly, * this should not cause a rebuild of dep_crate */ p.cargo("clean -p a").cwd("a").run(); p.cargo("run -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .cwd("a") .with_stdout_data(str![[r#" ftest off "#]]) .with_stderr_data(str![[r#" [COMPILING] a v0.0.1 ([ROOT]/foo/a) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/./target/debug/a[EXE]` "#]]) .run(); /* Build b/ package again. If we cache different feature dep builds correctly, * this should not cause a rebuild */ p.cargo("clean -p b").cwd("b").run(); p.cargo("run -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .cwd("b") .with_stdout_data(str![[r#" ftest on "#]]) .with_stderr_data(str![[r#" [COMPILING] b v0.0.1 ([ROOT]/foo/b) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/./target/debug/b[EXE]` "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn changing_bin_features_caches_targets() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.1" edition = "2015" [features] foo = [] "#, ) .file( "src/main.rs", r#" fn main() { let msg = if cfg!(feature = "foo") { "feature on" } else { "feature off" }; println!("{}", msg); } "#, ) .build(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.rename_run("foo", "off1") .with_stdout_data(str![[r#" feature off "#]]) .run(); p.cargo("build -Zchecksum-freshness --features foo") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.rename_run("foo", "on1") .with_stdout_data(str![[r#" feature on "#]]) .run(); /* Targets should be cached from the first build */ let mut e = p.cargo("build -Zchecksum-freshness -v"); e.masquerade_as_nightly_cargo(&["checksum-freshness"]); // MSVC does not include hash in binary filename, so it gets recompiled. if cfg!(target_env = "msvc") { e.with_stderr_data(str![[r#" [DIRTY] foo v0.0.1 ([ROOT]/foo): the list of features changed [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); } else { e.with_stderr_data(str![[r#" [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); } e.run(); p.rename_run("foo", "off2") .with_stdout_data(str![[r#" feature off "#]]) .run(); let mut e = p.cargo("build -Zchecksum-freshness --features foo -v"); e.masquerade_as_nightly_cargo(&["checksum-freshness"]); if cfg!(target_env = "msvc") { e.with_stderr_data(str![[r#" [DIRTY] foo v0.0.1 ([ROOT]/foo): the list of features changed [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); } else { e.with_stderr_data(str![[r#" [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); } e.run(); p.rename_run("foo", "on2") .with_stdout_data(str![[r#" feature on "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn rebuild_tests_if_lib_changes() { let p = project() .file("src/lib.rs", "pub fn foo() {}") .file("tests/foo-test.rs", "extern crate foo;") .build(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); p.cargo("test -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); p.change_file("src/lib.rs", ""); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); p.cargo("test -Zchecksum-freshness -v --test foo-test") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [DIRTY] foo v0.0.1 ([ROOT]/foo): the dependency foo was rebuilt ([TIME_DIFF_AFTER_LAST_BUILD]) [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo_test [..]` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/debug/deps/foo_test-[HASH][EXE]` "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn no_rebuild_transitive_target_deps() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] a = { path = "a" } [dev-dependencies] b = { path = "b" } "#, ) .file("src/lib.rs", "") .file("tests/foo.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] [target.foo.dependencies] c = { path = "../c" } "#, ) .file("a/src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.0.1" edition = "2015" authors = [] [dependencies] c = { path = "../c" } "#, ) .file("b/src/lib.rs", "") .file("c/Cargo.toml", &basic_manifest("c", "0.0.1")) .file("c/src/lib.rs", "") .build(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); p.cargo("test -Zchecksum-freshness --no-run") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [COMPILING] c v0.0.1 ([ROOT]/foo/c) [COMPILING] b v0.0.1 ([ROOT]/foo/b) [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [EXECUTABLE] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) [EXECUTABLE] tests/foo.rs (target/debug/deps/foo-[HASH][EXE]) "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn rerun_if_changed_in_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] a = { path = "a" } "#, ) .file("src/lib.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" "#, ) .file( "a/build.rs", r#" fn main() { println!("cargo::rerun-if-changed=build.rs"); } "#, ) .file("a/src/lib.rs", "") .build(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn same_build_dir_cached_packages() { let p = project() .no_manifest() .file( "a1/Cargo.toml", r#" [package] name = "a1" version = "0.0.1" edition = "2015" authors = [] [dependencies] b = { path = "../b" } "#, ) .file("a1/src/lib.rs", "") .file( "a2/Cargo.toml", r#" [package] name = "a2" version = "0.0.1" edition = "2015" authors = [] [dependencies] b = { path = "../b" } "#, ) .file("a2/src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.0.1" edition = "2015" authors = [] [dependencies] c = { path = "../c" } "#, ) .file("b/src/lib.rs", "") .file( "c/Cargo.toml", r#" [package] name = "c" version = "0.0.1" edition = "2015" authors = [] [dependencies] d = { path = "../d" } "#, ) .file("c/src/lib.rs", "") .file("d/Cargo.toml", &basic_manifest("d", "0.0.1")) .file("d/src/lib.rs", "") .file( ".cargo/config.toml", r#" [build] target-dir = "./target" "#, ) .build(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .cwd("a1") .with_stderr_data(str![[r#" [LOCKING] 3 packages to latest compatible versions [COMPILING] d v0.0.1 ([ROOT]/foo/d) [COMPILING] c v0.0.1 ([ROOT]/foo/c) [COMPILING] b v0.0.1 ([ROOT]/foo/b) [COMPILING] a1 v0.0.1 ([ROOT]/foo/a1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .cwd("a2") .with_stderr_data(str![[r#" [LOCKING] 3 packages to latest compatible versions [COMPILING] a2 v0.0.1 ([ROOT]/foo/a2) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn no_rebuild_if_build_artifacts_move_backwards_in_time() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] a = { path = "a" } "#, ) .file("src/lib.rs", "") .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) .file("a/src/lib.rs", "") .build(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); p.root().move_into_the_past(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stdout_data(str![]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn rebuild_if_environment_changes() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" description = "old desc" version = "0.0.1" edition = "2015" authors = [] "#, ) .file( "src/main.rs", r#" fn main() { println!("{}", env!("CARGO_PKG_DESCRIPTION")); } "#, ) .build(); p.cargo("run -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stdout_data(str![[r#" old desc "#]]) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .run(); p.change_file( "Cargo.toml", r#" [package] name = "foo" description = "new desc" version = "0.0.1" edition = "2015" authors = [] "#, ); p.cargo("run -Zchecksum-freshness -v") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stdout_data(str![[r#" new desc "#]]) .with_stderr_data(str![[r#" [DIRTY] foo v0.0.1 ([ROOT]/foo): the environment variable CARGO_PKG_DESCRIPTION changed [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn no_rebuild_when_rename_dir() { let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [workspace] [dependencies] foo = { path = "foo" } "#, ) .file("src/_unused.rs", "") .file("build.rs", "fn main() {}") .file("foo/Cargo.toml", &basic_manifest("foo", "0.0.1")) .file("foo/src/lib.rs", "") .file("foo/build.rs", "fn main() {}") .build(); // make sure the most recently modified file is `src/lib.rs`, not // `Cargo.toml`, to expose a historical bug where we forgot to strip the // `Cargo.toml` path from looking for the package root. fs::write(p.root().join("src/lib.rs"), "").unwrap(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); let mut new = p.root(); new.pop(); new.push("bar"); fs::rename(p.root(), &new).unwrap(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .cwd(&new) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn unused_optional_dep() { Package::new("registry1", "0.1.0").publish(); Package::new("registry2", "0.1.0").publish(); Package::new("registry3", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "p" authors = [] version = "0.1.0" edition = "2015" [dependencies] bar = { path = "bar" } baz = { path = "baz" } registry1 = "*" "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.1" edition = "2015" authors = [] [dev-dependencies] registry2 = "*" "#, ) .file("bar/src/lib.rs", "") .file( "baz/Cargo.toml", r#" [package] name = "baz" version = "0.1.1" edition = "2015" authors = [] [dependencies] registry3 = { version = "*", optional = true } "#, ) .file("baz/src/lib.rs", "") .build(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn path_dev_dep_registry_updates() { Package::new("registry1", "0.1.0").publish(); Package::new("registry2", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "p" authors = [] version = "0.1.0" edition = "2015" [dependencies] bar = { path = "bar" } "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.1" edition = "2015" authors = [] [dependencies] registry1 = "*" [dev-dependencies] baz = { path = "../baz"} "#, ) .file("bar/src/lib.rs", "") .file( "baz/Cargo.toml", r#" [package] name = "baz" version = "0.1.1" edition = "2015" authors = [] [dependencies] registry2 = "*" "#, ) .file("baz/src/lib.rs", "") .build(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn change_panic_mode() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ['bar', 'baz'] [profile.dev] panic = 'abort' "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1")) .file("bar/src/lib.rs", "") .file( "baz/Cargo.toml", r#" [package] name = "baz" version = "0.1.1" edition = "2015" authors = [] [lib] proc-macro = true [dependencies] bar = { path = '../bar' } "#, ) .file("baz/src/lib.rs", "extern crate bar;") .build(); p.cargo("build -Zchecksum-freshness -p bar") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); p.cargo("build -Zchecksum-freshness -p baz") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn dont_rebuild_based_on_plugins() { let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.1.1" edition = "2015" [workspace] members = ['baz'] [dependencies] proc-macro-thing = { path = 'proc-macro-thing' } "#, ) .file("src/lib.rs", "") .file( "proc-macro-thing/Cargo.toml", r#" [package] name = "proc-macro-thing" version = "0.1.1" edition = "2015" [lib] proc-macro = true [dependencies] qux = { path = '../qux' } "#, ) .file("proc-macro-thing/src/lib.rs", "") .file( "baz/Cargo.toml", r#" [package] name = "baz" version = "0.1.1" edition = "2015" [dependencies] qux = { path = '../qux' } "#, ) .file("baz/src/main.rs", "fn main() {}") .file("qux/Cargo.toml", &basic_manifest("qux", "0.1.1")) .file("qux/src/lib.rs", "") .build(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); p.cargo("build -Zchecksum-freshness -p baz") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -Zchecksum-freshness -p bar") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn reuse_workspace_lib() { let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.1.1" edition = "2015" [workspace] [dependencies] baz = { path = 'baz' } "#, ) .file("src/lib.rs", "") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.1")) .file("baz/src/lib.rs", "") .build(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); p.cargo("test -Zchecksum-freshness -p baz -v --no-run") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [COMPILING] baz v0.1.1 ([ROOT]/foo/baz) [RUNNING] `rustc --crate-name baz [..] [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [EXECUTABLE] `[ROOT]/foo/target/debug/deps/baz-[HASH][EXE]` "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn reuse_shared_build_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] shared = {path = "shared"} [workspace] members = ["shared", "bar"] "#, ) .file("src/main.rs", "fn main() {}") .file("shared/Cargo.toml", &basic_manifest("shared", "0.0.1")) .file("shared/src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" [build-dependencies] shared = { path = "../shared" } "#, ) .file("bar/src/lib.rs", "") .file("bar/build.rs", "fn main() {}") .build(); p.cargo("build -Zchecksum-freshness --workspace") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); // This should not recompile! p.cargo("build -Zchecksum-freshness -p foo -v") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [FRESH] shared v0.0.1 ([ROOT]/foo/shared) [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn changing_rustflags_is_cached() { let p = project().file("src/lib.rs", "").build(); // This isn't ever cached, we always have to recompile p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -Zchecksum-freshness -v") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env("RUSTFLAGS", "-C linker=cc") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -Zchecksum-freshness -v") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -Zchecksum-freshness -v") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env("RUSTFLAGS", "-C linker=cc") .with_stderr_data(str![[r#" [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn changing_rustc_extra_flags_is_cached() { let p = project().file("src/lib.rs", "").build(); // This isn't ever cached, we always have to recompile p.cargo("rustc -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("rustc -Zchecksum-freshness -v -- -C linker=cc") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("rustc -Zchecksum-freshness -v") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("rustc -Zchecksum-freshness -v -- -C linker=cc") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn reuse_panic_build_dep_test() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [build-dependencies] bar = { path = "bar" } [dev-dependencies] bar = { path = "bar" } [profile.dev] panic = "abort" "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "") .build(); // Check that `bar` is not built twice. It is only needed once (without `panic`). p.cargo("test -Zchecksum-freshness --lib --no-run -v") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [RUNNING] `rustc --crate-name bar [..] [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name build_script_build [..] [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc --crate-name foo [..] src/lib.rs [..]--test[..] [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [EXECUTABLE] `[ROOT]/foo/target/debug/deps/foo-[HASH][EXE]` "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn reuse_panic_pm() { // foo(panic) -> bar(panic) // somepm(nopanic) -> bar(nopanic) let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = { path = "bar" } somepm = { path = "somepm" } [profile.dev] panic = "abort" "#, ) .file("src/lib.rs", "extern crate bar;") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "") .file( "somepm/Cargo.toml", r#" [package] name = "somepm" version = "0.0.1" edition = "2015" [lib] proc-macro = true [dependencies] bar = { path = "../bar" } "#, ) .file("somepm/src/lib.rs", "extern crate bar;") .build(); // bar is built once without panic (for proc-macro) and once with (for the // normal dependency). p.cargo("build -Zchecksum-freshness -v") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data( str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [RUNNING] `rustc --crate-name bar [..] -C panic=abort [..] [RUNNING] `rustc --crate-name bar [..] [COMPILING] somepm v0.0.1 ([ROOT]/foo/somepm) [RUNNING] `rustc --crate-name somepm [..] [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] -C panic=abort [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn bust_patched_dep() { Package::new("registry1", "0.1.0").publish(); Package::new("registry2", "0.1.0") .dep("registry1", "0.1.0") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] registry2 = "0.1.0" [patch.crates-io] registry1 = { path = "reg1new" } "#, ) .file("src/lib.rs", "") .file("reg1new/Cargo.toml", &basic_manifest("registry1", "0.1.0")) .file("reg1new/src/lib.rs", "") .build(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); p.change_file("reg1new/src/lib.rs", "// modified"); p.cargo("build -Zchecksum-freshness -v") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [DIRTY] registry1 v0.1.0 ([ROOT]/foo/reg1new): file size changed (0 != 11) for `reg1new/src/lib.rs` [COMPILING] registry1 v0.1.0 ([ROOT]/foo/reg1new) [RUNNING] `rustc --crate-name registry1 [..] [DIRTY] registry2 v0.1.0: the dependency registry1 was rebuilt [COMPILING] registry2 v0.1.0 [RUNNING] `rustc --crate-name registry2 [..] [DIRTY] foo v0.0.1 ([ROOT]/foo): the dependency registry2 was rebuilt [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -Zchecksum-freshness -v") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [FRESH] registry1 v0.1.0 ([ROOT]/foo/reg1new) [FRESH] registry2 v0.1.0 [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn rebuild_on_mid_build_file_modification() { let server = TcpListener::bind("127.0.0.1:0").unwrap(); let addr = server.local_addr().unwrap(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["root", "proc_macro_dep"] "#, ) .file( "root/Cargo.toml", r#" [package] name = "root" version = "0.1.0" edition = "2015" authors = [] [dependencies] proc_macro_dep = { path = "../proc_macro_dep" } "#, ) .file( "root/src/lib.rs", r#" #[macro_use] extern crate proc_macro_dep; #[derive(Noop)] pub struct X; "#, ) .file( "proc_macro_dep/Cargo.toml", r#" [package] name = "proc_macro_dep" version = "0.1.0" edition = "2015" authors = [] [lib] proc-macro = true "#, ) .file( "proc_macro_dep/src/lib.rs", &format!( r#" extern crate proc_macro; use std::io::Read; use std::net::TcpStream; use proc_macro::TokenStream; #[proc_macro_derive(Noop)] pub fn noop(_input: TokenStream) -> TokenStream {{ let mut stream = TcpStream::connect("{}").unwrap(); let mut v = Vec::new(); stream.read_to_end(&mut v).unwrap(); "".parse().unwrap() }} "#, addr ), ) .build(); let root = p.root(); let t = thread::spawn(move || { let socket = server.accept().unwrap().0; let mut file = OpenOptions::new() .write(true) .append(true) .open(root.join("root/src/lib.rs")) .unwrap(); writeln!(file, "// modified").expect("Failed to append to root sources"); drop(file); drop(socket); drop(server.accept().unwrap()); }); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [COMPILING] proc_macro_dep v0.1.0 ([ROOT]/foo/proc_macro_dep) [COMPILING] root v0.1.0 ([ROOT]/foo/root) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -Zchecksum-freshness -v") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [FRESH] proc_macro_dep v0.1.0 ([ROOT]/foo/proc_macro_dep) [DIRTY] root v0.1.0 ([ROOT]/foo/root): file size changed (150 != 162) for `root/src/lib.rs` [COMPILING] root v0.1.0 ([ROOT]/foo/root) [RUNNING] `rustc --crate-name root [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); t.join().ok().unwrap(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn dirty_both_lib_and_test() { // This tests that all artifacts that depend on the results of a build // script will get rebuilt when the build script reruns, even for separate // commands. It does the following: // // 1. Project "foo" has a build script which will compile a small // staticlib to link against. Normally this would use the `cc` crate, // but here we just use rustc to avoid the `cc` dependency. // 2. Build the library. // 3. Build the unit test. The staticlib intentionally has a bad value. // 4. Rewrite the staticlib with the correct value. // 5. Build the library again. // 6. Build the unit test. This should recompile. let slib = |n| { format!( r#" #[no_mangle] pub extern "C" fn doit() -> i32 {{ return {}; }} "#, n ) }; let p = project() .file( "src/lib.rs", r#" extern "C" { fn doit() -> i32; } #[test] fn t1() { assert_eq!(unsafe { doit() }, 1, "doit assert failure"); } "#, ) .file( "build.rs", r#" use std::env; use std::path::PathBuf; use std::process::Command; fn main() { let rustc = env::var_os("RUSTC").unwrap(); let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); assert!( Command::new(rustc) .args(&[ "--crate-type=staticlib", "--out-dir", out_dir.to_str().unwrap(), "slib.rs" ]) .status() .unwrap() .success(), "slib build failed" ); println!("cargo::rustc-link-lib=slib"); println!("cargo::rustc-link-search={}", out_dir.display()); } "#, ) .file("slib.rs", &slib(2)) .build(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); // 2 != 1 p.cargo("test -Zchecksum-freshness --lib") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_status(101) .with_stdout_data("...\n[..]doit assert failure[..]\n...") .run(); // Fix the mistake. p.change_file("slib.rs", &slib(1)); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); // This should recompile with the new static lib, and the test should pass. p.cargo("test -Zchecksum-freshness --lib") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn script_fails_stay_dirty() { // Check if a script is aborted (such as hitting Ctrl-C) that it will re-run. // Steps: // 1. Build to establish fingerprints. // 2. Make a change that triggers the build script to re-run. Abort the // script while it is running. // 3. Run the build again and make sure it re-runs the script. let p = project() .file( "build.rs", r#" mod helper; fn main() { println!("cargo::rerun-if-changed=build.rs"); helper::doit(); } "#, ) .file("helper.rs", "pub fn doit() {}") .file("src/lib.rs", "") .build(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); p.change_file("helper.rs", r#"pub fn doit() {panic!("Crash!");}"#); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data("...\n[..]Crash![..]\n...") .with_status(101) .run(); // There was a bug where this second call would be "fresh". p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data("...\n[..]Crash![..]\n...") .with_status(101) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn edition_change_invalidates() { const MANIFEST: &str = r#" [package] name = "foo" version = "0.1.0" "#; let p = project() .file("Cargo.toml", MANIFEST) .file("src/lib.rs", "") .build(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); p.change_file("Cargo.toml", &format!("{}edition = \"2018\"", MANIFEST)); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.change_file( "Cargo.toml", &format!( r#"{}edition = "2018" [lib] edition = "2015" "#, MANIFEST ), ); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [WARNING] `edition` is set on library `foo` which is deprecated [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -Zchecksum-freshness -v") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [WARNING] `edition` is set on library `foo` which is deprecated [FRESH] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert_eq!(p.glob("target/debug/deps/libfoo-*.rlib").count(), 1); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn rename_with_path_deps() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies] a = { path = 'a' } "#, ) .file("src/lib.rs", "extern crate a; pub fn foo() { a::foo(); }") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.5.0" edition = "2015" authors = [] [dependencies] b = { path = 'b' } "#, ) .file("a/src/lib.rs", "extern crate b; pub fn foo() { b::foo() }") .file( "a/b/Cargo.toml", r#" [package] name = "b" version = "0.5.0" edition = "2015" authors = [] "#, ) .file("a/b/src/lib.rs", "pub fn foo() { }"); let p = p.build(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); // Now rename the root directory and rerun `cargo run`. Not only should we // not build anything but we also shouldn't crash. let mut new = p.root(); new.pop(); new.push("foo2"); fs::rename(p.root(), &new).unwrap(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .cwd(&new) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn move_target_directory_with_path_deps() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies] a = { path = "a" } "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.5.0" edition = "2015" authors = [] "#, ) .file("src/lib.rs", "extern crate a; pub use a::print_msg;") .file( "a/build.rs", r###" use std::env; use std::fs; use std::path::Path; fn main() { println!("cargo::rerun-if-changed=build.rs"); let out_dir = env::var("OUT_DIR").unwrap(); let dest_path = Path::new(&out_dir).join("hello.rs"); fs::write(&dest_path, r#" pub fn message() -> &'static str { "Hello, World!" } "#).unwrap(); } "###, ) .file( "a/src/lib.rs", r#" include!(concat!(env!("OUT_DIR"), "/hello.rs")); pub fn print_msg() { message(); } "#, ); let p = p.build(); let mut parent = p.root(); parent.pop(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); let new_target = p.root().join("target2"); fs::rename(p.root().join("target"), &new_target).unwrap(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env("CARGO_TARGET_DIR", &new_target) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn rerun_if_changes() { let p = project() .file( "build.rs", r#" fn main() { println!("cargo::rerun-if-env-changed=FOO"); if std::env::var("FOO").is_ok() { println!("cargo::rerun-if-env-changed=BAR"); } } "#, ) .file("src/lib.rs", "") .build(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -Zchecksum-freshness -v") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env("FOO", "1") .with_stderr_data(str![[r#" [DIRTY] foo v0.0.1 ([ROOT]/foo): the env variable FOO changed [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env("FOO", "1") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -Zchecksum-freshness -v") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env("FOO", "1") .env("BAR", "1") .with_stderr_data(str![[r#" [DIRTY] foo v0.0.1 ([ROOT]/foo): the env variable BAR changed [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env("FOO", "1") .env("BAR", "1") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -Zchecksum-freshness -v") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env("BAR", "2") .with_stderr_data(str![[r#" [DIRTY] foo v0.0.1 ([ROOT]/foo): the env variable FOO changed [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env("BAR", "2") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn channel_shares_filenames() { // Test that different "nightly" releases use the same output filename. // Create separate rustc binaries to emulate running different toolchains. let nightly1 = format!( "\ rustc 1.44.0-nightly (38114ff16 2020-03-21) binary: rustc commit-hash: 38114ff16e7856f98b2b4be7ab4cd29b38bed59a commit-date: 2020-03-21 host: {} release: 1.44.0-nightly LLVM version: 9.0 ", rustc_host() ); let nightly2 = format!( "\ rustc 1.44.0-nightly (a5b09d354 2020-03-31) binary: rustc commit-hash: a5b09d35473615e7142f5570f5c5fad0caf68bd2 commit-date: 2020-03-31 host: {} release: 1.44.0-nightly LLVM version: 9.0 ", rustc_host() ); let beta1 = format!( "\ rustc 1.43.0-beta.3 (4c587bbda 2020-03-25) binary: rustc commit-hash: 4c587bbda04ab55aaf56feab11dfdfe387a85d7a commit-date: 2020-03-25 host: {} release: 1.43.0-beta.3 LLVM version: 9.0 ", rustc_host() ); let beta2 = format!( "\ rustc 1.42.0-beta.5 (4e1c5f0e9 2020-02-28) binary: rustc commit-hash: 4e1c5f0e9769a588b91c977e3d81e140209ef3a2 commit-date: 2020-02-28 host: {} release: 1.42.0-beta.5 LLVM version: 9.0 ", rustc_host() ); let stable1 = format!( "\ rustc 1.42.0 (b8cedc004 2020-03-09) binary: rustc commit-hash: b8cedc00407a4c56a3bda1ed605c6fc166655447 commit-date: 2020-03-09 host: {} release: 1.42.0 LLVM version: 9.0 ", rustc_host() ); let stable2 = format!( "\ rustc 1.41.1 (f3e1a954d 2020-02-24) binary: rustc commit-hash: f3e1a954d2ead4e2fc197c7da7d71e6c61bad196 commit-date: 2020-02-24 host: {} release: 1.41.1 LLVM version: 9.0 ", rustc_host() ); let compiler = project() .at("compiler") .file("Cargo.toml", &basic_manifest("compiler", "0.1.0")) .file( "src/main.rs", r#" fn main() { if std::env::args_os().any(|a| a == "-vV") { print!("{}", env!("FUNKY_VERSION_TEST")); return; } let mut cmd = std::process::Command::new("rustc"); cmd.args(std::env::args_os().skip(1)); assert!(cmd.status().unwrap().success()); } "#, ) .build(); let makeit = |version, vv| { // Force a rebuild. compiler.target_debug_dir().join("deps").rm_rf(); compiler .cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env("FUNKY_VERSION_TEST", vv) .run(); fs::rename(compiler.bin("compiler"), compiler.bin(version)).unwrap(); }; makeit("nightly1", nightly1); makeit("nightly2", nightly2); makeit("beta1", beta1); makeit("beta2", beta2); makeit("stable1", stable1); makeit("stable2", stable2); // Run `cargo check` with different rustc versions to observe its behavior. let p = project().file("src/lib.rs", "").build(); // Runs `cargo check` and returns the rmeta filename created. // Checks that the freshness matches the given value. let check = |version, fresh| -> String { let output = p .cargo("check -Zchecksum-freshness --message-format=json") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env("RUSTC", compiler.bin(version)) .run(); // Collect the filenames generated. let mut artifacts: Vec<_> = std::str::from_utf8(&output.stdout) .unwrap() .lines() .filter_map(|line| { let value: serde_json::Value = serde_json::from_str(line).unwrap(); if value["reason"].as_str().unwrap() == "compiler-artifact" { assert_eq!(value["fresh"].as_bool().unwrap(), fresh); let filenames = value["filenames"].as_array().unwrap(); assert_eq!(filenames.len(), 1); Some(filenames[0].to_string()) } else { None } }) .collect(); // Should only generate one rmeta file. assert_eq!(artifacts.len(), 1); artifacts.pop().unwrap() }; let nightly1_name = check("nightly1", false); assert_eq!(check("nightly1", true), nightly1_name); assert_eq!(check("nightly2", false), nightly1_name); // same as before assert_eq!(check("nightly2", true), nightly1_name); // Should rebuild going back to nightly1. assert_eq!(check("nightly1", false), nightly1_name); let beta1_name = check("beta1", false); assert_ne!(beta1_name, nightly1_name); assert_eq!(check("beta1", true), beta1_name); assert_eq!(check("beta2", false), beta1_name); // same as before assert_eq!(check("beta2", true), beta1_name); // Should rebuild going back to beta1. assert_eq!(check("beta1", false), beta1_name); let stable1_name = check("stable1", false); assert_ne!(stable1_name, nightly1_name); assert_ne!(stable1_name, beta1_name); let stable2_name = check("stable2", false); assert_ne!(stable1_name, stable2_name); // Check everything is fresh. assert_eq!(check("stable1", true), stable1_name); assert_eq!(check("stable2", true), stable2_name); assert_eq!(check("beta1", true), beta1_name); assert_eq!(check("nightly1", true), nightly1_name); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn linking_interrupted() { // Interrupt during the linking phase shouldn't leave test executable as "fresh". // This is used to detect when linking starts, then to pause the linker so // that the test can kill cargo. let link_listener = TcpListener::bind("127.0.0.1:0").unwrap(); let link_addr = link_listener.local_addr().unwrap(); // This is used to detect when rustc exits. let rustc_listener = TcpListener::bind("127.0.0.1:0").unwrap(); let rustc_addr = rustc_listener.local_addr().unwrap(); // Create a linker that we can interrupt. let linker = project() .at("linker") .file("Cargo.toml", &basic_manifest("linker", "1.0.0")) .file( "src/main.rs", &r#" fn main() { // Figure out the output filename. let output = match std::env::args().find(|a| a.starts_with("/OUT:")) { Some(s) => s[5..].to_string(), None => { let mut args = std::env::args(); loop { if args.next().unwrap() == "-o" { break; } } args.next().unwrap() } }; std::fs::remove_file(&output).unwrap(); std::fs::write(&output, "").unwrap(); // Tell the test that we are ready to be interrupted. let mut socket = std::net::TcpStream::connect("__ADDR__").unwrap(); // Wait for the test to kill us. std::thread::sleep(std::time::Duration::new(60, 0)); } "# .replace("__ADDR__", &link_addr.to_string()), ) .build(); linker .cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); // Create a wrapper around rustc that will tell us when rustc is finished. let rustc = project() .at("rustc-waiter") .file("Cargo.toml", &basic_manifest("rustc-waiter", "1.0.0")) .file( "src/main.rs", &r#" fn main() { let mut conn = None; // Check for a normal build (not -vV or --print). if std::env::args().any(|arg| arg == "t1") { // Tell the test that rustc has started. conn = Some(std::net::TcpStream::connect("__ADDR__").unwrap()); } let status = std::process::Command::new("rustc") .args(std::env::args().skip(1)) .status() .expect("rustc to run"); std::process::exit(status.code().unwrap_or(1)); } "# .replace("__ADDR__", &rustc_addr.to_string()), ) .build(); rustc .cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); // Build it once so that the fingerprint gets saved to disk. let p = project() .file("src/lib.rs", "") .file("tests/t1.rs", "") .build(); p.cargo("test -Zchecksum-freshness --test t1 --no-run") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); // Make a change, start a build, then interrupt it. p.change_file("src/lib.rs", "// modified"); let linker_env = format!("CARGO_TARGET_{}_LINKER", rustc_host_env()); // NOTE: This assumes that the paths to the linker or rustc are not in the // fingerprint. But maybe they should be? let mut cmd = p .cargo("test -Zchecksum-freshness --test t1 --no-run") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env(&linker_env, linker.bin("linker")) .env("RUSTC", rustc.bin("rustc-waiter")) .build_command(); let mut child = cmd .stdout(Stdio::null()) .stderr(Stdio::null()) .env("__CARGO_TEST_SETSID_PLEASE_DONT_USE_ELSEWHERE", "1") .spawn() .unwrap(); // Wait for rustc to start. let mut rustc_conn = rustc_listener.accept().unwrap().0; // Wait for linking to start. drop(link_listener.accept().unwrap()); // Interrupt the child. death::ctrl_c(&mut child); assert!(!child.wait().unwrap().success()); // Wait for rustc to exit. If we don't wait, then the command below could // start while rustc is still being torn down. let mut buf = [0]; drop(rustc_conn.read_exact(&mut buf)); // Build again, shouldn't be fresh. p.cargo("test -Zchecksum-freshness --test t1 -v") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [DIRTY] foo v0.0.1 ([ROOT]/foo): the config settings changed [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] [RUNNING] `rustc --crate-name t1 [..] [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/debug/deps/t1-[HASH][EXE]` "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] #[cfg(all(target_arch = "x86_64", target_os = "windows", target_env = "msvc"))] fn lld_is_fresh() { // Check for bug when using lld linker that it remains fresh with dylib. let p = project() .file( ".cargo/config.toml", r#" [target.x86_64-pc-windows-msvc] linker = "rust-lld" "#, ) .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [lib] crate-type = ["dylib"] "#, ) .file("src/lib.rs", "") .build(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); p.cargo("build -Zchecksum-freshness -v") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [FRESH] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn env_in_code_causes_rebuild() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" "#, ) .file( "src/main.rs", r#" fn main() { println!("{:?}", option_env!("FOO")); println!("{:?}", option_env!("FOO\nBAR")); } "#, ) .build(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env_remove("FOO") .run(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env_remove("FOO") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -Zchecksum-freshness -v") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env("FOO", "bar") .with_stderr_data(str![[r#" [DIRTY] foo v0.1.0 ([ROOT]/foo): the environment variable FOO changed [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env("FOO", "bar") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -Zchecksum-freshness -v") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env("FOO", "baz") .with_stderr_data(str![[r#" [DIRTY] foo v0.1.0 ([ROOT]/foo): the environment variable FOO changed [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env("FOO", "baz") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -Zchecksum-freshness -v") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env_remove("FOO") .with_stderr_data(str![[r#" [DIRTY] foo v0.1.0 ([ROOT]/foo): the environment variable FOO changed [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env_remove("FOO") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let interesting = " #!$\nabc\r\\\t\u{8}\r\n"; p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env("FOO", interesting) .run(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env("FOO", interesting) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env("FOO\nBAR", interesting) .run(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env("FOO\nBAR", interesting) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn env_build_script_no_rebuild() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" "#, ) .file( "build.rs", r#" fn main() { println!("cargo::rustc-env=FOO=bar"); } "#, ) .file( "src/main.rs", r#" fn main() { println!("{:?}", env!("FOO")); } "#, ) .build(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn changing_linker() { // Changing linker should rebuild. let p = project().file("src/main.rs", "fn main() {}").build(); p.cargo("build -Zchecksum-freshness") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .run(); let linker_env = format!("CARGO_TARGET_{}_LINKER", rustc_host_env()); p.cargo("build -Zchecksum-freshness --verbose") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env(&linker_env, "nonexistent-linker") .with_status(101) .with_stderr_data(str![[r#" [DIRTY] foo v0.0.1 ([ROOT]/foo): the config settings changed [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] -C linker=nonexistent-linker [..]` [ERROR] linker `nonexistent-linker` not found ... "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn verify_source_before_recompile() { Package::new("bar", "0.1.0") .file("src/lib.rs", "") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("vendor --respect-source-config").run(); p.change_file( ".cargo/config.toml", r#" [source.crates-io] replace-with = 'vendor' [source.vendor] directory = 'vendor' "#, ); // Sanity check: vendoring works correctly. p.cargo("check -Zchecksum-freshness --verbose") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [CHECKING] bar v0.1.0 [RUNNING] `rustc --crate-name bar [..] [ROOT]/foo/vendor/bar/src/lib.rs [..] [CHECKING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] src/lib.rs [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Now modify vendored crate. p.change_file( "vendor/bar/src/lib.rs", r#"compile_error!("You shall not pass!");"#, ); // Should ignore modified sources without any recompile. p.cargo("check -Zchecksum-freshness --verbose") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .with_stderr_data(str![[r#" [FRESH] bar v0.1.0 [FRESH] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Add a `RUSTFLAGS` to trigger a recompile. // // Cargo should refuse to build because of checksum verification failure. // Cargo shouldn't recompile dependency `bar`. p.cargo("check -Zchecksum-freshness --verbose") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env("RUSTFLAGS", "-W warnings") .with_status(101) .with_stderr_data(str![[r#" [ERROR] the listed checksum of `[ROOT]/foo/vendor/bar/src/lib.rs` has changed: expected: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 actual: 66e843918c1d4ea8231af814f9f958958808249d4407de01114acb730ecd9bdf directory sources are not intended to be edited, if modifications are required then it is recommended that `[patch]` is used with a forked copy of the source "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn skip_checksum_check_in_selected_cargo_home_subdirs() { let p = project() .at("cargo_home/registry/foo") .file("Cargo.toml", &basic_lib_manifest("foo")) .file("src/lib.rs", "") .build(); let project_root = p.root(); let cargo_home = project_root.parent().unwrap().parent().unwrap(); p.cargo("check -Zchecksum-freshness -v") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env("CARGO_HOME", &cargo_home) .with_stderr_data(str![[r#" [CHECKING] foo v0.5.0 ([ROOT]/cargo_home/registry/foo) [RUNNING] `rustc --crate-name foo [..] src/lib.rs [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.change_file("src/lib.rs", "illegal syntax"); p.cargo("check -Zchecksum-freshness -v") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env("CARGO_HOME", &cargo_home) .with_stderr_data(str![[r#" [FRESH] foo v0.5.0 ([ROOT]/cargo_home/registry/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn use_checksum_cache_in_cargo_home() { let p = project() .at("cargo_home/foo") .file("Cargo.toml", &basic_lib_manifest("foo")) .file("src/lib.rs", "") .build(); let project_root = p.root(); let cargo_home = project_root.parent().unwrap(); p.cargo("check -Zchecksum-freshness -v") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env("CARGO_HOME", &cargo_home) .with_stderr_data(str![[r#" [CHECKING] foo v0.5.0 ([ROOT]/cargo_home/foo) [RUNNING] `rustc --crate-name foo [..] src/lib.rs [..] src/lib.rs [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.change_file("src/lib.rs", "illegal syntax"); p.cargo("check -Zchecksum-freshness -v") .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env("CARGO_HOME", &cargo_home) .with_status(101) .with_stderr_data(str![[r#" [DIRTY] foo v0.5.0 ([ROOT]/cargo_home/foo): file size changed (0 != 14) for `src/lib.rs` [CHECKING] foo v0.5.0 ([ROOT]/cargo_home/foo) [RUNNING] `rustc --crate-name foo [..] src/lib.rs [..] ... [ERROR] could not compile `foo` (lib) due to 1 previous error ... "#]]) .run(); } cargo-0.91.0/tests/testsuite/future_incompat_report.rs000064400000000000000000000642751046102023000213640ustar 00000000000000//! Tests for future-incompat-report messages //! //! Note that these tests use the -Zfuture-incompat-test for rustc. //! This causes rustc to treat *every* lint as future-incompatible. //! This is done because future-incompatible lints are inherently //! ephemeral, but we don't want to continually update these tests. //! So we pick some random lint that will likely always be the same //! over time. use crate::prelude::*; use cargo_test_support::registry::Package; use cargo_test_support::{Project, basic_manifest, project, str}; use super::config::write_config_toml; // An arbitrary lint (unused_variables) that triggers a lint. // We use a special flag to force it to generate a report. const FUTURE_EXAMPLE: &'static str = "pub fn foo() { let x = 1; }"; // Some text that will be displayed when the lint fires. const FUTURE_OUTPUT: &'static str = "[..]unused variable[..]"; /// A project with a future-incompat error in the local package. fn local_project() -> Project { project() .file("Cargo.toml", &basic_manifest("foo", "0.0.0")) .file("src/lib.rs", FUTURE_EXAMPLE) .build() } /// A project with a future-incompat error in a dependency. fn dependency_project() -> Project { Package::new("bar", "1.0.0") .file( "Cargo.toml", r#" [package] name = "bar" version = "1.0.0" edition = "2015" repository = "https://example.com/" "#, ) .file("src/lib.rs", FUTURE_EXAMPLE) .publish(); project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" edition = "2015" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build() } #[cargo_test( nightly, reason = "-Zfuture-incompat-test requires nightly (permanently)" )] fn incompat_in_local_crate() { // A simple example where a local crate triggers a future-incompatibility warning. let p = local_project(); p.cargo("check") .env("RUSTFLAGS", "-Zfuture-incompat-test") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.0 ([ROOT]/foo) [WARNING] unused variable: `x` ... [WARNING] `foo` (lib) generated 1 warning [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [WARNING] the following packages contain code that will be rejected by a future version of Rust: foo v0.0.0 ([ROOT]/foo) [NOTE] to see what the problems were, use the option `--future-incompat-report`, or run `cargo report future-incompatibilities --id 1` "#]]) .run(); p.cargo("check --future-incompat-report") .env("RUSTFLAGS", "-Zfuture-incompat-test") .with_stderr_data(str![[r#" [WARNING] unused variable: `x` ... [WARNING] `foo` (lib) generated 1 warning [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [WARNING] the following packages contain code that will be rejected by a future version of Rust: foo v0.0.0 ([ROOT]/foo) [NOTE] this report can be shown with `cargo report future-incompatibilities --id 1` "#]]) .run(); p.cargo("report future-incompatibilities --id 1") .with_stdout_data(str![[r#" The following warnings were discovered during the build. These warnings are an indication that the packages contain code that will become an error in a future release of Rust. These warnings typically cover changes to close soundness problems, unintended or undocumented behavior, or critical problems that cannot be fixed in a backwards-compatible fashion, and are not expected to be in wide use. Each warning should contain a link for more information on what the warning means and how to resolve it. The package `foo v0.0.0 ([ROOT]/foo)` currently triggers the following future incompatibility lints: > [WARNING] unused variable: `x` ... "#]]) .run(); } #[cargo_test( nightly, reason = "-Zfuture-incompat-test requires nightly (permanently)" )] fn incompat_in_dependency() { // A simple example where a remote dependency triggers a future-incompatibility warning. let p = dependency_project(); p.cargo("check") .env("RUSTFLAGS", "-Zfuture-incompat-test") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [CHECKING] bar v1.0.0 [CHECKING] foo v1.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [WARNING] the following packages contain code that will be rejected by a future version of Rust: bar v1.0.0 [NOTE] to see what the problems were, use the option `--future-incompat-report`, or run `cargo report future-incompatibilities --id 1` "#]]) .run(); p.cargo("check --future-incompat-report") .env("RUSTFLAGS", "-Zfuture-incompat-test") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [WARNING] the following packages contain code that will be rejected by a future version of Rust: bar v1.0.0 [NOTE] To solve this problem, you can try the following approaches: - If the issue is not solved by updating the dependencies, a fix has to be implemented by those dependencies. You can help with that by notifying the maintainers of this problem (e.g. by creating a bug report) or by proposing a fix to the maintainers (e.g. by creating a pull request): - bar@1.0.0 - Repository: https://example.com/ - Detailed warning command: `cargo report future-incompatibilities --id 1 --package bar@1.0.0` - If waiting for an upstream fix is not an option, you can use the `[patch]` section in `Cargo.toml` to use your own version of the dependency. For more information, see: https://doc.rust-lang.org/cargo/reference/overriding-dependencies.html#the-patch-section [NOTE] this report can be shown with `cargo report future-incompatibilities --id 1` "#]]) .run(); p.cargo("report future-incompatibilities --id 1") .with_stdout_data(str![[r#" The following warnings were discovered during the build. These warnings are an indication that the packages contain code that will become an error in a future release of Rust. These warnings typically cover changes to close soundness problems, unintended or undocumented behavior, or critical problems that cannot be fixed in a backwards-compatible fashion, and are not expected to be in wide use. Each warning should contain a link for more information on what the warning means and how to resolve it. To solve this problem, you can try the following approaches: - If the issue is not solved by updating the dependencies, a fix has to be implemented by those dependencies. You can help with that by notifying the maintainers of this problem (e.g. by creating a bug report) or by proposing a fix to the maintainers (e.g. by creating a pull request): - bar@1.0.0 - Repository: https://example.com/ - Detailed warning command: `cargo report future-incompatibilities --id 1 --package bar@1.0.0` - If waiting for an upstream fix is not an option, you can use the `[patch]` section in `Cargo.toml` to use your own version of the dependency. For more information, see: https://doc.rust-lang.org/cargo/reference/overriding-dependencies.html#the-patch-section The package `bar v1.0.0` currently triggers the following future incompatibility lints: > [WARNING] unused variable: `x` ... "#]]) .run(); } // This feature is stable, and should not be gated #[cargo_test] fn no_gate_future_incompat_report() { let p = local_project(); p.cargo("check --future-incompat-report") .with_status(0) .run(); p.cargo("report future-incompatibilities --id foo") .with_stderr_data(str![[r#" [ERROR] no reports are currently available "#]]) .with_status(101) .run(); } #[cargo_test( nightly, reason = "-Zfuture-incompat-test requires nightly (permanently)" )] fn test_zero_future_incompat() { let p = project() .file("Cargo.toml", &basic_manifest("foo", "0.0.0")) .file("src/main.rs", "fn main() {}") .build(); // No note if --future-incompat-report is not specified. p.cargo("check") .env("RUSTFLAGS", "-Zfuture-incompat-test") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check --future-incompat-report") .env("RUSTFLAGS", "-Zfuture-incompat-test") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [NOTE] 0 dependencies had future-incompatible warnings "#]]) .run(); } #[cargo_test( nightly, reason = "-Zfuture-incompat-test requires nightly (permanently)" )] fn test_single_crate() { let p = local_project(); for command in &["build", "check", "rustc", "test"] { let check_has_future_compat = || { p.cargo(command) .env("RUSTFLAGS", "-Zfuture-incompat-test") .with_stderr_data("\ ... [WARNING] unused variable: `x` ... [WARNING] the following packages contain code that will be rejected by a future version of Rust: foo v0.0.0 ([ROOT]/foo) ... ") .with_stderr_does_not_contain("[..]incompatibility[..]") .run(); }; // Check that we show a message with no [future-incompat-report] config section write_config_toml(""); check_has_future_compat(); // Check that we show a message with `frequency = "always"` write_config_toml( "\ [future-incompat-report] frequency = 'always' ", ); check_has_future_compat(); // Check that we do not show a message with `frequency = "never"` write_config_toml( "\ [future-incompat-report] frequency = 'never' ", ); p.cargo(command) .env("RUSTFLAGS", "-Zfuture-incompat-test") .with_stderr_data( "\ [WARNING] unused variable: `x` ... ", ) .with_stderr_does_not_contain("[..]rejected[..]") .with_stderr_does_not_contain("[..]incompatibility[..]") .run(); // Check that passing `--future-incompat-report` overrides `frequency = 'never'` p.cargo(command).arg("--future-incompat-report") .env("RUSTFLAGS", "-Zfuture-incompat-test") .with_stderr_data("\ [WARNING] unused variable: `x` ... [WARNING] the following packages contain code that will be rejected by a future version of Rust: foo v0.0.0 ([ROOT]/foo) ... [NOTE] this report can be shown with `cargo report future-incompatibilities --id [..]` ... ") .run(); } } #[cargo_test( nightly, reason = "-Zfuture-incompat-test requires nightly (permanently)" )] fn test_multi_crate() { Package::new("first-dep", "0.0.1") .file("src/lib.rs", FUTURE_EXAMPLE) .publish(); Package::new("second-dep", "0.0.2") .file("src/lib.rs", FUTURE_EXAMPLE) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" [dependencies] first-dep = "*" second-dep = "*" "#, ) .file("src/lib.rs", "") .build(); for command in &["build", "check", "rustc", "test"] { p.cargo(command) .env("RUSTFLAGS", "-Zfuture-incompat-test") .with_stderr_does_not_contain(FUTURE_OUTPUT) .with_stderr_data("\ ... [WARNING] the following packages contain code that will be rejected by a future version of Rust: first-dep v0.0.1, second-dep v0.0.2 ... ") // Check that we don't have the 'triggers' message shown at the bottom of this loop, // and that we don't explain how to show a per-package report .with_stderr_does_not_contain("[..]triggers[..]") .with_stderr_does_not_contain("[..]--package[..]") .with_stderr_does_not_contain("[..]-p[..]") .run(); p.cargo(command).arg("--future-incompat-report") .env("RUSTFLAGS", "-Zfuture-incompat-test") .with_stderr_data("\ ... [WARNING] the following packages contain code that will be rejected by a future version of Rust: first-dep v0.0.1, second-dep v0.0.2 ... - first-dep@0.0.1 ... - second-dep@0.0.2 ... ") .run(); p.cargo("report future-incompatibilities") .arg("--package") .arg("first-dep@0.0.1") .with_stdout_data( "\ ... The package `first-dep v0.0.1` currently triggers the following future incompatibility lints: > [WARNING] unused variable: `x` ... ", ) .with_stdout_does_not_contain("[..]second-dep-0.0.2/src[..]") .run(); p.cargo("report future-incompatibilities") .arg("--package") .arg("second-dep@0.0.2") .with_stdout_data( "\ ... The package `second-dep v0.0.2` currently triggers the following future incompatibility lints: > [WARNING] unused variable: `x` ... ", ) .with_stdout_does_not_contain("[..]first-dep-0.0.1/src[..]") .run(); } // Test that passing the correct id via '--id' doesn't generate a warning message let output = p .cargo("check") .env("RUSTFLAGS", "-Zfuture-incompat-test") .run(); // Extract the 'id' from the stdout. We are looking // for the id in a line of the form "run `cargo report future-incompatibilities --id yZ7S`" // which is generated by Cargo to tell the user what command to run // This is just to test that passing the id suppresses the warning mesasge. Any users needing // access to the report from a shell script should use the `--future-incompat-report` flag let stderr = std::str::from_utf8(&output.stderr).unwrap(); // Find '--id ' in the output let mut iter = stderr.split(' '); iter.find(|w| *w == "--id").unwrap(); let id = iter .next() .unwrap_or_else(|| panic!("Unexpected output:\n{}", stderr)); // Strip off the trailing '`' included in the output let id: String = id.chars().take_while(|c| *c != '`').collect(); p.cargo(&format!("report future-incompatibilities --id {}", id)) .with_stdout_data(str![[r#" ... The package `first-dep v0.0.1` currently triggers the following future incompatibility lints: ... The package `second-dep v0.0.2` currently triggers the following future incompatibility lints: ... "#]]) .run(); // Test without --id, and also the full output of the report. let output = p.cargo("report future-incompat").run(); let output = std::str::from_utf8(&output.stdout).unwrap(); assert!(output.starts_with("The following warnings were discovered")); let mut lines = output .lines() // Skip the beginning of the per-package information. .skip_while(|line| !line.starts_with("The package")); for expected in &["first-dep v0.0.1", "second-dep v0.0.2"] { assert_eq!( &format!( "The package `{}` currently triggers the following future incompatibility lints:", expected ), lines.next().unwrap(), "Bad output:\n{}", output ); let mut count = 0; while let Some(line) = lines.next() { if line.is_empty() { break; } count += 1; } assert!(count > 0); } assert_eq!(lines.next(), None); } #[cargo_test( nightly, reason = "-Zfuture-incompat-test requires nightly (permanently)" )] fn color() { let p = local_project(); p.cargo("check") .env("RUSTFLAGS", "-Zfuture-incompat-test") .masquerade_as_nightly_cargo(&["future-incompat-test"]) .run(); p.cargo("report future-incompatibilities") .with_stdout_does_not_contain("[..]\x1b[[..]") .run(); p.cargo("report future-incompatibilities") .env("CARGO_TERM_COLOR", "always") .with_stdout_contains("[..]\x1b[[..]") .run(); } #[cargo_test( nightly, reason = "-Zfuture-incompat-test requires nightly (permanently)" )] fn bad_ids() { let p = local_project(); p.cargo("report future-incompatibilities --id 1") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no reports are currently available "#]]) .run(); p.cargo("check") .env("RUSTFLAGS", "-Zfuture-incompat-test") .masquerade_as_nightly_cargo(&["future-incompat-test"]) .run(); p.cargo("report future-incompatibilities --id foo") .with_status(1) .with_stderr_data(str![ "[ERROR] Invalid value: could not parse `foo` as a number" ]) .run(); p.cargo("report future-incompatibilities --id 7") .with_status(101) .with_stderr_data(str![[r#" [ERROR] could not find report with ID 7 Available IDs are: 1 "#]]) .run(); } #[cargo_test( nightly, reason = "-Zfuture-incompat-test requires nightly (permanently)" )] fn suggestions_for_updates() { Package::new("with_updates", "1.0.0") .file("src/lib.rs", FUTURE_EXAMPLE) .publish(); Package::new("big_update", "1.0.0") .file("src/lib.rs", FUTURE_EXAMPLE) .publish(); Package::new("without_updates", "1.0.0") .file("src/lib.rs", FUTURE_EXAMPLE) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] with_updates = "1" big_update = "1" without_updates = "1" "#, ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); Package::new("with_updates", "1.0.1") .file("src/lib.rs", "") .publish(); Package::new("with_updates", "1.0.2") .file("src/lib.rs", "") .publish(); Package::new("with_updates", "3.0.1") .file("src/lib.rs", "") .publish(); Package::new("big_update", "2.0.0") .file("src/lib.rs", "") .publish(); // This is a hack to force cargo to update the index. Cargo can't do this // automatically because doing a network update on every build would be a // bad idea. Under normal circumstances, we'll hope the user has done // something else along the way to trigger an update (building some other // project or something). This could use some more consideration of how to // handle this better (maybe only trigger an update if it hasn't updated // in a long while?). p.cargo("update without_updates").run(); p.cargo("check --future-incompat-report") .masquerade_as_nightly_cargo(&["future-incompat-test"]) .env("RUSTFLAGS", "-Zfuture-incompat-test") .with_stderr_data(str![[r#" [DOWNLOADING] crates ... [DOWNLOADED] without_updates v1.0.0 (registry `dummy-registry`) [DOWNLOADED] with_updates v1.0.0 (registry `dummy-registry`) [DOWNLOADED] big_update v1.0.0 (registry `dummy-registry`) [CHECKING] with_updates v1.0.0 [CHECKING] big_update v1.0.0 [CHECKING] without_updates v1.0.0 [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [WARNING] the following packages contain code that will be rejected by a future version of Rust: big_update v1.0.0, with_updates v1.0.0, without_updates v1.0.0 [NOTE] To solve this problem, you can try the following approaches: - Some affected dependencies have newer versions available. You may want to consider updating them to a newer version to see if the issue has been fixed. big_update v1.0.0 has the following newer versions available: 2.0.0 with_updates v1.0.0 has the following newer versions available: 1.0.1, 1.0.2, 3.0.1 - If the issue is not solved by updating the dependencies, a fix has to be implemented by those dependencies. You can help with that by notifying the maintainers of this problem (e.g. by creating a bug report) or by proposing a fix to the maintainers (e.g. by creating a pull request): - big_update@1.0.0 - Repository: - Detailed warning command: `cargo report future-incompatibilities --id 1 --package big_update@1.0.0` - with_updates@1.0.0 - Repository: - Detailed warning command: `cargo report future-incompatibilities --id 1 --package with_updates@1.0.0` - without_updates@1.0.0 - Repository: - Detailed warning command: `cargo report future-incompatibilities --id 1 --package without_updates@1.0.0` - If waiting for an upstream fix is not an option, you can use the `[patch]` section in `Cargo.toml` to use your own version of the dependency. For more information, see: https://doc.rust-lang.org/cargo/reference/overriding-dependencies.html#the-patch-section [NOTE] this report can be shown with `cargo report future-incompatibilities --id 1` "#]].unordered()) .run(); p.cargo("report future-incompatibilities") .with_stdout_data(str![[r#" The following warnings were discovered during the build. These warnings are an indication that the packages contain code that will become an error in a future release of Rust. These warnings typically cover changes to close soundness problems, unintended or undocumented behavior, or critical problems that cannot be fixed in a backwards-compatible fashion, and are not expected to be in wide use. Each warning should contain a link for more information on what the warning means and how to resolve it. To solve this problem, you can try the following approaches: - Some affected dependencies have newer versions available. You may want to consider updating them to a newer version to see if the issue has been fixed. big_update v1.0.0 has the following newer versions available: 2.0.0 with_updates v1.0.0 has the following newer versions available: 1.0.1, 1.0.2, 3.0.1 - If the issue is not solved by updating the dependencies, a fix has to be implemented by those dependencies. You can help with that by notifying the maintainers of this problem (e.g. by creating a bug report) or by proposing a fix to the maintainers (e.g. by creating a pull request): - big_update@1.0.0 - Repository: - Detailed warning command: `cargo report future-incompatibilities --id 1 --package big_update@1.0.0` - with_updates@1.0.0 - Repository: - Detailed warning command: `cargo report future-incompatibilities --id 1 --package with_updates@1.0.0` - without_updates@1.0.0 - Repository: - Detailed warning command: `cargo report future-incompatibilities --id 1 --package without_updates@1.0.0` - If waiting for an upstream fix is not an option, you can use the `[patch]` section in `Cargo.toml` to use your own version of the dependency. For more information, see: https://doc.rust-lang.org/cargo/reference/overriding-dependencies.html#the-patch-section The package `big_update v1.0.0` currently triggers the following future incompatibility lints: > [WARNING] unused variable: `x` ... The package `with_updates v1.0.0` currently triggers the following future incompatibility lints: > [WARNING] unused variable: `x` ... The package `without_updates v1.0.0` currently triggers the following future incompatibility lints: > [WARNING] unused variable: `x` ... "#]]) .run(); } #[cargo_test( nightly, reason = "-Zfuture-incompat-test requires nightly (permanently)" )] fn correct_report_id_when_cached() { // Checks for a bug where the `--id` value was off-by-one when the report // is already cached. let p = dependency_project(); p.cargo("check --future-incompat-report") .env("RUSTFLAGS", "-Zfuture-incompat-test") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [CHECKING] bar v1.0.0 [CHECKING] foo v1.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [WARNING] the following packages contain code that will be rejected by a future version of Rust: bar v1.0.0 [NOTE] To solve this problem, you can try the following approaches: - If the issue is not solved by updating the dependencies, a fix has to be implemented by those dependencies. You can help with that by notifying the maintainers of this problem (e.g. by creating a bug report) or by proposing a fix to the maintainers (e.g. by creating a pull request): - bar@1.0.0 - Repository: https://example.com/ - Detailed warning command: `cargo report future-incompatibilities --id 1 --package bar@1.0.0` - If waiting for an upstream fix is not an option, you can use the `[patch]` section in `Cargo.toml` to use your own version of the dependency. For more information, see: https://doc.rust-lang.org/cargo/reference/overriding-dependencies.html#the-patch-section [NOTE] this report can be shown with `cargo report future-incompatibilities --id 1` "#]]) .run(); p.cargo("check --future-incompat-report") .env("RUSTFLAGS", "-Zfuture-incompat-test") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [WARNING] the following packages contain code that will be rejected by a future version of Rust: bar v1.0.0 [NOTE] To solve this problem, you can try the following approaches: - If the issue is not solved by updating the dependencies, a fix has to be implemented by those dependencies. You can help with that by notifying the maintainers of this problem (e.g. by creating a bug report) or by proposing a fix to the maintainers (e.g. by creating a pull request): - bar@1.0.0 - Repository: https://example.com/ - Detailed warning command: `cargo report future-incompatibilities --id 1 --package bar@1.0.0` - If waiting for an upstream fix is not an option, you can use the `[patch]` section in `Cargo.toml` to use your own version of the dependency. For more information, see: https://doc.rust-lang.org/cargo/reference/overriding-dependencies.html#the-patch-section [NOTE] this report can be shown with `cargo report future-incompatibilities --id 1` "#]]) .run(); } cargo-0.91.0/tests/testsuite/generate_lockfile.rs000064400000000000000000000161341046102023000202160ustar 00000000000000//! Tests for the `cargo generate-lockfile` command. use std::fs; use crate::prelude::*; use cargo_test_support::registry::{Package, RegistryBuilder}; use cargo_test_support::{ProjectBuilder, basic_manifest, paths, project, str}; #[cargo_test] fn adding_and_removing_packages() { let p = project() .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); let lock1 = p.read_lockfile(); // add a dep p.change_file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.1" [dependencies.bar] path = "bar" "#, ); p.cargo("generate-lockfile").run(); let lock2 = p.read_lockfile(); assert_ne!(lock1, lock2); // change the dep p.change_file("bar/Cargo.toml", &basic_manifest("bar", "0.0.2")); p.cargo("generate-lockfile").run(); let lock3 = p.read_lockfile(); assert_ne!(lock1, lock3); assert_ne!(lock2, lock3); // remove the dep println!("lock4"); p.change_file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.1" "#, ); p.cargo("generate-lockfile").run(); let lock4 = p.read_lockfile(); assert_eq!(lock1, lock4); } #[cargo_test] fn no_index_update_sparse() { let _registry = RegistryBuilder::new().http_index().build(); no_index_update( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version "#]], str![[r#" [LOCKING] 1 package to latest compatible version "#]], ); } #[cargo_test] fn no_index_update_git() { no_index_update( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version "#]], str![[r#" [LOCKING] 1 package to latest compatible version "#]], ); } fn no_index_update(expected: impl IntoData, expected_unstable_option: impl IntoData) { Package::new("serde", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.1" [dependencies] serde = "1.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("generate-lockfile") .with_stderr_data(expected) .run(); p.cargo("generate-lockfile -Zno-index-update") .masquerade_as_nightly_cargo(&["no-index-update"]) .with_stdout_data("") .with_stderr_data(expected_unstable_option) .run(); } #[cargo_test] fn preserve_metadata() { let p = project() .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); let metadata = r#" [metadata] bar = "baz" foo = "bar" "#; let lock = p.read_lockfile(); let data = lock + metadata; p.change_file("Cargo.lock", &data); // Build and make sure the metadata is still there p.cargo("build").run(); let lock = p.read_lockfile(); assert!(lock.contains(metadata.trim()), "{}", lock); // Update and make sure the metadata is still there p.cargo("update").run(); let lock = p.read_lockfile(); assert!(lock.contains(metadata.trim()), "{}", lock); } #[cargo_test] fn preserve_line_endings_issue_2076() { let p = project() .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "") .build(); let lockfile = p.root().join("Cargo.lock"); p.cargo("generate-lockfile").run(); assert!(lockfile.is_file()); p.cargo("generate-lockfile").run(); let lock0 = p.read_lockfile(); assert!(lock0.starts_with("# This file is automatically @generated by Cargo.\n# It is not intended for manual editing.\n")); let lock1 = lock0.replace("\n", "\r\n"); p.change_file("Cargo.lock", &lock1); p.cargo("generate-lockfile").run(); let lock2 = p.read_lockfile(); assert!(lock2.starts_with("# This file is automatically @generated by Cargo.\r\n# It is not intended for manual editing.\r\n")); assert_eq!(lock1, lock2); } #[cargo_test] fn cargo_update_generate_lockfile() { let p = project().file("src/main.rs", "fn main() {}").build(); let lockfile = p.root().join("Cargo.lock"); assert!(!lockfile.is_file()); p.cargo("update").with_stderr_data("").run(); assert!(lockfile.is_file()); fs::remove_file(p.root().join("Cargo.lock")).unwrap(); assert!(!lockfile.is_file()); p.cargo("update").with_stderr_data("").run(); assert!(lockfile.is_file()); } #[cargo_test] fn duplicate_entries_in_lockfile() { let _a = ProjectBuilder::new(paths::root().join("a")) .file( "Cargo.toml", r#" [package] name = "a" authors = [] version = "0.0.1" [dependencies] common = {path="common"} "#, ) .file("src/lib.rs", "") .build(); let common_toml = &basic_manifest("common", "0.0.1"); let _common_in_a = ProjectBuilder::new(paths::root().join("a/common")) .file("Cargo.toml", common_toml) .file("src/lib.rs", "") .build(); let b = ProjectBuilder::new(paths::root().join("b")) .file( "Cargo.toml", r#" [package] name = "b" authors = [] version = "0.0.1" edition = "2015" [dependencies] common = {path="common"} a = {path="../a"} "#, ) .file("src/lib.rs", "") .build(); let _common_in_b = ProjectBuilder::new(paths::root().join("b/common")) .file("Cargo.toml", common_toml) .file("src/lib.rs", "") .build(); // should fail due to a duplicate package `common` in the lock file b.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package collision in the lockfile: packages common v0.0.1 ([ROOT]/a/common) and common v0.0.1 ([ROOT]/b/common) are different, but only one can be written to lockfile unambiguously "#]]) .run(); } #[cargo_test] fn generate_lockfile_holds_lock_and_offline() { Package::new("syn", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" [dependencies] syn = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version "#]]) .run(); p.cargo("generate-lockfile --offline") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version "#]]) .run(); } cargo-0.91.0/tests/testsuite/git.rs000064400000000000000000003441231046102023000153410ustar 00000000000000//! Tests for git support. use std::fs; use std::io::prelude::*; use std::net::{TcpListener, TcpStream}; use std::path::Path; use std::str; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use std::thread; use crate::prelude::*; use cargo_test_support::git::{add_submodule, cargo_uses_gitoxide}; use cargo_test_support::paths; use cargo_test_support::registry::Package; use cargo_test_support::{Project, sleep_ms, str, t}; use cargo_test_support::{basic_lib_manifest, basic_manifest, git, main_file, project}; #[cargo_test] fn cargo_compile_simple_git_dep() { let project = project(); let git_project = git::new("dep1", |project| { project .file("Cargo.toml", &basic_lib_manifest("dep1")) .file( "src/dep1.rs", r#" pub fn hello() -> &'static str { "hello world" } "#, ) }); let project = project .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.dep1] git = '{}' "#, git_project.url() ), ) .file( "src/main.rs", &main_file(r#""{}", dep1::hello()"#, &["dep1"]), ) .build(); project .cargo("build") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/dep1` [LOCKING] 1 package to latest compatible version [COMPILING] dep1 v0.5.0 ([ROOTURL]/dep1#[..]) [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert!(project.bin("foo").is_file()); project .process(&project.bin("foo")) .with_stdout_data(str![[r#" hello world "#]]) .run(); } #[cargo_test] fn cargo_compile_git_dep_branch() { let project = project(); let git_project = git::new("dep1", |project| { project .file("Cargo.toml", &basic_lib_manifest("dep1")) .file( "src/dep1.rs", r#" pub fn hello() -> &'static str { "hello world" } "#, ) }); // Make a new branch based on the current HEAD commit let repo = git2::Repository::open(&git_project.root()).unwrap(); let head = repo.head().unwrap().target().unwrap(); let head = repo.find_commit(head).unwrap(); repo.branch("branchy", &head, true).unwrap(); let project = project .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.dep1] git = '{}' branch = "branchy" "#, git_project.url() ), ) .file( "src/main.rs", &main_file(r#""{}", dep1::hello()"#, &["dep1"]), ) .build(); project .cargo("build") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/dep1` [LOCKING] 1 package to latest compatible version [COMPILING] dep1 v0.5.0 ([ROOTURL]/dep1?branch=branchy#[..]) [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert!(project.bin("foo").is_file()); project .process(&project.bin("foo")) .with_stdout_data(str![[r#" hello world "#]]) .run(); } #[cargo_test] fn cargo_compile_git_dep_tag() { let project = project(); let git_project = git::new("dep1", |project| { project .file("Cargo.toml", &basic_lib_manifest("dep1")) .file( "src/dep1.rs", r#" pub fn hello() -> &'static str { "hello world" } "#, ) }); // Make a tag corresponding to the current HEAD let repo = git2::Repository::open(&git_project.root()).unwrap(); let head = repo.head().unwrap().target().unwrap(); repo.tag( "v0.1.0", &repo.find_object(head, None).unwrap(), &repo.signature().unwrap(), "make a new tag", false, ) .unwrap(); let project = project .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.dep1] git = '{}' tag = "v0.1.0" "#, git_project.url() ), ) .file( "src/main.rs", &main_file(r#""{}", dep1::hello()"#, &["dep1"]), ) .build(); project .cargo("build") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/dep1` [LOCKING] 1 package to latest compatible version [COMPILING] dep1 v0.5.0 ([ROOTURL]/dep1?tag=v0.1.0#[..]) [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert!(project.bin("foo").is_file()); project .process(&project.bin("foo")) .with_stdout_data(str![[r#" hello world "#]]) .run(); project.cargo("build").run(); } #[cargo_test] fn cargo_compile_git_dep_pull_request() { let project = project(); let git_project = git::new("dep1", |project| { project .file("Cargo.toml", &basic_lib_manifest("dep1")) .file( "src/dep1.rs", r#" pub fn hello() -> &'static str { "hello world" } "#, ) }); // Make a reference in GitHub's pull request ref naming convention. let repo = git2::Repository::open(&git_project.root()).unwrap(); let oid = repo.refname_to_id("HEAD").unwrap(); let force = false; let log_message = "open pull request"; repo.reference("refs/pull/330/head", oid, force, log_message) .unwrap(); let project = project .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.0" edition = "2015" [dependencies] dep1 = {{ git = "{}", rev = "refs/pull/330/head" }} "#, git_project.url() ), ) .file( "src/main.rs", &main_file(r#""{}", dep1::hello()"#, &["dep1"]), ) .build(); project .cargo("build") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/dep1` [LOCKING] 1 package to latest compatible version [COMPILING] dep1 v0.5.0 ([ROOTURL]/dep1?rev=refs%2Fpull%2F330%2Fhead#[..]) [COMPILING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert!(project.bin("foo").is_file()); } #[cargo_test] fn cargo_compile_with_nested_paths() { let git_project = git::new("dep1", |project| { project .file( "Cargo.toml", r#" [package] name = "dep1" version = "0.5.0" edition = "2015" authors = ["carlhuda@example.com"] [dependencies.dep2] version = "0.5.0" path = "vendor/dep2" [lib] name = "dep1" "#, ) .file( "src/dep1.rs", r#" extern crate dep2; pub fn hello() -> &'static str { dep2::hello() } "#, ) .file("vendor/dep2/Cargo.toml", &basic_lib_manifest("dep2")) .file( "vendor/dep2/src/dep2.rs", r#" pub fn hello() -> &'static str { "hello world" } "#, ) }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.dep1] version = "0.5.0" git = '{}' [[bin]] name = "foo" "#, git_project.url() ), ) .file( "src/foo.rs", &main_file(r#""{}", dep1::hello()"#, &["dep1"]), ) .build(); p.cargo("build").run(); assert!(p.bin("foo").is_file()); p.process(&p.bin("foo")) .with_stdout_data(str![[r#" hello world "#]]) .run(); } #[cargo_test] fn cargo_compile_with_malformed_nested_paths() { let git_project = git::new("dep1", |project| { project .file("Cargo.toml", &basic_lib_manifest("dep1")) .file( "src/dep1.rs", r#" pub fn hello() -> &'static str { "hello world" } "#, ) .file("vendor/dep2/Cargo.toml", "!INVALID!") .file( "vendor/dep3/Cargo.toml", r#" [package] name = "dep3" version = "0.5.0" edition = "2015" [dependencies] subdep1 = { path = "../require-extra-build-step" } "#, ) .file("vendor/dep3/src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.dep1] version = "0.5.0" git = '{}' [[bin]] name = "foo" "#, git_project.url() ), ) .file( "src/foo.rs", &main_file(r#""{}", dep1::hello()"#, &["dep1"]), ) .build(); p.cargo("build").run(); assert!(p.bin("foo").is_file()); p.process(&p.bin("foo")) .with_stdout_data(str![[r#" hello world "#]]) .run(); } #[cargo_test] fn cargo_compile_with_meta_package() { let git_project = git::new("meta-dep", |project| { project .file("dep1/Cargo.toml", &basic_lib_manifest("dep1")) .file( "dep1/src/dep1.rs", r#" pub fn hello() -> &'static str { "this is dep1" } "#, ) .file("dep2/Cargo.toml", &basic_lib_manifest("dep2")) .file( "dep2/src/dep2.rs", r#" pub fn hello() -> &'static str { "this is dep2" } "#, ) }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.dep1] version = "0.5.0" git = '{}' [dependencies.dep2] version = "0.5.0" git = '{}' [[bin]] name = "foo" "#, git_project.url(), git_project.url() ), ) .file( "src/foo.rs", &main_file( r#""{} {}", dep1::hello(), dep2::hello()"#, &["dep1", "dep2"], ), ) .build(); p.cargo("build").run(); assert!(p.bin("foo").is_file()); p.process(&p.bin("foo")) .with_stdout_data(str![[r#" this is dep1 this is dep2 "#]]) .run(); } #[cargo_test] fn cargo_compile_with_short_ssh_git() { let url = "git@github.com:a/dep"; let well_formed_url = "ssh://git@github.com/a/dep"; let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.dep] git = "{}" [[bin]] name = "foo" "#, url ), ) .file( "src/foo.rs", &main_file(r#""{}", dep1::hello()"#, &["dep1"]), ) .build(); p.cargo("check") .with_status(101) .with_stderr_data(&format!( "\ [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: invalid url `{}`: relative URL without a base; try using `{}` instead ", url, well_formed_url )) .run(); } #[cargo_test] fn recompilation() { let git_project = git::new("bar", |project| { project .file("Cargo.toml", &basic_lib_manifest("bar")) .file("src/bar.rs", "pub fn bar() {}") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.bar] version = "0.5.0" git = '{}' "#, git_project.url() ), ) .file("src/main.rs", &main_file(r#""{:?}", bar::bar()"#, &["bar"])) .build(); // First time around we should compile both foo and bar p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/bar` [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.5.0 ([ROOTURL]/bar#[..]) [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Don't recompile the second time p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Modify a file manually, shouldn't trigger a recompile git_project.change_file("src/bar.rs", r#"pub fn bar() { println!("hello!"); }"#); p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("update") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/bar` [LOCKING] 0 packages to latest compatible versions "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Commit the changes and make sure we don't trigger a recompile because the // lock file says not to change let repo = git2::Repository::open(&git_project.root()).unwrap(); git::add(&repo); git::commit(&repo); println!("compile after commit"); p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.root().move_into_the_past(); // Update the dependency and carry on! p.cargo("update") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/bar` [LOCKING] 1 package to latest compatible version [UPDATING] bar v0.5.0 ([ROOTURL]/bar#[..]) -> #[..] "#]]) .run(); println!("going for the last compile"); p.cargo("check") .with_stderr_data(str![[r#" [CHECKING] bar v0.5.0 ([ROOTURL]/bar#[..]) [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Make sure clean only cleans one dep p.cargo("clean -p foo") .with_stderr_data(str![[r#" [REMOVED] [FILE_NUM] files, [FILE_SIZE]B total "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn update_with_shared_deps() { let git_project = git::new("bar", |project| { project .file("Cargo.toml", &basic_lib_manifest("bar")) .file("src/bar.rs", "pub fn bar() {}") }); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.dep1] path = "dep1" [dependencies.dep2] path = "dep2" "#, ) .file( "src/main.rs", r#" #[allow(unused_extern_crates)] extern crate dep1; #[allow(unused_extern_crates)] extern crate dep2; fn main() {} "#, ) .file( "dep1/Cargo.toml", &format!( r#" [package] name = "dep1" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.bar] version = "0.5.0" git = '{}' "#, git_project.url() ), ) .file("dep1/src/lib.rs", "") .file( "dep2/Cargo.toml", &format!( r#" [package] name = "dep2" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.bar] version = "0.5.0" git = '{}' "#, git_project.url() ), ) .file("dep2/src/lib.rs", "") .build(); // First time around we should compile both foo and bar p.cargo("check") .with_stderr_data( str![[r#" [UPDATING] git repository `[ROOTURL]/bar` [LOCKING] 3 packages to latest compatible versions [CHECKING] bar v0.5.0 ([ROOTURL]/bar#[..]) [CHECKING] dep1 v0.5.0 ([ROOT]/foo/dep1) [CHECKING] dep2 v0.5.0 ([ROOT]/foo/dep2) [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); // Modify a file manually, and commit it git_project.change_file("src/bar.rs", r#"pub fn bar() { println!("hello!"); }"#); let repo = git2::Repository::open(&git_project.root()).unwrap(); let old_head = repo.head().unwrap().target().unwrap(); git::add(&repo); git::commit(&repo); sleep_ms(1000); // By default, not transitive updates println!("dep1 update"); p.cargo("update dep1") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/bar` [LOCKING] 1 package to latest compatible version [UPDATING] bar v0.5.0 ([ROOTURL]/bar#[..]) -> #[..] "#]]) .run(); // Don't do anything bad on a weird --precise argument println!("bar bad precise update"); p.cargo("update bar --precise 0.1.2") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/bar` [ERROR] Unable to update [ROOTURL]/bar#0.1.2 Caused by: revspec '0.1.2' not found; class=Reference (4); code=NotFound (-3) "#]]) .run(); // Specifying a precise rev to the old rev shouldn't actually update // anything because we already have the rev in the db. println!("bar precise update"); p.cargo("update bar --precise") .arg(&old_head.to_string()) .with_stderr_data(str![[r#" [UPDATING] bar v0.5.0 ([ROOTURL]/bar#[..]) -> #[..] "#]]) .run(); // Updating recursively should, however, update the repo. println!("dep1 recursive update"); p.cargo("update dep1 --recursive") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/bar` [LOCKING] 1 package to latest compatible version [UPDATING] bar v0.5.0 ([ROOTURL]/bar#[..]) -> #[..] "#]]) .run(); // Make sure we still only compile one version of the git repo println!("build"); p.cargo("check") .with_stderr_data( str![[r#" [CHECKING] bar v0.5.0 ([ROOTURL]/bar#[..]) [CHECKING] dep1 v0.5.0 ([ROOT]/foo/dep1) [CHECKING] dep2 v0.5.0 ([ROOT]/foo/dep2) [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); // We should be able to update transitive deps p.cargo("update bar") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/bar` [LOCKING] 0 packages to latest compatible versions "#]]) .run(); } #[cargo_test] fn dep_with_submodule() { let project = project(); let git_project = git::new("dep1", |project| { project.file("Cargo.toml", &basic_manifest("dep1", "0.5.0")) }); let git_project2 = git::new("dep2", |project| project.file("lib.rs", "pub fn dep() {}")); let repo = git2::Repository::open(&git_project.root()).unwrap(); let url = git_project2.root().to_url().to_string(); git::add_submodule(&repo, &url, Path::new("src")); git::commit(&repo); let project = project .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.dep1] git = '{}' "#, git_project.url() ), ) .file( "src/lib.rs", "extern crate dep1; pub fn foo() { dep1::dep() }", ) .build(); project .cargo("check") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/dep1` [UPDATING] git submodule `[ROOTURL]/dep2` [LOCKING] 1 package to latest compatible version [CHECKING] dep1 v0.5.0 ([ROOTURL]/dep1#[..]) [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn dep_with_relative_submodule() { let foo = project(); let base = git::new("base", |project| { project .file( "Cargo.toml", r#" [package] name = "base" version = "0.5.0" edition = "2015" [dependencies] deployment.path = "deployment" "#, ) .file( "src/lib.rs", r#" pub fn dep() { deployment::deployment_func(); } "#, ) }); let _deployment = git::new("deployment", |project| { project .file("src/lib.rs", "pub fn deployment_func() {}") .file("Cargo.toml", &basic_lib_manifest("deployment")) }); let base_repo = git2::Repository::open(&base.root()).unwrap(); git::add_submodule(&base_repo, "../deployment", Path::new("deployment")); git::commit(&base_repo); let project = foo .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" [dependencies.base] git = '{}' "#, base.url() ), ) .file("src/lib.rs", "pub fn foo() { }") .build(); project .cargo("check") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/base` [UPDATING] git submodule `[ROOTURL]/deployment` [LOCKING] 2 packages to latest compatible versions [CHECKING] deployment v0.5.0 ([ROOTURL]/base#[..]) [CHECKING] base v0.5.0 ([ROOTURL]/base#[..]) [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn dep_with_bad_submodule() { let project = project(); let git_project = git::new("dep1", |project| { project.file("Cargo.toml", &basic_manifest("dep1", "0.5.0")) }); let git_project2 = git::new("dep2", |project| project.file("lib.rs", "pub fn dep() {}")); let repo = git2::Repository::open(&git_project.root()).unwrap(); let url = git_project2.root().to_url().to_string(); git::add_submodule(&repo, &url, Path::new("src")); git::commit(&repo); // now amend the first commit on git_project2 to make submodule ref point to not-found // commit let repo = git2::Repository::open(&git_project2.root()).unwrap(); let original_submodule_ref = repo.refname_to_id("refs/heads/master").unwrap(); let commit = repo.find_commit(original_submodule_ref).unwrap(); commit .amend( Some("refs/heads/master"), None, None, None, Some("something something"), None, ) .unwrap(); let p = project .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.dep1] git = '{}' "#, git_project.url() ), ) .file( "src/lib.rs", "extern crate dep1; pub fn foo() { dep1::dep() }", ) .build(); let expected = str![[r#" [UPDATING] git repository `[ROOTURL]/dep1` [UPDATING] git submodule `[ROOTURL]/dep2` [ERROR] failed to get `dep1` as a dependency of package `foo v0.5.0 ([ROOT]/foo)` Caused by: failed to load source for dependency `dep1` Caused by: Unable to update [ROOTURL]/dep1 Caused by: failed to update submodule `src` Caused by: object not found - no match for id ([..]); class=Odb (9); code=NotFound (-3) "#]]; p.cargo("check") .with_stderr_data(expected) .with_status(101) .run(); } #[cargo_test] fn dep_with_skipped_submodule() { // Ensure we skip dependency submodules if their update strategy is `none`. let qux = git::new("qux", |project| { project.no_manifest().file("README", "skip me") }); let bar = git::new("bar", |project| { project .file("Cargo.toml", &basic_manifest("bar", "0.0.0")) .file("src/lib.rs", "") }); // `qux` is a submodule of `bar`, but we don't want to update it. let repo = git2::Repository::open(&bar.root()).unwrap(); git::add_submodule(&repo, qux.url().as_str(), Path::new("qux")); let mut conf = git2::Config::open(&bar.root().join(".gitmodules")).unwrap(); conf.set_str("submodule.qux.update", "none").unwrap(); git::add(&repo); git::commit(&repo); let foo = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [dependencies.bar] git = "{}" "#, bar.url() ), ) .file("src/main.rs", "fn main() {}") .build(); foo.cargo("check") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/bar` [SKIPPING] git submodule `[ROOTURL]/qux` due to update strategy in .gitmodules [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.0.0 ([ROOTURL]/bar#[..]) [CHECKING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn ambiguous_published_deps() { let project = project(); let git_project = git::new("dep", |project| { project .file( "duplicate1/Cargo.toml", &format!( r#" [package] name = "duplicate" version = "0.5.0" edition = "2015" publish = true "# ), ) .file("duplicate1/src/lib.rs", "") .file( "duplicate2/Cargo.toml", &format!( r#" [package] name = "duplicate" version = "0.5.0" edition = "2015" publish = true "# ), ) .file("duplicate2/src/lib.rs", "") }); let p = project .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.duplicate] git = '{}' "#, git_project.url() ), ) .file("src/main.rs", "fn main() { }") .build(); p.cargo("build").run(); p.cargo("run") .with_stderr_data(str![[r#" [WARNING] skipping duplicate package `duplicate v0.5.0 ([ROOTURL]/dep#[..])`: [ROOT]/home/.cargo/git/checkouts/dep-[HASH]/[..]/duplicate2/Cargo.toml in favor of [ROOT]/home/.cargo/git/checkouts/dep-[HASH]/[..]/duplicate1/Cargo.toml [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .run(); } #[cargo_test] fn unused_ambiguous_published_deps() { let project = project(); let git_project = git::new("dep", |project| { project .file( "unique/Cargo.toml", &format!( r#" [package] name = "unique" version = "0.5.0" edition = "2015" publish = true "# ), ) .file("unique/src/lib.rs", "") .file( "duplicate1/Cargo.toml", &format!( r#" [package] name = "duplicate" version = "0.5.0" edition = "2015" publish = true "# ), ) .file("duplicate1/src/lib.rs", "") .file( "duplicate2/Cargo.toml", &format!( r#" [package] name = "duplicate" version = "0.5.0" edition = "2015" publish = true "# ), ) .file("duplicate2/src/lib.rs", "") .file( "invalid/Cargo.toml", &format!( r#" [package name = "bar" version = "0.5.0" edition = "2015" publish = true "# ), ) .file("invalid/src/lib.rs", "") }); let p = project .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.unique] git = '{}' "#, git_project.url() ), ) .file("src/main.rs", "fn main() { }") .build(); p.cargo("build").run(); p.cargo("run") .with_stderr_data(str![[r#" [ERROR] unclosed table, expected `]` --> ../home/.cargo/git/checkouts/dep-[HASH]/[..]/invalid/Cargo.toml:2:29 | 2 | [package | ^ | [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .run(); } #[cargo_test] fn two_deps_only_update_one() { let project = project(); let git1 = git::new("dep1", |project| { project .file("Cargo.toml", &basic_manifest("dep1", "0.5.0")) .file("src/lib.rs", "") }); let git2 = git::new("dep2", |project| { project .file("Cargo.toml", &basic_manifest("dep2", "0.5.0")) .file("src/lib.rs", "") }); let p = project .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.dep1] git = '{}' [dependencies.dep2] git = '{}' "#, git1.url(), git2.url() ), ) .file("src/main.rs", "fn main() {}") .build(); fn oid_to_short_sha(oid: git2::Oid) -> String { oid.to_string()[..8].to_string() } fn git_repo_head_sha(p: &Project) -> String { let repo = git2::Repository::open(p.root()).unwrap(); let head = repo.head().unwrap().target().unwrap(); oid_to_short_sha(head) } println!("dep1 head sha: {}", git_repo_head_sha(&git1)); println!("dep2 head sha: {}", git_repo_head_sha(&git2)); p.cargo("check") .with_stderr_data( str![[r#" [UPDATING] git repository `[ROOTURL]/dep1` [UPDATING] git repository `[ROOTURL]/dep2` [LOCKING] 2 packages to latest compatible versions [CHECKING] dep1 v0.5.0 ([ROOTURL]/dep1#[..]) [CHECKING] dep2 v0.5.0 ([ROOTURL]/dep2#[..]) [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); git1.change_file("src/lib.rs", "pub fn foo() {}"); let repo = git2::Repository::open(&git1.root()).unwrap(); git::add(&repo); let oid = git::commit(&repo); println!("dep1 head sha: {}", oid_to_short_sha(oid)); p.cargo("update dep1") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/dep1` [LOCKING] 1 package to latest compatible version [UPDATING] dep1 v0.5.0 ([ROOTURL]/dep1#[..]) -> #[..] "#]]) .run(); } #[cargo_test] fn stale_cached_version() { let bar = git::new("meta-dep", |project| { project .file("Cargo.toml", &basic_manifest("bar", "0.0.0")) .file("src/lib.rs", "pub fn bar() -> i32 { 1 }") }); // Update the git database in the cache with the current state of the git // repo let foo = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [dependencies.bar] git = '{}' "#, bar.url() ), ) .file( "src/main.rs", r#" extern crate bar; fn main() { assert_eq!(bar::bar(), 1) } "#, ) .build(); foo.cargo("build").run(); foo.process(&foo.bin("foo")).run(); // Update the repo, and simulate someone else updating the lock file and then // us pulling it down. bar.change_file("src/lib.rs", "pub fn bar() -> i32 { 1 + 0 }"); let repo = git2::Repository::open(&bar.root()).unwrap(); git::add(&repo); git::commit(&repo); sleep_ms(1000); let rev = repo.revparse_single("HEAD").unwrap().id(); foo.change_file( "Cargo.lock", &format!( r#" [[package]] name = "foo" version = "0.0.0" dependencies = [ 'bar 0.0.0 (git+{url}#{hash})' ] [[package]] name = "bar" version = "0.0.0" source = 'git+{url}#{hash}' "#, url = bar.url(), hash = rev ), ); // Now build! foo.cargo("build") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/meta-dep` [COMPILING] bar v0.0.0 ([ROOTURL]/meta-dep#[..]) [COMPILING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); foo.process(&foo.bin("foo")).run(); } #[cargo_test] fn dep_with_changed_submodule() { let project = project(); let git_project = git::new("dep1", |project| { project.file("Cargo.toml", &basic_manifest("dep1", "0.5.0")) }); let git_project2 = git::new("dep2", |project| { project.file("lib.rs", "pub fn dep() -> &'static str { \"project2\" }") }); let git_project3 = git::new("dep3", |project| { project.file("lib.rs", "pub fn dep() -> &'static str { \"project3\" }") }); let repo = git2::Repository::open(&git_project.root()).unwrap(); let mut sub = git::add_submodule(&repo, git_project2.url().as_ref(), Path::new("src")); git::commit(&repo); let p = project .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.dep1] git = '{}' "#, git_project.url() ), ) .file( "src/main.rs", " extern crate dep1; pub fn main() { println!(\"{}\", dep1::dep()) } ", ) .build(); println!("first run"); p.cargo("run") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/dep1` [UPDATING] git submodule `[ROOTURL]/dep2` [LOCKING] 1 package to latest compatible version [COMPILING] dep1 v0.5.0 ([ROOTURL]/dep1#[..]) [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .with_stdout_data(str![[r#" project2 "#]]) .run(); git_project.change_file( ".gitmodules", &format!( "[submodule \"src\"]\n\tpath = src\n\turl={}", git_project3.url() ), ); // Sync the submodule and reset it to the new remote. sub.sync().unwrap(); { let subrepo = sub.open().unwrap(); subrepo .remote_add_fetch("origin", "refs/heads/*:refs/heads/*") .unwrap(); subrepo .remote_set_url("origin", git_project3.url().as_ref()) .unwrap(); let mut origin = subrepo.find_remote("origin").unwrap(); origin.fetch(&Vec::::new(), None, None).unwrap(); let id = subrepo.refname_to_id("refs/remotes/origin/master").unwrap(); let obj = subrepo.find_object(id, None).unwrap(); subrepo.reset(&obj, git2::ResetType::Hard, None).unwrap(); } sub.add_to_index(true).unwrap(); git::add(&repo); git::commit(&repo); sleep_ms(1000); // Update the dependency and carry on! println!("update"); p.cargo("update") .with_stdout_data(str![]) .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/dep1` [UPDATING] git submodule `[ROOTURL]/dep3` [LOCKING] 1 package to latest compatible version [UPDATING] dep1 v0.5.0 ([ROOTURL]/dep1#[..]) -> #[..] "#]]) .run(); println!("last run"); p.cargo("run") .with_stderr_data(str![[r#" [COMPILING] dep1 v0.5.0 ([ROOTURL]/dep1#[..]) [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .with_stdout_data(str![[r#" project3 "#]]) .run(); } #[cargo_test] fn dev_deps_with_testing() { let p2 = git::new("bar", |project| { project .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) .file( "src/lib.rs", r#" pub fn gimme() -> &'static str { "zoidberg" } "#, ) }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dev-dependencies.bar] version = "0.5.0" git = '{}' "#, p2.url() ), ) .file( "src/main.rs", r#" fn main() {} #[cfg(test)] mod tests { extern crate bar; #[test] fn foo() { bar::gimme(); } } "#, ) .build(); // Generate a lock file which did not use `bar` to compile, but had to update // `bar` to generate the lock file p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/bar` [LOCKING] 1 package to latest compatible version [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Make sure we use the previous resolution of `bar` instead of updating it // a second time. p.cargo("test") .with_stderr_data(str![[r#" [COMPILING] bar v0.5.0 ([ROOTURL]/bar#[..]) [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/main.rs (target/debug/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" ... test tests::foo ... ok ... "#]]) .run(); } #[cargo_test] fn git_build_cmd_freshness() { let foo = git::new("foo", |project| { project .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file("build.rs", "fn main() {}") .file("src/lib.rs", "pub fn bar() -> i32 { 1 }") .file(".gitignore", "src/bar.rs") }); foo.root().move_into_the_past(); sleep_ms(1000); foo.cargo("check") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Smoke test to make sure it doesn't compile again println!("first pass"); foo.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Modify an ignored file and make sure we don't rebuild println!("second pass"); foo.change_file("src/bar.rs", ""); foo.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn git_name_not_always_needed() { let p2 = git::new("bar", |project| { project .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) .file( "src/lib.rs", r#" pub fn gimme() -> &'static str { "zoidberg" } "#, ) }); let repo = git2::Repository::open(&p2.root()).unwrap(); let mut cfg = repo.config().unwrap(); let _ = cfg.remove("user.name"); let _ = cfg.remove("user.email"); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dev-dependencies.bar] git = '{}' "#, p2.url() ), ) .file("src/main.rs", "fn main() {}") .build(); // Generate a lock file which did not use `bar` to compile, but had to update // `bar` to generate the lock file p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/bar` [LOCKING] 1 package to latest compatible version [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn git_repo_changing_no_rebuild() { let bar = git::new("bar", |project| { project .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) .file("src/lib.rs", "pub fn bar() -> i32 { 1 }") }); // Lock p1 to the first rev in the git repo let p1 = project() .at("p1") .file( "Cargo.toml", &format!( r#" [package] name = "p1" version = "0.5.0" edition = "2015" authors = [] build = 'build.rs' [dependencies.bar] git = '{}' "#, bar.url() ), ) .file("src/main.rs", "fn main() {}") .file("build.rs", "fn main() {}") .build(); p1.root().move_into_the_past(); p1.cargo("check") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/bar` [LOCKING] 1 package to latest compatible version [COMPILING] p1 v0.5.0 ([ROOT]/p1) [CHECKING] bar v0.5.0 ([ROOTURL]/bar#[..]) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Make a commit to lock p2 to a different rev bar.change_file("src/lib.rs", "pub fn bar() -> i32 { 2 }"); let repo = git2::Repository::open(&bar.root()).unwrap(); git::add(&repo); git::commit(&repo); // Lock p2 to the second rev let p2 = project() .at("p2") .file( "Cargo.toml", &format!( r#" [package] name = "p2" version = "0.5.0" edition = "2015" authors = [] [dependencies.bar] git = '{}' "#, bar.url() ), ) .file("src/main.rs", "fn main() {}") .build(); p2.cargo("check") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/bar` [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.5.0 ([ROOTURL]/bar#[..]) [CHECKING] p2 v0.5.0 ([ROOT]/p2) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // And now for the real test! Make sure that p1 doesn't get rebuilt // even though the git repo has changed. p1.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn git_dep_build_cmd() { let p = git::new("foo", |project| { project .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.bar] version = "0.5.0" path = "bar" [[bin]] name = "foo" "#, ) .file("src/foo.rs", &main_file(r#""{}", bar::gimme()"#, &["bar"])) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] build = "build.rs" [lib] name = "bar" path = "src/bar.rs" "#, ) .file( "bar/src/bar.rs.in", r#" pub fn gimme() -> i32 { 0 } "#, ) .file( "bar/build.rs", r#" use std::fs; fn main() { fs::copy("src/bar.rs.in", "src/bar.rs").unwrap(); } "#, ) }); p.root().join("bar").move_into_the_past(); p.cargo("build").run(); p.process(&p.bin("foo")) .with_stdout_data(str![[r#" 0 "#]]) .run(); // Touching bar.rs.in should cause the `build` command to run again. p.change_file("bar/src/bar.rs.in", "pub fn gimme() -> i32 { 1 }"); p.cargo("build").run(); p.process(&p.bin("foo")) .with_stdout_data(str![[r#" 1 "#]]) .run(); } #[cargo_test] fn fetch_downloads() { let bar = git::new("bar", |project| { project .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) .file("src/lib.rs", "pub fn bar() -> i32 { 1 }") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies.bar] git = '{}' "#, bar.url() ), ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("fetch") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/bar` [LOCKING] 1 package to latest compatible version "#]]) .run(); p.cargo("fetch").with_stderr_data(str![]).run(); } #[cargo_test] fn fetch_downloads_with_git2_first_then_with_gitoxide_and_vice_versa() { let bar = git::new("bar", |project| { project .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) .file("src/lib.rs", "pub fn bar() -> i32 { 1 }") }); let feature_configuration = if cargo_uses_gitoxide() { // When we are always using `gitoxide` by default, create the registry with git2 as well as the download… "-Zgitoxide=internal-use-git2" } else { // …otherwise create the registry and the git download with `gitoxide`. "-Zgitoxide=fetch" }; let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies.bar] git = '{url}' "#, url = bar.url() ), ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("fetch") .arg(feature_configuration) .masquerade_as_nightly_cargo(&["unstable features must be available for -Z gitoxide"]) .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/bar` [LOCKING] 1 package to latest compatible version "#]]) .run(); Package::new("bar", "1.0.0").publish(); // trigger a crates-index change. p.cargo("fetch").with_stderr_data(str![]).run(); } #[cargo_test] fn warnings_in_git_dep() { let bar = git::new("bar", |project| { project .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) .file("src/lib.rs", "fn unused() {}") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies.bar] git = '{}' "#, bar.url() ), ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/bar` [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.5.0 ([ROOTURL]/bar#[..]) [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn update_ambiguous() { let bar1 = git::new("bar1", |project| { project .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) .file("src/lib.rs", "") }); let bar2 = git::new("bar2", |project| { project .file("Cargo.toml", &basic_manifest("bar", "0.6.0")) .file("src/lib.rs", "") }); let baz = git::new("baz", |project| { project .file( "Cargo.toml", &format!( r#" [package] name = "baz" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.bar] git = '{}' "#, bar2.url() ), ) .file("src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies.bar] git = '{}' [dependencies.baz] git = '{}' "#, bar1.url(), baz.url() ), ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("generate-lockfile").run(); p.cargo("update bar") .with_status(101) .with_stderr_data(str![[r#" [ERROR] There are multiple `bar` packages in your project, and the specification `bar` is ambiguous. Please re-run this command with one of the following specifications: bar@0.5.0 bar@0.6.0 "#]]) .run(); } #[cargo_test] fn update_one_dep_in_repo_with_many_deps() { let bar = git::new("bar", |project| { project .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) .file("src/lib.rs", "") .file("a/Cargo.toml", &basic_manifest("a", "0.5.0")) .file("a/src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies.bar] git = '{}' [dependencies.a] git = '{}' "#, bar.url(), bar.url() ), ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("generate-lockfile").run(); p.cargo("update bar") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/bar` [LOCKING] 0 packages to latest compatible versions "#]]) .run(); } #[cargo_test] fn switch_deps_does_not_update_transitive() { let transitive = git::new("transitive", |project| { project .file("Cargo.toml", &basic_manifest("transitive", "0.5.0")) .file("src/lib.rs", "") }); let dep1 = git::new("dep1", |project| { project .file( "Cargo.toml", &format!( r#" [package] name = "dep" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.transitive] git = '{}' "#, transitive.url() ), ) .file("src/lib.rs", "") }); let dep2 = git::new("dep2", |project| { project .file( "Cargo.toml", &format!( r#" [package] name = "dep" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.transitive] git = '{}' "#, transitive.url() ), ) .file("src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies.dep] git = '{}' "#, dep1.url() ), ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/dep1` [UPDATING] git repository `[ROOTURL]/transitive` [LOCKING] 2 packages to latest compatible versions [CHECKING] transitive v0.5.0 ([ROOTURL]/transitive#[..]) [CHECKING] dep v0.5.0 ([ROOTURL]/dep1#[..]) [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Update the dependency to point to the second repository, but this // shouldn't update the transitive dependency which is the same. p.change_file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies.dep] git = '{}' "#, dep2.url() ), ); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/dep2` [LOCKING] 1 package to latest compatible version [ADDING] dep v0.5.0 ([ROOTURL]/dep2#[..]) [CHECKING] dep v0.5.0 ([ROOTURL]/dep2#[..]) [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn update_one_source_updates_all_packages_in_that_git_source() { let dep = git::new("dep", |project| { project .file( "Cargo.toml", r#" [package] name = "dep" version = "0.5.0" edition = "2015" authors = [] [dependencies.a] path = "a" "#, ) .file("src/lib.rs", "") .file("a/Cargo.toml", &basic_manifest("a", "0.5.0")) .file("a/src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies.dep] git = '{}' "#, dep.url() ), ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check").run(); let repo = git2::Repository::open(&dep.root()).unwrap(); let rev1 = repo.revparse_single("HEAD").unwrap().id(); // Just be sure to change a file dep.change_file("src/lib.rs", "pub fn bar() -> i32 { 2 }"); git::add(&repo); git::commit(&repo); p.cargo("update dep").run(); let lockfile = p.read_lockfile(); assert!( !lockfile.contains(&rev1.to_string()), "{} in {}", rev1, lockfile ); } #[cargo_test] fn switch_sources() { let a1 = git::new("a1", |project| { project .file("Cargo.toml", &basic_manifest("a", "0.5.0")) .file("src/lib.rs", "") }); let a2 = git::new("a2", |project| { project .file("Cargo.toml", &basic_manifest("a", "0.5.1")) .file("src/lib.rs", "") }); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies.b] path = "b" "#, ) .file("src/main.rs", "fn main() {}") .file( "b/Cargo.toml", &format!( r#" [package] name = "b" version = "0.5.0" edition = "2015" authors = [] [dependencies.a] git = '{}' "#, a1.url() ), ) .file("b/src/lib.rs", "pub fn main() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/a1` [LOCKING] 2 packages to latest compatible versions [CHECKING] a v0.5.0 ([ROOTURL]/a1#[..]) [CHECKING] b v0.5.0 ([ROOT]/foo/b) [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.change_file( "b/Cargo.toml", &format!( r#" [package] name = "b" version = "0.5.0" edition = "2015" authors = [] [dependencies.a] git = '{}' "#, a2.url() ), ); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/a2` [LOCKING] 1 package to latest compatible version [ADDING] a v0.5.1 ([ROOTURL]/a2#[..]) [CHECKING] a v0.5.1 ([ROOTURL]/a2#[..]) [CHECKING] b v0.5.0 ([ROOT]/foo/b) [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn dont_require_submodules_are_checked_out() { let p = project().build(); let git1 = git::new("dep1", |p| { p.file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] build = "build.rs" "#, ) .file("build.rs", "fn main() {}") .file("src/lib.rs", "") .file("a/foo", "") }); let git2 = git::new("dep2", |p| p); let repo = git2::Repository::open(&git1.root()).unwrap(); let url = git2.root().to_url().to_string(); git::add_submodule(&repo, &url, Path::new("a/submodule")); git::commit(&repo); git2::Repository::init(&p.root()).unwrap(); let url = git1.root().to_url().to_string(); let dst = paths::home().join("foo"); git2::Repository::clone(&url, &dst).unwrap(); git1.cargo("check -v").cwd(&dst).run(); } #[cargo_test] fn doctest_same_name() { let a2 = git::new("a2", |p| { p.file("Cargo.toml", &basic_manifest("a", "0.5.0")) .file("src/lib.rs", "pub fn a2() {}") }); let a1 = git::new("a1", |p| { p.file( "Cargo.toml", &format!( r#" [package] name = "a" version = "0.5.0" edition = "2015" authors = [] [dependencies] a = {{ git = '{}' }} "#, a2.url() ), ) .file("src/lib.rs", "extern crate a; pub fn a1() {}") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] a = {{ git = '{}' }} "#, a1.url() ), ) .file( "src/lib.rs", r#" #[macro_use] extern crate a; "#, ) .build(); p.cargo("test -v").run(); } #[cargo_test] fn lints_are_suppressed() { let a = git::new("a", |p| { p.file("Cargo.toml", &basic_manifest("a", "0.5.0")).file( "src/lib.rs", " use std::option; ", ) }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] a = {{ git = '{}' }} "#, a.url() ), ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/a` [LOCKING] 1 package to latest compatible version [CHECKING] a v0.5.0 ([ROOTURL]/a#[..]) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn denied_lints_are_allowed() { let a = git::new("a", |p| { p.file("Cargo.toml", &basic_manifest("a", "0.5.0")).file( "src/lib.rs", " #![deny(warnings)] use std::option; ", ) }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] a = {{ git = '{}' }} "#, a.url() ), ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/a` [LOCKING] 1 package to latest compatible version [CHECKING] a v0.5.0 ([ROOTURL]/a#[..]) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn add_a_git_dep() { let git = git::new("git", |p| { p.file("Cargo.toml", &basic_manifest("git", "0.5.0")) .file("src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] a = {{ path = 'a' }} git = {{ git = '{}' }} "#, git.url() ), ) .file("src/lib.rs", "") .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) .file("a/src/lib.rs", "") .build(); p.cargo("check").run(); assert!(paths::home().join(".cargo/git/CACHEDIR.TAG").is_file()); p.change_file( "a/Cargo.toml", &format!( r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] [dependencies] git = {{ git = '{}' }} "#, git.url() ), ); p.cargo("check").run(); } #[cargo_test] fn two_at_rev_instead_of_tag() { let git = git::new("git", |p| { p.file("Cargo.toml", &basic_manifest("git1", "0.5.0")) .file("src/lib.rs", "") .file("a/Cargo.toml", &basic_manifest("git2", "0.5.0")) .file("a/src/lib.rs", "") }); // Make a tag corresponding to the current HEAD let repo = git2::Repository::open(&git.root()).unwrap(); let head = repo.head().unwrap().target().unwrap(); repo.tag( "v0.1.0", &repo.find_object(head, None).unwrap(), &repo.signature().unwrap(), "make a new tag", false, ) .unwrap(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] git1 = {{ git = '{0}', rev = 'v0.1.0' }} git2 = {{ git = '{0}', rev = 'v0.1.0' }} "#, git.url() ), ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); p.cargo("check -v").run(); } #[cargo_test] fn include_overrides_gitignore() { // Make sure that `package.include` takes precedence over .gitignore. let p = git::new("foo", |repo| { repo.file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" include = ["src/lib.rs", "ignored.txt", "Cargo.toml"] "#, ) .file( ".gitignore", r#" /target Cargo.lock ignored.txt "#, ) .file("src/lib.rs", "") .file("ignored.txt", "") .file("build.rs", "fn main() {}") }); p.cargo("check").run(); p.change_file("ignored.txt", "Trigger rebuild."); p.cargo("check -v") .with_stderr_data(str![[r#" [DIRTY] foo v0.5.0 ([ROOT]/foo): the precalculated components changed [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("package --list --allow-dirty") .with_stdout_data(str![[r#" .cargo_vcs_info.json Cargo.lock Cargo.toml Cargo.toml.orig ignored.txt src/lib.rs "#]]) .run(); } #[cargo_test] fn invalid_git_dependency_manifest() { let project = project(); let git_project = git::new("dep1", |project| { project .file( "Cargo.toml", r#" [package] name = "dep1" version = "0.5.0" edition = "2015" authors = ["carlhuda@example.com"] categories = ["algorithms"] categories = ["algorithms"] [lib] name = "dep1" "#, ) .file( "src/dep1.rs", r#" pub fn hello() -> &'static str { "hello world" } "#, ) }); let project = project .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.dep1] git = '{}' "#, git_project.url() ), ) .file( "src/main.rs", &main_file(r#""{}", dep1::hello()"#, &["dep1"]), ) .build(); project .cargo("check") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/dep1` [ERROR] duplicate key --> ../home/.cargo/git/checkouts/dep1-[HASH]/[..]/Cargo.toml:9:21 | 9 | categories = ["algorithms"] | ^^^^^^^^^^ | [ERROR] failed to get `dep1` as a dependency of package `foo v0.5.0 ([ROOT]/foo)` Caused by: failed to load source for dependency `dep1` Caused by: Unable to update [ROOTURL]/dep1 "#]]) .run(); } #[cargo_test] fn failed_submodule_checkout() { let project = project(); let git_project = git::new("dep1", |project| { project.file("Cargo.toml", &basic_manifest("dep1", "0.5.0")) }); let git_project2 = git::new("dep2", |project| project.file("lib.rs", "")); let listener = TcpListener::bind("127.0.0.1:0").unwrap(); let addr = listener.local_addr().unwrap(); let done = Arc::new(AtomicBool::new(false)); let done2 = done.clone(); let t = thread::spawn(move || { while !done2.load(Ordering::SeqCst) { if let Ok((mut socket, _)) = listener.accept() { drop(socket.write_all(b"foo\r\n")); } } }); let repo = git2::Repository::open(&git_project2.root()).unwrap(); let url = format!("https://{}:{}/", addr.ip(), addr.port()); { let mut s = repo.submodule(&url, Path::new("bar"), false).unwrap(); let subrepo = s.open().unwrap(); let mut cfg = subrepo.config().unwrap(); cfg.set_str("user.email", "foo@bar.com").unwrap(); cfg.set_str("user.name", "Foo Bar").unwrap(); git::commit(&subrepo); s.add_finalize().unwrap(); } git::commit(&repo); drop((repo, url)); let repo = git2::Repository::open(&git_project.root()).unwrap(); let url = git_project2.root().to_url().to_string(); git::add_submodule(&repo, &url, Path::new("src")); git::commit(&repo); drop(repo); let project = project .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies] dep1 = {{ git = '{}' }} "#, git_project.url() ), ) .file("src/lib.rs", "") .build(); project .cargo("check") .with_status(101) .with_stderr_data(str![[r#" ... failed to update submodule `src` ... failed to update submodule `bar` ... "#]]) .run(); project .cargo("check") .with_status(101) .with_stderr_data(str![[r#" ... failed to update submodule `src` ... failed to update submodule `bar` ... "#]]) .run(); done.store(true, Ordering::SeqCst); drop(TcpStream::connect(&addr)); t.join().unwrap(); } #[cargo_test(requires = "git")] fn use_the_cli() { let project = project(); let git_project = git::new("dep1", |project| { project .file("Cargo.toml", &basic_manifest("dep1", "0.5.0")) .file("src/lib.rs", "") }); let project = project .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies] dep1 = {{ git = '{}' }} "#, git_project.url() ), ) .file("src/lib.rs", "") .file( ".cargo/config.toml", " [net] git-fetch-with-cli = true ", ) .build(); let stderr = str![[r#" [UPDATING] git repository `[ROOTURL]/dep1` [RUNNING] `git fetch --no-tags --verbose --force --update-head-ok [..][ROOTURL]/dep1[..] [..]+HEAD:refs/remotes/origin/HEAD[..]` From [ROOTURL]/dep1 * [new ref] [..] -> origin/HEAD[..] [LOCKING] 1 package to latest compatible version [CHECKING] dep1 v0.5.0 ([ROOTURL]/dep1#[..]) [RUNNING] `rustc --crate-name dep1 [..]` [CHECKING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]; project .cargo("check -v") .env("LC_ALL", "C") .with_stderr_data(stderr) .run(); assert!(paths::home().join(".cargo/git/CACHEDIR.TAG").is_file()); } #[cargo_test] fn templatedir_doesnt_cause_problems() { let git_project2 = git::new("dep2", |project| { project .file("Cargo.toml", &basic_manifest("dep2", "0.5.0")) .file("src/lib.rs", "") }); let git_project = git::new("dep1", |project| { project .file("Cargo.toml", &basic_manifest("dep1", "0.5.0")) .file("src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "fo" version = "0.5.0" edition = "2015" authors = [] [dependencies] dep1 = {{ git = '{}' }} "#, git_project.url() ), ) .file("src/main.rs", "fn main() {}") .build(); fs::write( paths::home().join(".gitconfig"), format!( r#" [init] templatedir = {} "#, git_project2 .url() .to_file_path() .unwrap() .to_str() .unwrap() .replace("\\", "/") ), ) .unwrap(); p.cargo("check").run(); } #[cargo_test(requires = "git")] fn git_with_cli_force() { // Supports a force-pushed repo. let git_project = git::new("dep1", |project| { project .file("Cargo.toml", &basic_lib_manifest("dep1")) .file("src/lib.rs", r#"pub fn f() { println!("one"); }"#) }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2018" [dependencies] dep1 = {{ git = "{}" }} "#, git_project.url() ), ) .file("src/main.rs", "fn main() { dep1::f(); }") .file( ".cargo/config.toml", " [net] git-fetch-with-cli = true ", ) .build(); p.cargo("build").run(); p.rename_run("foo", "foo1") .with_stdout_data(str![[r#" one "#]]) .run(); // commit --amend a change that will require a force fetch. let repo = git2::Repository::open(&git_project.root()).unwrap(); git_project.change_file("src/lib.rs", r#"pub fn f() { println!("two"); }"#); git::add(&repo); let id = repo.refname_to_id("HEAD").unwrap(); let commit = repo.find_commit(id).unwrap(); let tree_id = t!(t!(repo.index()).write_tree()); t!(commit.amend( Some("HEAD"), None, None, None, None, Some(&t!(repo.find_tree(tree_id))) )); // Perform the fetch. p.cargo("update").run(); p.cargo("build").run(); p.rename_run("foo", "foo2") .with_stdout_data(str![[r#" two "#]]) .run(); } #[cargo_test(requires = "git")] fn git_fetch_cli_env_clean() { // This tests that git-fetch-with-cli works when GIT_DIR environment // variable is set (for whatever reason). let git_dep = git::new("dep1", |project| { project .file("Cargo.toml", &basic_manifest("dep1", "0.5.0")) .file("src/lib.rs", "") }); let git_proj = git::new("foo", |project| { project .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] dep1 = {{ git = '{}' }} "#, git_dep.url() ), ) .file("src/lib.rs", "pub extern crate dep1;") .file( ".cargo/config.toml", " [net] git-fetch-with-cli = true ", ) }); // The directory set here isn't too important. Pointing to our own git // directory causes git to be confused and fail. Can also point to an // empty directory, or a nonexistent one. git_proj .cargo("fetch") .env("GIT_DIR", git_proj.root().join(".git")) .run(); } #[cargo_test] fn dirty_submodule() { // `cargo package` warns for dirty file in submodule. let (git_project, repo) = git::new_repo("foo", |project| { project .file("Cargo.toml", &basic_manifest("foo", "0.5.0")) // This is necessary because `git::add` is too eager. .file(".gitignore", "/target") }); let git_project2 = git::new("src", |project| { project.no_manifest().file("lib.rs", "pub fn f() {}") }); let url = git_project2.root().to_url().to_string(); git::add_submodule(&repo, &url, Path::new("src")); // Submodule added, but not committed. git_project .cargo("package --no-verify") .with_status(101) .with_stderr_data(str![[r#" [WARNING] manifest has no description, license, license-file, documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [ERROR] 2 files in the working directory contain changes that were not yet committed into git: .gitmodules src/lib.rs to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag "#]]) .run(); git::commit(&repo); git_project.cargo("package --no-verify").run(); // Modify file, check for warning. git_project.change_file("src/lib.rs", ""); git_project .cargo("package --no-verify") .with_status(101) .with_stderr_data(str![[r#" [WARNING] manifest has no description, license, license-file, documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [ERROR] 1 files in the working directory contain changes that were not yet committed into git: src/lib.rs to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag "#]]) .run(); // Commit the change. let sub_repo = git2::Repository::open(git_project.root().join("src")).unwrap(); git::add(&sub_repo); git::commit(&sub_repo); git::add(&repo); git::commit(&repo); git_project.cargo("package --no-verify").run(); // Try with a nested submodule. let git_project3 = git::new("bar", |project| project.no_manifest().file("mod.rs", "")); let url = git_project3.root().to_url().to_string(); git::add_submodule(&sub_repo, &url, Path::new("bar")); git_project .cargo("package --no-verify") .with_status(101) .with_stderr_data(str![[r#" [WARNING] manifest has no description, license, license-file, documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [ERROR] 2 files in the working directory contain changes that were not yet committed into git: src/.gitmodules src/bar/mod.rs to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag "#]]) .run(); // Commit the submodule addition. git::commit(&sub_repo); git::add(&repo); git::commit(&repo); git_project.cargo("package --no-verify").run(); // Modify within nested submodule. git_project.change_file("src/bar/new_file.rs", "//test"); git_project .cargo("package --no-verify") .with_status(101) .with_stderr_data(str![[r#" [WARNING] manifest has no description, license, license-file, documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [ERROR] 1 files in the working directory contain changes that were not yet committed into git: src/bar/new_file.rs to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag "#]]) .run(); // And commit the change. let sub_sub_repo = git2::Repository::open(git_project.root().join("src/bar")).unwrap(); git::add(&sub_sub_repo); git::commit(&sub_sub_repo); git::add(&sub_repo); git::commit(&sub_repo); git::add(&repo); git::commit(&repo); git_project.cargo("package --no-verify").run(); } #[cargo_test] fn default_not_master() { let project = project(); // Create a repository with a `master` branch, but switch the head to a // branch called `main` at the same time. let (git_project, repo) = git::new_repo("dep1", |project| { project .file("Cargo.toml", &basic_lib_manifest("dep1")) .file("src/lib.rs", "pub fn foo() {}") }); let head_id = repo.head().unwrap().target().unwrap(); let head = repo.find_commit(head_id).unwrap(); repo.branch("main", &head, false).unwrap(); repo.set_head("refs/heads/main").unwrap(); // Then create a commit on the new `main` branch so `master` and `main` // differ. git_project.change_file("src/lib.rs", "pub fn bar() {}"); git::add(&repo); git::commit(&repo); let project = project .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" [dependencies] dep1 = {{ git = '{}' }} "#, git_project.url() ), ) .file("src/lib.rs", "pub fn foo() { dep1::bar() }") .build(); project .cargo("check") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/dep1` [LOCKING] 1 package to latest compatible version [CHECKING] dep1 v0.5.0 ([ROOTURL]/dep1#[..]) [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn historical_lockfile_works() { let project = project(); let (git_project, repo) = git::new_repo("dep1", |project| { project .file("Cargo.toml", &basic_lib_manifest("dep1")) .file("src/lib.rs", "") }); let head_id = repo.head().unwrap().target().unwrap(); let project = project .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" [dependencies] dep1 = {{ git = '{}', branch = 'master' }} "#, git_project.url() ), ) .file("src/lib.rs", "") .build(); project.cargo("check").run(); project.change_file( "Cargo.lock", &format!( r#"# This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "dep1" version = "0.5.0" source = "git+{}#{}" [[package]] name = "foo" version = "0.5.0" dependencies = [ "dep1", ] "#, git_project.url(), head_id ), ); project .cargo("check") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [ADDING] dep1 v0.5.0 ([ROOTURL]/dep1?branch=master#[..]) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn historical_lockfile_works_with_vendor() { let project = project(); let (git_project, repo) = git::new_repo("dep1", |project| { project .file("Cargo.toml", &basic_lib_manifest("dep1")) .file("src/lib.rs", "") }); let head_id = repo.head().unwrap().target().unwrap(); let project = project .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" [dependencies] dep1 = {{ git = '{}', branch = 'master' }} "#, git_project.url() ), ) .file("src/lib.rs", "") .build(); let output = project.cargo("vendor").run(); project.change_file( ".cargo/config.toml", str::from_utf8(&output.stdout).unwrap(), ); project.change_file( "Cargo.lock", &format!( r#"# This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "dep1" version = "0.5.0" source = "git+{}#{}" [[package]] name = "foo" version = "0.5.0" dependencies = [ "dep1", ] "#, git_project.url(), head_id ), ); project.cargo("check").run(); } #[cargo_test] fn two_dep_forms() { let project = project(); let (git_project, _repo) = git::new_repo("dep1", |project| { project .file("Cargo.toml", &basic_lib_manifest("dep1")) .file("src/lib.rs", "") }); let project = project .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" [dependencies] dep1 = {{ git = '{}', branch = 'master' }} a = {{ path = 'a' }} "#, git_project.url() ), ) .file("src/lib.rs", "") .file( "a/Cargo.toml", &format!( r#" [package] name = "a" version = "0.5.0" edition = "2015" [dependencies] dep1 = {{ git = '{}' }} "#, git_project.url() ), ) .file("a/src/lib.rs", "") .build(); // This'll download the git repository twice, one with HEAD and once with // the master branch. Then it'll compile 4 crates, the 2 git deps, then // the two local deps. project .cargo("check") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/dep1` [UPDATING] git repository `[ROOTURL]/dep1` [LOCKING] 3 packages to latest compatible versions [CHECKING] dep1 v0.5.0 ([ROOTURL]/dep1#[..]) [CHECKING] dep1 v0.5.0 ([ROOTURL]/dep1?branch=master#[..]) [CHECKING] a v0.5.0 ([ROOT]/foo/a) [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn metadata_master_consistency() { // SourceId consistency in the `cargo metadata` output when `master` is // explicit or implicit, using new or old Cargo.lock. let (git_project, git_repo) = git::new_repo("bar", |project| { project .file("Cargo.toml", &basic_manifest("bar", "1.0.0")) .file("src/lib.rs", "") }); let bar_hash = git_repo.head().unwrap().target().unwrap().to_string(); // Explicit branch="master" with a lock file created before 1.47 (does not contain ?branch=master). let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = {{ git = "{}", branch = "master" }} "#, git_project.url() ), ) .file( "Cargo.lock", &format!( r#" [[package]] name = "bar" version = "1.0.0" source = "git+{}#{}" [[package]] name = "foo" version = "0.1.0" dependencies = [ "bar", ] "#, git_project.url(), bar_hash, ), ) .file("src/lib.rs", "") .build(); let metadata = |bar_source| -> String { r#" { "packages": [ { "name": "bar", "version": "1.0.0", "id": "__BAR_SOURCE__#1.0.0", "license": null, "license_file": null, "description": null, "source": "__BAR_SOURCE__#__BAR_HASH__", "dependencies": [], "targets": "{...}", "features": {}, "manifest_path": "[..]", "metadata": null, "publish": null, "authors": [], "categories": [], "default_run": null, "keywords": [], "readme": null, "repository": null, "rust_version": null, "homepage": null, "documentation": null, "edition": "2015", "links": null }, { "name": "foo", "version": "0.1.0", "id": "[..]foo#0.1.0", "license": null, "license_file": null, "description": null, "source": null, "dependencies": [ { "name": "bar", "source": "__BAR_SOURCE__", "req": "*", "kind": null, "rename": null, "optional": false, "uses_default_features": true, "features": [], "target": null, "registry": null } ], "targets": "{...}", "features": {}, "manifest_path": "[..]", "metadata": null, "publish": null, "authors": [], "categories": [], "default_run": null, "keywords": [], "readme": null, "repository": null, "rust_version": null, "homepage": null, "documentation": null, "edition": "2015", "links": null } ], "workspace_members": [ "[..]foo#0.1.0" ], "workspace_default_members": [ "[..]foo#0.1.0" ], "resolve": { "nodes": [ { "id": "__BAR_SOURCE__#1.0.0", "dependencies": [], "deps": [], "features": [] }, { "id": "[..]foo#0.1.0", "dependencies": [ "__BAR_SOURCE__#1.0.0" ], "deps": [ { "name": "bar", "pkg": "__BAR_SOURCE__#1.0.0", "dep_kinds": [ { "kind": null, "target": null } ] } ], "features": [] } ], "root": "[..]foo#0.1.0" }, "target_directory": "[..]", "version": 1, "workspace_root": "[..]", "metadata": null } "# .replace("__BAR_SOURCE__", bar_source) .replace("__BAR_HASH__", &bar_hash) }; let bar_source = "git+[ROOTURL]/bar?branch=master"; p.cargo("metadata") .with_stdout_data(&metadata(&bar_source).is_json()) .run(); // Conversely, remove branch="master" from Cargo.toml, but use a new Cargo.lock that has ?branch=master. let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = {{ git = "{}" }} "#, git_project.url() ), ) .file( "Cargo.lock", &format!( r#" [[package]] name = "bar" version = "1.0.0" source = "git+{}?branch=master#{}" [[package]] name = "foo" version = "0.1.0" dependencies = [ "bar", ] "#, git_project.url(), bar_hash ), ) .file("src/lib.rs", "") .build(); // No ?branch=master! let bar_source = "git+[ROOTURL]/bar"; p.cargo("metadata") .with_stdout_data(&metadata(&bar_source).is_json()) .run(); } #[cargo_test] fn git_with_force_push() { // Checks that cargo can handle force-pushes to git repos. // This works by having a git dependency that is updated with an amend // commit, and tries with various forms (default branch, branch, rev, // tag). let main = |text| format!(r#"pub fn f() {{ println!("{}"); }}"#, text); let (git_project, repo) = git::new_repo("dep1", |project| { project .file("Cargo.toml", &basic_lib_manifest("dep1")) .file("src/lib.rs", &main("one")) }); let manifest = |extra| { format!( r#" [package] name = "foo" version = "0.0.1" edition = "2018" [dependencies] dep1 = {{ git = "{}"{} }} "#, git_project.url(), extra ) }; let p = project() .file("Cargo.toml", &manifest("")) .file("src/main.rs", "fn main() { dep1::f(); }") .build(); // Download the original and make sure it is OK. p.cargo("build").run(); p.rename_run("foo", "foo1") .with_stdout_data(str![[r#" one "#]]) .run(); let find_head = || t!(t!(repo.head()).peel_to_commit()); let amend_commit = |text| { // commit --amend a change that will require a force fetch. git_project.change_file("src/lib.rs", &main(text)); git::add(&repo); let commit = find_head(); let tree_id = t!(t!(repo.index()).write_tree()); t!(commit.amend( Some("HEAD"), None, None, None, None, Some(&t!(repo.find_tree(tree_id))) )); }; let mut rename_annoyance = 1; let mut verify = |text| { // Perform the fetch. p.cargo("update").run(); p.cargo("build").run(); rename_annoyance += 1; p.rename_run("foo", &format!("foo{}", rename_annoyance)) .with_stdout_data(text) .run(); }; amend_commit("two"); verify(str![[r#" two "#]]); // Try with a rev. let head1 = find_head().id().to_string(); let extra = format!(", rev = \"{}\"", head1); p.change_file("Cargo.toml", &manifest(&extra)); verify(str![[r#" two "#]]); amend_commit("three"); let head2 = find_head().id().to_string(); assert_ne!(&head1, &head2); let extra = format!(", rev = \"{}\"", head2); p.change_file("Cargo.toml", &manifest(&extra)); verify(str![[r#" three "#]]); // Try with a tag. git::tag(&repo, "my-tag"); p.change_file("Cargo.toml", &manifest(", tag = \"my-tag\"")); verify(str![[r#" three "#]]); amend_commit("tag-three"); let head = t!(t!(repo.head()).peel(git2::ObjectType::Commit)); t!(repo.tag("my-tag", &head, &t!(repo.signature()), "move tag", true)); verify(str![[r#" tag-three "#]]); // Try with a branch. let br = t!(repo.branch("awesome-stuff", &find_head(), false)); t!(repo.checkout_tree(&t!(br.get().peel(git2::ObjectType::Tree)), None)); t!(repo.set_head("refs/heads/awesome-stuff")); git_project.change_file("src/lib.rs", &main("awesome-three")); git::add(&repo); git::commit(&repo); p.change_file("Cargo.toml", &manifest(", branch = \"awesome-stuff\"")); verify(str![[r#" awesome-three "#]]); amend_commit("awesome-four"); verify(str![[r#" awesome-four "#]]); } #[cargo_test] fn corrupted_checkout() { // Test what happens if the checkout is corrupted somehow. _corrupted_checkout(false); } #[cargo_test] fn corrupted_checkout_with_cli() { // Test what happens if the checkout is corrupted somehow with git cli. _corrupted_checkout(true); } fn _corrupted_checkout(with_cli: bool) { let (git_project, repository) = git::new_repo("dep1", |project| { project .file("Cargo.toml", &basic_manifest("dep1", "0.5.0")) .file("src/lib.rs", "") }); let project2 = git::new("dep2", |project| { project.no_manifest().file("README.md", "") }); let url = project2.root().to_url().to_string(); add_submodule(&repository, &url, Path::new("dep2")); git::commit(&repository); drop(repository); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] dep1 = {{ git = "{}" }} "#, git_project.url() ), ) .file("src/lib.rs", "") .build(); p.cargo("fetch").run(); let mut dep1_co_paths = t!(glob::glob( paths::home() .join(".cargo/git/checkouts/dep1-*/*") .to_str() .unwrap() )); let dep1_co_path = dep1_co_paths.next().unwrap().unwrap(); let dep1_ok = dep1_co_path.join(".cargo-ok"); let dep1_manifest = dep1_co_path.join("Cargo.toml"); let dep2_readme = dep1_co_path.join("dep2/README.md"); // Deleting this file simulates an interrupted checkout. t!(fs::remove_file(&dep1_ok)); t!(fs::remove_file(&dep1_manifest)); t!(fs::remove_file(&dep2_readme)); // This should refresh the checkout. let mut e = p.cargo("fetch"); if with_cli { e.env("CARGO_NET_GIT_FETCH_WITH_CLI", "true"); } e.run(); assert!(dep1_ok.exists()); assert!(dep1_manifest.exists()); assert!(dep2_readme.exists()); } #[cargo_test] fn cleans_temp_pack_files() { // Checks that cargo removes temp files left by libgit2 when it is // interrupted (see clean_repo_temp_files). Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("fetch").run(); // Simulate what happens when libgit2 is interrupted while indexing a pack file. let tmp_path = super::git_gc::find_index().join(".git/objects/pack/pack_git2_91ab40da04fdc2e7"); fs::write(&tmp_path, "test").unwrap(); let mut perms = fs::metadata(&tmp_path).unwrap().permissions(); perms.set_readonly(true); fs::set_permissions(&tmp_path, perms).unwrap(); // Trigger an index update. p.cargo("generate-lockfile").run(); assert!(!tmp_path.exists()); } #[cargo_test] fn different_user_relative_submodules() { let user1_git_project = git::new("user1/dep1", |project| { project .file("Cargo.toml", &basic_lib_manifest("dep1")) .file("src/lib.rs", "") }); let user2_git_project = git::new("user2/dep1", |project| { project .file("Cargo.toml", &basic_lib_manifest("dep1")) .file("src/lib.rs", "") }); let _user2_git_project2 = git::new("user2/dep2", |project| { project .file("Cargo.toml", &basic_lib_manifest("dep1")) .file("src/lib.rs", "") }); let user2_repo = git2::Repository::open(&user2_git_project.root()).unwrap(); let url = "../dep2"; git::add_submodule(&user2_repo, url, Path::new("dep2")); git::commit(&user2_repo); let user1_repo = git2::Repository::open(&user1_git_project.root()).unwrap(); let url = user2_git_project.url(); git::add_submodule(&user1_repo, url.as_str(), Path::new("user2/dep1")); git::commit(&user1_repo); let project = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" [dependencies.dep1] git = '{}' "#, user1_git_project.url() ), ) .file("src/main.rs", &main_file(r#""hello""#, &[])) .build(); project .cargo("build") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/user1/dep1` [UPDATING] git submodule `[ROOTURL]/user2/dep1` [UPDATING] git submodule `[ROOTURL]/user2/dep2` [LOCKING] 1 package to latest compatible version [COMPILING] dep1 v0.5.0 ([ROOTURL]/user1/dep1#[..]) [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert!(project.bin("foo").is_file()); } #[cargo_test] fn git_worktree_with_original_repo_renamed() { let project = project().build(); let git_project = git::new("foo", |project| { project .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] license = "MIR OR Apache-2.0" description = "A test!" homepage = "https://example.org" documentation = "" repository = "https://example.org" readme = "./README.md" "#, ) .file("src/lib.rs", "") .file("README.md", "") }); let repo = git2::Repository::open(&git_project.root()).unwrap(); let repo_root = repo.workdir().unwrap().parent().unwrap(); let opts = git2::WorktreeAddOptions::new(); let _ = repo .worktree("bar", &repo_root.join("bar"), Some(&opts)) .unwrap(); // Rename the original repository let new = repo_root.join("foo2"); fs::rename(&git_project.root(), &new).unwrap(); project .cargo("package --list") .cwd(&new) .with_stdout_data(str![[r#" .cargo_vcs_info.json Cargo.lock Cargo.toml Cargo.toml.orig README.md src/lib.rs "#]]) .run(); project .cargo("check") .cwd(&new) .with_stderr_data(str![[r#" [CHECKING] foo v0.5.0 ([ROOT]/foo2) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(public_network_test, requires = "git")] fn github_fastpath_error_message() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bitflags = { git = "https://github.com/rust-lang/bitflags.git", rev="11111b376b93484341c68fbca3ca110ae5cd2790" } "#, ) .file("src/lib.rs", "") .build(); p.cargo("fetch") .env("CARGO_NET_GIT_FETCH_WITH_CLI", "true") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] git repository `https://github.com/rust-lang/bitflags.git` fatal: remote [ERROR] upload-pack: not our ref 11111b376b93484341c68fbca3ca110ae5cd2790 [ERROR] failed to get `bitflags` as a dependency of package `foo v0.1.0 ([ROOT]/foo)` Caused by: failed to load source for dependency `bitflags` Caused by: Unable to update https://github.com/rust-lang/bitflags.git?rev=11111b376b93484341c68fbca3ca110ae5cd2790 Caused by: failed to clone into: [ROOT]/home/.cargo/git/db/bitflags-[HASH] Caused by: revision 11111b376b93484341c68fbca3ca110ae5cd2790 not found Caused by: process didn't exit successfully: `git fetch --no-tags --force --update-head-ok [..] "#]]) .run(); } #[cargo_test(public_network_test)] fn git_fetch_libgit2_error_message() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bitflags = { git = "https://github.com/rust-lang/bitflags.git", rev="11111b376b93484341c68fbca3ca110ae5cd2790" } "#, ) .file("src/lib.rs", "") .build(); p.cargo("fetch") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] git repository `https://github.com/rust-lang/bitflags.git` ... [ERROR] failed to get `bitflags` as a dependency of package `foo v0.1.0 ([ROOT]/foo)` Caused by: failed to load source for dependency `bitflags` Caused by: Unable to update https://github.com/rust-lang/bitflags.git?rev=11111b376b93484341c68fbca3ca110ae5cd2790 Caused by: failed to clone into: [ROOT]/home/.cargo/git/db/bitflags-[HASH] Caused by: revision 11111b376b93484341c68fbca3ca110ae5cd2790 not found ... "#]]) .run(); } #[cargo_test] fn git_worktree_with_bare_original_repo() { let project = project().build(); let git_project = git::new("foo", |project| { project .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] license = "MIR OR Apache-2.0" description = "A test!" homepage = "https://example.org" documentation = "" repository = "https://example.org" readme = "./README.md" "#, ) .file("src/lib.rs", "") .file("README.md", "") }); // Create a "bare" Git repository. // Keep the `.git` folder and delete the others. let repo = { let mut repo_builder = git2::build::RepoBuilder::new(); repo_builder .bare(true) .clone_local(git2::build::CloneLocal::Local) .clone( git_project.root().to_url().as_str(), &paths::root().join("foo-bare"), ) .unwrap() }; assert!(repo.is_bare()); let opts = git2::WorktreeAddOptions::new(); let wt = repo .worktree("bar", &paths::root().join("bar"), Some(&opts)) .unwrap(); project .cargo("package --list") .cwd(wt.path()) .with_stdout_data(str![[r#" .cargo_vcs_info.json Cargo.lock Cargo.toml Cargo.toml.orig README.md src/lib.rs "#]]) .run(); project .cargo("check") .cwd(wt.path()) .with_stderr_data(str![[r#" [CHECKING] foo v0.5.0 ([ROOT]/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] #[cfg(unix)] fn simple_with_fifo() { let git_project = git::new("foo", |project| { project .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" "#, ) .file("src/main.rs", "fn main() {}") }); std::process::Command::new("mkfifo") .current_dir(git_project.root()) .arg(git_project.root().join("blocks-when-read")) .status() .expect("a FIFO can be created"); // Avoid actual blocking even in case of failure, assuming that what it lists here // would also be read eventually. git_project .cargo("package -l") .with_stdout_data(str![[r#" .cargo_vcs_info.json Cargo.lock Cargo.toml Cargo.toml.orig src/main.rs "#]]) .run(); } cargo-0.91.0/tests/testsuite/git_auth.rs000064400000000000000000000336431046102023000163640ustar 00000000000000//! Tests for git authentication. use std::collections::HashSet; use std::io::BufReader; use std::io::prelude::*; use std::net::{SocketAddr, TcpListener}; use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; use std::thread::{self, JoinHandle}; use crate::prelude::*; use cargo_test_support::basic_manifest; use cargo_test_support::git::cargo_uses_gitoxide; use cargo_test_support::paths; use cargo_test_support::project; fn setup_failed_auth_test() -> (SocketAddr, JoinHandle<()>, Arc) { let server = TcpListener::bind("127.0.0.1:0").unwrap(); let addr = server.local_addr().unwrap(); fn headers(rdr: &mut dyn BufRead) -> HashSet { let valid = ["GET", "Authorization", "Accept"]; rdr.lines() .map(|s| s.unwrap()) .take_while(|s| s.len() > 2) .map(|s| s.trim().to_string()) .filter(|s| valid.iter().any(|prefix| s.starts_with(*prefix))) .collect() } let connections = Arc::new(AtomicUsize::new(0)); let connections2 = connections.clone(); let t = thread::spawn(move || { let mut conn = BufReader::new(server.accept().unwrap().0); let req = headers(&mut conn); connections2.fetch_add(1, SeqCst); conn.get_mut() .write_all( b"HTTP/1.1 401 Unauthorized\r\n\ WWW-Authenticate: Basic realm=\"wheee\"\r\n\ Content-Length: 0\r\n\ \r\n", ) .unwrap(); assert_eq!( req, vec![ "GET /foo/bar/info/refs?service=git-upload-pack HTTP/1.1", "Accept: */*", ] .into_iter() .map(|s| s.to_string()) .collect() ); let req = headers(&mut conn); connections2.fetch_add(1, SeqCst); conn.get_mut() .write_all( b"HTTP/1.1 401 Unauthorized\r\n\ WWW-Authenticate: Basic realm=\"wheee\"\r\n\ \r\n", ) .unwrap(); assert_eq!( req, vec![ "GET /foo/bar/info/refs?service=git-upload-pack HTTP/1.1", "Authorization: Basic Zm9vOmJhcg==", "Accept: */*", ] .into_iter() .map(|s| s.to_string()) .collect() ); }); let script = project() .at("script") .file("Cargo.toml", &basic_manifest("script", "0.1.0")) .file( "src/main.rs", r#" fn main() { println!("username=foo"); println!("password=bar"); } "#, ) .build(); script.cargo("build -v").run(); let script = script.bin("script"); let config = paths::home().join(".gitconfig"); let mut config = git2::Config::open(&config).unwrap(); config .set_str( "credential.helper", // This is a bash script so replace `\` with `/` for Windows &script.display().to_string().replace("\\", "/"), ) .unwrap(); (addr, t, connections) } // Tests that HTTP auth is offered from `credential.helper`. #[cargo_test] fn http_auth_offered() { let (addr, t, connections) = setup_failed_auth_test(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] git = "http://127.0.0.1:{}/foo/bar" "#, addr.port() ), ) .file("src/main.rs", "") .file( ".cargo/config.toml", "[net] retry = 0 ", ) .build(); // This is a "contains" check because the last error differs by platform, // may span multiple lines, and isn't relevant to this test. p.cargo("check") .with_status(101) .with_stderr_data(&format!( "\ [UPDATING] git repository `http://{addr}/foo/bar` [ERROR] failed to get `bar` as a dependency of package `foo v0.0.1 ([ROOT]/foo)` Caused by: failed to load source for dependency `bar` Caused by: Unable to update http://{addr}/foo/bar Caused by: failed to clone into: [ROOT]/home/.cargo/git/db/bar-[HASH] Caused by: failed to authenticate when downloading repository * attempted to find username/password via `credential.helper`, but maybe the found credentials were incorrect if the git CLI succeeds then `net.git-fetch-with-cli` may help here https://doc.rust-lang.org/cargo/reference/config.html#netgit-fetch-with-cli Caused by: {trailer} ", trailer = if cargo_uses_gitoxide() { format!(r#"[CREDENTIAL]s provided for "http://{addr}/foo/bar" were not accepted by the remote Caused by: Received HTTP status 401"#) } else { " no authentication methods succeeded".to_string() } )) .run(); assert_eq!(connections.load(SeqCst), 2); t.join().ok().unwrap(); } // Boy, sure would be nice to have a TLS implementation in rust! #[cargo_test] fn https_something_happens() { let server = TcpListener::bind("127.0.0.1:0").unwrap(); let addr = server.local_addr().unwrap(); let t = thread::spawn(move || { let mut conn = server.accept().unwrap().0; drop(conn.write(b"1234")); drop(conn.shutdown(std::net::Shutdown::Write)); drop(conn.read(&mut [0; 16])); }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] git = "https://127.0.0.1:{}/foo/bar" "#, addr.port() ), ) .file("src/main.rs", "") .file( ".cargo/config.toml", "[net] retry = 0 ", ) .build(); p.cargo("check -v") .with_status(101) .with_stderr_data(&format!( "\ [UPDATING] git repository `https://{addr}/foo/bar` [ERROR] failed to get `bar` as a dependency of package `foo v0.0.1 ([ROOT]/foo)` Caused by: failed to load source for dependency `bar` Caused by: Unable to update https://{addr}/foo/bar Caused by: failed to clone into: [ROOT]/home/.cargo/git/db/bar-[HASH] Caused by: {errmsg} ", errmsg = if cargo_uses_gitoxide() { r" network failure seems to have happened if a proxy or similar is necessary `net.git-fetch-with-cli` may help here https://doc.rust-lang.org/cargo/reference/config.html#netgit-fetch-with-cli Caused by: An IO error occurred when talking to the server Caused by: [35] SSL connect error ([..])" } else if cfg!(windows) { "[..]failed to send request: [..]\n..." } else if cfg!(target_os = "macos") { // macOS is difficult to tests as some builds may use Security.framework, // while others may use OpenSSL. In that case, let's just not verify the error // message here. "..." } else { "[..]SSL [ERROR][..]" } )) .run(); t.join().ok().unwrap(); } // It would sure be nice to have an SSH implementation in Rust! #[cargo_test] fn ssh_something_happens() { let server = TcpListener::bind("127.0.0.1:0").unwrap(); let addr = server.local_addr().unwrap(); let t = thread::spawn(move || { drop(server.accept().unwrap()); }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] git = "ssh://127.0.0.1:{}/foo/bar" "#, addr.port() ), ) .file("src/main.rs", "") .build(); let expected = if cargo_uses_gitoxide() { // Due to the usage of `ssh` and `ssh.exe` respectively, the messages change. // This will be adjusted to use `ssh2` to get rid of this dependency and have uniform messaging. let message = if cfg!(windows) { // The order of multiple possible messages isn't deterministic within `ssh`, and `gitoxide` detects both // but gets to report only the first. Thus this test can flip-flop from one version of the error to the other // and we can't test for that. // We'd want to test for: // "[..]ssh: connect to host 127.0.0.1 [..]" // ssh: connect to host example.org port 22: No route to host // "[..]banner exchange: Connection to 127.0.0.1 [..]" // banner exchange: Connection to 127.0.0.1 port 62250: Software caused connection abort // But since there is no common meaningful sequence or word, we can only match a small telling sequence of characters. "[..]onnect[..]" } else { "[..]Connection [..] by [..]" }; format!( "\ [UPDATING] git repository `ssh://{addr}/foo/bar` ... {message} ... " ) } else { format!( "\ [UPDATING] git repository `ssh://{addr}/foo/bar` [ERROR] failed to get `bar` as a dependency of package `foo v0.0.1 ([ROOT]/foo)` Caused by: failed to load source for dependency `bar` Caused by: Unable to update ssh://{addr}/foo/bar Caused by: failed to clone into: [ROOT]/home/.cargo/git/db/bar-[HASH] Caused by: network failure seems to have happened if a proxy or similar is necessary `net.git-fetch-with-cli` may help here https://doc.rust-lang.org/cargo/reference/config.html#netgit-fetch-with-cli Caused by: failed to start SSH session: Failed getting banner; class=Ssh (23) " ) }; p.cargo("check -v") .with_status(101) .with_stderr_data(expected) .run(); t.join().ok().unwrap(); } #[cargo_test] fn net_err_suggests_fetch_with_cli() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [dependencies] foo = { git = "ssh://needs-proxy.invalid/git" } "#, ) .file("src/lib.rs", "") .build(); p.cargo("check -v") .with_status(101) .with_stderr_data(format!( "\ [UPDATING] git repository `ssh://needs-proxy.invalid/git` [WARNING] spurious network error (3 tries remaining): [..] resolve [..] needs-proxy.invalid: [..] known[..] [WARNING] spurious network error (2 tries remaining): [..] resolve [..] needs-proxy.invalid: [..] known[..] [WARNING] spurious network error (1 try remaining): [..] resolve [..] needs-proxy.invalid: [..] known[..] [ERROR] failed to get `foo` as a dependency of package `foo v0.0.0 ([ROOT]/foo)` Caused by: failed to load source for dependency `foo` Caused by: Unable to update ssh://needs-proxy.invalid/git Caused by: failed to clone into: [ROOT]/home/.cargo/git/db/git-[HASH] Caused by: network failure seems to have happened if a proxy or similar is necessary `net.git-fetch-with-cli` may help here https://doc.rust-lang.org/cargo/reference/config.html#netgit-fetch-with-cli Caused by: {trailer} ", trailer = if cargo_uses_gitoxide() { r" An IO error occurred when talking to the server Caused by: ssh: Could not resolve hostname needs-proxy.invalid[..]" } else { " failed to resolve address for needs-proxy.invalid: [..] known[..]; class=Net (12)" } )) .run(); p.change_file( ".cargo/config.toml", " [net] git-fetch-with-cli = true ", ); p.cargo("check -v") .with_status(101) .with_stderr_contains("[..]Unable to update[..]") .with_stderr_does_not_contain("[..]try enabling `git-fetch-with-cli`[..]") .run(); } #[cargo_test] fn instead_of_url_printed() { let (addr, t, _connections) = setup_failed_auth_test(); let config = paths::home().join(".gitconfig"); let mut config = git2::Config::open(&config).unwrap(); config .set_str( &format!("url.http://{}/.insteadOf", addr), "https://foo.bar/", ) .unwrap(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] git = "https://foo.bar/foo/bar" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(&format!( "\ [UPDATING] git repository `https://foo.bar/foo/bar` [ERROR] failed to get `bar` as a dependency of package `foo v0.0.1 ([ROOT]/foo)` Caused by: failed to load source for dependency `bar` Caused by: Unable to update https://foo.bar/foo/bar Caused by: failed to clone into: [ROOT]/home/.cargo/git/db/bar-[HASH] Caused by: failed to authenticate when downloading repository: http://{addr}/foo/bar * attempted to find username/password via `credential.helper`, but maybe the found credentials were incorrect if the git CLI succeeds then `net.git-fetch-with-cli` may help here https://doc.rust-lang.org/cargo/reference/config.html#netgit-fetch-with-cli Caused by: ... " )) .run(); t.join().ok().unwrap(); } cargo-0.91.0/tests/testsuite/git_gc.rs000064400000000000000000000061531046102023000160100ustar 00000000000000//! Tests for git garbage collection. use std::env; use std::ffi::OsStr; use std::path::PathBuf; use crate::prelude::*; use cargo_test_support::git; use cargo_test_support::git::cargo_uses_gitoxide; use cargo_test_support::paths; use cargo_test_support::project; use cargo_test_support::registry::Package; use url::Url; pub fn find_index() -> PathBuf { let dir = paths::home().join(".cargo/registry/index"); dir.read_dir().unwrap().next().unwrap().unwrap().path() } fn run_test(path_env: Option<&OsStr>) { const N: usize = 50; let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [dependencies] bar = "*" "#, ) .file("src/lib.rs", "") .build(); Package::new("bar", "0.1.0").publish(); foo.cargo("check").run(); let index = find_index(); let path = paths::home().join("tmp"); let url = Url::from_file_path(&path).unwrap().to_string(); let repo = git2::Repository::init(&path).unwrap(); let index = git2::Repository::open(&index).unwrap(); let mut cfg = repo.config().unwrap(); cfg.set_str("user.email", "foo@bar.com").unwrap(); cfg.set_str("user.name", "Foo Bar").unwrap(); let mut cfg = index.config().unwrap(); cfg.set_str("user.email", "foo@bar.com").unwrap(); cfg.set_str("user.name", "Foo Bar").unwrap(); for _ in 0..N { git::commit(&repo); index .remote_anonymous(&url) .unwrap() .fetch(&["refs/heads/master:refs/remotes/foo/master"], None, None) .unwrap(); } drop((repo, index)); Package::new("bar", "0.1.1").publish(); let before = find_index() .join(".git/objects/pack") .read_dir() .unwrap() .count(); assert!(before > N); let mut cmd = foo.cargo("update"); cmd.env("__CARGO_PACKFILE_LIMIT", "10"); if let Some(path) = path_env { cmd.env("PATH", path); } cmd.env("CARGO_LOG", "trace"); cmd.run(); let after = find_index() .join(".git/objects/pack") .read_dir() .unwrap() .count(); assert!( after < before, "packfiles before: {}\n\ packfiles after: {}", before, after ); } #[cargo_test(requires = "git")] fn use_git_gc() { run_test(None); } #[cargo_test] fn avoid_using_git() { if cargo_uses_gitoxide() { // file protocol without git binary is currently not possible - needs built-in upload-pack. // See https://github.com/Byron/gitoxide/issues/734 (support for the file protocol) progress updates. return; } let path = env::var_os("PATH").unwrap_or_default(); let mut paths = env::split_paths(&path).collect::>(); let idx = paths .iter() .position(|p| p.join("git").exists() || p.join("git.exe").exists()); match idx { Some(i) => { paths.remove(i); } None => return, } run_test(Some(&env::join_paths(&paths).unwrap())); } cargo-0.91.0/tests/testsuite/git_shallow.rs000064400000000000000000000573611046102023000170770ustar 00000000000000use crate::prelude::*; use cargo_test_support::registry::Package; use cargo_test_support::{basic_manifest, git, paths, project}; use crate::git_gc::find_index; enum Backend { Git2, Gitoxide, } impl Backend { fn to_arg(&self) -> &'static str { match self { Backend::Git2 => "", Backend::Gitoxide => "-Zgitoxide=fetch", } } } enum RepoMode { Shallow, Complete, } impl RepoMode { fn to_deps_arg(&self) -> &'static str { match self { RepoMode::Complete => "", RepoMode::Shallow => "-Zgit=shallow-deps", } } } #[cargo_test] fn gitoxide_fetch_shallow_dep_two_revs() { fetch_dep_two_revs(Backend::Gitoxide, RepoMode::Shallow) } #[cargo_test] fn git2_fetch_complete_dep_two_revs() { fetch_dep_two_revs(Backend::Git2, RepoMode::Complete) } fn fetch_dep_two_revs(backend: Backend, mode: RepoMode) { let bar = git::new("meta-dep", |project| { project .file("Cargo.toml", &basic_manifest("bar", "0.0.0")) .file("src/lib.rs", "pub fn bar() -> i32 { 1 }") }); let repo = git2::Repository::open(&bar.root()).unwrap(); let rev1 = repo.revparse_single("HEAD").unwrap().id(); // Commit the changes and make sure we trigger a recompile bar.change_file("src/lib.rs", "pub fn bar() -> i32 { 2 }"); git::add(&repo); let rev2 = git::commit(&repo); let foo = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.0" authors = [] [dependencies.bar] git = '{}' rev = "{}" [dependencies.baz] path = "../baz" "#, bar.url(), rev1 ), ) .file( "src/main.rs", r#" extern crate bar; extern crate baz; fn main() { assert_eq!(bar::bar(), 1); assert_eq!(baz::baz(), 2); } "#, ) .build(); let _baz = project() .at("baz") .file( "Cargo.toml", &format!( r#" [package] name = "baz" version = "0.0.0" authors = [] [dependencies.bar] git = '{}' rev = "{}" "#, bar.url(), rev2 ), ) .file( "src/lib.rs", r#" extern crate bar; pub fn baz() -> i32 { bar::bar() } "#, ) .build(); foo.cargo("check -v") .arg_line(backend.to_arg()) .arg_line(mode.to_deps_arg()) .env("__CARGO_USE_GITOXIDE_INSTEAD_OF_GIT2", "0") // respect `backend` .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-deps"]) .run(); } #[cargo_test] fn gitoxide_fetch_shallow_dep_branch_and_rev() -> anyhow::Result<()> { let (bar, bar_repo) = git::new_repo("bar", |p| { p.file("Cargo.toml", &basic_manifest("bar", "1.0.0")) .file("src/lib.rs", "") }); // this commit would not be available in a shallow fetch. let first_commit_pre_change = bar_repo.head().unwrap().target().unwrap(); bar.change_file("src/lib.rs", "// change"); git::add(&bar_repo); git::commit(&bar_repo); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [dependencies] bar-renamed = {{ package = "bar", git = "{}", rev = "{}" }} bar = {{ git = "{}", branch = "master" }} "#, bar.url(), first_commit_pre_change, bar.url(), ), ) .file("src/lib.rs", "") .build(); p.cargo("check") .arg("-Zgitoxide=fetch") .arg("-Zgit=shallow-deps") .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-deps"]) .run(); let db_paths = glob::glob(paths::home().join(".cargo/git/db/bar-*").to_str().unwrap())? .map(Result::unwrap) .collect::>(); assert_eq!( db_paths.len(), 1, "only one db checkout source is used per dependency" ); let db_clone = gix::open_opts(&db_paths[0], gix::open::Options::isolated())?; assert!( db_clone.is_shallow(), "the repo is shallow while having all data it needs" ); Ok(()) } #[cargo_test] fn gitoxide_fetch_shallow_dep_branch_to_rev() -> anyhow::Result<()> { // db exists from previous build, then dependency changes to refer to revision that isn't // available in the shallow fetch. let (bar, bar_repo) = git::new_repo("bar", |p| { p.file("Cargo.toml", &basic_manifest("bar", "1.0.0")) .file("src/lib.rs", "") }); // this commit would not be available in a shallow fetch. let first_commit_pre_change = bar_repo.head().unwrap().target().unwrap(); bar.change_file("src/lib.rs", "// change"); git::add(&bar_repo); git::commit(&bar_repo); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [dependencies] bar = {{ git = "{}", branch = "master" }} "#, bar.url(), ), ) .file("src/lib.rs", "") .build(); p.cargo("check") .arg("-Zgitoxide=fetch") .arg("-Zgit=shallow-deps") .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-deps"]) .run(); let db_clone = gix::open_opts( find_bar_db(RepoMode::Shallow), gix::open::Options::isolated(), )?; assert!(db_clone.is_shallow()); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [dependencies] bar = {{ git = "{}", rev = "{}" }} "#, bar.url(), first_commit_pre_change ), ) .file("src/lib.rs", "") .build(); p.cargo("check") .arg("-Zgitoxide=fetch") .arg("-Zgit=shallow-deps") .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-deps"]) .run(); assert!( db_clone.is_shallow(), "we maintain shallowness and never unshallow" ); Ok(()) } #[cargo_test] fn gitoxide_fetch_shallow_index_then_git2_fetch_complete() -> anyhow::Result<()> { Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("fetch") .arg("-Zgitoxide=fetch") .arg("-Zgit=shallow-index") .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-index"]) .run(); let shallow_repo = gix::open_opts(find_index(), gix::open::Options::isolated())?; assert_eq!( shallow_repo .rev_parse_single("origin/HEAD")? .ancestors() .all()? .count(), 1, "shallow fetch always start at depth of 1 to minimize download size" ); assert!(shallow_repo.is_shallow()); Package::new("bar", "1.1.0").publish(); p.cargo("update") .env("__CARGO_USE_GITOXIDE_INSTEAD_OF_GIT2", "0") .run(); let repo = gix::open_opts( find_remote_index(RepoMode::Complete), gix::open::Options::isolated(), )?; assert_eq!( repo.rev_parse_single("origin/HEAD")? .ancestors() .all()? .count(), 3, "an entirely new repo was fetched which is never shallow" ); assert!(!repo.is_shallow()); Ok(()) } #[cargo_test] fn gitoxide_fetch_shallow_dep_then_git2_fetch_complete() -> anyhow::Result<()> { // Example where an old lockfile with an explicit branch="master" in Cargo.toml. Package::new("bar", "1.0.0").publish(); let (bar, bar_repo) = git::new_repo("bar", |p| { p.file("Cargo.toml", &basic_manifest("bar", "1.0.0")) .file("src/lib.rs", "") }); bar.change_file("src/lib.rs", "// change"); git::add(&bar_repo); git::commit(&bar_repo); { let mut walk = bar_repo.revwalk()?; walk.push_head()?; assert_eq!( walk.count(), 2, "original repo has initial commit and change commit" ); } let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [dependencies] bar = {{ version = "1.0", git = "{}", branch = "master" }} "#, bar.url() ), ) .file("src/lib.rs", "") .build(); p.cargo("update") .arg("-Zgitoxide=fetch") .arg("-Zgit=shallow-deps") .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-deps"]) .run(); let db_clone = gix::open_opts( find_bar_db(RepoMode::Shallow), gix::open::Options::isolated(), )?; assert!(db_clone.is_shallow()); assert_eq!( db_clone .rev_parse_single("origin/master")? .ancestors() .all()? .count(), 1, "db fetch are shallow and have a shortened history" ); let dep_checkout = gix::open_opts( find_lexicographically_first_bar_checkout(), gix::open::Options::isolated(), )?; assert!(dep_checkout.is_shallow()); assert_eq!( dep_checkout.head_id()?.ancestors().all()?.count(), 1, "db checkouts are hard-linked fetches with the shallow file copied separately." ); bar.change_file("src/lib.rs", "// another change"); git::add(&bar_repo); git::commit(&bar_repo); { let mut walk = bar_repo.revwalk()?; walk.push_head()?; assert_eq!( walk.count(), 3, "original repo has initial commit and change commit, and another change" ); } p.cargo("update") .env("__CARGO_USE_GITOXIDE_INSTEAD_OF_GIT2", "0") .run(); let db_clone = gix::open_opts( find_bar_db(RepoMode::Complete), gix::open::Options::isolated(), )?; assert_eq!( db_clone .rev_parse_single("origin/master")? .ancestors() .all()? .count(), 3, "the db clone was re-initialized and has all commits" ); assert!( !db_clone.is_shallow(), "shallow-ness was removed as git2 does not support it" ); assert_eq!( dep_checkout.head_id()?.ancestors().all()?.count(), 1, "the original dep checkout didn't change - there is a new one for each update we get locally" ); let max_history_depth = glob::glob( paths::home() .join(".cargo/git/checkouts/bar-*/*/.git") .to_str() .unwrap(), )? .map(|path| -> anyhow::Result { let dep_checkout = gix::open_opts(path?, gix::open::Options::isolated())?; let depth = dep_checkout.head_id()?.ancestors().all()?.count(); assert_eq!(dep_checkout.is_shallow(), depth == 1, "the first checkout is done with gitoxide and shallow, the second one is git2 non-shallow"); Ok(depth) }) .map(Result::unwrap) .max() .expect("two checkout repos"); assert_eq!( max_history_depth, 3, "the new checkout sees all commits of the non-shallow DB repository" ); Ok(()) } #[cargo_test] fn gitoxide_fetch_shallow_dep_then_gitoxide_fetch_complete() -> anyhow::Result<()> { Package::new("bar", "1.0.0").publish(); let (bar, bar_repo) = git::new_repo("bar", |p| { p.file("Cargo.toml", &basic_manifest("bar", "1.0.0")) .file("src/lib.rs", "") }); bar.change_file("src/lib.rs", "// change"); git::add(&bar_repo); git::commit(&bar_repo); { let mut walk = bar_repo.revwalk()?; walk.push_head()?; assert_eq!( walk.count(), 2, "original repo has initial commit and change commit" ); } let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [dependencies] bar = {{ version = "1.0", git = "{}", branch = "master" }} "#, bar.url() ), ) .file("src/lib.rs", "") .build(); p.cargo("update") .arg("-Zgitoxide=fetch") .arg("-Zgit=shallow-deps") .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-deps"]) .run(); let shallow_db_clone = gix::open_opts( find_bar_db(RepoMode::Shallow), gix::open::Options::isolated(), )?; assert!(shallow_db_clone.is_shallow()); assert_eq!( shallow_db_clone .rev_parse_single("origin/master")? .ancestors() .all()? .count(), 1, "db fetches are shallow and have a shortened history" ); let dep_checkout = gix::open_opts( find_lexicographically_first_bar_checkout(), gix::open::Options::isolated(), )?; assert!(dep_checkout.is_shallow()); assert_eq!( dep_checkout.head_id()?.ancestors().all()?.count(), 1, "db checkouts are hard-linked fetches with the shallow file copied separately." ); bar.change_file("src/lib.rs", "// another change"); git::add(&bar_repo); git::commit(&bar_repo); { let mut walk = bar_repo.revwalk()?; walk.push_head()?; assert_eq!( walk.count(), 3, "original repo has initial commit and change commit, and another change" ); } p.cargo("update") .arg("-Zgitoxide=fetch") // shallow-deps is omitted intentionally .masquerade_as_nightly_cargo(&["gitoxide=fetch"]) .run(); let db_clone = gix::open_opts( find_bar_db(RepoMode::Complete), gix::open::Options::isolated(), )?; assert_eq!( db_clone .rev_parse_single("origin/master")? .ancestors() .all()? .count(), 3, "we created an entirely new non-shallow clone" ); assert!(!db_clone.is_shallow()); assert_eq!( dep_checkout.head_id()?.ancestors().all()?.count(), 1, "the original dep checkout didn't change - there is a new one for each update we get locally" ); let max_history_depth = glob::glob( paths::home() .join(".cargo/git/checkouts/bar-*/*/.git") .to_str() .unwrap(), )? .map(|path| -> anyhow::Result { let path = path?; let dep_checkout = gix::open_opts(&path, gix::open::Options::isolated())?; assert_eq!( dep_checkout.is_shallow(), path.to_string_lossy().contains("-shallow"), "checkouts of shallow db repos are shallow as well" ); let depth = dep_checkout.head_id()?.ancestors().all()?.count(); Ok(depth) }) .map(Result::unwrap) .max() .expect("two checkout repos"); assert_eq!( max_history_depth, 3, "we see the previous shallow checkout as well as new unshallow one" ); Ok(()) } #[cargo_test] fn gitoxide_fetch_shallow_index_then_preserve_shallow() -> anyhow::Result<()> { Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("fetch") .arg("-Zgitoxide=fetch") .arg("-Zgit=shallow-index") .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-index"]) .run(); let repo = gix::open_opts(find_index(), gix::open::Options::isolated())?; assert_eq!( repo.rev_parse_single("origin/HEAD")? .ancestors() .all()? .count(), 1, "shallow fetches always start at depth of 1 to minimize download size" ); assert!(repo.is_shallow()); Package::new("bar", "1.1.0").publish(); p.cargo("update") .arg("-Zgitoxide=fetch") .arg("-Zgit=shallow-index") // NOTE: the flag needs to be consistent or else a different index is created .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-index"]) .run(); assert_eq!( repo.rev_parse_single("origin/HEAD")? .ancestors() .all()? .count(), 1, "subsequent shallow fetches wont' fetch what's inbetween, only the single commit that we need while leveraging existing commits" ); assert!(repo.is_shallow()); Package::new("bar", "1.2.0").publish(); Package::new("bar", "1.3.0").publish(); p.cargo("update") .arg("-Zgitoxide=fetch") .arg("-Zgit=shallow-index") .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-index"]) .run(); assert_eq!( repo.rev_parse_single("origin/HEAD")? .ancestors() .all()? .count(), 1, "shallow boundaries are moved with each fetch to maintain only a single commit of history" ); assert!(repo.is_shallow()); Ok(()) } /// If there is shallow *and* non-shallow fetches, non-shallow will naturally be returned due to sort order. #[cargo_test] fn gitoxide_fetch_complete_index_then_shallow() -> anyhow::Result<()> { Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("fetch") .arg("-Zgitoxide=fetch") .masquerade_as_nightly_cargo(&["gitoxide=fetch"]) .run(); let repo = gix::open_opts(find_index(), gix::open::Options::isolated())?; assert_eq!( repo.rev_parse_single("origin/HEAD")? .ancestors() .all()? .count(), 2, "initial commit and the first crate" ); assert!(!repo.is_shallow()); Package::new("bar", "1.1.0").publish(); p.cargo("update") .arg("-Zgitoxide=fetch") .arg("-Zgit=shallow-index") .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-index"]) .run(); let shallow_repo = gix::open_opts( find_remote_index(RepoMode::Shallow), gix::open::Options::isolated(), )?; assert_eq!( shallow_repo .rev_parse_single("origin/HEAD")? .ancestors() .all()? .count(), 1, "the follow up fetch an entirely new index which is now shallow and which is in its own location" ); assert!(shallow_repo.is_shallow()); Package::new("bar", "1.2.0").publish(); Package::new("bar", "1.3.0").publish(); p.cargo("update") .arg("-Zgitoxide=fetch") .arg("-Zgit=shallow-index") .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-index"]) .run(); assert_eq!( shallow_repo .rev_parse_single("origin/HEAD")? .ancestors() .all()? .count(), 1, "subsequent shallow fetches wont' fetch what's inbetween, only the single commit that we need while leveraging existing commits" ); assert!(shallow_repo.is_shallow()); p.cargo("update") .arg("-Zgitoxide=fetch") .masquerade_as_nightly_cargo(&["gitoxide=fetch"]) .run(); assert_eq!( repo.rev_parse_single("origin/HEAD")? .ancestors() .all()? .count(), 5, "we can separately fetch the non-shallow index as well and it sees all commits" ); Ok(()) } #[cargo_test] fn gitoxide_fetch_shallow_index_then_abort_and_update() -> anyhow::Result<()> { Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("fetch") .arg("-Zgitoxide=fetch") .arg("-Zgit=shallow-index") .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-index"]) .run(); let repo = gix::open_opts(find_index(), gix::open::Options::isolated())?; assert_eq!( repo.rev_parse_single("origin/HEAD")? .ancestors() .all()? .count(), 1, "shallow fetches always start at depth of 1 to minimize download size" ); assert!(repo.is_shallow()); let shallow_lock = repo.shallow_file().with_extension("lock"); // adding a lock file and deleting the original simulates a left-over fetch that was aborted, leaving a lock file // in place without ever having moved it to the right location. std::fs::write(&shallow_lock, &[])?; std::fs::remove_file(repo.shallow_file())?; Package::new("bar", "1.1.0").publish(); p.cargo("update") .arg("-Zgitoxide=fetch") .arg("-Zgit=shallow-index") .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-index"]) .run(); assert!(!shallow_lock.is_file(), "the repository was re-initialized"); assert!(repo.is_shallow()); assert_eq!( repo.rev_parse_single("origin/HEAD")? .ancestors() .all()? .count(), 1, "it's a fresh shallow fetch - otherwise it would have 2 commits if the previous shallow fetch would still be present" ); Ok(()) } fn find_lexicographically_first_bar_checkout() -> std::path::PathBuf { glob::glob( paths::home() .join(".cargo/git/checkouts/bar-*/*/.git") .to_str() .unwrap(), ) .unwrap() .next() .unwrap() .unwrap() .to_owned() } fn find_remote_index(mode: RepoMode) -> std::path::PathBuf { glob::glob( paths::home() .join(".cargo/registry/index/*") .to_str() .unwrap(), ) .unwrap() .map(Result::unwrap) .filter(|p| p.to_string_lossy().ends_with("-shallow") == matches!(mode, RepoMode::Shallow)) .next() .unwrap() } /// Find a checkout directory for bar, `shallow` or not. fn find_bar_db(mode: RepoMode) -> std::path::PathBuf { glob::glob(paths::home().join(".cargo/git/db/bar-*").to_str().unwrap()) .unwrap() .map(Result::unwrap) .filter(|p| p.to_string_lossy().ends_with("-shallow") == matches!(mode, RepoMode::Shallow)) .next() .unwrap() .to_owned() } cargo-0.91.0/tests/testsuite/glob_targets.rs000064400000000000000000000323231046102023000172260ustar 00000000000000//! Tests for target filter flags with glob patterns. use crate::prelude::*; use cargo_test_support::{Project, project, str}; #[cargo_test] fn build_example() { full_project() .cargo("build -v --example 'ex*1'") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name example1 [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn build_bin() { full_project() .cargo("build -v --bin 'bi*1'") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name bin1 [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn build_bench() { full_project() .cargo("build -v --bench 'be*1'") .with_stderr_data( str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]` [RUNNING] `rustc --crate-name bench1 [..]` [RUNNING] `rustc --crate-name bin2 [..]` [RUNNING] `rustc --crate-name bin1 [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn build_test() { full_project() .cargo("build -v --test 'te*1'") .with_stderr_data( str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name bin1 [..]` [RUNNING] `rustc --crate-name foo [..]` [RUNNING] `rustc --crate-name bin2 [..]` [RUNNING] `rustc --crate-name test1 [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn check_example() { full_project() .cargo("check -v --example 'ex*1'") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name example1 [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn check_bin() { full_project() .cargo("check -v --bin 'bi*1'") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name bin1 [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn check_bench() { full_project() .cargo("check -v --bench 'be*1'") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name bench1 [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn check_test() { full_project() .cargo("check -v --test 'te*1'") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name test1 [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn doc_bin() { full_project() .cargo("doc -v --bin 'bi*1'") .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustdoc --edition=2015 --crate-type bin --crate-name bin1 [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/bin1/index.html "#]]) .run(); } #[cargo_test] fn fix_example() { full_project() .cargo("fix -v --example 'ex*1' --allow-no-vcs") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[..] rustc --crate-name example1 [..]` [FIXING] examples/example1.rs [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn fix_bin() { full_project() .cargo("fix -v --bin 'bi*1' --allow-no-vcs") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[..] rustc --crate-name bin1 [..]` [FIXING] src/bin/bin1.rs [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn fix_bench() { full_project() .cargo("fix -v --bench 'be*1' --allow-no-vcs") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[..] rustc --crate-name bench1 [..]` [FIXING] benches/bench1.rs [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn fix_test() { full_project() .cargo("fix -v --test 'te*1' --allow-no-vcs") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[..] rustc --crate-name test1 [..]` [FIXING] tests/test1.rs [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn run_example_and_bin() { let p = full_project(); p.cargo("run -v --bin 'bi*1'") .with_status(101) .with_stderr_data(str![[r#" [ERROR] `cargo run` does not support glob patterns on target selection "#]]) .run(); p.cargo("run -v --example 'ex*1'") .with_status(101) .with_stderr_data(str![[r#" [ERROR] `cargo run` does not support glob patterns on target selection "#]]) .run(); } #[cargo_test] fn test_example() { full_project() .cargo("test -v --example 'ex*1'") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name example1 [..]` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/debug/examples/example1-[HASH][EXE]` "#]]) .run(); } #[cargo_test] fn test_bin() { full_project() .cargo("test -v --bin 'bi*1'") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name bin1 [..]` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/debug/deps/bin1-[HASH][EXE]` "#]]) .run(); } #[cargo_test] fn test_bench() { full_project() .cargo("test -v --bench 'be*1'") .with_stderr_data( str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name bin2 [..]` [RUNNING] `rustc --crate-name foo [..]` [RUNNING] `rustc --crate-name bench1 [..]` [RUNNING] `rustc --crate-name bin1 [..]` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/debug/deps/bench1-[HASH][EXE]` "#]] .unordered(), ) .run(); } #[cargo_test] fn test_test() { full_project() .cargo("test -v --test 'te*1'") .with_stderr_data( str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name test1 [..]` [RUNNING] `rustc --crate-name bin1 [..]` [RUNNING] `rustc --crate-name foo [..]` [RUNNING] `rustc --crate-name bin2 [..]` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/debug/deps/test1-[HASH][EXE]` "#]] .unordered(), ) .run(); } #[cargo_test] fn bench_example() { full_project() .cargo("bench -v --example 'ex*1'") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name example1 [..]` [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/release/examples/example1-[HASH][EXE] --bench` "#]]) .run(); } #[cargo_test] fn bench_bin() { full_project() .cargo("bench -v --bin 'bi*1'") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name bin1 [..]` [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/release/deps/bin1-[HASH][EXE] --bench` "#]]) .run(); } #[cargo_test] fn bench_bench() { full_project() .cargo("bench -v --bench 'be*1'") .with_stderr_data( str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name bin1 [..]` [RUNNING] `rustc --crate-name bin2 [..]` [RUNNING] `rustc --crate-name foo [..]` [RUNNING] `rustc --crate-name bench1 [..]` [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/release/deps/bench1-[HASH][EXE] --bench` "#]] .unordered(), ) .run(); } #[cargo_test] fn bench_test() { full_project() .cargo("bench -v --test 'te*1'") .with_stderr_data( str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name bin2 [..]` [RUNNING] `rustc --crate-name foo [..]` [RUNNING] `rustc --crate-name test1 [..]` [RUNNING] `rustc --crate-name bin1 [..]` [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/release/deps/test1-[HASH][EXE] --bench` "#]] .unordered(), ) .run(); } #[cargo_test] fn install_example() { full_project() .cargo("install --path . --example 'ex*1'") .with_stderr_data(str![[r#" [INSTALLING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/example1[EXE] [INSTALLED] package `foo v0.0.1 ([ROOT]/foo)` (executable `example1[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); } #[cargo_test] fn install_bin() { full_project() .cargo("install --path . --bin 'bi*1'") .with_stderr_data(str![[r#" [INSTALLING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/bin1[EXE] [INSTALLED] package `foo v0.0.1 ([ROOT]/foo)` (executable `bin1[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); } #[cargo_test] fn rustdoc_example() { full_project() .cargo("rustdoc -v --example 'ex*1'") .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustdoc --edition=2015 --crate-type bin --crate-name example1 [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/example1/index.html "#]]) .run(); } #[cargo_test] fn rustdoc_bin() { full_project() .cargo("rustdoc -v --bin 'bi*1'") .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustdoc --edition=2015 --crate-type bin --crate-name bin1 [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/bin1/index.html "#]]) .run(); } #[cargo_test] fn rustdoc_bench() { full_project() .cargo("rustdoc -v --bench 'be*1'") .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustdoc --edition=2015 --crate-type bin --crate-name bench1 [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/bench1/index.html "#]]) .run(); } #[cargo_test] fn rustdoc_test() { full_project() .cargo("rustdoc -v --test 'te*1'") .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustdoc --edition=2015 --crate-type bin --crate-name test1 [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/test1/index.html "#]]) .run(); } #[cargo_test] fn rustc_example() { full_project() .cargo("rustc -v --example 'ex*1'") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name example1 [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn rustc_bin() { full_project() .cargo("rustc -v --bin 'bi*1'") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name bin1 [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn rustc_bench() { full_project() .cargo("rustc -v --bench 'be*1'") .with_stderr_data( str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name bin1 [..]` [RUNNING] `rustc --crate-name bench1 [..]` [RUNNING] `rustc --crate-name bin2 [..]` [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn rustc_test() { full_project() .cargo("rustc -v --test 'te*1'") .with_stderr_data( str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name bin1 [..]` [RUNNING] `rustc --crate-name bin2 [..]` [RUNNING] `rustc --crate-name foo [..]` [RUNNING] `rustc --crate-name test1 [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } fn full_project() -> Project { project() .file("examples/example1.rs", "fn main() { }") .file("examples/example2.rs", "fn main() { }") .file("benches/bench1.rs", "") .file("benches/bench2.rs", "") .file("tests/test1.rs", "") .file("tests/test2.rs", "") .file("src/main.rs", "fn main() { }") .file("src/bin/bin1.rs", "fn main() { }") .file("src/bin/bin2.rs", "fn main() { }") .build() } cargo-0.91.0/tests/testsuite/global_cache_tracker.rs000064400000000000000000002045611046102023000206550ustar 00000000000000//! Tests for last-use tracking and auto-gc. //! //! Cargo supports an environment variable called `__CARGO_TEST_LAST_USE_NOW` //! to have cargo pretend that the current time is the given time (in seconds //! since the unix epoch). This is used throughout these tests to simulate //! what happens when time passes. The [`days_ago_unix`] and //! [`months_ago_unix`] functions help with setting this value. use std::env; use std::fmt::Write; use std::path::Path; use std::path::PathBuf; use std::process::Stdio; use std::sync::OnceLock; use std::time::{Duration, SystemTime}; use crate::prelude::*; use crate::utils::cargo_process; use cargo::GlobalContext; use cargo::core::global_cache_tracker::{self, DeferredGlobalLastUse, GlobalCacheTracker}; use cargo::util::cache_lock::CacheLockMode; use cargo_test_support::compare::assert_e2e; use cargo_test_support::paths; use cargo_test_support::registry::{Package, RegistryBuilder}; use cargo_test_support::{ Execs, Project, basic_manifest, execs, git, process, project, retry, sleep_ms, str, thread_wait_timeout, }; use itertools::Itertools; use super::config::GlobalContextBuilder; /// Helper to create a simple `foo` project which depends on a registry /// dependency called `bar`. fn basic_foo_bar_project() -> Project { Package::new("bar", "1.0.0").publish(); project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build() } /// Helper to get the names of files in a directory as strings. fn get_names(glob: &str) -> Vec { let mut names: Vec<_> = glob::glob(paths::home().join(glob).to_str().unwrap()) .unwrap() .map(|p| p.unwrap().file_name().unwrap().to_str().unwrap().to_owned()) .collect(); names.sort(); names } fn get_registry_names(which: &str) -> Vec { get_names(&format!(".cargo/registry/{which}/*/*")) } fn get_index_names() -> Vec { get_names(&format!(".cargo/registry/index/*")) } fn get_git_db_names() -> Vec { get_names(&format!(".cargo/git/db/*")) } fn get_git_checkout_names(db_name: &str) -> Vec { get_names(&format!(".cargo/git/checkouts/{db_name}/*")) } fn days_ago(n: u64) -> SystemTime { now() - Duration::from_secs(60 * 60 * 24 * n) } fn now() -> SystemTime { // This captures the time once to avoid potential time boundaries or // inconsistencies affecting a test. For example, on a fast system // `days_ago(1)` called twice in a row will return the same answer. // However, on a slower system, or if the clock happens to flip over from // one second to the next, then it would return different answers. This // ensures that it always returns the same answer. static START: OnceLock = OnceLock::new(); *START.get_or_init(|| SystemTime::now()) } /// Helper for simulating running cargo in the past. Use with the /// `__CARGO_TEST_LAST_USE_NOW` environment variable. fn days_ago_unix(n: u64) -> String { days_ago(n) .duration_since(SystemTime::UNIX_EPOCH) .unwrap() .as_secs() .to_string() } /// Helper for simulating running cargo in the past. Use with the /// `__CARGO_TEST_LAST_USE_NOW` environment variable. fn months_ago_unix(n: u64) -> String { days_ago_unix(n * 30) } /// Populates last-use database and the cache files. /// /// This makes it easier to more accurately specify exact sizes. Creating /// specific sizes with `Package` is too difficult. fn populate_cache( gctx: &GlobalContext, test_crates: &[(&str, u64, u64, u64)], ) -> (PathBuf, PathBuf) { let cache_dir = paths::home().join(".cargo/registry/cache/example.com-a6c4a5adcb232b9a"); let src_dir = paths::home().join(".cargo/registry/src/example.com-a6c4a5adcb232b9a"); GlobalCacheTracker::db_path(&gctx) .into_path_unlocked() .rm_rf(); let _lock = gctx .acquire_package_cache_lock(CacheLockMode::MutateExclusive) .unwrap(); let mut tracker = GlobalCacheTracker::new(&gctx).unwrap(); let mut deferred = DeferredGlobalLastUse::new(); cache_dir.rm_rf(); cache_dir.mkdir_p(); src_dir.rm_rf(); src_dir.mkdir_p(); paths::home() .join(".cargo/registry/index/example.com-a6c4a5adcb232b9a") .mkdir_p(); let mut create = |name: &str, age, crate_size: u64, src_size: u64| { let crate_filename = format!("{name}.crate").into(); deferred.mark_registry_crate_used_stamp( global_cache_tracker::RegistryCrate { encoded_registry_name: "example.com-a6c4a5adcb232b9a".into(), crate_filename, size: crate_size, }, Some(&days_ago(age)), ); deferred.mark_registry_src_used_stamp( global_cache_tracker::RegistrySrc { encoded_registry_name: "example.com-a6c4a5adcb232b9a".into(), package_dir: name.into(), size: Some(src_size), }, Some(&days_ago(age)), ); std::fs::write( cache_dir.join(crate_filename), "x".repeat(crate_size as usize), ) .unwrap(); let path = src_dir.join(name); path.mkdir_p(); std::fs::write(path.join("data"), "x".repeat(src_size as usize)).unwrap() }; for (name, age, crate_size, src_size) in test_crates { create(name, *age, *crate_size, *src_size); } deferred.save(&mut tracker).unwrap(); (cache_dir, src_dir) } /// Returns an `Execs` that will run the rustup `cargo` proxy from the global /// system's cargo home directory. fn rustup_cargo() -> Execs { // Modify the PATH to ensure that `cargo` and `rustc` comes from // CARGO_HOME. This is necessary because cargo adds the "deps" directory // into PATH on Windows, which points to the wrong cargo. let real_cargo_home_bin = Path::new(&std::env::var_os("CARGO_HOME").unwrap()).join("bin"); let mut paths = vec![real_cargo_home_bin]; paths.extend(env::split_paths(&env::var_os("PATH").unwrap_or_default())); let path = env::join_paths(paths).unwrap(); let mut e = execs().with_process_builder(process("cargo")); e.env("PATH", path); e } #[cargo_test] fn clean_gc_gated() { cargo_process("clean gc") .with_status(101) .with_stderr_data(str![[r#" [ERROR] the `cargo clean gc` command is unstable, and only available on the nightly channel of Cargo, but this is the `stable` channel See https://doc.rust-lang.org/book/appendix-07-nightly-rust.html for more information about Rust release channels. See https://github.com/rust-lang/cargo/issues/12633 for more information about the `cargo clean gc` command. "#]] ) .run(); } #[cargo_test] fn implies_source() { // Checks that when a src, crate, or checkout is marked as used, the // corresponding index or git db also gets marked as used. let gctx = GlobalContextBuilder::new().build(); let _lock = gctx .acquire_package_cache_lock(CacheLockMode::MutateExclusive) .unwrap(); let mut deferred = DeferredGlobalLastUse::new(); let mut tracker = GlobalCacheTracker::new(&gctx).unwrap(); deferred.mark_registry_crate_used(global_cache_tracker::RegistryCrate { encoded_registry_name: "example.com-a6c4a5adcb232b9a".into(), crate_filename: "regex-1.8.4.crate".into(), size: 123, }); deferred.mark_registry_src_used(global_cache_tracker::RegistrySrc { encoded_registry_name: "index.crates.io-6f17d22bba15001f".into(), package_dir: "rand-0.8.5".into(), size: None, }); deferred.mark_git_checkout_used(global_cache_tracker::GitCheckout { encoded_git_name: "cargo-e7ff1db891893a9e".into(), short_name: "f0a4ee0".into(), size: None, }); deferred.save(&mut tracker).unwrap(); let mut indexes = tracker.registry_index_all().unwrap(); assert_eq!(indexes.len(), 2); indexes.sort_by(|a, b| a.0.encoded_registry_name.cmp(&b.0.encoded_registry_name)); assert_eq!( indexes[0].0.encoded_registry_name, "example.com-a6c4a5adcb232b9a" ); assert_eq!( indexes[1].0.encoded_registry_name, "index.crates.io-6f17d22bba15001f" ); let dbs = tracker.git_db_all().unwrap(); assert_eq!(dbs.len(), 1); assert_eq!(dbs[0].0.encoded_git_name, "cargo-e7ff1db891893a9e"); } #[cargo_test] fn auto_gc_defaults() { // Checks that the auto-gc deletes old entries, and leaves new ones intact. Package::new("old", "1.0.0").publish(); Package::new("new", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] old = "1.0" new = "1.0" "#, ) .file("src/lib.rs", "") .build(); // Populate the last-use data. p.cargo("check") .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(4)) .run(); assert_eq!(get_registry_names("src"), ["new-1.0.0", "old-1.0.0"]); assert_eq!( get_registry_names("cache"), ["new-1.0.0.crate", "old-1.0.0.crate"] ); // Run again with just one package. Make sure the old src gets deleted, // but .crate does not. p.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] new = "1.0" "#, ); p.cargo("check") .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(2)) .run(); assert_eq!(get_registry_names("src"), ["new-1.0.0"]); assert_eq!( get_registry_names("cache"), ["new-1.0.0.crate", "old-1.0.0.crate"] ); // Run again after the .crate should have aged out. p.cargo("check").run(); assert_eq!(get_registry_names("src"), ["new-1.0.0"]); assert_eq!(get_registry_names("cache"), ["new-1.0.0.crate"]); } #[cargo_test] fn auto_gc_config_gated() { // gc.auto config options should be ignored without -Zgc Package::new("old", "1.0.0").publish(); Package::new("new", "1.0.0").publish(); let p = project() .file( ".cargo/config.toml", r#" [gc.auto] frequency = "always" max-src-age = "1 day" max-crate-age = "3 days" max-index-age = "3 days" max-git-co-age = "1 day" max-git-db-age = "3 days" "#, ) .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] old = "1.0" new = "1.0" "#, ) .file("src/lib.rs", "") .build(); // Populate the last-use data. p.cargo("check") .env("__CARGO_TEST_LAST_USE_NOW", days_ago_unix(4)) .run(); assert_eq!(get_registry_names("src"), ["new-1.0.0", "old-1.0.0"]); assert_eq!( get_registry_names("cache"), ["new-1.0.0.crate", "old-1.0.0.crate"] ); // Run again with just one package. Without -Zgc, it should use the // defaults and ignore the config. Nothing should get deleted since the // defaults are much greater than 4 days. p.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] new = "1.0" "#, ); p.cargo("check").run(); assert_eq!(get_registry_names("src"), ["new-1.0.0", "old-1.0.0"]); assert_eq!( get_registry_names("cache"), ["new-1.0.0.crate", "old-1.0.0.crate"] ); } #[cargo_test] fn auto_gc_config() { // Can configure auto gc settings. Package::new("old", "1.0.0").publish(); Package::new("new", "1.0.0").publish(); let p = project() .file( ".cargo/config.toml", r#" [cache] auto-clean-frequency = "always" [cache.global-clean] max-src-age = "1 day" max-crate-age = "3 days" max-index-age = "3 days" max-git-co-age = "1 day" max-git-db-age = "3 days" "#, ) .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] old = "1.0" new = "1.0" "#, ) .file("src/lib.rs", "") .build(); // Populate the last-use data. p.cargo("check") .env("__CARGO_TEST_LAST_USE_NOW", days_ago_unix(4)) .run(); assert_eq!(get_registry_names("src"), ["new-1.0.0", "old-1.0.0"]); assert_eq!( get_registry_names("cache"), ["new-1.0.0.crate", "old-1.0.0.crate"] ); // Run again with just one package. Make sure the old src gets deleted, // but .crate does not. p.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] new = "1.0" "#, ); p.cargo("check -Zgc") .masquerade_as_nightly_cargo(&["gc"]) .env("__CARGO_TEST_LAST_USE_NOW", days_ago_unix(2)) .run(); assert_eq!(get_registry_names("src"), ["new-1.0.0"]); assert_eq!( get_registry_names("cache"), ["new-1.0.0.crate", "old-1.0.0.crate"] ); // Run again after the .crate should have aged out. p.cargo("check -Zgc") .masquerade_as_nightly_cargo(&["gc"]) .run(); assert_eq!(get_registry_names("src"), ["new-1.0.0"]); assert_eq!(get_registry_names("cache"), ["new-1.0.0.crate"]); } #[cargo_test] fn frequency() { // cache.auto-clean-frequency settings let p = basic_foo_bar_project(); p.change_file( ".cargo/config.toml", r#" [cache] auto-clean-frequency = "never" "#, ); // Populate data in the past. p.cargo("check") .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(4)) .run(); assert_eq!(get_index_names().len(), 1); assert_eq!(get_registry_names("src"), ["bar-1.0.0"]); assert_eq!(get_registry_names("cache"), ["bar-1.0.0.crate"]); p.change_file("Cargo.toml", &basic_manifest("foo", "0.2.0")); // Try after the default expiration time, with "never" it shouldn't gc. p.cargo("check").run(); assert_eq!(get_index_names().len(), 1); assert_eq!(get_registry_names("src"), ["bar-1.0.0"]); assert_eq!(get_registry_names("cache"), ["bar-1.0.0.crate"]); // Try again with a setting that allows it to run. p.cargo("check") .env("CARGO_CACHE_AUTO_CLEAN_FREQUENCY", "1 day") .run(); assert_eq!(get_index_names().len(), 0); assert_eq!(get_registry_names("src").len(), 0); assert_eq!(get_registry_names("cache").len(), 0); } #[cargo_test] fn auto_gc_index() { // Deletes the index if it hasn't been used in a while. let p = basic_foo_bar_project(); p.cargo("check") .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(4)) .run(); assert_eq!(get_index_names().len(), 1); // Make sure it stays within the time frame. p.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" "#, ); p.cargo("check") .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(2)) .run(); assert_eq!(get_index_names().len(), 1); // After it expires, it should be deleted. p.cargo("check").run(); assert_eq!(get_index_names().len(), 0); } #[cargo_test] fn auto_gc_git() { // auto-gc should delete git checkouts and dbs. // Returns the short git name of a checkout. let short_id = |repo: &git2::Repository| -> String { let head = repo.revparse_single("HEAD").unwrap(); let short_id = head.short_id().unwrap(); short_id.as_str().unwrap().to_owned() }; // Set up a git dependency and fetch it and populate the database, // 6 months in the past. let (git_project, git_repo) = git::new_repo("bar", |p| { p.file("Cargo.toml", &basic_manifest("bar", "1.0.0")) .file("src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = {{ git = '{}' }} "#, git_project.url() ), ) .file("src/lib.rs", "") .build(); p.cargo("check") .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(6)) .run(); let db_names = get_git_db_names(); assert_eq!(db_names.len(), 1); let first_short_oid = short_id(&git_repo); assert_eq!( get_git_checkout_names(&db_names[0]), [first_short_oid.clone()] ); // Use a new git checkout, should keep both. git_project.change_file("src/lib.rs", "// modified"); git::add(&git_repo); git::commit(&git_repo); p.cargo("update") .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(6)) .run(); assert_eq!(get_git_db_names().len(), 1); let second_short_oid = short_id(&git_repo); let mut both = vec![first_short_oid, second_short_oid.clone()]; both.sort(); assert_eq!(get_git_checkout_names(&db_names[0]), both); // In the future, using the second checkout should delete the first. p.cargo("check") .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(4)) .run(); assert_eq!(get_git_db_names().len(), 1); assert_eq!( get_git_checkout_names(&db_names[0]), [second_short_oid.clone()] ); // After three months, the db should get deleted. p.change_file("Cargo.toml", &basic_manifest("foo", "0.2.0")); p.cargo("check").run(); assert_eq!(get_git_db_names().len(), 0); assert_eq!(get_git_checkout_names(&db_names[0]).len(), 0); } #[cargo_test] fn auto_gc_various_commands() { // Checks that auto gc works with a variety of commands. // // Auto-gc is only run on a subset of commands. Generally it is run on // commands that are already doing a lot of work, or heavily involve the // use of the registry. Package::new("bar", "1.0.0").publish(); let cmds = ["check", "fetch"]; for cmd in cmds { eprintln!("checking command {cmd}"); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); // Populate the last-use data. p.cargo(cmd) .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(4)) .run(); let gctx = GlobalContextBuilder::new().build(); let lock = gctx .acquire_package_cache_lock(CacheLockMode::MutateExclusive) .unwrap(); let tracker = GlobalCacheTracker::new(&gctx).unwrap(); let indexes = tracker.registry_index_all().unwrap(); assert_eq!(indexes.len(), 1); let crates = tracker.registry_crate_all().unwrap(); assert_eq!(crates.len(), 1); let srcs = tracker.registry_src_all().unwrap(); assert_eq!(srcs.len(), 1); drop(lock); // After everything is aged out, it should all be deleted. p.change_file("Cargo.toml", &basic_manifest("foo", "0.2.0")); p.cargo(cmd).run(); let lock = gctx .acquire_package_cache_lock(CacheLockMode::MutateExclusive) .unwrap(); let indexes = tracker.registry_index_all().unwrap(); assert_eq!(indexes.len(), 0); let crates = tracker.registry_crate_all().unwrap(); assert_eq!(crates.len(), 0); let srcs = tracker.registry_src_all().unwrap(); assert_eq!(srcs.len(), 0); drop(tracker); drop(lock); paths::home().join(".cargo/registry").rm_rf(); GlobalCacheTracker::db_path(&gctx) .into_path_unlocked() .rm_rf(); } } #[cargo_test] fn updates_last_use_various_commands() { // Checks that last-use tracking is updated by various commands. // // Not *all* commands update the index tracking, even though they // technically involve reading the index. There isn't a convenient place // to ensure it gets saved while avoiding saving too often in other // commands. For the most part, this should be fine, since these commands // usually aren't run without running one of the commands that does save // the tracking. Some of the commands are: // // - login, owner, yank, search // - report future-incompatibilities // - package --no-verify // - fetch --locked Package::new("bar", "1.0.0").publish(); let cmds = [ // name, expected_crates (0=doesn't download) ("check", 1), ("fetch", 1), ("tree", 1), ("generate-lockfile", 0), ("update", 0), ("metadata", 1), ("vendor --respect-source-config", 1), ]; for (cmd, expected_crates) in cmds { eprintln!("checking command {cmd}"); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); // Populate the last-use data. p.cargo(cmd).run(); let gctx = GlobalContextBuilder::new().build(); let lock = gctx .acquire_package_cache_lock(CacheLockMode::MutateExclusive) .unwrap(); let tracker = GlobalCacheTracker::new(&gctx).unwrap(); let indexes = tracker.registry_index_all().unwrap(); assert_eq!(indexes.len(), 1); let crates = tracker.registry_crate_all().unwrap(); assert_eq!(crates.len(), expected_crates); let srcs = tracker.registry_src_all().unwrap(); assert_eq!(srcs.len(), expected_crates); drop(tracker); drop(lock); paths::home().join(".cargo/registry").rm_rf(); GlobalCacheTracker::db_path(&gctx) .into_path_unlocked() .rm_rf(); } } #[cargo_test] fn both_git_and_http_index_cleans() { // Checks that either the git or http index cache gets cleaned. let _crates_io = RegistryBuilder::new().build(); let _alternative = RegistryBuilder::new().alternative().http_index().build(); Package::new("from_git", "1.0.0").publish(); Package::new("from_http", "1.0.0") .alternative(true) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] from_git = "1.0" from_http = { version = "1.0", registry = "alternative" } "#, ) .file("src/lib.rs", "") .build(); p.cargo("update") .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(4)) .run(); let gctx = GlobalContextBuilder::new().build(); let lock = gctx .acquire_package_cache_lock(CacheLockMode::MutateExclusive) .unwrap(); let tracker = GlobalCacheTracker::new(&gctx).unwrap(); let indexes = tracker.registry_index_all().unwrap(); assert_eq!(indexes.len(), 2); assert_eq!(get_index_names().len(), 2); drop(lock); // Running in the future without these indexes should delete them. p.change_file("Cargo.toml", &basic_manifest("foo", "0.2.0")); p.cargo("clean gc -Zgc") .masquerade_as_nightly_cargo(&["gc"]) .run(); let lock = gctx .acquire_package_cache_lock(CacheLockMode::MutateExclusive) .unwrap(); let indexes = tracker.registry_index_all().unwrap(); assert_eq!(indexes.len(), 0); assert_eq!(get_index_names().len(), 0); drop(lock); } #[cargo_test] fn clean_gc_dry_run() { // Basic `clean --gc --dry-run` test. let p = basic_foo_bar_project(); // Populate the last-use data. p.cargo("fetch") .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(4)) .run(); let registry_root = paths::home().join(".cargo/registry"); let glob_registry = |name| -> PathBuf { let mut paths: Vec<_> = glob::glob(registry_root.join(name).join("*").to_str().unwrap()) .unwrap() .map(|p| p.unwrap()) .collect(); assert_eq!(paths.len(), 1); paths.pop().unwrap() }; let index = glob_registry("index").ls_r(); let src = glob_registry("src").ls_r(); let cache = glob_registry("cache").ls_r(); let mut expected_files = index .iter() .chain(src.iter()) .chain(cache.iter()) .map(|p| p.to_str().unwrap()) .join("\n"); expected_files.push_str("\n"); let expected_files = snapbox::filter::normalize_paths(&expected_files); let expected_files = assert_e2e().redactions().redact(&expected_files); p.cargo("clean gc --dry-run -v -Zgc") .masquerade_as_nightly_cargo(&["gc"]) .with_stdout_data(expected_files.as_str().unordered()) .with_stderr_data(str![[r#" [SUMMARY] [FILE_NUM] files, [FILE_SIZE]B total [WARNING] no files deleted due to --dry-run "#]]) .run(); // Again, make sure the information is still tracked. p.cargo("clean gc --dry-run -v -Zgc") .masquerade_as_nightly_cargo(&["gc"]) .with_stdout_data(expected_files.as_str().unordered()) .with_stderr_data(str![[r#" [SUMMARY] [FILE_NUM] files, [FILE_SIZE]B total [WARNING] no files deleted due to --dry-run "#]]) .run(); } #[cargo_test] fn clean_default_gc() { // `clean gc` without options should also gc let p = basic_foo_bar_project(); // Populate the last-use data. p.cargo("fetch") .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(4)) .run(); p.cargo("clean gc -v -Zgc") .masquerade_as_nightly_cargo(&["gc"]) .with_stderr_data( str![[r#" [REMOVING] [ROOT]/home/.cargo/registry/index/-[HASH] [REMOVING] [ROOT]/home/.cargo/registry/src/-[HASH] [REMOVING] [ROOT]/home/.cargo/registry/cache/-[HASH] [REMOVED] [FILE_NUM] files, [FILE_SIZE]B total "#]] .unordered(), ) .run(); } #[cargo_test] fn tracks_sizes() { // Checks that sizes are properly tracked in the db. Package::new("dep1", "1.0.0") .file("src/lib.rs", "") .publish(); Package::new("dep2", "1.0.0") .file("src/lib.rs", "") .file("data", &"abcdefghijklmnopqrstuvwxyz".repeat(1000)) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] dep1 = "1.0" dep2 = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("fetch").run(); // Check that the crate sizes are the same as on disk. let gctx = GlobalContextBuilder::new().build(); let _lock = gctx .acquire_package_cache_lock(CacheLockMode::MutateExclusive) .unwrap(); let tracker = GlobalCacheTracker::new(&gctx).unwrap(); let mut crates = tracker.registry_crate_all().unwrap(); crates.sort_by(|a, b| a.0.crate_filename.cmp(&b.0.crate_filename)); let db_sizes: Vec<_> = crates.iter().map(|c| c.0.size).collect(); let mut actual: Vec<_> = p .glob(paths::home().join(".cargo/registry/cache/*/*")) .map(|p| p.unwrap()) .collect(); actual.sort(); let actual_sizes: Vec<_> = actual .iter() .map(|path| std::fs::metadata(path).unwrap().len()) .collect(); assert_eq!(db_sizes, actual_sizes); // Also check the src sizes are computed. let mut srcs = tracker.registry_src_all().unwrap(); srcs.sort_by(|a, b| a.0.package_dir.cmp(&b.0.package_dir)); let db_sizes: Vec<_> = srcs.iter().map(|c| c.0.size.unwrap()).collect(); let mut actual: Vec<_> = p .glob(paths::home().join(".cargo/registry/src/*/*")) .map(|p| p.unwrap()) .collect(); actual.sort(); // .cargo-ok is not tracked in the size. actual.iter().for_each(|p| p.join(".cargo-ok").rm_rf()); let actual_sizes: Vec<_> = actual .iter() .map(|path| cargo_util::du(path, &[]).unwrap()) .collect(); assert_eq!(db_sizes, actual_sizes); assert!(db_sizes[1] > 26000); } #[cargo_test] fn max_size() { // Checks --max-crate-size and --max-src-size with various cleaning thresholds. let gctx = GlobalContextBuilder::new().build(); let test_crates = [ // name, age, crate_size, src_size ("a-1.0.0", 5, 1, 1), ("b-1.0.0", 6, 2, 2), ("c-1.0.0", 3, 3, 3), ("d-1.0.0", 2, 4, 4), ("e-1.0.0", 2, 5, 5), ("f-1.0.0", 9, 6, 6), ("g-1.0.0", 1, 1, 1), ]; // Determine the order things get deleted so they can be verified. let mut names_by_timestamp: Vec<_> = test_crates .iter() .map(|(name, age, _, _)| (days_ago_unix(*age), name)) .collect(); names_by_timestamp.sort(); let names_by_timestamp: Vec<_> = names_by_timestamp .into_iter() .map(|(_, name)| name) .collect(); // This exercises the different boundary conditions. for (clean_size, files) in [ (22, 0), (21, 1), (16, 1), (15, 2), (14, 2), (13, 3), (12, 4), (10, 4), (9, 5), (6, 5), (5, 6), (1, 6), (0, 7), ] { let (removed, kept) = names_by_timestamp.split_at(files); // --max-crate-size let (cache_dir, src_dir) = populate_cache(&gctx, &test_crates); let mut stderr = String::new(); for name in removed { writeln!(stderr, "[REMOVING] [..]{name}.crate").unwrap(); } let total_display = if removed.is_empty() { "" } else { ", [FILE_SIZE]B total" }; let files_display = if files == 0 { "0 files" } else if files == 1 { "1 file" } else { "[FILE_NUM] files" }; writeln!(stderr, "[REMOVED] {files_display}{total_display}").unwrap(); cargo_process(&format!("clean gc -Zgc -v --max-crate-size={clean_size}")) .masquerade_as_nightly_cargo(&["gc"]) .with_stderr_data(stderr.unordered()) .run(); for name in kept { assert!(cache_dir.join(format!("{name}.crate")).exists()); } for name in removed { assert!(!cache_dir.join(format!("{name}.crate")).exists()); } // --max-src-size populate_cache(&gctx, &test_crates); let mut stderr = String::new(); for name in removed { writeln!(stderr, "[REMOVING] [..]{name}").unwrap(); } let total_display = if removed.is_empty() { "" } else { ", [FILE_SIZE]B total" }; writeln!(stderr, "[REMOVED] {files_display}{total_display}").unwrap(); cargo_process(&format!("clean gc -Zgc -v --max-src-size={clean_size}")) .masquerade_as_nightly_cargo(&["gc"]) .with_stderr_data(stderr.unordered()) .run(); for name in kept { assert!(src_dir.join(name).exists()); } for name in removed { assert!(!src_dir.join(name).exists()); } } } #[cargo_test] fn max_size_untracked_crate() { // When a .crate file exists from an older version of cargo that did not // track sizes, `clean --max-crate-size` should populate the db with the // sizes. let gctx = GlobalContextBuilder::new().build(); let cache = paths::home().join(".cargo/registry/cache/example.com-a6c4a5adcb232b9a"); cache.mkdir_p(); paths::home() .join(".cargo/registry/index/example.com-a6c4a5adcb232b9a") .mkdir_p(); // Create the `.crate files. let test_crates = [ // name, size ("a-1.0.0.crate", 1234), ("b-1.0.0.crate", 42), ("c-1.0.0.crate", 0), ]; for (name, size) in test_crates { std::fs::write(cache.join(name), "x".repeat(size as usize)).unwrap() } // This should scan the directory and populate the db with the size information. cargo_process("clean gc -Zgc -v --max-crate-size=100000") .masquerade_as_nightly_cargo(&["gc"]) .with_stderr_data(str![[r#" [REMOVED] 0 files "#]]) .run(); // Check that it stored the size data. let _lock = gctx .acquire_package_cache_lock(CacheLockMode::MutateExclusive) .unwrap(); let tracker = GlobalCacheTracker::new(&gctx).unwrap(); let crates = tracker.registry_crate_all().unwrap(); let mut actual: Vec<_> = crates .iter() .map(|(rc, _time)| (rc.crate_filename.as_str(), rc.size)) .collect(); actual.sort(); assert_eq!(test_crates, actual.as_slice()); } /// Helper to prepare the max-size test. fn max_size_untracked_prepare() -> (GlobalContext, Project) { // First, publish and download a dependency. let p = basic_foo_bar_project(); p.cargo("fetch").run(); // Pretend it was an older version that did not track last-use. let gctx = GlobalContextBuilder::new().build(); GlobalCacheTracker::db_path(&gctx) .into_path_unlocked() .rm_rf(); (gctx, p) } /// Helper to verify the max-size test. fn max_size_untracked_verify(gctx: &GlobalContext) { let actual: Vec<_> = glob::glob( paths::home() .join(".cargo/registry/src/*/*") .to_str() .unwrap(), ) .unwrap() .map(|p| p.unwrap()) .collect(); assert_eq!(actual.len(), 1); let actual_size = cargo_util::du(&actual[0], &[]).unwrap(); let lock = gctx .acquire_package_cache_lock(CacheLockMode::MutateExclusive) .unwrap(); let tracker = GlobalCacheTracker::new(&gctx).unwrap(); let srcs = tracker.registry_src_all().unwrap(); assert_eq!(srcs.len(), 1); assert_eq!(srcs[0].0.size, Some(actual_size)); drop(lock); } #[cargo_test] fn max_size_untracked_src_from_use() { // When a src directory exists from an older version of cargo that did not // track sizes, doing a build should populate the db with an entry with an // unknown size. `clean --max-src-size` should then fix the size. let (gctx, p) = max_size_untracked_prepare(); // Run a command that will update the db with an unknown src size. p.cargo("tree").run(); // Check that it is None. let lock = gctx .acquire_package_cache_lock(CacheLockMode::MutateExclusive) .unwrap(); let tracker = GlobalCacheTracker::new(&gctx).unwrap(); let srcs = tracker.registry_src_all().unwrap(); assert_eq!(srcs.len(), 1); assert_eq!(srcs[0].0.size, None); drop(lock); // Fix the size. p.cargo("clean gc -v --max-src-size=10000 -Zgc") .masquerade_as_nightly_cargo(&["gc"]) .with_stderr_data(str![[r#" [REMOVED] 0 files "#]]) .run(); max_size_untracked_verify(&gctx); } #[cargo_test] fn max_size_untracked_src_from_clean() { // When a src directory exists from an older version of cargo that did not // track sizes, `clean --max-src-size` should populate the db with the // sizes. let (gctx, p) = max_size_untracked_prepare(); // Clean should scan the src and update the db. p.cargo("clean gc -v --max-src-size=10000 -Zgc") .masquerade_as_nightly_cargo(&["gc"]) .with_stderr_data(str![[r#" [REMOVED] 0 files "#]]) .run(); max_size_untracked_verify(&gctx); } #[cargo_test] fn max_download_size() { // --max-download-size // // This creates some sample crates of specific sizes, and then tries // deleting at various specific size thresholds that exercise different // edge conditions. let gctx = GlobalContextBuilder::new().build(); let test_crates = [ // name, age, crate_size, src_size ("d-1.0.0", 4, 4, 5), ("c-1.0.0", 3, 3, 3), ("a-1.0.0", 1, 2, 5), ("b-1.0.0", 1, 1, 7), ]; for (max_size, num_deleted, files_deleted) in [ (30, 0, 0), (29, 1, 1), (24, 2, 2), (20, 3, 3), (1, 7, 7), (0, 8, 8), ] { populate_cache(&gctx, &test_crates); // Determine the order things will be deleted. let delete_order: Vec = test_crates .iter() .flat_map(|(name, _, _, _)| [name.to_string(), format!("{name}.crate")]) .collect(); let (removed, _kept) = delete_order.split_at(num_deleted); let mut stderr = String::new(); for name in removed { writeln!(stderr, "[REMOVING] [..]{name}").unwrap(); } let files_display = if files_deleted == 0 { "0 files" } else if files_deleted == 1 { "1 file" } else { "[FILE_NUM] files" }; let total_display = if removed.is_empty() { "" } else { ", [FILE_SIZE]B total" }; writeln!(stderr, "[REMOVED] {files_display}{total_display}",).unwrap(); cargo_process(&format!("clean gc -Zgc -v --max-download-size={max_size}")) .masquerade_as_nightly_cargo(&["gc"]) .with_stderr_data(stderr.unordered()) .run(); } } #[cargo_test] fn package_cache_lock_during_build() { // Verifies that a shared lock is held during a build. Resolution and // downloads should be OK while that is held, but mutation should block. // // This works by launching a build with a build script that will pause. // Then it performs other cargo commands and verifies their behavior. Package::new("bar", "1.0.0").publish(); let p_foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { std::fs::write("blocking", "").unwrap(); let path = std::path::Path::new("ready"); loop { if path.exists() { break; } else { std::thread::sleep(std::time::Duration::from_millis(100)) } } } "#, ) .build(); let p_foo2 = project() .at("foo2") .file( "Cargo.toml", r#" [package] name = "foo2" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); // Start a build that will pause once the build starts. let mut foo_child = p_foo .cargo("check") .build_command() .stdout(Stdio::piped()) .stderr(Stdio::piped()) .spawn() .unwrap(); // Wait for it to enter build script. retry(100, || p_foo.root().join("blocking").exists().then_some(())); // Start a build with a different target directory. It should not block, // even though it gets a download lock, and then a shared lock. // // Also verify that auto-gc gets disabled. p_foo2 .cargo("check") .env("CARGO_CACHE_AUTO_CLEAN_FREQUENCY", "always") .env("CARGO_LOG", "gc=debug") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [..]s DEBUG gc: unable to acquire mutate lock, auto gc disabled [CHECKING] bar v1.0.0 [CHECKING] foo2 v0.1.0 ([ROOT]/foo2) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Ensure that the first build really blocked. assert!(matches!(foo_child.try_wait(), Ok(None))); // Cleaning while a command is running should block. let mut clean_cmd = p_foo2 .cargo("clean gc --max-download-size=0 -Zgc") .masquerade_as_nightly_cargo(&["gc"]) .build_command(); clean_cmd.stderr(Stdio::piped()); let mut clean_child = clean_cmd.spawn().unwrap(); // Give the clean command a chance to finish (it shouldn't). sleep_ms(500); // They should both still be running. assert!(matches!(foo_child.try_wait(), Ok(None))); assert!(matches!(clean_child.try_wait(), Ok(None))); // Let the original build finish. p_foo.change_file("ready", ""); // Wait for clean to finish. let thread = std::thread::spawn(|| clean_child.wait_with_output().unwrap()); let output = thread_wait_timeout(100, thread); assert!(output.status.success()); // Validate the output of the clean. execs() .with_stderr_data(str![[r#" [BLOCKING] waiting for file lock on package cache mutation [REMOVED] [FILE_NUM] files, [FILE_SIZE]B total "#]]) .run_output(&output); } #[cargo_test] fn read_only_locking_auto_gc() { // Tests the behavior for auto-gc on a read-only directory. let p = basic_foo_bar_project(); // Populate cache. p.cargo("fetch").run(); let cargo_home = paths::home().join(".cargo"); let mut perms = std::fs::metadata(&cargo_home).unwrap().permissions(); // Test when it can't update auto-gc db. perms.set_readonly(true); std::fs::set_permissions(&cargo_home, perms.clone()).unwrap(); p.cargo("check -Zgc") .masquerade_as_nightly_cargo(&["gc"]) .with_stderr_data(str![[r#" [CHECKING] bar v1.0.0 [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Try again without the last-use existing (such as if the cache was // populated by an older version of cargo). perms.set_readonly(false); std::fs::set_permissions(&cargo_home, perms.clone()).unwrap(); let gctx = GlobalContextBuilder::new().build(); GlobalCacheTracker::db_path(&gctx) .into_path_unlocked() .rm_rf(); perms.set_readonly(true); std::fs::set_permissions(&cargo_home, perms.clone()).unwrap(); p.cargo("check -Zgc") .masquerade_as_nightly_cargo(&["gc"]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); perms.set_readonly(false); std::fs::set_permissions(&cargo_home, perms).unwrap(); } #[cargo_test] fn delete_index_also_deletes_crates() { // Checks that when an index is delete that src and cache directories also get deleted. let p = basic_foo_bar_project(); p.cargo("fetch") .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(4)) .run(); assert_eq!(get_registry_names("src"), ["bar-1.0.0"]); assert_eq!(get_registry_names("cache"), ["bar-1.0.0.crate"]); p.cargo("clean gc") .arg("--max-index-age=0 days") .arg("-Zgc") .masquerade_as_nightly_cargo(&["gc"]) .with_stderr_data(str![[r#" [REMOVED] [FILE_NUM] files, [FILE_SIZE]B total "#]]) .run(); assert_eq!(get_registry_names("src").len(), 0); assert_eq!(get_registry_names("cache").len(), 0); } #[cargo_test] fn clean_syncs_missing_files() { // When files go missing in the cache, clean operations that need to track // the size should also remove them from the database. Package::new("bar", "1.0.0").publish(); Package::new("baz", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" baz = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("fetch").run(); // Verify things are tracked. let gctx = GlobalContextBuilder::new().build(); let lock = gctx .acquire_package_cache_lock(CacheLockMode::MutateExclusive) .unwrap(); let tracker = GlobalCacheTracker::new(&gctx).unwrap(); let crates = tracker.registry_crate_all().unwrap(); assert_eq!(crates.len(), 2); let srcs = tracker.registry_src_all().unwrap(); assert_eq!(srcs.len(), 2); drop(lock); // Remove the files. for pattern in [ ".cargo/registry/cache/*/bar-1.0.0.crate", ".cargo/registry/src/*/bar-1.0.0", ] { p.glob(paths::home().join(pattern)) .map(|p| p.unwrap()) .next() .unwrap() .rm_rf(); } // Clean should update the db. p.cargo("clean gc -v --max-download-size=1GB -Zgc") .masquerade_as_nightly_cargo(&["gc"]) .with_stderr_data(str![[r#" [REMOVED] 0 files "#]]) .run(); // Verify let crates = tracker.registry_crate_all().unwrap(); assert_eq!(crates.len(), 1); let srcs = tracker.registry_src_all().unwrap(); assert_eq!(srcs.len(), 1); } #[cargo_test] fn offline_doesnt_auto_gc() { // When running offline, auto-gc shouldn't run. let p = basic_foo_bar_project(); p.cargo("fetch") .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(4)) .run(); // Remove the dependency. p.change_file("Cargo.toml", &basic_manifest("foo", "0.1.0")); // Run offline, make sure it doesn't delete anything p.cargo("check --offline") .with_stderr_data(str![[r#" [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert_eq!(get_registry_names("src"), ["bar-1.0.0"]); assert_eq!(get_registry_names("cache"), ["bar-1.0.0.crate"]); // Run online, make sure auto-gc runs. p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert_eq!(get_registry_names("src"), &[] as &[String]); assert_eq!(get_registry_names("cache"), &[] as &[String]); } #[cargo_test] fn can_handle_future_schema() -> anyhow::Result<()> { // It should work when a future version of cargo has made schema changes // to the database. let p = basic_foo_bar_project(); p.cargo("fetch") .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(4)) .run(); // Modify the schema to pretend this is done by a future version of cargo. let gctx = GlobalContextBuilder::new().build(); let db_path = GlobalCacheTracker::db_path(&gctx).into_path_unlocked(); let conn = rusqlite::Connection::open(&db_path)?; let user_version: u32 = conn.query_row("SELECT user_version FROM pragma_user_version", [], |row| { row.get(0) })?; conn.execute("ALTER TABLE global_data ADD COLUMN foo DEFAULT 123", [])?; conn.pragma_update(None, "user_version", &(user_version + 1))?; drop(conn); // Verify it doesn't blow up. p.cargo("clean gc --max-download-size=0 -Zgc") .masquerade_as_nightly_cargo(&["gc"]) .with_stderr_data(str![[r#" [REMOVED] [FILE_NUM] files, [FILE_SIZE]B total "#]]) .run(); Ok(()) } #[cargo_test] fn clean_max_git_age() { // --max-git-*-age flags let (git_a, git_a_repo) = git::new_repo("git_a", |p| { p.file("Cargo.toml", &basic_manifest("git_a", "1.0.0")) .file("src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] git_a = {{ git = '{}' }} "#, git_a.url() ), ) .file("src/lib.rs", "") .build(); // Populate last-use tracking. p.cargo("fetch") .env("__CARGO_TEST_LAST_USE_NOW", days_ago_unix(4)) .run(); // Update git_a to create a separate checkout. git_a.change_file("src/lib.rs", "// test"); git::add(&git_a_repo); git::commit(&git_a_repo); // Update last-use tracking, where the first git checkout will stay "old". p.cargo("update -p git_a") .env("__CARGO_TEST_LAST_USE_NOW", days_ago_unix(2)) .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/git_a` [LOCKING] 1 package to latest compatible version [UPDATING] git_a v1.0.0 ([ROOTURL]/git_a#[..]) -> #[..] "#]]) .run(); let db_names = get_git_db_names(); assert_eq!(db_names.len(), 1); let db_name = &db_names[0]; let co_names = get_git_checkout_names(&db_name); assert_eq!(co_names.len(), 2); // Delete the first checkout p.cargo("clean gc -v -Zgc") .arg("--max-git-co-age=3 days") .masquerade_as_nightly_cargo(&["gc"]) .with_stderr_data(str![[r#" [REMOVING] [ROOT]/home/.cargo/git/checkouts/git_a-[HASH]/[..] [REMOVED] [FILE_NUM] files, [FILE_SIZE]B total "#]]) .run(); let db_names = get_git_db_names(); assert_eq!(db_names.len(), 1); let co_names = get_git_checkout_names(&db_name); assert_eq!(co_names.len(), 1); // delete the second checkout p.cargo("clean gc -v -Zgc") .arg("--max-git-co-age=0 days") .masquerade_as_nightly_cargo(&["gc"]) .with_stderr_data(str![[r#" [REMOVING] [ROOT]/home/.cargo/git/checkouts/git_a-[HASH]/[..] [REMOVED] [FILE_NUM] files, [FILE_SIZE]B total "#]]) .run(); let db_names = get_git_db_names(); assert_eq!(db_names.len(), 1); let co_names = get_git_checkout_names(&db_name); assert_eq!(co_names.len(), 0); // delete the db p.cargo("clean gc -v -Zgc") .arg("--max-git-db-age=1 days") .masquerade_as_nightly_cargo(&["gc"]) .with_stderr_data(str![[r#" [REMOVING] [ROOT]/home/.cargo/git/db/git_a-[HASH] [REMOVING] [ROOT]/home/.cargo/git/checkouts/git_a-[HASH] [REMOVED] [FILE_NUM] files, [FILE_SIZE]B total "#]]) .run(); let db_names = get_git_db_names(); assert_eq!(db_names.len(), 0); let co_names = get_git_checkout_names(&db_name); assert_eq!(co_names.len(), 0); } #[cargo_test] fn clean_max_src_crate_age() { // --max-src-age and --max-crate-age flags let p = basic_foo_bar_project(); // Populate last-use tracking. p.cargo("fetch") .env("__CARGO_TEST_LAST_USE_NOW", days_ago_unix(4)) .run(); // Update bar to create a separate copy with a different timestamp. Package::new("bar", "1.0.1").publish(); p.cargo("update -p bar") .env("__CARGO_TEST_LAST_USE_NOW", days_ago_unix(2)) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [UPDATING] bar v1.0.0 -> v1.0.1 "#]]) .run(); p.cargo("fetch") .env("__CARGO_TEST_LAST_USE_NOW", days_ago_unix(2)) .with_stderr_data(str![[r#" [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.1 (registry `dummy-registry`) "#]]) .run(); assert_eq!(get_registry_names("src"), ["bar-1.0.0", "bar-1.0.1"]); assert_eq!( get_registry_names("cache"), ["bar-1.0.0.crate", "bar-1.0.1.crate"] ); // Delete the old src. p.cargo("clean gc -v -Zgc") .arg("--max-src-age=3 days") .masquerade_as_nightly_cargo(&["gc"]) .with_stderr_data(str![[r#" [REMOVING] [ROOT]/home/.cargo/registry/src/-[HASH]/bar-1.0.0 [REMOVED] [FILE_NUM] files, [FILE_SIZE]B total "#]]) .run(); // delete the second src p.cargo("clean gc -v -Zgc") .arg("--max-src-age=0 days") .masquerade_as_nightly_cargo(&["gc"]) .with_stderr_data(str![[r#" [REMOVING] [ROOT]/home/.cargo/registry/src/-[HASH]/bar-1.0.1 [REMOVED] [FILE_NUM] files, [FILE_SIZE]B total "#]]) .run(); // delete the old crate p.cargo("clean gc -v -Zgc") .arg("--max-crate-age=3 days") .masquerade_as_nightly_cargo(&["gc"]) .with_stderr_data(str![[r#" [REMOVING] [ROOT]/home/.cargo/registry/cache/-[HASH]/bar-1.0.0.crate [REMOVED] 1 file, [FILE_SIZE]B total "#]]) .run(); // delete the seecond crate p.cargo("clean gc -v -Zgc") .arg("--max-crate-age=0 days") .masquerade_as_nightly_cargo(&["gc"]) .with_stderr_data(str![[r#" [REMOVING] [ROOT]/home/.cargo/registry/cache/-[HASH]/bar-1.0.1.crate [REMOVED] 1 file, [FILE_SIZE]B total "#]]) .run(); } #[cargo_test] fn clean_max_git_size() { // clean --max-git-size // // Creates two checkouts. The sets a size threshold to delete one. And // then with 0 max size to delete everything. let (git_project, git_repo) = git::new_repo("bar", |p| { p.file("Cargo.toml", &basic_manifest("bar", "1.0.0")) .file("src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = {{ git = '{}' }} "#, git_project.url() ), ) .file("src/lib.rs", "") .build(); // Fetch and populate db. p.cargo("fetch") .env("__CARGO_TEST_LAST_USE_NOW", days_ago_unix(3)) .run(); // Figure out the name of the first checkout. let git_root = paths::home().join(".cargo/git"); let db_names = get_git_db_names(); assert_eq!(db_names.len(), 1); let db_name = &db_names[0]; let co_names = get_git_checkout_names(&db_name); assert_eq!(co_names.len(), 1); let first_co_name = &co_names[0]; // Make an update and create a new checkout. git_project.change_file("src/lib.rs", "// modified"); git::add(&git_repo); git::commit(&git_repo); p.cargo("update") // Use a different time so that the first checkout timestamp is less // than the second. .env("__CARGO_TEST_LAST_USE_NOW", days_ago_unix(2)) .run(); // Figure out the threshold to use. let mut co_names = get_git_checkout_names(&db_name); assert_eq!(co_names.len(), 2); co_names.retain(|name| name != first_co_name); assert_eq!(co_names.len(), 1); let second_co_name = &co_names[0]; let second_co_path = git_root .join("checkouts") .join(db_name) .join(second_co_name); let second_co_size = cargo_util::du(&second_co_path, &["!.git"]).unwrap(); let db_size = cargo_util::du(&git_root.join("db").join(db_name), &[]).unwrap(); let threshold = db_size + second_co_size; p.cargo(&format!("clean gc --max-git-size={threshold} -Zgc -v")) .masquerade_as_nightly_cargo(&["gc"]) .with_stderr_data(&format!( "\ [REMOVING] [ROOT]/home/.cargo/git/checkouts/bar-[HASH]/{first_co_name} [REMOVED] [..] " )) .run(); // And then try cleaning everything. p.cargo("clean gc --max-git-size=0 -Zgc -v") .masquerade_as_nightly_cargo(&["gc"]) .with_stderr_data( format!( "\ [REMOVING] [ROOT]/home/.cargo/git/checkouts/bar-[HASH]/{second_co_name} [REMOVING] [ROOT]/home/.cargo/git/db/bar-[HASH] [REMOVED] [..] " ) .unordered(), ) .run(); } // Helper for setting up fake git sizes for git size cleaning. fn setup_fake_git_sizes(db_name: &str, db_size: usize, co_sizes: &[usize]) { let base_git = paths::home().join(".cargo/git"); let db_path = base_git.join("db").join(db_name); db_path.mkdir_p(); std::fs::write(db_path.join("test"), "x".repeat(db_size)).unwrap(); let base_co = base_git.join("checkouts").join(db_name); for (i, size) in co_sizes.iter().enumerate() { let co_name = format!("co{i}"); let co_path = base_co.join(co_name); co_path.mkdir_p(); std::fs::write(co_path.join("test"), "x".repeat(*size)).unwrap(); } } #[cargo_test] fn clean_max_git_size_untracked() { // If there are git directories that aren't tracked in the database, // `--max-git-size` should pick it up. // // The db_name of "example" depends on the sorting order of the names ("e" // should be after "c"), so that the db comes after the checkouts. setup_fake_git_sizes("example", 5000, &[1000, 2000]); cargo_process(&format!("clean gc -Zgc -v --max-git-size=7000")) .masquerade_as_nightly_cargo(&["gc"]) .with_stderr_data(str![[r#" [REMOVING] [ROOT]/home/.cargo/git/checkouts/example/co0 [REMOVED] 1 file, [FILE_SIZE]B total "#]]) .run(); cargo_process(&format!("clean gc -Zgc -v --max-git-size=5000")) .masquerade_as_nightly_cargo(&["gc"]) .with_stderr_data(str![[r#" [REMOVING] [ROOT]/home/.cargo/git/checkouts/example/co1 [REMOVED] 1 file, [FILE_SIZE]B total "#]]) .run(); cargo_process(&format!("clean gc -Zgc -v --max-git-size=0")) .masquerade_as_nightly_cargo(&["gc"]) .with_stderr_data(str![[r#" [REMOVING] [ROOT]/home/.cargo/git/db/example [REMOVED] 1 file, [FILE_SIZE]B total "#]]) .run(); } #[cargo_test] fn clean_max_git_size_deletes_co_from_db() { // In the scenario where it thinks it needs to delete the db, it should // also delete all the checkouts. // // The db_name of "abc" depends on the sorting order of the names ("a" // should be before "c"), so that the db comes before the checkouts. setup_fake_git_sizes("abc", 5000, &[1000, 2000]); // This deletes everything because it tries to delete the db, which then // deletes all checkouts. cargo_process(&format!("clean gc -Zgc -v --max-git-size=3000")) .masquerade_as_nightly_cargo(&["gc"]) .with_stderr_data(str![[r#" [REMOVING] [ROOT]/home/.cargo/git/db/abc [REMOVING] [ROOT]/home/.cargo/git/checkouts/abc/co1 [REMOVING] [ROOT]/home/.cargo/git/checkouts/abc/co0 [REMOVED] [FILE_NUM] files, [FILE_SIZE]B total "#]]) .run(); } #[cargo_test] fn handles_missing_index() { // Checks behavior when index is missing. let p = basic_foo_bar_project(); p.cargo("fetch").run(); paths::home().join(".cargo/registry/index").rm_rf(); cargo_process("clean gc -v --max-download-size=0 -Zgc") .masquerade_as_nightly_cargo(&["gc"]) .with_stderr_data( str![[r#" [REMOVING] [ROOT]/home/.cargo/registry/cache/-[HASH] [REMOVING] [ROOT]/home/.cargo/registry/src/-[HASH] [REMOVED] [FILE_NUM] files, [FILE_SIZE]B total "#]] .unordered(), ) .run(); } #[cargo_test] fn handles_missing_git_db() { // Checks behavior when git db is missing. let git_project = git::new("bar", |p| { p.file("Cargo.toml", &basic_manifest("bar", "1.0.0")) .file("src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = {{ git = '{}' }} "#, git_project.url() ), ) .file("src/lib.rs", "") .build(); p.cargo("fetch").run(); paths::home().join(".cargo/git/db").rm_rf(); cargo_process("clean gc -v --max-git-size=0 -Zgc") .masquerade_as_nightly_cargo(&["gc"]) .with_stderr_data(str![[r#" [REMOVING] [ROOT]/home/.cargo/git/checkouts/bar-[HASH] [REMOVED] [FILE_NUM] files, [FILE_SIZE]B total "#]]) .run(); } #[cargo_test] fn clean_gc_quiet_is_quiet() { // Checks that --quiet works with `cargo clean gc`, since there was a // subtle issue with how the flag is defined as a global flag. let p = basic_foo_bar_project(); p.cargo("fetch") .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(4)) .run(); p.cargo("clean gc --quiet -Zgc --dry-run") .masquerade_as_nightly_cargo(&["gc"]) .with_stdout_data("") .with_stderr_data("") .run(); // Verify exact same command without -q would actually display something. p.cargo("clean gc -Zgc --dry-run") .masquerade_as_nightly_cargo(&["gc"]) .with_stdout_data("") .with_stderr_data(str![[r#" [SUMMARY] [FILE_NUM] files, [FILE_SIZE]B total [WARNING] no files deleted due to --dry-run "#]]) .run(); } #[cargo_test(requires_rustup_stable)] fn compatible_with_older_cargo() { // Ensures that db stays backwards compatible across versions. // T-4 months: Current version, build the database. Package::new("old", "1.0.0").publish(); Package::new("middle", "1.0.0").publish(); Package::new("new", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] old = "1.0" middle = "1.0" new = "1.0" "#, ) .file("src/lib.rs", "") .build(); // Populate the last-use data. p.cargo("check") .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(4)) .run(); assert_eq!( get_registry_names("src"), ["middle-1.0.0", "new-1.0.0", "old-1.0.0"] ); assert_eq!( get_registry_names("cache"), ["middle-1.0.0.crate", "new-1.0.0.crate", "old-1.0.0.crate"] ); // T-2 months: Stable version, make sure it reads and deletes old src. p.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] new = "1.0" middle = "1.0" "#, ); rustup_cargo() .args(&["+stable", "check"]) .cwd(p.root()) .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(2)) .run(); assert_eq!(get_registry_names("src"), ["middle-1.0.0", "new-1.0.0"]); assert_eq!( get_registry_names("cache"), ["middle-1.0.0.crate", "new-1.0.0.crate", "old-1.0.0.crate"] ); // T-0 months: Current version, make sure it can read data from stable, // deletes old crate and middle src. p.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] new = "1.0" "#, ); p.cargo("check").run(); assert_eq!(get_registry_names("src"), ["new-1.0.0"]); assert_eq!( get_registry_names("cache"), ["middle-1.0.0.crate", "new-1.0.0.crate"] ); } #[cargo_test(requires_rustup_stable)] fn forward_compatible() { // Checks that db created in an older version can be read in a newer version. Package::new("bar", "1.0.0").publish(); let git_project = git::new("from_git", |p| { p.file("Cargo.toml", &basic_manifest("from_git", "1.0.0")) .file("src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" [dependencies] bar = "1.0.0" from_git = {{ git = '{}' }} "#, git_project.url() ), ) .file("src/lib.rs", "") .build(); rustup_cargo() .args(&["+stable", "check"]) .cwd(p.root()) .run(); let config = GlobalContextBuilder::new().build(); let lock = config .acquire_package_cache_lock(CacheLockMode::MutateExclusive) .unwrap(); let tracker = GlobalCacheTracker::new(&config).unwrap(); // Don't want to check the actual index name here, since although the // names are semi-stable, they might change over long periods of time. let indexes = tracker.registry_index_all().unwrap(); assert_eq!(indexes.len(), 1); let crates = tracker.registry_crate_all().unwrap(); let names: Vec<_> = crates .iter() .map(|(krate, _timestamp)| krate.crate_filename) .collect(); assert_eq!(names, &["bar-1.0.0.crate"]); let srcs = tracker.registry_src_all().unwrap(); let names: Vec<_> = srcs .iter() .map(|(src, _timestamp)| src.package_dir) .collect(); assert_eq!(names, &["bar-1.0.0"]); let dbs: Vec<_> = tracker.git_db_all().unwrap(); assert_eq!(dbs.len(), 1); let cos: Vec<_> = tracker.git_checkout_all().unwrap(); assert_eq!(cos.len(), 1); drop(lock); } #[cargo_test] fn resilient_to_unexpected_files() { // Tests that it doesn't choke on unexpected files. Package::new("bar", "1.0.0").publish(); let git_project = git::new("from_git", |p| { p.file("Cargo.toml", &basic_manifest("from_git", "1.0.0")) .file("src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" [dependencies] bar = "1.0.0" from_git = {{ git = '{}' }} "#, git_project.url() ), ) .file("src/lib.rs", "") .build(); p.cargo("fetch -Zgc") .masquerade_as_nightly_cargo(&["gc"]) .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(4)) .run(); let root = paths::home().join(".cargo"); std::fs::write(root.join("registry/index/foo"), "").unwrap(); std::fs::write(root.join("registry/cache/foo"), "").unwrap(); std::fs::write(root.join("registry/src/foo"), "").unwrap(); std::fs::write(root.join("git/db/foo"), "").unwrap(); std::fs::write(root.join("git/checkouts/foo"), "").unwrap(); p.cargo("clean gc -Zgc") .masquerade_as_nightly_cargo(&["gc"]) .with_stderr_data(str![[r#" [REMOVED] [FILE_NUM] files, [FILE_SIZE]B total "#]]) .run(); } cargo-0.91.0/tests/testsuite/help.rs000064400000000000000000000112301046102023000154740ustar 00000000000000//! Tests for cargo's help output. use std::fs; use std::path::Path; use std::str::from_utf8; use crate::prelude::*; use crate::utils::cargo_process; use cargo_test_support::registry::Package; use cargo_test_support::str; use cargo_test_support::{basic_manifest, paths, project}; #[cargo_test] fn help() { cargo_process("").run(); cargo_process("help").run(); cargo_process("-h").run(); cargo_process("help build").run(); cargo_process("build -h").run(); cargo_process("help help").run(); } #[cargo_test] fn help_external_subcommand() { // Check that `help external-subcommand` forwards the --help flag to the // given subcommand. Package::new("cargo-fake-help", "1.0.0") .file( "src/main.rs", r#" fn main() { if ::std::env::args().nth(2) == Some(String::from("--help")) { println!("fancy help output"); } } "#, ) .publish(); cargo_process("install cargo-fake-help").run(); cargo_process("help fake-help") .with_stdout_data(str![[r#" fancy help output "#]]) .run(); } fn help_with_man(display_command: &str) { // Build a "man" process that just echoes the contents. let p = project() .at(display_command) .file("Cargo.toml", &basic_manifest(display_command, "1.0.0")) .file( "src/main.rs", &r#" fn main() { eprintln!("custom __COMMAND__"); let path = std::env::args().skip(1).next().unwrap(); let mut f = std::fs::File::open(path).unwrap(); std::io::copy(&mut f, &mut std::io::stdout()).unwrap(); } "# .replace("__COMMAND__", display_command), ) .build(); p.cargo("build").run(); help_with_man_and_path(display_command, "build", "build", &p.target_debug_dir()); } fn help_with_man_and_path( display_command: &str, subcommand: &str, actual_subcommand: &str, path: &Path, ) { let contents = if display_command == "man" { fs::read_to_string(format!("src/etc/man/cargo-{}.1", actual_subcommand)).unwrap() } else { fs::read_to_string(format!( "src/doc/man/generated_txt/cargo-{}.txt", actual_subcommand )) .unwrap() }; let output = cargo_process(&format!("help {subcommand}")) .env("PATH", path) .run(); let stderr = from_utf8(&output.stderr).unwrap(); if display_command.is_empty() { assert_eq!(stderr, ""); } else { assert_eq!(stderr, format!("custom {}\n", display_command)); } let stdout = from_utf8(&output.stdout).unwrap(); assert_eq!(stdout, contents); } fn help_with_stdout_and_path(subcommand: &str, path: &Path) -> String { let output = cargo_process(&format!("help {subcommand}")) .env("PATH", path) .run(); let stderr = from_utf8(&output.stderr).unwrap(); assert_eq!(stderr, ""); let stdout = from_utf8(&output.stdout).unwrap(); stdout.to_string() } #[cargo_test] fn help_man() { // Checks that `help command` displays the man page using the given command. help_with_man("man"); help_with_man("less"); help_with_man("more"); // Check with no commands in PATH. help_with_man_and_path("", "build", "build", Path::new("")); } #[cargo_test] fn help_alias() { // Check that `help some_alias` will resolve. help_with_man_and_path("", "b", "build", Path::new("")); let config = paths::root().join(".cargo/config.toml"); fs::create_dir_all(config.parent().unwrap()).unwrap(); fs::write( config, r#" [alias] empty-alias = "" simple-alias = "build" complex-alias = ["build", "--release"] "#, ) .unwrap(); // The `empty-alias` returns an error. cargo_process("help empty-alias") .env("PATH", Path::new("")) .with_status(101) .with_stderr_data(str![[r#" [ERROR] no such command: `empty-alias` [HELP] a command with a similar name exists: `empty-alias` [HELP] view all installed commands with `cargo --list` [HELP] find a package to install `empty-alias` with `cargo search cargo-empty-alias` "#]]) .run(); // Because `simple-alias` aliases a subcommand with no arguments, help shows the manpage. help_with_man_and_path("", "simple-alias", "build", Path::new("")); // Help for `complex-alias` displays the full alias command. let out = help_with_stdout_and_path("complex-alias", Path::new("")); assert_eq!(out, "`complex-alias` is aliased to `build --release`\n"); } cargo-0.91.0/tests/testsuite/hints.rs000064400000000000000000000205141046102023000156760ustar 00000000000000//! Tests for hints. use crate::prelude::*; use cargo_test_support::registry::Package; use cargo_test_support::{project, str}; #[cargo_test] fn empty_hints_no_warn() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [hints] "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check -v") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn unknown_hints_warn() { Package::new("bar", "1.0.0") .file( "Cargo.toml", r#" [package] name = "bar" version = "1.0.0" edition = "2015" [hints] this-is-an-unknown-hint = true "#, ) .file("src/lib.rs", "") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = "1.0" [hints] this-is-an-unknown-hint = true "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check -v") .with_stderr_data(str![[r#" [WARNING] unused manifest key: hints.this-is-an-unknown-hint [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [CHECKING] bar v1.0.0 [RUNNING] `rustc --crate-name bar [..]` [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn hint_unknown_type_warn() { Package::new("bar", "1.0.0") .file( "Cargo.toml", r#" [package] name = "bar" version = "1.0.0" edition = "2015" [hints] mostly-unused = 1 "#, ) .file("src/lib.rs", "") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = "1.0" [hints] mostly-unused = "string" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check -v") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [WARNING] foo@0.0.1: ignoring unsupported value type (string) for 'hints.mostly-unused', which expects a boolean [CHECKING] bar v1.0.0 [RUNNING] `rustc --crate-name bar [..]` [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .with_stderr_does_not_contain("-Zhint-mostly-unused") .run(); } #[cargo_test] fn hints_mostly_unused_warn_without_gate() { Package::new("bar", "1.0.0") .file( "Cargo.toml", r#" [package] name = "bar" version = "1.0.0" edition = "2015" [hints] mostly-unused = true "#, ) .file("src/lib.rs", "") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = "1.0" [hints] mostly-unused = true "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check -v") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [WARNING] foo@0.0.1: ignoring 'hints.mostly-unused', pass `-Zprofile-hint-mostly-unused` to enable it [CHECKING] bar v1.0.0 [RUNNING] `rustc --crate-name bar [..]` [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .with_stderr_does_not_contain("-Zhint-mostly-unused") .run(); } #[cargo_test(nightly, reason = "-Zhint-mostly-unused is unstable")] fn hints_mostly_unused_nightly() { Package::new("bar", "1.0.0") .file( "Cargo.toml", r#" [package] name = "bar" version = "1.0.0" edition = "2015" [hints] mostly-unused = true "#, ) .file("src/lib.rs", "") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = "1.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check -Zprofile-hint-mostly-unused -v") .masquerade_as_nightly_cargo(&["profile-hint-mostly-unused"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [CHECKING] bar v1.0.0 [RUNNING] `rustc --crate-name bar [..] -Zhint-mostly-unused [..]` [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .with_stderr_does_not_contain( "[RUNNING] `rustc --crate-name foo [..] -Zhint-mostly-unused [..]", ) .run(); } #[cargo_test(nightly, reason = "-Zhint-mostly-unused is unstable")] fn mostly_unused_profile_overrides_hints_nightly() { Package::new("bar", "1.0.0") .file( "Cargo.toml", r#" [package] name = "bar" version = "1.0.0" edition = "2015" [hints] mostly-unused = true "#, ) .file("src/lib.rs", "") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = "1.0" [profile.dev.package.bar] hint-mostly-unused = false "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check -Zprofile-hint-mostly-unused -v") .masquerade_as_nightly_cargo(&["profile-hint-mostly-unused"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [CHECKING] bar v1.0.0 [RUNNING] `rustc --crate-name bar [..]` [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .with_stderr_does_not_contain("-Zhint-mostly-unused") .run(); } #[cargo_test(nightly, reason = "-Zhint-mostly-unused is unstable")] fn mostly_unused_profile_overrides_hints_on_self_nightly() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [hints] mostly-unused = true [profile.dev] hint-mostly-unused = false "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check -v") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .with_stderr_does_not_contain("-Zhint-mostly-unused") .run(); } cargo-0.91.0/tests/testsuite/https.rs000064400000000000000000000121031046102023000157060ustar 00000000000000//! Network tests for https transport. //! //! Note that these tests will generally require setting `CARGO_CONTAINER_TESTS` //! or `CARGO_PUBLIC_NETWORK_TESTS`. use crate::prelude::*; use cargo_test_support::containers::Container; use cargo_test_support::project; use cargo_test_support::str; #[cargo_test(container_test)] fn self_signed_should_fail() { // Cargo should not allow a connection to a self-signed certificate. let apache = Container::new("apache").launch(); let port = apache.port_mappings[&443]; let url = format!("https://127.0.0.1:{port}/repos/bar.git"); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = {{ git = "{url}" }} "# ), ) .file("src/lib.rs", "") .build(); // I think the text here depends on the curl backend. let err_msg = if cfg!(target_os = "macos") { "untrusted connection error; class=Ssl (16)[..]" } else if cfg!(unix) { "the SSL certificate is invalid; class=Ssl (16)[..]" } else if cfg!(windows) { "user cancelled certificate check; class=Http (34); code=Certificate (-17)" } else { panic!("target not supported"); }; p.cargo("fetch") .with_status(101) .with_stderr_data(&format!( "\ [UPDATING] git repository `https://127.0.0.1:[..]/repos/bar.git` [ERROR] failed to get `bar` as a dependency of package `foo v0.1.0 ([ROOT]/foo)` Caused by: failed to load source for dependency `bar` Caused by: Unable to update https://127.0.0.1:[..]/repos/bar.git Caused by: failed to clone into: [ROOT]/home/.cargo/git/db/bar-[HASH] Caused by: network failure seems to have happened if a proxy or similar is necessary `net.git-fetch-with-cli` may help here https://doc.rust-lang.org/cargo/reference/config.html#netgit-fetch-with-cli Caused by: {err_msg} " )) .run(); } #[cargo_test(container_test)] fn self_signed_with_cacert() { // When using cainfo, that should allow a connection to a self-signed cert. if cfg!(target_os = "macos") { // This test only seems to work with the // curl-sys/force-system-lib-on-osx feature enabled. For some reason // SecureTransport doesn't seem to like the self-signed certificate. // It works if the certificate is manually approved via Keychain // Access. The system libcurl is built with a LibreSSL fallback which // is used when CAINFO is set, which seems to work correctly. This // could use some more investigation. The official Rust binaries use // curl-sys/force-system-lib-on-osx so it is mostly an issue for local // testing. // // The error is: // [60] SSL peer certificate or SSH remote key was not OK (SSL: // certificate verification failed (result: 5)); class=Net (12) let curl_v = curl::Version::get(); if curl_v.vendored() { eprintln!( "vendored curl not supported on macOS, \ set curl-sys/force-system-lib-on-osx to enable" ); return; } } let apache = Container::new("apache").launch(); let port = apache.port_mappings[&443]; let url = format!("https://127.0.0.1:{port}/repos/bar.git"); let server_crt = apache.read_file("/usr/local/apache2/conf/server.crt"); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = {{ git = "{url}" }} "# ), ) .file("src/lib.rs", "") .file( ".cargo/config.toml", &format!( r#" [http] cainfo = "server.crt" "# ), ) .file("server.crt", &server_crt) .build(); p.cargo("fetch") .with_stderr_data(str![[r#" [UPDATING] git repository `https://127.0.0.1:[..]/repos/bar.git` [LOCKING] 1 package to latest compatible version "#]]) .run(); } #[cargo_test(public_network_test)] fn github_works() { // Check that an https connection to github.com works. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bitflags = { git = "https://github.com/rust-lang/bitflags.git", tag="1.3.2" } "#, ) .file("src/lib.rs", "") .build(); p.cargo("fetch") .with_stderr_data(str![[r#" [UPDATING] git repository `https://github.com/rust-lang/bitflags.git` [LOCKING] 1 package to latest compatible version "#]]) .run(); } cargo-0.91.0/tests/testsuite/inheritable_workspace_fields.rs000064400000000000000000001505511046102023000224500ustar 00000000000000//! Tests for inheriting Cargo.toml fields with field.workspace = true use crate::prelude::*; use cargo_test_support::registry::{Dependency, Package, RegistryBuilder}; use cargo_test_support::{ basic_lib_manifest, basic_manifest, git, paths, project, publish, registry, str, }; #[cargo_test] fn permit_additional_workspace_fields() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar"] [workspace.package] version = "1.2.3" authors = ["Rustaceans"] description = "This is a crate" documentation = "https://www.rust-lang.org/learn" readme = "README.md" homepage = "https://www.rust-lang.org" repository = "https://github.com/example/example" license = "MIT" license-file = "LICENSE" keywords = ["cli"] categories = ["development-tools"] publish = false edition = "2018" rust-version = "1.60" exclude = ["foo.txt"] include = ["bar.txt", "**/*.rs", "Cargo.toml", "LICENSE", "README.md"] [workspace.dependencies] dep = "0.1" "#, ) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] workspace = ".." "#, ) .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("check") // Should not warn about unused fields. .with_stderr_data(str![[r#" [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check").run(); let lockfile = p.read_lockfile(); assert!(!lockfile.contains("dep")); } #[cargo_test] fn deny_optional_dependencies() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar"] [workspace.dependencies] dep1 = { version = "0.1", optional = true } "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] workspace = ".." "#, ) .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: dep1 is optional, but workspace dependencies cannot be optional "#]]) .run(); } #[cargo_test] fn inherit_own_workspace_fields() { let registry = RegistryBuilder::new().http_api().http_index().build(); let p = project().build(); let _ = git::repo(&paths::root().join("foo")) .file( "Cargo.toml", r#" [package] name = "foo" version.workspace = true authors.workspace = true description.workspace = true documentation.workspace = true homepage.workspace = true repository.workspace = true license.workspace = true keywords.workspace = true categories.workspace = true publish.workspace = true edition.workspace = true rust-version.workspace = true exclude.workspace = true include.workspace = true [workspace] members = [] [workspace.package] version = "1.2.3" authors = ["Rustaceans"] description = "This is a crate" documentation = "https://www.rust-lang.org/learn" homepage = "https://www.rust-lang.org" repository = "https://github.com/example/example" license = "MIT" keywords = ["cli"] categories = ["development-tools"] publish = true edition = "2018" rust-version = "1.60" exclude = ["foo.txt"] include = ["bar.txt", "**/*.rs", "Cargo.toml"] "#, ) .file("src/main.rs", "fn main() {}") .file("foo.txt", "") // should be ignored when packaging .file("bar.txt", "") // should be included when packaging .build(); p.cargo("publish") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] both package.include and package.exclude are specified; the exclude list will be ignored [PACKAGING] foo v1.2.3 ([ROOT]/foo) [PACKAGED] 6 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v1.2.3 ([ROOT]/foo) [COMPILING] foo v1.2.3 ([ROOT]/foo/target/package/foo-1.2.3) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] foo v1.2.3 ([ROOT]/foo) [UPLOADED] foo v1.2.3 to registry `crates-io` [NOTE] waiting for foo v1.2.3 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v1.2.3 at registry `crates-io` "#]]) .run(); publish::validate_upload_with_contents( r#" { "authors": ["Rustaceans"], "badges": {}, "categories": ["development-tools"], "deps": [], "description": "This is a crate", "documentation": "https://www.rust-lang.org/learn", "features": {}, "homepage": "https://www.rust-lang.org", "keywords": ["cli"], "license": "MIT", "license_file": null, "links": null, "name": "foo", "readme": null, "readme_file": null, "repository": "https://github.com/example/example", "rust_version": "1.60", "vers": "1.2.3" } "#, "foo-1.2.3.crate", &[ "Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs", ".cargo_vcs_info.json", "bar.txt", ], [( "Cargo.toml", str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" rust-version = "1.60" name = "foo" version = "1.2.3" authors = ["Rustaceans"] build = false exclude = ["foo.txt"] include = [ "bar.txt", "**/*.rs", "Cargo.toml", ] publish = true autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "This is a crate" homepage = "https://www.rust-lang.org" documentation = "https://www.rust-lang.org/learn" readme = false keywords = ["cli"] categories = ["development-tools"] license = "MIT" repository = "https://github.com/example/example" [[bin]] name = "foo" path = "src/main.rs" "##]], )], ); } #[cargo_test] fn inherit_own_dependencies() { let registry = RegistryBuilder::new().http_api().http_index().build(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.2.0" edition = "2015" authors = [] [dependencies] dep.workspace = true [build-dependencies] dep-build.workspace = true [dev-dependencies] dep-dev.workspace = true [workspace] members = [] [workspace.dependencies] dep = "0.1" dep-build = "0.8" dep-dev = "0.5.2" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("dep", "0.1.2").publish(); Package::new("dep-build", "0.8.2").publish(); Package::new("dep-dev", "0.5.2").publish(); p.cargo("check") // Unordered because the download order is nondeterministic. .with_stderr_data( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 3 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] dep v0.1.2 (registry `dummy-registry`) [DOWNLOADED] dep-build v0.8.2 (registry `dummy-registry`) [CHECKING] dep v0.1.2 [CHECKING] bar v0.2.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); p.cargo("check").run(); let lockfile = p.read_lockfile(); assert!(lockfile.contains("dep")); assert!(lockfile.contains("dep-dev")); assert!(lockfile.contains("dep-build")); p.cargo("publish") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no description, license, license-file, documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] bar v0.2.0 ([ROOT]/foo) [UPDATING] crates.io index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] bar v0.2.0 ([ROOT]/foo) [COMPILING] dep v0.1.2 [COMPILING] bar v0.2.0 ([ROOT]/foo/target/package/bar-0.2.0) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] bar v0.2.0 ([ROOT]/foo) [UPLOADED] bar v0.2.0 to registry `crates-io` [NOTE] waiting for bar v0.2.0 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] bar v0.2.0 at registry `crates-io` "#]]) .run(); publish::validate_upload_with_contents( r#" { "authors": [], "badges": {}, "categories": [], "deps": [ { "default_features": true, "features": [], "kind": "normal", "name": "dep", "optional": false, "target": null, "version_req": "^0.1" }, { "default_features": true, "features": [], "kind": "dev", "name": "dep-dev", "optional": false, "target": null, "version_req": "^0.5.2" }, { "default_features": true, "features": [], "kind": "build", "name": "dep-build", "optional": false, "target": null, "version_req": "^0.8" } ], "description": null, "documentation": null, "features": {}, "homepage": null, "keywords": [], "license": null, "license_file": null, "links": null, "name": "bar", "readme": null, "readme_file": null, "repository": null, "rust_version": null, "vers": "0.2.0" } "#, "bar-0.2.0.crate", &["Cargo.toml", "Cargo.toml.orig", "Cargo.lock", "src/main.rs"], [( "Cargo.toml", str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "bar" version = "0.2.0" authors = [] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false readme = false [[bin]] name = "bar" path = "src/main.rs" [dependencies.dep] version = "0.1" [dev-dependencies.dep-dev] version = "0.5.2" [build-dependencies.dep-build] version = "0.8" "##]], )], ); } #[cargo_test] fn inherit_own_detailed_dependencies() { let registry = RegistryBuilder::new().http_api().http_index().build(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.2.0" edition = "2015" authors = [] [dependencies] dep.workspace = true [workspace] members = [] [workspace.dependencies] dep = { version = "0.1.2", features = ["testing"] } "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("dep", "0.1.2") .feature("testing", &[]) .publish(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] dep v0.1.2 (registry `dummy-registry`) [CHECKING] dep v0.1.2 [CHECKING] bar v0.2.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check").run(); let lockfile = p.read_lockfile(); assert!(lockfile.contains("dep")); p.cargo("publish") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no description, license, license-file, documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] bar v0.2.0 ([ROOT]/foo) [UPDATING] crates.io index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] bar v0.2.0 ([ROOT]/foo) [COMPILING] dep v0.1.2 [COMPILING] bar v0.2.0 ([ROOT]/foo/target/package/bar-0.2.0) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] bar v0.2.0 ([ROOT]/foo) [UPLOADED] bar v0.2.0 to registry `crates-io` [NOTE] waiting for bar v0.2.0 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] bar v0.2.0 at registry `crates-io` "#]]) .run(); publish::validate_upload_with_contents( r#" { "authors": [], "badges": {}, "categories": [], "deps": [ { "default_features": true, "features": ["testing"], "kind": "normal", "name": "dep", "optional": false, "target": null, "version_req": "^0.1.2" } ], "description": null, "documentation": null, "features": {}, "homepage": null, "keywords": [], "license": null, "license_file": null, "links": null, "name": "bar", "readme": null, "readme_file": null, "repository": null, "rust_version": null, "vers": "0.2.0" } "#, "bar-0.2.0.crate", &["Cargo.toml", "Cargo.toml.orig", "Cargo.lock", "src/main.rs"], [( "Cargo.toml", str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "bar" version = "0.2.0" authors = [] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false readme = false [[bin]] name = "bar" path = "src/main.rs" [dependencies.dep] version = "0.1.2" features = ["testing"] "##]], )], ); } #[cargo_test] fn inherit_from_own_undefined_field() { registry::init(); let p = project().build(); let _ = git::repo(&paths::root().join("foo")) .file( "Cargo.toml", r#" [package] name = "foo" version = "1.2.5" edition = "2015" authors = ["rustaceans"] description.workspace = true [workspace] members = [] "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: error inheriting `description` from workspace root manifest's `workspace.package.description` Caused by: `workspace.package.description` was not defined "#]]) .run(); } #[cargo_test] fn inherited_dependencies_union_features() { Package::new("dep", "0.1.0") .feature("fancy", &["fancy_dep"]) .feature("dancy", &["dancy_dep"]) .add_dep(Dependency::new("fancy_dep", "0.2").optional(true)) .add_dep(Dependency::new("dancy_dep", "0.6").optional(true)) .file("src/lib.rs", "") .publish(); Package::new("fancy_dep", "0.2.4").publish(); Package::new("dancy_dep", "0.6.8").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.2.0" edition = "2015" authors = [] [dependencies] dep = { workspace = true, features = ["dancy"] } [workspace] members = [] [workspace.dependencies] dep = { version = "0.1", features = ["fancy"] } "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_stderr_data( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 3 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] fancy_dep v0.2.4 (registry `dummy-registry`) [DOWNLOADED] dep v0.1.0 (registry `dummy-registry`) [DOWNLOADED] dancy_dep v0.6.8 (registry `dummy-registry`) [CHECKING] fancy_dep v0.2.4 [CHECKING] dancy_dep v0.6.8 [CHECKING] dep v0.1.0 [CHECKING] bar v0.2.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); let lockfile = p.read_lockfile(); assert!(lockfile.contains("dep")); assert!(lockfile.contains("fancy_dep")); assert!(lockfile.contains("dancy_dep")); } #[cargo_test] fn inherit_workspace_fields() { let registry = RegistryBuilder::new().http_api().http_index().build(); let p = project().build(); let _ = git::repo(&paths::root().join("foo")) .file( "Cargo.toml", r#" [workspace] members = ["bar"] [workspace.package] version = "1.2.3" authors = ["Rustaceans"] description = "This is a crate" documentation = "https://www.rust-lang.org/learn" readme = "README.md" homepage = "https://www.rust-lang.org" repository = "https://github.com/example/example" license = "MIT" license-file = "LICENSE" keywords = ["cli"] categories = ["development-tools"] publish = true edition = "2018" rust-version = "1.60" exclude = ["foo.txt"] include = ["bar.txt", "**/*.rs", "Cargo.toml", "LICENSE", "README.md"] "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" workspace = ".." version.workspace = true authors.workspace = true description.workspace = true documentation.workspace = true readme.workspace = true homepage.workspace = true repository.workspace = true license.workspace = true license-file.workspace = true keywords.workspace = true categories.workspace = true publish.workspace = true edition.workspace = true rust-version.workspace = true exclude.workspace = true include.workspace = true "#, ) .file("LICENSE", "license") .file("README.md", "README.md") .file("bar/src/main.rs", "fn main() {}") .file("bar/foo.txt", "") // should be ignored when packaging .file("bar/bar.txt", "") // should be included when packaging .build(); p.cargo("publish") .replace_crates_io(registry.index_url()) .cwd("bar") .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] both package.include and package.exclude are specified; the exclude list will be ignored [PACKAGING] bar v1.2.3 ([ROOT]/foo/bar) [PACKAGED] 8 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] bar v1.2.3 ([ROOT]/foo/bar) [WARNING] only one of `license` or `license-file` is necessary `license` should be used if the package license can be expressed with a standard SPDX expression. `license-file` should be used if the package uses a non-standard license. See https://doc.rust-lang.org/cargo/reference/manifest.html#the-license-and-license-file-fields for more information. [COMPILING] bar v1.2.3 ([ROOT]/foo/target/package/bar-1.2.3) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] bar v1.2.3 ([ROOT]/foo/bar) [UPLOADED] bar v1.2.3 to registry `crates-io` [NOTE] waiting for bar v1.2.3 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] bar v1.2.3 at registry `crates-io` "#]]) .run(); publish::validate_upload_with_contents( r#" { "authors": ["Rustaceans"], "badges": {}, "categories": ["development-tools"], "deps": [], "description": "This is a crate", "documentation": "https://www.rust-lang.org/learn", "features": {}, "homepage": "https://www.rust-lang.org", "keywords": ["cli"], "license": "MIT", "license_file": "LICENSE", "links": null, "name": "bar", "readme": "README.md", "readme_file": "README.md", "repository": "https://github.com/example/example", "rust_version": "1.60", "vers": "1.2.3" } "#, "bar-1.2.3.crate", &[ "Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs", "README.md", "LICENSE", ".cargo_vcs_info.json", "bar.txt", ], [( "Cargo.toml", str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" rust-version = "1.60" name = "bar" version = "1.2.3" authors = ["Rustaceans"] build = false exclude = ["foo.txt"] include = [ "bar.txt", "**/*.rs", "Cargo.toml", "LICENSE", "README.md", ] publish = true autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "This is a crate" homepage = "https://www.rust-lang.org" documentation = "https://www.rust-lang.org/learn" readme = "README.md" keywords = ["cli"] categories = ["development-tools"] license = "MIT" license-file = "LICENSE" repository = "https://github.com/example/example" [[bin]] name = "bar" path = "src/main.rs" "##]], )], ); } #[cargo_test] fn inherit_dependencies() { let registry = RegistryBuilder::new().http_api().http_index().build(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar"] [workspace.dependencies] dep = "0.1" dep-build = "0.8" dep-dev = "0.5.2" "#, ) .file( "bar/Cargo.toml", r#" [package] workspace = ".." name = "bar" version = "0.2.0" edition = "2015" authors = [] [dependencies] dep.workspace = true [build-dependencies] dep-build.workspace = true [dev-dependencies] dep-dev.workspace = true "#, ) .file("bar/src/main.rs", "fn main() {}") .build(); Package::new("dep", "0.1.2").publish(); Package::new("dep-build", "0.8.2").publish(); Package::new("dep-dev", "0.5.2").publish(); p.cargo("check") // Unordered because the download order is nondeterministic. .with_stderr_data( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 3 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] dep v0.1.2 (registry `dummy-registry`) [DOWNLOADED] dep-build v0.8.2 (registry `dummy-registry`) [CHECKING] dep v0.1.2 [CHECKING] bar v0.2.0 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); p.cargo("check").run(); let lockfile = p.read_lockfile(); assert!(lockfile.contains("dep")); assert!(lockfile.contains("dep-dev")); assert!(lockfile.contains("dep-build")); p.cargo("publish") .replace_crates_io(registry.index_url()) .cwd("bar") .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no description, license, license-file, documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] bar v0.2.0 ([ROOT]/foo/bar) [UPDATING] crates.io index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] bar v0.2.0 ([ROOT]/foo/bar) [COMPILING] dep v0.1.2 [COMPILING] bar v0.2.0 ([ROOT]/foo/target/package/bar-0.2.0) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] bar v0.2.0 ([ROOT]/foo/bar) [UPLOADED] bar v0.2.0 to registry `crates-io` [NOTE] waiting for bar v0.2.0 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] bar v0.2.0 at registry `crates-io` "#]]) .run(); publish::validate_upload_with_contents( r#" { "authors": [], "badges": {}, "categories": [], "deps": [ { "default_features": true, "features": [], "kind": "normal", "name": "dep", "optional": false, "target": null, "version_req": "^0.1" }, { "default_features": true, "features": [], "kind": "dev", "name": "dep-dev", "optional": false, "target": null, "version_req": "^0.5.2" }, { "default_features": true, "features": [], "kind": "build", "name": "dep-build", "optional": false, "target": null, "version_req": "^0.8" } ], "description": null, "documentation": null, "features": {}, "homepage": null, "keywords": [], "license": null, "license_file": null, "links": null, "name": "bar", "readme": null, "readme_file": null, "repository": null, "rust_version": null, "vers": "0.2.0" } "#, "bar-0.2.0.crate", &["Cargo.toml", "Cargo.toml.orig", "Cargo.lock", "src/main.rs"], [( "Cargo.toml", str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "bar" version = "0.2.0" authors = [] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false readme = false [[bin]] name = "bar" path = "src/main.rs" [dependencies.dep] version = "0.1" [dev-dependencies.dep-dev] version = "0.5.2" [build-dependencies.dep-build] version = "0.8" "##]], )], ); } #[cargo_test] fn inherit_target_dependencies() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar"] [workspace.dependencies] dep = "0.1" "#, ) .file( "bar/Cargo.toml", r#" [package] workspace = ".." name = "bar" version = "0.2.0" edition = "2015" authors = [] [target.'cfg(unix)'.dependencies] dep.workspace = true [target.'cfg(windows)'.dependencies] dep.workspace = true "#, ) .file("bar/src/main.rs", "fn main() {}") .build(); Package::new("dep", "0.1.2").publish(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] dep v0.1.2 (registry `dummy-registry`) [CHECKING] dep v0.1.2 [CHECKING] bar v0.2.0 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let lockfile = p.read_lockfile(); assert!(lockfile.contains("dep")); } #[cargo_test] fn inherit_dependency_override_optional() { Package::new("dep", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar"] [workspace.dependencies] dep = "0.1.0" "#, ) .file( "bar/Cargo.toml", r#" [package] workspace = ".." name = "bar" version = "0.2.0" edition = "2015" authors = [] [dependencies] dep = { workspace = true, optional = true } "#, ) .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.2.0 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn inherit_dependency_features() { Package::new("dep", "0.1.0") .feature("fancy", &["fancy_dep"]) .add_dep(Dependency::new("fancy_dep", "0.2").optional(true)) .file("src/lib.rs", "") .publish(); Package::new("fancy_dep", "0.2.4").publish(); Package::new("dancy_dep", "0.6.8").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.2.0" edition = "2015" authors = [] [dependencies] dep = { workspace = true, features = ["fancy"] } [workspace] members = [] [workspace.dependencies] dep = "0.1" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] fancy_dep v0.2.4 (registry `dummy-registry`) [DOWNLOADED] dep v0.1.0 (registry `dummy-registry`) [CHECKING] fancy_dep v0.2.4 [CHECKING] dep v0.1.0 [CHECKING] bar v0.2.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let lockfile = p.read_lockfile(); assert!(lockfile.contains("dep")); assert!(lockfile.contains("fancy_dep")); } #[cargo_test] fn inherit_detailed_dependencies() { let git_project = git::new("detailed", |project| { project .file("Cargo.toml", &basic_lib_manifest("detailed")) .file( "src/detailed.rs", r#" pub fn hello() -> &'static str { "hello world" } "#, ) }); // Make a new branch based on the current HEAD commit let repo = git2::Repository::open(&git_project.root()).unwrap(); let head = repo.head().unwrap().target().unwrap(); let head = repo.find_commit(head).unwrap(); repo.branch("branchy", &head, true).unwrap(); let p = project() .file( "Cargo.toml", &format!( r#" [workspace] members = ["bar"] [workspace.dependencies] detailed = {{ git = '{}', branch = "branchy" }} "#, git_project.url() ), ) .file( "bar/Cargo.toml", r#" [package] workspace = ".." name = "bar" version = "0.2.0" edition = "2015" authors = [] [dependencies] detailed.workspace = true "#, ) .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/detailed` [LOCKING] 1 package to latest compatible version [CHECKING] detailed v0.5.0 ([ROOTURL]/detailed?branch=branchy#[..]) [CHECKING] bar v0.2.0 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn inherit_path_dependencies() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar"] [workspace.dependencies] dep = { path = "dep" } "#, ) .file( "bar/Cargo.toml", r#" [package] workspace = ".." name = "bar" version = "0.2.0" edition = "2015" authors = [] [dependencies] dep.workspace = true "#, ) .file("bar/src/main.rs", "fn main() {}") .file("dep/Cargo.toml", &basic_manifest("dep", "0.9.0")) .file("dep/src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [CHECKING] dep v0.9.0 ([ROOT]/foo/dep) [CHECKING] bar v0.2.0 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let lockfile = p.read_lockfile(); assert!(lockfile.contains("dep")); } #[cargo_test] fn error_workspace_false() { registry::init(); let p = project().build(); let _ = git::repo(&paths::root().join("foo")) .file( "Cargo.toml", r#" [workspace] members = ["bar"] "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" workspace = ".." version = "1.2.3" edition = "2015" authors = ["rustaceans"] description = { workspace = false } "#, ) .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("check") .cwd("bar") .with_status(101) .with_stderr_data(str![[r#" [ERROR] `workspace` cannot be false --> Cargo.toml:8:41 | 8 | description = { workspace = false } | ^^^^^ | "#]]) .run(); } #[cargo_test] fn error_workspace_dependency_looked_for_workspace_itself() { registry::init(); let p = project().build(); let _ = git::repo(&paths::root().join("foo")) .file( "Cargo.toml", r#" [package] name = "bar" version = "1.2.3" edition = "2015" [dependencies] dep.workspace = true [workspace] members = [] [workspace.dependencies] dep.workspace = true "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: dependency (dep) specified without providing a local path, Git repository, version, or workspace dependency to use "#]]) .run(); } #[cargo_test] fn error_malformed_workspace_root() { registry::init(); let p = project().build(); let _ = git::repo(&paths::root().join("foo")) .file( "Cargo.toml", r#" [workspace] members = [invalid toml "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" workspace = ".." version = "1.2.3" edition = "2015" authors = ["rustaceans"] "#, ) .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("check") .cwd("bar") .with_status(101) .with_stderr_data(str![[r#" [ERROR] unclosed array, expected `]` --> ../Cargo.toml:4:13 | 4 | ... | ^ | "#]]) .run(); } #[cargo_test] fn error_no_root_workspace() { registry::init(); let p = project().build(); let _ = git::repo(&paths::root().join("foo")) .file( "bar/Cargo.toml", r#" [package] name = "bar" workspace = ".." version = "1.2.3" edition = "2015" authors = ["rustaceans"] description.workspace = true "#, ) .file("src/main.rs", "fn main() {}") .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("check") .cwd("bar") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/bar/Cargo.toml` Caused by: error inheriting `description` from workspace root manifest's `workspace.package.description` Caused by: root of a workspace inferred but wasn't a root: [ROOT]/foo/Cargo.toml "#]]) .run(); } #[cargo_test] fn error_inherit_unspecified_dependency() { let p = project().build(); let _ = git::repo(&paths::root().join("foo")) .file( "Cargo.toml", r#" [workspace] members = ["bar"] "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" workspace = ".." version = "1.2.3" edition = "2015" authors = ["rustaceans"] [dependencies] foo.workspace = true "#, ) .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("check") .cwd("bar") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/bar/Cargo.toml` Caused by: error inheriting `foo` from workspace root manifest's `workspace.dependencies.foo` Caused by: `workspace.dependencies` was not defined "#]]) .run(); } #[cargo_test] fn warn_inherit_def_feat_true_member_def_feat_false() { Package::new("dep", "0.1.0") .feature("default", &["fancy_dep"]) .add_dep(Dependency::new("fancy_dep", "0.2").optional(true)) .file("src/lib.rs", "") .publish(); Package::new("fancy_dep", "0.2.4").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.2.0" edition = "2015" authors = [] [dependencies] dep = { workspace = true, default-features = false } [workspace] members = [] [workspace.dependencies] dep = { version = "0.1.0", default-features = true } "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check").with_stderr_data(str![[r#" [WARNING] [ROOT]/foo/Cargo.toml: `default-features` is ignored for dep, since `default-features` was true for `workspace.dependencies.dep`, this could become a hard error in the future [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] fancy_dep v0.2.4 (registry `dummy-registry`) [DOWNLOADED] dep v0.1.0 (registry `dummy-registry`) [CHECKING] fancy_dep v0.2.4 [CHECKING] dep v0.1.0 [CHECKING] bar v0.2.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn warn_inherit_def_feat_true_member_def_feat_false_2024_edition() { Package::new("dep", "0.1.0") .feature("default", &["fancy_dep"]) .add_dep(Dependency::new("fancy_dep", "0.2").optional(true)) .file("src/lib.rs", "") .publish(); Package::new("fancy_dep", "0.2.4").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.2.0" edition = "2024" authors = [] [dependencies] dep = { workspace = true, default-features = false } [workspace] members = [] [workspace.dependencies] dep = { version = "0.1.0", default-features = true } "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: error inheriting `dep` from workspace root manifest's `workspace.dependencies.dep` Caused by: `default-features = false` cannot override workspace's `default-features` "#]]) .run(); } #[cargo_test] fn warn_inherit_simple_member_def_feat_false() { Package::new("dep", "0.1.0") .feature("default", &["fancy_dep"]) .add_dep(Dependency::new("fancy_dep", "0.2").optional(true)) .file("src/lib.rs", "") .publish(); Package::new("fancy_dep", "0.2.4").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.2.0" edition = "2015" authors = [] [dependencies] dep = { workspace = true, default-features = false } [workspace] members = [] [workspace.dependencies] dep = "0.1.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check").with_stderr_data(str![[r#" [WARNING] [ROOT]/foo/Cargo.toml: `default-features` is ignored for dep, since `default-features` was not specified for `workspace.dependencies.dep`, this could become a hard error in the future [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] fancy_dep v0.2.4 (registry `dummy-registry`) [DOWNLOADED] dep v0.1.0 (registry `dummy-registry`) [CHECKING] fancy_dep v0.2.4 [CHECKING] dep v0.1.0 [CHECKING] bar v0.2.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn warn_inherit_simple_member_def_feat_false_2024_edition() { Package::new("dep", "0.1.0") .feature("default", &["fancy_dep"]) .add_dep(Dependency::new("fancy_dep", "0.2").optional(true)) .file("src/lib.rs", "") .publish(); Package::new("fancy_dep", "0.2.4").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.2.0" edition = "2024" authors = [] [dependencies] dep = { workspace = true, default-features = false } [workspace] members = [] [workspace.dependencies] dep = "0.1.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: error inheriting `dep` from workspace root manifest's `workspace.dependencies.dep` Caused by: `default-features = false` cannot override workspace's `default-features` "#]]) .run(); } #[cargo_test] fn inherit_def_feat_false_member_def_feat_true() { Package::new("dep", "0.1.0") .feature("default", &["fancy_dep"]) .add_dep(Dependency::new("fancy_dep", "0.2").optional(true)) .file("src/lib.rs", "") .publish(); Package::new("fancy_dep", "0.2.4").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.2.0" edition = "2015" authors = [] [dependencies] dep = { workspace = true, default-features = true } [workspace] members = [] [workspace.dependencies] dep = { version = "0.1.0", default-features = false } "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] fancy_dep v0.2.4 (registry `dummy-registry`) [DOWNLOADED] dep v0.1.0 (registry `dummy-registry`) [CHECKING] fancy_dep v0.2.4 [CHECKING] dep v0.1.0 [CHECKING] bar v0.2.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn cannot_inherit_in_patch() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = [] [workspace.dependencies] bar = { path = "bar" } [package] name = "foo" version = "0.2.0" edition = "2015" [patch.crates-io] bar.workspace = true [dependencies] bar = "0.1.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: dependency (bar) specified without providing a local path, Git repository, version, or workspace dependency to use "#]]) .run(); } #[cargo_test] fn warn_inherit_unused_manifest_key_dep() { Package::new("dep", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = [] [workspace.dependencies] dep = { version = "0.1", wxz = "wxz" } [package] name = "bar" version = "0.2.0" edition = "2015" authors = [] [dependencies] dep = { workspace = true, wxz = "wxz" } "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [WARNING] [ROOT]/foo/Cargo.toml: unused manifest key: workspace.dependencies.dep.wxz [WARNING] [ROOT]/foo/Cargo.toml: unused manifest key: dependencies.dep.wxz [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] dep v0.1.0 (registry `dummy-registry`) [CHECKING] dep v0.1.0 [CHECKING] bar v0.2.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn warn_unused_workspace_package_field() { Package::new("dep", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = [] [workspace.package] name = "unused" [package] name = "foo" edition = "2015" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [WARNING] [ROOT]/foo/Cargo.toml: unused manifest key: workspace.package.name [CHECKING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn warn_inherit_unused_manifest_key_package() { Package::new("dep", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = [] [workspace.package] version = "1.2.3" authors = ["Rustaceans"] description = "This is a crate" documentation = "https://www.rust-lang.org/learn" homepage = "https://www.rust-lang.org" repository = "https://github.com/example/example" license = "MIT" keywords = ["cli"] categories = ["development-tools"] publish = true edition = "2018" rust-version = "1.60" exclude = ["foo.txt"] include = ["bar.txt", "**/*.rs", "Cargo.toml"] [package] name = "bar" version = { workspace = true, xyz = "abc"} authors = { workspace = true, xyz = "abc"} description = { workspace = true, xyz = "abc"} documentation = { workspace = true, xyz = "abc"} homepage = { workspace = true, xyz = "abc"} repository = { workspace = true, xyz = "abc"} license = { workspace = true, xyz = "abc"} keywords = { workspace = true, xyz = "abc"} categories = { workspace = true, xyz = "abc"} publish = { workspace = true, xyz = "abc"} edition = { workspace = true, xyz = "abc"} rust-version = { workspace = true, xyz = "abc"} exclude = { workspace = true, xyz = "abc"} include = { workspace = true, xyz = "abc"} "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [WARNING] [ROOT]/foo/Cargo.toml: unused manifest key: package.authors.xyz [WARNING] [ROOT]/foo/Cargo.toml: unused manifest key: package.categories.xyz [WARNING] [ROOT]/foo/Cargo.toml: unused manifest key: package.description.xyz [WARNING] [ROOT]/foo/Cargo.toml: unused manifest key: package.documentation.xyz [WARNING] [ROOT]/foo/Cargo.toml: unused manifest key: package.edition.xyz [WARNING] [ROOT]/foo/Cargo.toml: unused manifest key: package.exclude.xyz [WARNING] [ROOT]/foo/Cargo.toml: unused manifest key: package.homepage.xyz [WARNING] [ROOT]/foo/Cargo.toml: unused manifest key: package.include.xyz [WARNING] [ROOT]/foo/Cargo.toml: unused manifest key: package.keywords.xyz [WARNING] [ROOT]/foo/Cargo.toml: unused manifest key: package.license.xyz [WARNING] [ROOT]/foo/Cargo.toml: unused manifest key: package.publish.xyz [WARNING] [ROOT]/foo/Cargo.toml: unused manifest key: package.repository.xyz [WARNING] [ROOT]/foo/Cargo.toml: unused manifest key: package.rust-version.xyz [WARNING] [ROOT]/foo/Cargo.toml: unused manifest key: package.version.xyz [CHECKING] bar v1.2.3 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } cargo-0.91.0/tests/testsuite/install.rs000064400000000000000000002451171046102023000162270ustar 00000000000000//! Tests for the `cargo install` command. use std::env; use std::fs::{self, OpenOptions}; use std::io::prelude::*; use std::path::Path; use std::path::PathBuf; use std::thread; use crate::prelude::*; use crate::utils::cargo_process; use cargo_test_support::compare::assert_e2e; use cargo_test_support::cross_compile; use cargo_test_support::git; use cargo_test_support::registry::{self, Package}; use cargo_test_support::str; use cargo_test_support::{basic_manifest, project, project_in, symlink_supported, t}; use cargo_util::{ProcessBuilder, ProcessError}; use crate::utils::cross_compile::disabled as cross_compile_disabled; use cargo_test_support::install::{assert_has_installed_exe, assert_has_not_installed_exe, exe}; use cargo_test_support::paths; fn pkg(name: &str, vers: &str) { Package::new(name, vers) .file("src/lib.rs", "") .file( "src/main.rs", &format!("extern crate {}; fn main() {{}}", name), ) .publish(); } #[cargo_test] fn simple() { pkg("foo", "0.0.1"); cargo_process("install foo").with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] foo v0.0.1 (registry `dummy-registry`) [INSTALLING] foo v0.0.1 [COMPILING] foo v0.0.1 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE] [INSTALLED] package `foo v0.0.1` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]).run(); assert_has_installed_exe(paths::cargo_home(), "foo"); cargo_process("uninstall foo") .with_stderr_data(str![[r#" [REMOVING] [ROOT]/home/.cargo/bin/foo[EXE] "#]]) .run(); assert_has_not_installed_exe(paths::cargo_home(), "foo"); } #[cargo_test] fn install_the_same_version_twice() { pkg("foo", "0.0.1"); cargo_process("install foo foo") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] foo v0.0.1 (registry `dummy-registry`) [INSTALLING] foo v0.0.1 [COMPILING] foo v0.0.1 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE] [INSTALLED] package `foo v0.0.1` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); assert_has_installed_exe(paths::cargo_home(), "foo"); } #[cargo_test] fn toolchain() { pkg("foo", "0.0.1"); cargo_process("install +nightly") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid character `+` in package name: `+nightly` Use `cargo +nightly install` if you meant to use the `nightly` toolchain. "#]]) .run(); } #[cargo_test] fn url() { pkg("foo", "0.0.1"); cargo_process("install https://github.com/bar/foo") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid package name: `https://github.com/bar/foo` Use `cargo install --git https://github.com/bar/foo` if you meant to install from a git repository. "#]]) .run(); } #[cargo_test] fn simple_with_message_format() { pkg("foo", "0.0.1"); cargo_process("install foo --message-format=json") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] foo v0.0.1 (registry `dummy-registry`) [INSTALLING] foo v0.0.1 [COMPILING] foo v0.0.1 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE] [INSTALLED] package `foo v0.0.1` (executable `foo[EXE]`) [WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries "#]]) .with_stdout_data( str![[r#" [ { "executable": null, "features": [], "filenames": "{...}", "fresh": false, "manifest_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/foo-0.0.1/Cargo.toml", "package_id": "registry+https://github.com/rust-lang/crates.io-index#foo@0.0.1", "profile": "{...}", "reason": "compiler-artifact", "target": { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "foo", "src_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/foo-0.0.1/src/lib.rs", "test": true } }, { "executable": "[..]", "features": [], "filenames": "{...}", "fresh": false, "manifest_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/foo-0.0.1/Cargo.toml", "package_id": "registry+https://github.com/rust-lang/crates.io-index#foo@0.0.1", "profile": "{...}", "reason": "compiler-artifact", "target": { "crate_types": [ "bin" ], "doc": true, "doctest": false, "edition": "2015", "kind": [ "bin" ], "name": "foo", "src_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/foo-0.0.1/src/main.rs", "test": true } }, { "reason": "build-finished", "success": true } ] "#]] .is_json() .against_jsonlines(), ) .run(); assert_has_installed_exe(paths::cargo_home(), "foo"); } #[cargo_test] fn with_index() { let registry = registry::init(); pkg("foo", "0.0.1"); cargo_process("install foo --index") .arg(registry.index_url().as_str()) .with_stderr_data(str![[r#" [UPDATING] `[ROOT]/registry` index [DOWNLOADING] crates ... [DOWNLOADED] foo v0.0.1 (registry `[ROOT]/registry`) [INSTALLING] foo v0.0.1 (registry `[ROOT]/registry`) [COMPILING] foo v0.0.1 (registry `[ROOT]/registry`) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE] [INSTALLED] package `foo v0.0.1 (registry `[ROOT]/registry`)` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); assert_has_installed_exe(paths::cargo_home(), "foo"); cargo_process("uninstall foo") .with_stderr_data(str![[r#" [REMOVING] [ROOT]/home/.cargo/bin/foo[EXE] "#]]) .run(); assert_has_not_installed_exe(paths::cargo_home(), "foo"); } #[cargo_test] fn multiple_pkgs() { pkg("foo", "0.0.1"); pkg("bar", "0.0.2"); cargo_process("install foo bar baz") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] foo v0.0.1 (registry `dummy-registry`) [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.2 (registry `dummy-registry`) [ERROR] could not find `baz` in registry `crates-io` with version `*` [INSTALLING] foo v0.0.1 [COMPILING] foo v0.0.1 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE] [INSTALLED] package `foo v0.0.1` (executable `foo[EXE]`) [INSTALLING] bar v0.0.2 [COMPILING] bar v0.0.2 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/bar[EXE] [INSTALLED] package `bar v0.0.2` (executable `bar[EXE]`) [SUMMARY] Successfully installed foo, bar! Failed to install baz (see error(s) above). [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries [ERROR] some crates failed to install "#]]) .run(); assert_has_installed_exe(paths::cargo_home(), "foo"); assert_has_installed_exe(paths::cargo_home(), "bar"); cargo_process("uninstall foo bar") .with_stderr_data(str![[r#" [REMOVING] [ROOT]/home/.cargo/bin/foo[EXE] [REMOVING] [ROOT]/home/.cargo/bin/bar[EXE] [SUMMARY] Successfully uninstalled foo, bar! "#]]) .run(); assert_has_not_installed_exe(paths::cargo_home(), "foo"); assert_has_not_installed_exe(paths::cargo_home(), "bar"); } fn path() -> Vec { env::split_paths(&env::var_os("PATH").unwrap_or_default()).collect() } #[cargo_test] fn multiple_pkgs_path_set() { // confirm partial failure results in 101 status code and does not have the // '[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries' // even if CARGO_HOME/bin is in the PATH pkg("foo", "0.0.1"); pkg("bar", "0.0.2"); // add CARGO_HOME/bin to path let mut path = path(); path.push(paths::cargo_home().join("bin")); let new_path = env::join_paths(path).unwrap(); cargo_process("install foo bar baz") .env("PATH", new_path) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] foo v0.0.1 (registry `dummy-registry`) [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.2 (registry `dummy-registry`) [ERROR] could not find `baz` in registry `crates-io` with version `*` [INSTALLING] foo v0.0.1 [COMPILING] foo v0.0.1 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE] [INSTALLED] package `foo v0.0.1` (executable `foo[EXE]`) [INSTALLING] bar v0.0.2 [COMPILING] bar v0.0.2 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/bar[EXE] [INSTALLED] package `bar v0.0.2` (executable `bar[EXE]`) [SUMMARY] Successfully installed foo, bar! Failed to install baz (see error(s) above). [ERROR] some crates failed to install "#]]) .run(); assert_has_installed_exe(paths::cargo_home(), "foo"); assert_has_installed_exe(paths::cargo_home(), "bar"); cargo_process("uninstall foo bar") .with_stderr_data(str![[r#" [REMOVING] [ROOT]/home/.cargo/bin/foo[EXE] [REMOVING] [ROOT]/home/.cargo/bin/bar[EXE] [SUMMARY] Successfully uninstalled foo, bar! "#]]) .run(); assert_has_not_installed_exe(paths::cargo_home(), "foo"); assert_has_not_installed_exe(paths::cargo_home(), "bar"); } #[cargo_test] fn pick_max_version() { pkg("foo", "0.1.0"); pkg("foo", "0.2.0"); pkg("foo", "0.2.1"); pkg("foo", "0.2.1-pre.1"); pkg("foo", "0.3.0-pre.2"); cargo_process("install foo").with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] foo v0.2.1 (registry `dummy-registry`) [INSTALLING] foo v0.2.1 [COMPILING] foo v0.2.1 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE] [INSTALLED] package `foo v0.2.1` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]).run(); assert_has_installed_exe(paths::cargo_home(), "foo"); } #[cargo_test] fn installs_beta_version_by_explicit_name_from_git() { let p = git::repo(&paths::root().join("foo")) .file("Cargo.toml", &basic_manifest("foo", "0.3.0-beta.1")) .file("src/main.rs", "fn main() {}") .build(); cargo_process("install --git") .arg(p.url().to_string()) .arg("foo") .run(); assert_has_installed_exe(paths::cargo_home(), "foo"); } #[cargo_test] fn missing() { pkg("foo", "0.0.1"); cargo_process("install bar") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] could not find `bar` in registry `crates-io` with version `*` "#]]) .run(); } #[cargo_test] fn missing_current_working_directory() { cargo_process("install .") .with_status(101) .with_stderr_data(str![[r#" [ERROR] To install the binaries for the package in current working directory use `cargo install --path .`. Use `cargo build` if you want to simply build the package. "#]]) .run(); } #[cargo_test] fn bad_version() { pkg("foo", "0.0.1"); cargo_process("install foo --version=0.2.0") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] could not find `foo` in registry `crates-io` with version `=0.2.0` "#]]) .run(); } #[cargo_test] fn missing_at_symbol_before_version() { pkg("foo", "0.0.1"); cargo_process("install foo=0.2.0") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid character `=` in package name: `foo=0.2.0`, characters must be Unicode XID characters (numbers, `-`, `_`, or most letters) [HELP] if this is meant to be a package name followed by a version, insert an `@` like `foo@=0.2.0` "#]]) .run(); } #[cargo_test] fn bad_paths() { cargo_process("install") .with_status(101) .with_stderr_data(str![[r#" [ERROR] `[ROOT]` is not a crate root; specify a crate to install from crates.io, or use --path or --git to specify an alternate source "#]]) .run(); cargo_process("install --path .") .with_status(101) .with_stderr_data(str![[r#" [ERROR] `[ROOT]` does not contain a Cargo.toml file. --path must point to a directory containing a Cargo.toml file. "#]]) .run(); let toml = paths::root().join("Cargo.toml"); fs::write(toml, "").unwrap(); cargo_process("install --path Cargo.toml") .with_status(101) .with_stderr_data(str![[r#" [ERROR] `[ROOT]/Cargo.toml` is not a directory. --path must point to a directory containing a Cargo.toml file. "#]]) .run(); cargo_process("install --path .") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/Cargo.toml` ... "#]]) .run(); } #[cargo_test] fn install_location_precedence() { pkg("foo", "0.0.1"); let root = paths::root(); let t1 = root.join("t1"); let t2 = root.join("t2"); let t3 = root.join("t3"); let t4 = paths::cargo_home(); fs::create_dir(root.join(".cargo")).unwrap(); fs::write( root.join(".cargo/config.toml"), &format!( "[install] root = '{}' ", t3.display() ), ) .unwrap(); println!("install --root"); cargo_process("install foo --root") .arg(&t1) .env("CARGO_INSTALL_ROOT", &t2) .run(); assert_has_installed_exe(&t1, "foo"); assert_has_not_installed_exe(&t2, "foo"); println!("install CARGO_INSTALL_ROOT"); cargo_process("install foo") .env("CARGO_INSTALL_ROOT", &t2) .run(); assert_has_installed_exe(&t2, "foo"); assert_has_not_installed_exe(&t3, "foo"); println!("install install.root"); cargo_process("install foo").run(); assert_has_installed_exe(&t3, "foo"); assert_has_not_installed_exe(&t4, "foo"); fs::remove_file(root.join(".cargo/config.toml")).unwrap(); println!("install cargo home"); cargo_process("install foo").run(); assert_has_installed_exe(&t4, "foo"); } #[cargo_test] fn install_path() { let p = project().file("src/main.rs", "fn main() {}").build(); cargo_process("install --path").arg(p.root()).run(); assert_has_installed_exe(paths::cargo_home(), "foo"); // path-style installs force a reinstall p.cargo("install --path .").with_stderr_data(str![[r#" [INSTALLING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [REPLACING] [ROOT]/home/.cargo/bin/foo[EXE] [REPLACED] package `foo v0.0.1 ([ROOT]/foo)` with `foo v0.0.1 ([ROOT]/foo)` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]).run(); } #[cargo_test] fn install_target_dir() { let p = project().file("src/main.rs", "fn main() {}").build(); p.cargo("install --target-dir td_test") .with_stderr_data(str![[r#" [WARNING] Using `cargo install` to install the binaries from the package in current working directory is deprecated, use `cargo install --path .` instead. Use `cargo build` if you want to simply build the package. [INSTALLING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE] [INSTALLED] package `foo v0.0.1 ([ROOT]/foo)` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); let mut path = p.root(); path.push("td_test"); assert!(path.exists()); #[cfg(not(windows))] path.push("release/foo"); #[cfg(windows)] path.push("release/foo.exe"); assert!(path.exists()); } #[cargo_test] #[cfg(target_os = "linux")] fn install_path_with_lowercase_cargo_toml() { let toml = paths::root().join("cargo.toml"); fs::write(toml, "").unwrap(); cargo_process("install --path .") .with_status(101) .with_stderr_data(str![[r#" [ERROR] `[ROOT]` does not contain a Cargo.toml file, but found cargo.toml please try to rename it to Cargo.toml. --path must point to a directory containing a Cargo.toml file. "#]] ) .run(); } #[cargo_test] fn install_relative_path_outside_current_ws() { let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.1.0" authors = [] [workspace] members = ["baz"] "#, ) .file("src/main.rs", "fn main() {}") .file( "baz/Cargo.toml", r#" [package] name = "baz" version = "0.1.0" authors = [] edition = "2021" [dependencies] foo = "1" "#, ) .file("baz/src/lib.rs", "") .build(); let _bin_project = project_in("bar") .file("src/main.rs", "fn main() {}") .build(); p.cargo("install --path ../bar/foo") .with_stderr_data(str![[r#" [INSTALLING] foo v0.0.1 ([ROOT]/bar/foo) [COMPILING] foo v0.0.1 ([ROOT]/bar/foo) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE] [INSTALLED] package `foo v0.0.1 ([ROOT]/bar/foo)` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); // Validate the workspace error message to display available targets. p.cargo("install --path ../bar/foo --bin") .with_status(101) .with_stderr_data(str![[r#" [ERROR] "--bin" takes one argument. Available binaries: foo "#]]) .run(); } #[cargo_test] fn multiple_packages_containing_binaries() { let p = git::repo(&paths::root().join("foo")) .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("src/main.rs", "fn main() {}") .file("a/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("a/src/main.rs", "fn main() {}") .build(); cargo_process("install --git") .arg(p.url().to_string()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/foo` [ERROR] multiple packages with binaries found: bar, foo. When installing a git repository, cargo will always search the entire repo for any Cargo.toml. Please specify a package, e.g. `cargo install --git [ROOTURL]/foo bar`. "#]]) .run(); } #[cargo_test] fn multiple_packages_matching_example() { let p = git::repo(&paths::root().join("foo")) .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("src/lib.rs", "") .file("examples/ex1.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "") .file("bar/examples/ex1.rs", "fn main() {}") .build(); cargo_process("install --example ex1 --git") .arg(p.url().to_string()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/foo` [ERROR] multiple packages with examples found: bar, foo. When installing a git repository, cargo will always search the entire repo for any Cargo.toml. Please specify a package, e.g. `cargo install --git [ROOTURL]/foo bar`. "#]]) .run(); } #[cargo_test] fn multiple_binaries_deep_select_uses_package_name() { let p = git::repo(&paths::root().join("foo")) .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("src/main.rs", "fn main() {}") .file("bar/baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("bar/baz/src/main.rs", "fn main() {}") .build(); cargo_process("install --git") .arg(p.url().to_string()) .arg("baz") .run(); } #[cargo_test] fn multiple_binaries_in_selected_package_installs_all() { let p = git::repo(&paths::root().join("foo")) .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/bin/bin1.rs", "fn main() {}") .file("bar/src/bin/bin2.rs", "fn main() {}") .build(); cargo_process("install --git") .arg(p.url().to_string()) .arg("bar") .run(); let cargo_home = paths::cargo_home(); assert_has_installed_exe(&cargo_home, "bin1"); assert_has_installed_exe(&cargo_home, "bin2"); } #[cargo_test] fn multiple_binaries_in_selected_package_with_bin_option_installs_only_one() { let p = git::repo(&paths::root().join("foo")) .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/bin/bin1.rs", "fn main() {}") .file("bar/src/bin/bin2.rs", "fn main() {}") .build(); cargo_process("install --bin bin1 --git") .arg(p.url().to_string()) .arg("bar") .run(); let cargo_home = paths::cargo_home(); assert_has_installed_exe(&cargo_home, "bin1"); assert_has_not_installed_exe(&cargo_home, "bin2"); } #[cargo_test] fn multiple_crates_select() { let p = git::repo(&paths::root().join("foo")) .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("src/main.rs", "fn main() {}") .file("a/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("a/src/main.rs", "fn main() {}") .build(); cargo_process("install --git") .arg(p.url().to_string()) .arg("foo") .run(); assert_has_installed_exe(paths::cargo_home(), "foo"); assert_has_not_installed_exe(paths::cargo_home(), "bar"); cargo_process("install --git") .arg(p.url().to_string()) .arg("bar") .run(); assert_has_installed_exe(paths::cargo_home(), "bar"); } #[cargo_test] fn multiple_crates_git_all() { let p = git::repo(&paths::root().join("foo")) .file( "Cargo.toml", r#" [workspace] members = ["bin1", "bin2"] "#, ) .file("bin1/Cargo.toml", &basic_manifest("bin1", "0.1.0")) .file("bin2/Cargo.toml", &basic_manifest("bin2", "0.1.0")) .file( "bin1/src/main.rs", r#"fn main() { println!("Hello, world!"); }"#, ) .file( "bin2/src/main.rs", r#"fn main() { println!("Hello, world!"); }"#, ) .build(); cargo_process(&format!("install --git {} bin1 bin2", p.url())).run(); } #[cargo_test] fn multiple_crates_auto_binaries() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" authors = [] [dependencies] bar = { path = "a" } "#, ) .file("src/main.rs", "extern crate bar; fn main() {}") .file("a/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("a/src/lib.rs", "") .build(); cargo_process("install --path").arg(p.root()).run(); assert_has_installed_exe(paths::cargo_home(), "foo"); } #[cargo_test] fn multiple_crates_auto_examples() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" authors = [] [dependencies] bar = { path = "a" } "#, ) .file("src/lib.rs", "extern crate bar;") .file( "examples/foo.rs", " extern crate bar; extern crate foo; fn main() {} ", ) .file("a/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("a/src/lib.rs", "") .build(); cargo_process("install --path") .arg(p.root()) .arg("--example=foo") .run(); assert_has_installed_exe(paths::cargo_home(), "foo"); } #[cargo_test] fn no_binaries_or_examples() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" authors = [] [dependencies] bar = { path = "a" } "#, ) .file("src/lib.rs", "") .file("a/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("a/src/lib.rs", "") .build(); cargo_process("install --path") .arg(p.root()) .with_status(101) .with_stderr_data(str![[r#" [ERROR] no packages found with binaries or examples "#]]) .run(); } #[cargo_test] fn no_binaries() { let p = project() .file("src/lib.rs", "") .file("examples/foo.rs", "fn main() {}") .build(); cargo_process("install --path") .arg(p.root()) .arg("foo") .with_status(101) .with_stderr_data(str![[r#" [ERROR] there is nothing to install in `foo v0.0.1 ([ROOT]/foo)`, because it has no binaries `cargo install` is only for installing programs, and can't be used with libraries. To use a library crate, add it as a dependency to a Cargo project with `cargo add`. "#]]) .run(); } #[cargo_test] fn examples() { let p = project() .file("src/lib.rs", "") .file("examples/foo.rs", "extern crate foo; fn main() {}") .build(); cargo_process("install --path") .arg(p.root()) .arg("--example=foo") .run(); assert_has_installed_exe(paths::cargo_home(), "foo"); } #[cargo_test] fn install_force() { let p = project().file("src/main.rs", "fn main() {}").build(); cargo_process("install --path").arg(p.root()).run(); let p = project() .at("foo2") .file("Cargo.toml", &basic_manifest("foo", "0.2.0")) .file("src/main.rs", "fn main() {}") .build(); cargo_process("install --force --path") .arg(p.root()) .with_stderr_data(str![[r#" [INSTALLING] foo v0.2.0 ([ROOT]/foo2) [COMPILING] foo v0.2.0 ([ROOT]/foo2) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [REPLACING] [ROOT]/home/.cargo/bin/foo[EXE] [REPLACED] package `foo v0.0.1 ([ROOT]/foo)` with `foo v0.2.0 ([ROOT]/foo2)` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); cargo_process("install --list") .with_stdout_data(str![[r#" foo v0.2.0 ([ROOT]/foo2): foo[EXE] "#]]) .run(); } #[cargo_test] fn install_force_partial_overlap() { let p = project() .file("src/bin/foo-bin1.rs", "fn main() {}") .file("src/bin/foo-bin2.rs", "fn main() {}") .build(); cargo_process("install --path").arg(p.root()).run(); let p = project() .at("foo2") .file("Cargo.toml", &basic_manifest("foo", "0.2.0")) .file("src/bin/foo-bin2.rs", "fn main() {}") .file("src/bin/foo-bin3.rs", "fn main() {}") .build(); cargo_process("install --force --path") .arg(p.root()) .with_stderr_data(str![[r#" [INSTALLING] foo v0.2.0 ([ROOT]/foo2) [COMPILING] foo v0.2.0 ([ROOT]/foo2) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/foo-bin3[EXE] [REPLACING] [ROOT]/home/.cargo/bin/foo-bin2[EXE] [REMOVING] executable `[ROOT]/home/.cargo/bin/foo-bin1[EXE]` from previous version foo v0.0.1 ([ROOT]/foo) [INSTALLED] package `foo v0.2.0 ([ROOT]/foo2)` (executable `foo-bin3[EXE]`) [REPLACED] package `foo v0.0.1 ([ROOT]/foo)` with `foo v0.2.0 ([ROOT]/foo2)` (executable `foo-bin2[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); cargo_process("install --list") .with_stdout_data(str![[r#" foo v0.2.0 ([ROOT]/foo2): foo-bin2[EXE] foo-bin3[EXE] "#]]) .run(); } #[cargo_test] fn install_force_bin() { let p = project() .file("src/bin/foo-bin1.rs", "fn main() {}") .file("src/bin/foo-bin2.rs", "fn main() {}") .build(); cargo_process("install --path").arg(p.root()).run(); let p = project() .at("foo2") .file("Cargo.toml", &basic_manifest("foo", "0.2.0")) .file("src/bin/foo-bin1.rs", "fn main() {}") .file("src/bin/foo-bin2.rs", "fn main() {}") .build(); cargo_process("install --force --bin foo-bin2 --path") .arg(p.root()) .with_stderr_data(str![[r#" [INSTALLING] foo v0.2.0 ([ROOT]/foo2) [COMPILING] foo v0.2.0 ([ROOT]/foo2) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [REPLACING] [ROOT]/home/.cargo/bin/foo-bin2[EXE] [REPLACED] package `foo v0.0.1 ([ROOT]/foo)` with `foo v0.2.0 ([ROOT]/foo2)` (executable `foo-bin2[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); cargo_process("install --list") .with_stdout_data(str![[r#" foo v0.0.1 ([ROOT]/foo): foo-bin1[EXE] foo v0.2.0 ([ROOT]/foo2): foo-bin2[EXE] "#]]) .run(); } #[cargo_test] fn compile_failure() { let p = project().file("src/main.rs", "").build(); cargo_process("install --path") .arg(p.root()) .with_status(101) .with_stderr_data(str![[r#" ... [ERROR] could not compile `foo` (bin "foo") due to 1 previous error [ERROR] failed to compile `foo v0.0.1 ([ROOT]/foo)`, intermediate artifacts can be found at `[ROOT]/foo/target`. To reuse those artifacts with a future compilation, set the environment variable `CARGO_TARGET_DIR` to that path. ... "#]]) .run(); } #[cargo_test] fn git_repo() { let p = git::repo(&paths::root().join("foo")) .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("src/main.rs", "fn main() {}") .build(); // Use `--locked` to test that we don't even try to write a lock file. cargo_process("install --locked --git") .arg(p.url().to_string()) .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/foo` [WARNING] no Cargo.lock file published in foo v0.1.0 ([ROOTURL]/foo#[..]) [INSTALLING] foo v0.1.0 ([ROOTURL]/foo#[..]) [COMPILING] foo v0.1.0 ([ROOT]/home/.cargo/git/checkouts/foo-[HASH]/[..]) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE] [INSTALLED] package `foo v0.1.0 ([ROOTURL]/foo#[..])` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); assert_has_installed_exe(paths::cargo_home(), "foo"); assert_has_installed_exe(paths::cargo_home(), "foo"); } #[cargo_test] #[cfg(target_os = "linux")] fn git_repo_with_lowercase_cargo_toml() { let p = git::repo(&paths::root().join("foo")) .file("cargo.toml", &basic_manifest("foo", "0.1.0")) .file("src/main.rs", "fn main() {}") .build(); cargo_process("install --git") .arg(p.url().to_string()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] git repository [..] [ERROR] Could not find Cargo.toml in `[..]`, but found cargo.toml please try to rename it to Cargo.toml "#]] ) .run(); } #[cargo_test] fn list() { pkg("foo", "0.0.1"); pkg("bar", "0.2.1"); pkg("bar", "0.2.2"); cargo_process("install --list").with_stdout_data("").run(); cargo_process("install bar --version =0.2.1").run(); cargo_process("install foo").run(); cargo_process("install --list") .with_stdout_data(str![[r#" bar v0.2.1: bar[EXE] foo v0.0.1: foo[EXE] "#]]) .run(); } #[cargo_test] fn list_error() { pkg("foo", "0.0.1"); cargo_process("install foo").run(); cargo_process("install --list") .with_stdout_data(str![[r#" foo v0.0.1: foo[EXE] "#]]) .run(); let mut worldfile_path = paths::cargo_home(); worldfile_path.push(".crates.toml"); let mut worldfile = OpenOptions::new() .write(true) .open(worldfile_path) .expect(".crates.toml should be there"); worldfile.write_all(b"\x00").unwrap(); drop(worldfile); cargo_process("install --list --verbose") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse crate metadata at `[ROOT]/home/.cargo/.crates.toml` Caused by: invalid TOML found for metadata Caused by: TOML parse error at line 1, column 4 | 1 | v1] | ^ key with no value, expected `=` "#]]) .run(); } #[cargo_test] fn uninstall_pkg_does_not_exist() { cargo_process("uninstall foo") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package ID specification `foo` did not match any packages "#]]) .run(); } #[cargo_test] fn uninstall_bin_does_not_exist() { pkg("foo", "0.0.1"); cargo_process("install foo").run(); cargo_process("uninstall foo --bin=bar") .with_status(101) .with_stderr_data(str![[r#" [ERROR] binary `bar[EXE]` not installed as part of `foo v0.0.1` "#]]) .run(); } #[cargo_test] fn uninstall_piecemeal() { let p = project() .file("src/bin/foo.rs", "fn main() {}") .file("src/bin/bar.rs", "fn main() {}") .build(); cargo_process("install --path").arg(p.root()).run(); assert_has_installed_exe(paths::cargo_home(), "foo"); assert_has_installed_exe(paths::cargo_home(), "bar"); cargo_process("uninstall foo --bin=bar") .with_stderr_data(str![[r#" [REMOVING] [ROOT]/home/.cargo/bin/bar[EXE] "#]]) .run(); assert_has_installed_exe(paths::cargo_home(), "foo"); assert_has_not_installed_exe(paths::cargo_home(), "bar"); cargo_process("uninstall foo --bin=foo") .with_stderr_data(str![[r#" [REMOVING] [ROOT]/home/.cargo/bin/foo[EXE] "#]]) .run(); assert_has_not_installed_exe(paths::cargo_home(), "foo"); cargo_process("uninstall foo") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package ID specification `foo` did not match any packages "#]]) .run(); } #[cargo_test] fn subcommand_works_out_of_the_box() { Package::new("cargo-foo", "1.0.0") .file("src/main.rs", r#"fn main() { println!("bar"); }"#) .publish(); cargo_process("install cargo-foo").run(); cargo_process("foo") .with_stdout_data(str![[r#" bar "#]]) .run(); cargo_process("--list") .with_stdout_data(str![[r#" ... foo ... "#]]) .run(); } #[cargo_test] fn installs_from_cwd_by_default() { let p = project().file("src/main.rs", "fn main() {}").build(); p.cargo("install").with_stderr_data(str![[r#" [WARNING] Using `cargo install` to install the binaries from the package in current working directory is deprecated, use `cargo install --path .` instead. Use `cargo build` if you want to simply build the package. ... "#]]).run(); assert_has_installed_exe(paths::cargo_home(), "foo"); } #[cargo_test] fn installs_from_cwd_with_2018_warnings() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" authors = [] edition = "2018" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("install") .with_status(101) .with_stderr_data(str![[r#" [ERROR] Using `cargo install` to install the binaries from the package in current working directory is no longer supported, use `cargo install --path .` instead. Use `cargo build` if you want to simply build the package. "#]]) .run(); assert_has_not_installed_exe(paths::cargo_home(), "foo"); } #[cargo_test] fn uninstall_cwd() { let p = project().file("src/main.rs", "fn main() {}").build(); p.cargo("install --path .").with_stderr_data(str![[r#" [INSTALLING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE] [INSTALLED] package `foo v0.0.1 ([ROOT]/foo)` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]).run(); assert_has_installed_exe(paths::cargo_home(), "foo"); p.cargo("uninstall") .with_stdout_data("") .with_stderr_data(str![[r#" [REMOVING] [ROOT]/home/.cargo/bin/foo[EXE] "#]]) .run(); assert_has_not_installed_exe(paths::cargo_home(), "foo"); } #[cargo_test] fn uninstall_cwd_not_installed() { let p = project().file("src/main.rs", "fn main() {}").build(); p.cargo("uninstall") .with_status(101) .with_stdout_data("") .with_stderr_data(str![[r#" [ERROR] package `foo v0.0.1 ([ROOT]/foo)` is not installed "#]]) .run(); } #[cargo_test] fn uninstall_cwd_no_project() { cargo_process("uninstall") .with_status(101) .with_stdout_data("") .with_stderr_data(str![[r#" [ERROR] failed to read `[ROOT]/Cargo.toml` Caused by: [NOT_FOUND] "#]]) .run(); } #[cargo_test] fn do_not_rebuilds_on_local_install() { let p = project().file("src/main.rs", "fn main() {}").build(); p.cargo("build --release").run(); cargo_process("install --path") .arg(p.root()) .with_stderr_data(str![[r#" [INSTALLING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE] [INSTALLED] package `foo v0.0.1 ([ROOT]/foo)` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); assert!(p.build_dir().exists()); assert!(p.release_bin("foo").exists()); assert_has_installed_exe(paths::cargo_home(), "foo"); } #[cargo_test] fn reports_unsuccessful_subcommand_result() { Package::new("cargo-fail", "1.0.0") .file("src/main.rs", r#"fn main() { panic!("EXPLICIT PANIC!"); }"#) .publish(); cargo_process("install cargo-fail").run(); cargo_process("--list") .with_stdout_data(str![[r#" ... fail ... "#]]) .run(); cargo_process("fail") .with_status(101) .with_stderr_data("...\n[..]EXPLICIT PANIC![..]\n...") .run(); } #[cargo_test] fn git_with_lockfile() { let p = git::repo(&paths::root().join("foo")) .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" authors = [] [dependencies] bar = { path = "bar" } "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "fn main() {}") .file( "Cargo.lock", r#" [[package]] name = "foo" version = "0.1.0" dependencies = [ "bar 0.1.0" ] [[package]] name = "bar" version = "0.1.0" "#, ) .build(); cargo_process("install --git") .arg(p.url().to_string()) .run(); } #[cargo_test] fn q_silences_warnings() { let p = project().file("src/main.rs", "fn main() {}").build(); cargo_process("install -q --path") .arg(p.root()) .with_stderr_data("") .run(); } #[cargo_test] fn readonly_dir() { pkg("foo", "0.0.1"); let root = paths::root(); let dir = &root.join("readonly"); fs::create_dir(root.join("readonly")).unwrap(); let mut perms = fs::metadata(dir).unwrap().permissions(); perms.set_readonly(true); fs::set_permissions(dir, perms).unwrap(); cargo_process("install foo").cwd(dir).run(); assert_has_installed_exe(paths::cargo_home(), "foo"); } #[cargo_test] fn use_path_workspace() { Package::new("foo", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.1.0" authors = [] [workspace] members = ["baz"] "#, ) .file("src/main.rs", "fn main() {}") .file( "baz/Cargo.toml", r#" [package] name = "baz" version = "0.1.0" authors = [] [dependencies] foo = "1" "#, ) .file("baz/src/lib.rs", "") .build(); p.cargo("build").run(); let lock = p.read_lockfile(); p.cargo("install").run(); let lock2 = p.read_lockfile(); assert_eq!(lock, lock2, "different lockfiles"); } #[cargo_test] fn path_install_workspace_root_despite_default_members() { let p = project() .file( "Cargo.toml", r#" [package] name = "ws-root" version = "0.1.0" authors = [] [workspace] members = ["ws-member"] default-members = ["ws-member"] "#, ) .file("src/main.rs", "fn main() {}") .file( "ws-member/Cargo.toml", r#" [package] name = "ws-member" version = "0.1.0" authors = [] "#, ) .file("ws-member/src/main.rs", "fn main() {}") .build(); p.cargo("install --path") .arg(p.root()) .arg("ws-root") .with_stderr_data(str![[r#" [INSTALLING] ws-root v0.1.0 ([ROOT]/foo) [COMPILING] ws-root v0.1.0 ([ROOT]/foo) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/ws-root[EXE] [INSTALLED] package `ws-root v0.1.0 ([ROOT]/foo)` (executable `ws-root[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) // Particularly avoid "Installed package `ws-root v0.1.0 ([..]])` (executable `ws-member`)": .with_stderr_does_not_contain("ws-member") .run(); } #[cargo_test] fn git_install_workspace_root_despite_default_members() { let p = git::repo(&paths::root().join("foo")) .file( "Cargo.toml", r#" [package] name = "ws-root" version = "0.1.0" authors = [] [workspace] members = ["ws-member"] default-members = ["ws-member"] "#, ) .file("src/main.rs", "fn main() {}") .file( "ws-member/Cargo.toml", r#" [package] name = "ws-member" version = "0.1.0" authors = [] "#, ) .file("ws-member/src/main.rs", "fn main() {}") .build(); cargo_process("install --git") .arg(p.url().to_string()) .arg("ws-root") .with_stderr_data(str![[r#" ... [INSTALLED] package `ws-root v0.1.0 ([ROOTURL]/foo#[..])` (executable `ws-root[EXE]`) ... "#]]) // Particularly avoid "Installed package `ws-root v0.1.0 ([..]])` (executable `ws-member`)": .with_stderr_does_not_contain("ws-member") .run(); } #[cargo_test] fn dev_dependencies_no_check() { Package::new("foo", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.1.0" authors = [] [dev-dependencies] baz = "1.0.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" ... [ERROR] no matching package named `baz` found ... "#]]) .run(); p.cargo("install").run(); } #[cargo_test] fn dev_dependencies_lock_file_untouched() { Package::new("foo", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" authors = [] [dev-dependencies] bar = { path = "a" } "#, ) .file("src/main.rs", "fn main() {}") .file("a/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("a/src/lib.rs", "") .build(); p.cargo("build").run(); let lock = p.read_lockfile(); p.cargo("install").run(); let lock2 = p.read_lockfile(); assert!(lock == lock2, "different lockfiles"); } #[cargo_test] fn install_target_native() { pkg("foo", "0.1.0"); cargo_process("install foo --target") .arg(cargo_test_support::rustc_host()) .run(); assert_has_installed_exe(paths::cargo_home(), "foo"); } #[cargo_test] fn install_target_foreign() { if cross_compile_disabled() { return; } pkg("foo", "0.1.0"); cargo_process("install foo --target") .arg(cross_compile::alternate()) .run(); assert_has_installed_exe(paths::cargo_home(), "foo"); } #[cargo_test] fn vers_precise() { pkg("foo", "0.1.1"); pkg("foo", "0.1.2"); cargo_process("install foo --vers 0.1.1") .with_stderr_data(str![[r#" ... [DOWNLOADED] foo v0.1.1 (registry `dummy-registry`) ... "#]]) .run(); } #[cargo_test] fn version_precise() { pkg("foo", "0.1.1"); pkg("foo", "0.1.2"); cargo_process("install foo --version 0.1.1") .with_stderr_data(str![[r#" ... [DOWNLOADED] foo v0.1.1 (registry `dummy-registry`) ... "#]]) .run(); } #[cargo_test] fn inline_version_precise() { pkg("foo", "0.1.1"); pkg("foo", "0.1.2"); cargo_process("install foo@0.1.1") .with_stderr_data(str![[r#" ... [DOWNLOADED] foo v0.1.1 (registry `dummy-registry`) ... "#]]) .run(); } #[cargo_test] fn inline_version_multiple() { pkg("foo", "0.1.0"); pkg("foo", "0.1.1"); pkg("foo", "0.1.2"); pkg("bar", "0.2.0"); pkg("bar", "0.2.1"); pkg("bar", "0.2.2"); cargo_process("install foo@0.1.1 bar@0.2.1") .with_stderr_data(str![[r#" ... [DOWNLOADED] foo v0.1.1 (registry `dummy-registry`) ... [DOWNLOADED] bar v0.2.1 (registry `dummy-registry`) ... "#]]) .run(); } #[cargo_test] fn inline_version_without_name() { pkg("foo", "0.1.1"); pkg("foo", "0.1.2"); cargo_process("install @0.1.1") .with_status(1) .with_stderr_data(str![[r#" [ERROR] invalid value '@0.1.1' for '[CRATE[@]]...': missing crate name before '@' For more information, try '--help'. "#]]) .run(); } #[cargo_test] fn inline_and_explicit_version() { pkg("foo", "0.1.1"); pkg("foo", "0.1.2"); cargo_process("install foo@0.1.1 --version 0.1.1") .with_status(101) .with_stderr_data(str![[r#" [ERROR] cannot specify both `@` and `--version ` "#]]) .run(); } #[cargo_test] fn not_both_vers_and_version() { pkg("foo", "0.1.1"); pkg("foo", "0.1.2"); cargo_process("install foo --version 0.1.1 --vers 0.1.2") .with_status(1) .with_stderr_data(str![[r#" [ERROR] the argument '--version ' cannot be used multiple times Usage: cargo[EXE] install [OPTIONS] [CRATE[@]]... For more information, try '--help'. "#]]) .run(); } #[cargo_test] fn test_install_git_cannot_be_a_base_url() { cargo_process("install --git github.com:rust-lang/rustfmt.git") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid url `github.com:rust-lang/rustfmt.git`: cannot-be-a-base-URLs are not supported "#]]) .run(); } #[cargo_test] fn uninstall_multiple_and_specifying_bin() { cargo_process("uninstall foo bar --bin baz") .with_status(101) .with_stderr_data(str![[r#" [ERROR] A binary can only be associated with a single installed package, specifying multiple specs with --bin is redundant. "#]]) .run(); } #[cargo_test] fn uninstall_with_empty_package_option() { cargo_process("uninstall -p") .with_status(101) .with_stderr_data(str![[r#" [ERROR] "--package " requires a SPEC format value. Run `cargo help pkgid` for more information about SPEC format. "#]]) .run(); } #[cargo_test] fn uninstall_multiple_and_some_pkg_does_not_exist() { pkg("foo", "0.0.1"); cargo_process("install foo").run(); cargo_process("uninstall foo bar") .with_status(101) .with_stderr_data(str![[r#" [REMOVING] [ROOT]/home/.cargo/bin/foo[EXE] [ERROR] package ID specification `bar` did not match any packages [SUMMARY] Successfully uninstalled foo! Failed to uninstall bar (see error(s) above). [ERROR] some packages failed to uninstall "#]]) .run(); assert_has_not_installed_exe(paths::cargo_home(), "foo"); assert_has_not_installed_exe(paths::cargo_home(), "bar"); } #[cargo_test] fn custom_target_dir_for_git_source() { let p = git::repo(&paths::root().join("foo")) .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("src/main.rs", "fn main() {}") .build(); cargo_process("install --git") .arg(p.url().to_string()) .run(); assert!(!paths::root().join("target/release").is_dir()); cargo_process("install --force --git") .arg(p.url().to_string()) .env("CARGO_TARGET_DIR", "target") .run(); assert!(paths::root().join("target/release").is_dir()); } #[cargo_test] fn install_respects_lock_file() { // `cargo install` now requires --locked to use a Cargo.lock. Package::new("bar", "0.1.0").publish(); Package::new("bar", "0.1.1") .file("src/lib.rs", "not rust") .publish(); Package::new("foo", "0.1.0") .dep("bar", "0.1") .file("src/lib.rs", "") .file( "src/main.rs", "extern crate foo; extern crate bar; fn main() {}", ) .file( "Cargo.lock", r#" [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "foo" version = "0.1.0" dependencies = [ "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] "#, ) .publish(); cargo_process("install foo") .with_stderr_data(str![[r#" ... [..]not rust[..] ... "#]]) .with_status(101) .run(); cargo_process("install --locked foo").run(); } #[cargo_test] fn install_path_respects_lock_file() { // --path version of install_path_respects_lock_file, --locked is required // to use Cargo.lock. Package::new("bar", "0.1.0").publish(); Package::new("bar", "0.1.1") .file("src/lib.rs", "not rust") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] bar = "0.1" "#, ) .file("src/main.rs", "extern crate bar; fn main() {}") .file( "Cargo.lock", r#" [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "foo" version = "0.1.0" dependencies = [ "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] "#, ) .build(); p.cargo("install --path .") .with_stderr_data(str![[r#" ... [..]not rust[..] ... "#]]) .with_status(101) .run(); p.cargo("install --path . --locked").run(); } #[cargo_test] fn lock_file_path_deps_ok() { Package::new("bar", "0.1.0").publish(); Package::new("foo", "0.1.0") .dep("bar", "0.1") .file("src/lib.rs", "") .file( "src/main.rs", "extern crate foo; extern crate bar; fn main() {}", ) .file( "Cargo.lock", r#" [[package]] name = "bar" version = "0.1.0" [[package]] name = "foo" version = "0.1.0" dependencies = [ "bar 0.1.0", ] "#, ) .publish(); cargo_process("install foo").run(); } #[cargo_test] fn install_empty_argument() { // Bug 5229 cargo_process("install") .arg("") .with_status(1) .with_stderr_data(str![[r#" [ERROR] invalid value '' for '[CRATE[@]]...': crate name is empty For more information, try '--help'. "#]]) .run(); } #[cargo_test] fn git_repo_replace() { let p = git::repo(&paths::root().join("foo")) .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("src/main.rs", "fn main() {}") .build(); let repo = git2::Repository::open(&p.root()).unwrap(); let old_rev = repo.revparse_single("HEAD").unwrap().id(); cargo_process("install --git") .arg(p.url().to_string()) .run(); git::commit(&repo); let new_rev = repo.revparse_single("HEAD").unwrap().id(); let mut path = paths::home(); path.push(".cargo/.crates.toml"); assert_ne!(old_rev, new_rev); assert!( fs::read_to_string(path.clone()) .unwrap() .contains(&format!("{}", old_rev)) ); cargo_process("install --force --git") .arg(p.url().to_string()) .run(); assert!( fs::read_to_string(path) .unwrap() .contains(&format!("{}", new_rev)) ); } #[cargo_test] fn workspace_uses_workspace_target_dir() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" authors = [] [workspace] [dependencies] bar = { path = 'bar' } "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("build --release").cwd("bar").run(); cargo_process("install --path") .arg(p.root().join("bar")) .with_stderr_data(str![[r#" [INSTALLING] bar v0.1.0 ([ROOT]/foo/bar) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/bar[EXE] [INSTALLED] package `bar v0.1.0 ([ROOT]/foo/bar)` (executable `bar[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); } #[cargo_test] fn install_ignores_local_cargo_config() { pkg("bar", "0.0.1"); let p = project() .file( ".cargo/config.toml", r#" [build] target = "non-existing-target" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("install bar").run(); assert_has_installed_exe(paths::cargo_home(), "bar"); } #[cargo_test] fn install_ignores_unstable_table_in_local_cargo_config() { pkg("bar", "0.0.1"); let p = project() .file( ".cargo/config.toml", r#" [unstable] build-std = ["core"] "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("install bar") .masquerade_as_nightly_cargo(&["build-std"]) .run(); assert_has_installed_exe(paths::cargo_home(), "bar"); } #[cargo_test] fn install_global_cargo_config() { pkg("bar", "0.0.1"); let config = paths::cargo_home().join("config.toml"); let mut toml = fs::read_to_string(&config).unwrap_or_default(); toml.push_str( r#" [build] target = 'nonexistent' "#, ); fs::write(&config, toml).unwrap(); cargo_process("install bar") .with_status(101) .with_stderr_data( str![[r#" [INSTALLING] bar v0.0.1 Caused by: process didn't exit successfully: `rustc [..]--target nonexistent[..]` ([EXIT_STATUS]: 1) ... "#]] .unordered(), ) .run(); } #[cargo_test] fn install_path_config() { project() .file( ".cargo/config.toml", r#" [build] target = 'nonexistent' "#, ) .file("src/main.rs", "fn main() {}") .build(); cargo_process("install --path foo") .with_status(101) .with_stderr_data(str![[r#" ... process didn't exit successfully: `rustc [..]--target nonexistent[..]` ([EXIT_STATUS]: 1) ... "#]]) .run(); } #[cargo_test] fn install_version_req() { // Try using a few versionreq styles. pkg("foo", "0.0.3"); pkg("foo", "1.0.4"); pkg("foo", "1.0.5"); cargo_process("install foo --version=*") .with_stderr_does_not_contain("[WARNING][..]is not a valid semver[..]") .with_stderr_data(str![[r#" ... [INSTALLING] foo v1.0.5 ... "#]]) .run(); cargo_process("uninstall foo").run(); cargo_process("install foo --version=^1.0") .with_stderr_does_not_contain("[WARNING][..]is not a valid semver[..]") .with_stderr_data(str![[r#" ... [INSTALLING] foo v1.0.5 ... "#]]) .run(); cargo_process("uninstall foo").run(); cargo_process("install foo --version=0.0.*") .with_stderr_does_not_contain("[WARNING][..]is not a valid semver[..]") .with_stderr_data(str![[r#" ... [INSTALLING] foo v0.0.3 ... "#]]) .run(); } #[cargo_test] fn git_install_reads_workspace_manifest() { let p = git::repo(&paths::root().join("foo")) .file( "Cargo.toml", r#" [workspace] members = ["bin1"] [profile.release] incremental = 3 "#, ) .file("bin1/Cargo.toml", &basic_manifest("bin1", "0.1.0")) .file( "bin1/src/main.rs", r#"fn main() { println!("Hello, world!"); }"#, ) .build(); cargo_process(&format!("install --git {}", p.url())) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/foo` [ERROR] invalid type: integer `3`, expected a boolean --> home/.cargo/git/checkouts/foo-[HASH]/[..]/Cargo.toml:6:27 | 6 | incremental = 3 | ^ | [ERROR] invalid type: integer `3`, expected a boolean --> home/.cargo/git/checkouts/foo-[HASH]/[..]/Cargo.toml:6:27 | 6 | incremental = 3 | ^ | "#]]) .run(); } #[cargo_test] fn install_git_with_symlink_home() { // Ensure that `cargo install` with a git repo is OK when CARGO_HOME is a // symlink, and uses an build script. if !symlink_supported() { return; } let p = git::new("foo", |p| { p.file("Cargo.toml", &basic_manifest("foo", "1.0.0")) .file("src/main.rs", "fn main() {}") // This triggers discover_git_and_list_files for detecting changed files. .file("build.rs", "fn main() {}") }); #[cfg(unix)] use std::os::unix::fs::symlink; #[cfg(windows)] use std::os::windows::fs::symlink_dir as symlink; let actual = paths::root().join("actual-home"); t!(std::fs::create_dir(&actual)); t!(symlink(&actual, paths::home().join(".cargo"))); cargo_process("install --git") .arg(p.url().to_string()) .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/foo` [INSTALLING] foo v1.0.0 ([ROOTURL]/foo#[..]) [COMPILING] foo v1.0.0 ([ROOT]/home/.cargo/git/checkouts/foo-[HASH]/[..]) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE] [INSTALLED] package `foo v1.0.0 ([ROOTURL]/foo#[..])` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); } #[cargo_test] fn install_yanked_cargo_package() { Package::new("baz", "0.0.1").yanked(true).publish(); cargo_process("install baz --version 0.0.1") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] cannot install package `baz`, it has been yanked from registry `crates-io` "#]]) .run(); } #[cargo_test] fn install_cargo_package_in_a_patched_workspace() { pkg("foo", "0.1.0"); pkg("fizz", "1.0.0"); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.1.0" authors = [] [workspace] members = ["baz"] "#, ) .file("src/main.rs", "fn main() {}") .file( "baz/Cargo.toml", r#" [package] name = "baz" version = "0.1.0" authors = [] [dependencies] fizz = "1" [patch.crates-io] fizz = { version = "=1.0.0" } "#, ) .file("baz/src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [WARNING] patch for the non root package will be ignored, specify patch at the workspace root: package: [ROOT]/foo/baz/Cargo.toml workspace: [ROOT]/foo/Cargo.toml ... "#]]) .run(); // A crate installation must not emit any message from a workspace under // current working directory. // See https://github.com/rust-lang/cargo/issues/8619 p.cargo("install foo").with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] foo v0.1.0 (registry `dummy-registry`) [INSTALLING] foo v0.1.0 [COMPILING] foo v0.1.0 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE] [INSTALLED] package `foo v0.1.0` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]).run(); assert_has_installed_exe(paths::cargo_home(), "foo"); } #[cargo_test] fn locked_install_without_published_lockfile() { Package::new("foo", "0.1.0") .file("src/main.rs", "//! Some docs\nfn main() {}") .publish(); cargo_process("install foo --locked") .with_stderr_data(str![[r#" ... [WARNING] no Cargo.lock file published in foo v0.1.0 ... "#]]) .run(); } #[cargo_test] fn install_semver_metadata() { // Check trying to install a package that uses semver metadata. // This uses alt registry because the bug this is exercising doesn't // trigger with a replaced source. registry::alt_init(); Package::new("foo", "1.0.0+abc") .alternative(true) .file("src/main.rs", "fn main() {}") .publish(); cargo_process("install foo --registry alternative --version 1.0.0+abc").run(); cargo_process("install foo --registry alternative") .with_stderr_data(str![[r#" [UPDATING] `alternative` index [IGNORED] package `foo v1.0.0+abc (registry `alternative`)` is already installed, use --force to override [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); // "Updating" is not displayed here due to the --version fast-path. cargo_process("install foo --registry alternative --version 1.0.0+abc") .with_stderr_data(str![[r#" [IGNORED] package `foo v1.0.0+abc (registry `alternative`)` is already installed, use --force to override [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); cargo_process("install foo --registry alternative --version 1.0.0 --force") .with_stderr_data(str![[r#" [UPDATING] `alternative` index [INSTALLING] foo v1.0.0+abc (registry `alternative`) [COMPILING] foo v1.0.0+abc (registry `alternative`) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [REPLACING] [ROOT]/home/.cargo/bin/foo[EXE] [REPLACED] package `foo v1.0.0+abc (registry `alternative`)` with `foo v1.0.0+abc (registry `alternative`)` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); // Check that from a fresh cache will work without metadata, too. paths::home().join(".cargo/registry").rm_rf(); paths::home().join(".cargo/bin").rm_rf(); cargo_process("install foo --registry alternative --version 1.0.0") .with_stderr_data(str![[r#" [UPDATING] `alternative` index [DOWNLOADING] crates ... [DOWNLOADED] foo v1.0.0+abc (registry `alternative`) [INSTALLING] foo v1.0.0+abc (registry `alternative`) [COMPILING] foo v1.0.0+abc (registry `alternative`) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE] [INSTALLED] package `foo v1.0.0+abc (registry `alternative`)` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); } #[cargo_test] fn no_auto_fix_note() { Package::new("auto_fix", "0.0.1") .file("src/lib.rs", "use std::io;") .file( "src/main.rs", &format!("extern crate {}; use std::io; fn main() {{}}", "auto_fix"), ) .publish(); // This should not contain a suggestion to run `cargo fix` // // This is checked by matching the full output as `with_stderr_does_not_contain` // can be brittle cargo_process("install auto_fix") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] auto_fix v0.0.1 (registry `dummy-registry`) [INSTALLING] auto_fix v0.0.1 [COMPILING] auto_fix v0.0.1 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/auto_fix[EXE] [INSTALLED] package `auto_fix v0.0.1` (executable `auto_fix[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); assert_has_installed_exe(paths::cargo_home(), "auto_fix"); cargo_process("uninstall auto_fix") .with_stderr_data(str![[r#" [REMOVING] [ROOT]/home/.cargo/bin/auto_fix[EXE] "#]]) .run(); assert_has_not_installed_exe(paths::cargo_home(), "auto_fix"); } #[cargo_test] fn failed_install_retains_temp_directory() { // Verifies that the temporary directory persists after a build failure. Package::new("foo", "0.0.1") .file("src/main.rs", "x") .publish(); let err = cargo_process("install foo").exec_with_output().unwrap_err(); let err = err.downcast::().unwrap(); let stderr = String::from_utf8(err.stderr.unwrap()).unwrap(); assert_e2e().eq(&stderr, str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] foo v0.0.1 (registry `dummy-registry`) [INSTALLING] foo v0.0.1 [COMPILING] foo v0.0.1 [ERROR] expected one of `!` or `::`, found `` --> [ROOT]/home/.cargo/registry/src/-[..]/foo-0.0.1/src/main.rs:1:1 | 1 | x | ^ expected one of `!` or `::` [ERROR] could not compile `foo` (bin "foo") due to 1 previous error [ERROR] failed to compile `foo v0.0.1`, intermediate artifacts can be found at `[..]`. To reuse those artifacts with a future compilation, set the environment variable `CARGO_TARGET_DIR` to that path. "#]]); // Find the path in the output. let stderr = stderr.split_once("found at `").unwrap().1; let end = stderr.find('.').unwrap() - 1; let path = Path::new(&stderr[..end]); assert!(path.exists()); assert!(path.join("release/deps").exists()); } #[cargo_test] fn sparse_install() { // Checks for an issue where uninstalling something corrupted // the SourceIds of sparse registries. // See https://github.com/rust-lang/cargo/issues/11751 let _registry = registry::RegistryBuilder::new().http_index().build(); pkg("foo", "0.0.1"); pkg("bar", "0.0.1"); cargo_process("install foo --registry dummy-registry") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] foo v0.0.1 (registry `dummy-registry`) [INSTALLING] foo v0.0.1 (registry `dummy-registry`) [UPDATING] `dummy-registry` index [COMPILING] foo v0.0.1 (registry `dummy-registry`) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE] [INSTALLED] package `foo v0.0.1 (registry `dummy-registry`)` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); assert_has_installed_exe(paths::cargo_home(), "foo"); let assert_v1 = |expected| { let v1 = fs::read_to_string(paths::home().join(".cargo/.crates.toml")).unwrap(); assert_e2e().eq(&v1, expected); }; assert_v1(str![[r#" [v1] "foo 0.0.1 (sparse+http://127.0.0.1:[..]/index/)" = ["foo[EXE]"] "#]]); cargo_process("install bar").run(); assert_has_installed_exe(paths::cargo_home(), "bar"); assert_v1(str![[r#" [v1] "bar 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = ["bar[EXE]"] "foo 0.0.1 (sparse+http://127.0.0.1:[..]/index/)" = ["foo[EXE]"] "#]]); cargo_process("uninstall bar") .with_stderr_data(str![[r#" [REMOVING] [ROOT]/home/.cargo/bin/bar[EXE] "#]]) .run(); assert_has_not_installed_exe(paths::cargo_home(), "bar"); assert_v1(str![[r#" [v1] "foo 0.0.1 (sparse+http://127.0.0.1:[..]/index/)" = ["foo[EXE]"] "#]]); cargo_process("uninstall foo") .with_stderr_data(str![[r#" [REMOVING] [ROOT]/home/.cargo/bin/foo[EXE] "#]]) .run(); assert_has_not_installed_exe(paths::cargo_home(), "foo"); assert_v1(str![[r#" [v1] "#]]); } #[cargo_test] fn self_referential() { // Some packages build-dep on prior versions of themselves. Package::new("foo", "0.0.1") .file("src/lib.rs", "fn hello() {}") .file("src/main.rs", "fn main() {}") .file("build.rs", "fn main() {}") .publish(); Package::new("foo", "0.0.2") .file("src/lib.rs", "fn hello() {}") .file("src/main.rs", "fn main() {}") .file("build.rs", "fn main() {}") .build_dep("foo", "0.0.1") .publish(); cargo_process("install foo").with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] foo v0.0.2 (registry `dummy-registry`) [INSTALLING] foo v0.0.2 [LOCKING] 1 package to latest compatible version [ADDING] foo v0.0.1 (available: v0.0.2) [DOWNLOADING] crates ... [DOWNLOADED] foo v0.0.1 (registry `dummy-registry`) [COMPILING] foo v0.0.1 [COMPILING] foo v0.0.2 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE] [INSTALLED] package `foo v0.0.2` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]).run(); assert_has_installed_exe(paths::cargo_home(), "foo"); } #[cargo_test] fn ambiguous_registry_vs_local_package() { // Correctly install 'foo' from a local package, even if that package also // depends on a registry dependency named 'foo'. Package::new("foo", "0.0.1") .file("src/lib.rs", "fn hello() {}") .publish(); let p = project() .file("src/main.rs", "fn main() {}") .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" authors = [] edition = "2021" [dependencies] foo = "0.0.1" "#, ) .build(); cargo_process("install --path") .arg(p.root()) .with_stderr_data(str![[r#" [INSTALLING] foo v0.1.0 ([ROOT]/foo) [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] foo v0.0.1 (registry `dummy-registry`) [COMPILING] foo v0.0.1 [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE] [INSTALLED] package `foo v0.1.0 ([ROOT]/foo)` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); assert_has_installed_exe(paths::cargo_home(), "foo"); } #[cargo_test] fn install_with_redundant_default_mode() { pkg("foo", "0.0.1"); cargo_process("install foo --release") .with_stderr_data(str![[r#" [ERROR] unexpected argument '--release' found tip: `--release` is the default for `cargo install`; instead `--debug` is supported Usage: cargo[EXE] install [OPTIONS] [CRATE[@]]... For more information, try '--help'. "#]]) .with_status(1) .run(); } #[cargo_test] fn install_incompat_msrv() { Package::new("foo", "0.1.0") .file("src/main.rs", "fn main() {}") .rust_version("1.30") .publish(); Package::new("foo", "0.2.0") .file("src/main.rs", "fn main() {}") .rust_version("1.9876.0") .publish(); cargo_process("install foo") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] cannot install package `foo 0.2.0`, it requires rustc 1.9876.0 or newer, while the currently active rustc version is [..] `foo 0.1.0` supports rustc 1.30 "#]]) .with_status(101) .run(); } fn assert_tracker_noexistence(key: &str) { let v1_data: toml::Value = toml::from_str(&fs::read_to_string(paths::cargo_home().join(".crates.toml")).unwrap()) .unwrap(); let v2_data: serde_json::Value = serde_json::from_str( &fs::read_to_string(paths::cargo_home().join(".crates2.json")).unwrap(), ) .unwrap(); assert!(v1_data["v1"].get(key).is_none()); assert!(v2_data["installs"][key].is_null()); } #[cargo_test] fn uninstall_running_binary() { use std::io::Write; Package::new("foo", "0.0.1") .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" "#, ) .file( "src/main.rs", r#" use std::net::TcpStream; use std::env::var; use std::io::Read; fn main() { for i in 0..2 { TcpStream::connect(&var("__ADDR__").unwrap()[..]) .unwrap() .read_to_end(&mut Vec::new()) .unwrap(); } } "#, ) .publish(); cargo_process("install foo").with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] foo v0.0.1 (registry `dummy-registry`) [INSTALLING] foo v0.0.1 [COMPILING] foo v0.0.1 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE] [INSTALLED] package `foo v0.0.1` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]).run(); assert_has_installed_exe(paths::cargo_home(), "foo"); let foo_bin = paths::cargo_home().join("bin").join(exe("foo")); let l = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); let addr = l.local_addr().unwrap().to_string(); let t = thread::spawn(move || { ProcessBuilder::new(foo_bin) .env("__ADDR__", addr) .exec() .unwrap(); }); let key = "foo 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)"; #[cfg(windows)] { // Ensure foo is running before the first `cargo uninstall` call l.accept().unwrap().0.write_all(&[1]).unwrap(); cargo_process("uninstall foo") .with_status(101) .with_stderr_data(str![[r#" ... [ERROR] failed to remove file `[ROOT]/home/.cargo/bin/foo[EXE]` ... "#]]) .run(); // Ensure foo is stopped before the second `cargo uninstall` call l.accept().unwrap().0.write_all(&[1]).unwrap(); t.join().unwrap(); cargo_process("uninstall foo") .with_stderr_data(str![[r#" [REMOVING] [ROOT]/home/.cargo/bin/foo[EXE] "#]]) .run(); }; #[cfg(not(windows))] { // Ensure foo is running before the first `cargo uninstall` call l.accept().unwrap().0.write_all(&[1]).unwrap(); cargo_process("uninstall foo") .with_stderr_data(str![[r#" [REMOVING] [ROOT]/home/.cargo/bin/foo[EXE] "#]]) .run(); l.accept().unwrap().0.write_all(&[1]).unwrap(); t.join().unwrap(); }; assert_has_not_installed_exe(paths::cargo_home(), "foo"); assert_tracker_noexistence(key); cargo_process("install foo").with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [INSTALLING] foo v0.0.1 [COMPILING] foo v0.0.1 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE] [INSTALLED] package `foo v0.0.1` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]).run(); } #[cargo_test] fn dry_run() { pkg("foo", "0.0.1"); cargo_process("-Z unstable-options install --dry-run foo") .masquerade_as_nightly_cargo(&["install::dry-run"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] foo v0.0.1 (registry `dummy-registry`) [INSTALLING] foo v0.0.1 [INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE] [WARNING] aborting install due to dry run [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); assert_has_not_installed_exe(paths::cargo_home(), "foo"); } #[cargo_test] fn dry_run_incompatible_package() { Package::new("some-package-from-the-distant-future", "0.0.1") .rust_version("1.2345.0") .file("src/main.rs", "fn main() {}") .publish(); cargo_process("-Z unstable-options install --dry-run some-package-from-the-distant-future") .masquerade_as_nightly_cargo(&["install::dry-run"]) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] cannot install package `some-package-from-the-distant-future 0.0.1`, it requires rustc 1.2345.0 or newer, while the currently active rustc version is [..] "#]]) .run(); assert_has_not_installed_exe(paths::cargo_home(), "some-package-from-the-distant-future"); } #[cargo_test] fn dry_run_incompatible_package_dependecy() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" authors = [] [dependencies] some-package-from-the-distant-future = { path = "a" } "#, ) .file("src/main.rs", "fn main() {}") .file( "a/Cargo.toml", r#" [package] name = "some-package-from-the-distant-future" version = "0.1.0" authors = [] rust-version = "1.2345.0" "#, ) .file("a/src/lib.rs", "") .build(); cargo_process("-Z unstable-options install --dry-run --path") .arg(p.root()) .arg("foo") .masquerade_as_nightly_cargo(&["install::dry-run"]) .with_status(101) .with_stderr_data(str![[r#" [INSTALLING] foo v0.1.0 ([ROOT]/foo) [LOCKING] 1 package to latest compatible version [ERROR] failed to compile `foo v0.1.0 ([ROOT]/foo)`, intermediate artifacts can be found at `[ROOT]/foo/target`. To reuse those artifacts with a future compilation, set the environment variable `CARGO_TARGET_DIR` to that path. Caused by: rustc [..] is not supported by the following package: some-package-from-the-distant-future@0.1.0 requires rustc 1.2345.0 "#]]) .run(); assert_has_not_installed_exe(paths::cargo_home(), "foo"); } #[cargo_test] fn dry_run_upgrade() { pkg("foo", "0.0.1"); cargo_process("install foo").run(); assert_has_installed_exe(paths::cargo_home(), "foo"); pkg("foo", "0.0.2"); cargo_process("-Z unstable-options install --dry-run foo") .masquerade_as_nightly_cargo(&["install::dry-run"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] foo v0.0.2 (registry `dummy-registry`) [INSTALLING] foo v0.0.2 [REPLACING] [ROOT]/home/.cargo/bin/foo[EXE] [WARNING] aborting install due to dry run [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); assert_has_installed_exe(paths::cargo_home(), "foo"); } #[cargo_test] fn dry_run_remove_orphan() { Package::new("bar", "1.0.0") .file("src/bin/client.rs", "fn main() {}") .file("src/bin/server.rs", "fn main() {}") .publish(); cargo_process("install bar") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [INSTALLING] bar v1.0.0 [COMPILING] bar v1.0.0 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/client[EXE] [INSTALLING] [ROOT]/home/.cargo/bin/server[EXE] [INSTALLED] package `bar v1.0.0` (executables `client[EXE]`, `server[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); assert_has_installed_exe(paths::cargo_home(), "client"); assert_has_installed_exe(paths::cargo_home(), "server"); Package::new("bar", "2.0.0") .file("src/bin/client.rs", "fn main() {}") .publish(); cargo_process("-Z unstable-options install --dry-run bar") .masquerade_as_nightly_cargo(&["install::dry-run"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] bar v2.0.0 (registry `dummy-registry`) [INSTALLING] bar v2.0.0 [REPLACING] [ROOT]/home/.cargo/bin/client[EXE] [REMOVING] executable `[ROOT]/home/.cargo/bin/server[EXE]` from previous version bar v1.0.0 [WARNING] aborting install due to dry run [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); assert_has_installed_exe(paths::cargo_home(), "client"); // Ensure server is still installed after the dry run assert_has_installed_exe(paths::cargo_home(), "server"); } #[cargo_test] fn prefixed_v_in_version() { pkg("foo", "0.0.1"); cargo_process("install foo@v0.0.1") .with_status(1) .with_stderr_data(str![[r#" [ERROR] invalid value 'foo@v0.0.1' for '[CRATE[@]]...': the version provided, `v0.0.1` is not a valid SemVer requirement [HELP] try changing the version to `0.0.1` For more information, try '--help'. "#]]) .run(); } cargo-0.91.0/tests/testsuite/install_upgrade.rs000064400000000000000000000741721046102023000177370ustar 00000000000000//! Tests for `cargo install` where it upgrades a package if it is out-of-date. use std::collections::BTreeSet; use std::env; use std::fs; use std::path::PathBuf; use std::sync::atomic::{AtomicUsize, Ordering}; use crate::prelude::*; use crate::utils::cargo_process; use crate::utils::cross_compile::disabled as cross_compile_disabled; use cargo::core::PackageId; use cargo_test_support::install::exe; use cargo_test_support::paths; use cargo_test_support::registry::{self, Package}; use cargo_test_support::{Execs, basic_manifest, cross_compile, execs, git, process, project, str}; fn pkg_maybe_yanked(name: &str, vers: &str, yanked: bool) { Package::new(name, vers) .yanked(yanked) .file( "src/main.rs", r#"fn main() { println!("{}", env!("CARGO_PKG_VERSION")) }"#, ) .publish(); } // Helper for publishing a package. fn pkg(name: &str, vers: &str) { pkg_maybe_yanked(name, vers, false) } fn v1_path() -> PathBuf { paths::cargo_home().join(".crates.toml") } fn v2_path() -> PathBuf { paths::cargo_home().join(".crates2.json") } fn load_crates1() -> toml::Value { toml::from_str(&fs::read_to_string(v1_path()).unwrap()).unwrap() } fn load_crates2() -> serde_json::Value { serde_json::from_str(&fs::read_to_string(v2_path()).unwrap()).unwrap() } fn installed_exe(name: &str) -> PathBuf { paths::cargo_home().join("bin").join(exe(name)) } /// Helper for executing binaries installed by cargo. fn installed_process(name: &str) -> Execs { static NEXT_ID: AtomicUsize = AtomicUsize::new(0); thread_local!(static UNIQUE_ID: usize = NEXT_ID.fetch_add(1, Ordering::SeqCst)); // This copies the executable to a unique name so that it may be safely // replaced on Windows. See Project::rename_run for details. let src = installed_exe(name); let dst = installed_exe(&UNIQUE_ID.with(|my_id| format!("{}-{}", name, my_id))); // Note: Cannot use copy. On Linux, file descriptors may be left open to // the executable as other tests in other threads are constantly spawning // new processes (see https://github.com/rust-lang/cargo/pull/5557 for // more). fs::rename(&src, &dst) .unwrap_or_else(|e| panic!("Failed to rename `{:?}` to `{:?}`: {}", src, dst, e)); // Leave behind a fake file so that reinstall duplicate check works. fs::write(src, "").unwrap(); let p = process(dst); execs().with_process_builder(p) } /// Check that the given package name/version has the following bins listed in /// the trackers. Also verifies that both trackers are in sync and valid. /// Pass in an empty `bins` list to assert that the package is *not* installed. fn validate_trackers(name: &str, version: &str, bins: &[&str]) { let v1 = load_crates1(); let v1_table = v1.get("v1").unwrap().as_table().unwrap(); let v2 = load_crates2(); let v2_table = v2["installs"].as_object().unwrap(); assert_eq!(v1_table.len(), v2_table.len()); // Convert `bins` to a BTreeSet. let bins: BTreeSet = bins .iter() .map(|b| format!("{}{}", b, env::consts::EXE_SUFFIX)) .collect(); // Check every entry matches between v1 and v2. for (pkg_id_str, v1_bins) in v1_table { let pkg_id: PackageId = toml::Value::from(pkg_id_str.to_string()) .try_into() .unwrap(); let v1_bins: BTreeSet = v1_bins .as_array() .unwrap() .iter() .map(|b| b.as_str().unwrap().to_string()) .collect(); if pkg_id.name().as_str() == name && pkg_id.version().to_string() == version { if bins.is_empty() { panic!( "Expected {} to not be installed, but found: {:?}", name, v1_bins ); } else { assert_eq!(bins, v1_bins); } } let pkg_id_value = serde_json::to_value(&pkg_id).unwrap(); let pkg_id_str = pkg_id_value.as_str().unwrap(); let v2_info = v2_table .get(pkg_id_str) .expect("v2 missing v1 pkg") .as_object() .unwrap(); let v2_bins = v2_info["bins"].as_array().unwrap(); let v2_bins: BTreeSet = v2_bins .iter() .map(|b| b.as_str().unwrap().to_string()) .collect(); assert_eq!(v1_bins, v2_bins); } } #[cargo_test] fn registry_upgrade() { // Installing and upgrading from a registry. pkg("foo", "1.0.0"); cargo_process("install foo").with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] foo v1.0.0 (registry `dummy-registry`) [INSTALLING] foo v1.0.0 [COMPILING] foo v1.0.0 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE] [INSTALLED] package `foo v1.0.0` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]).run(); installed_process("foo") .with_stdout_data(str![[r#" 1.0.0 "#]]) .run(); validate_trackers("foo", "1.0.0", &["foo"]); cargo_process("install foo").with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [IGNORED] package `foo v1.0.0` is already installed, use --force to override [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]).run(); pkg("foo", "1.0.1"); cargo_process("install foo").with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] foo v1.0.1 (registry `dummy-registry`) [INSTALLING] foo v1.0.1 [COMPILING] foo v1.0.1 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [REPLACING] [ROOT]/home/.cargo/bin/foo[EXE] [REPLACED] package `foo v1.0.0` with `foo v1.0.1` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]).run(); installed_process("foo") .with_stdout_data(str![[r#" 1.0.1 "#]]) .run(); validate_trackers("foo", "1.0.1", &["foo"]); cargo_process("install foo --version=1.0.0") .with_stderr_data(str![[r#" ... [COMPILING] foo v1.0.0 ... "#]]) .run(); installed_process("foo") .with_stdout_data(str![[r#" 1.0.0 "#]]) .run(); validate_trackers("foo", "1.0.0", &["foo"]); cargo_process("install foo --version=^1.0") .with_stderr_data(str![[r#" ... [COMPILING] foo v1.0.1 ... "#]]) .run(); installed_process("foo") .with_stdout_data(str![[r#" 1.0.1 "#]]) .run(); validate_trackers("foo", "1.0.1", &["foo"]); cargo_process("install foo --version=^1.0") .with_stderr_data(str![[r#" ... [IGNORED] package `foo v1.0.1` is already installed, use --force to override ... "#]]) .run(); } #[cargo_test] fn uninstall() { // Basic uninstall test. pkg("foo", "1.0.0"); cargo_process("install foo").run(); cargo_process("uninstall foo").run(); let data = load_crates2(); assert_eq!(data["installs"].as_object().unwrap().len(), 0); let v1_table = load_crates1(); assert_eq!(v1_table.get("v1").unwrap().as_table().unwrap().len(), 0); } #[cargo_test] fn upgrade_force() { pkg("foo", "1.0.0"); cargo_process("install foo").run(); cargo_process("install foo --force") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [INSTALLING] foo v1.0.0 [COMPILING] foo v1.0.0 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [REPLACING] [ROOT]/home/.cargo/bin/foo[EXE] [REPLACED] package `foo v1.0.0` with `foo v1.0.0` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); validate_trackers("foo", "1.0.0", &["foo"]); } #[cargo_test] fn ambiguous_version_no_longer_allowed() { // Non-semver-requirement is not allowed for `--version`. pkg("foo", "1.0.0"); cargo_process("install foo --version=1.0") .with_stderr_data(str![[r#" [ERROR] invalid value '1.0' for '--version ': unexpected end of input while parsing minor version number tip: if you want to specify SemVer range, add an explicit qualifier, like '^1.0' For more information, try '--help'. "#]]) .with_status(1) .run(); } #[cargo_test] fn path_is_always_dirty() { // --path should always reinstall. let p = project().file("src/main.rs", "fn main() {}").build(); p.cargo("install --path .").run(); p.cargo("install --path .") .with_stderr_data(str![[r#" ... [REPLACING] [ROOT]/home/.cargo/bin/foo[EXE] ... "#]]) .run(); } #[cargo_test] fn fails_for_conflicts_unknown() { // If an untracked file is in the way, it should fail. pkg("foo", "1.0.0"); let exe = installed_exe("foo"); exe.parent().unwrap().mkdir_p(); fs::write(exe, "").unwrap(); cargo_process("install foo") .with_stderr_data(str![[r#" ... [ERROR] binary `foo[EXE]` already exists in destination ... "#]]) .with_status(101) .run(); } #[cargo_test] fn fails_for_conflicts_known() { // If the same binary exists in another package, it should fail. pkg("foo", "1.0.0"); Package::new("bar", "1.0.0") .file("src/bin/foo.rs", "fn main() {}") .publish(); cargo_process("install foo").run(); cargo_process("install bar") .with_stderr_data(str![[r#" ... [ERROR] binary `foo[EXE]` already exists in destination as part of `foo v1.0.0` ... "#]]) .with_status(101) .run(); } #[cargo_test] fn supports_multiple_binary_names() { // Can individually install with --bin or --example Package::new("foo", "1.0.0") .file("src/main.rs", r#"fn main() { println!("foo"); }"#) .file("src/bin/a.rs", r#"fn main() { println!("a"); }"#) .file("examples/ex1.rs", r#"fn main() { println!("ex1"); }"#) .publish(); cargo_process("install foo --bin foo").run(); installed_process("foo") .with_stdout_data(str![[r#" foo "#]]) .run(); assert!(!installed_exe("a").exists()); assert!(!installed_exe("ex1").exists()); validate_trackers("foo", "1.0.0", &["foo"]); cargo_process("install foo --bin a").run(); installed_process("a") .with_stdout_data(str![[r#" a "#]]) .run(); assert!(!installed_exe("ex1").exists()); validate_trackers("foo", "1.0.0", &["a", "foo"]); cargo_process("install foo --example ex1").run(); installed_process("ex1") .with_stdout_data(str![[r#" ex1 "#]]) .run(); validate_trackers("foo", "1.0.0", &["a", "ex1", "foo"]); cargo_process("uninstall foo --bin foo").run(); assert!(!installed_exe("foo").exists()); assert!(installed_exe("ex1").exists()); validate_trackers("foo", "1.0.0", &["a", "ex1"]); cargo_process("uninstall foo").run(); assert!(!installed_exe("ex1").exists()); assert!(!installed_exe("a").exists()); } #[cargo_test] fn v1_already_installed_fresh() { // Install with v1, then try to install again with v2. pkg("foo", "1.0.0"); cargo_process("install foo").run(); cargo_process("install foo") .with_stderr_data(str![[r#" ... [IGNORED] package `foo v1.0.0` is already installed, use --force to override ... "#]]) .run(); } #[cargo_test] fn v1_already_installed_dirty() { // Install with v1, then install a new version with v2. pkg("foo", "1.0.0"); cargo_process("install foo").run(); pkg("foo", "1.0.1"); cargo_process("install foo") .with_stderr_data(str![[r#" ... [COMPILING] foo v1.0.1 ... [REPLACING] [ROOT]/home/.cargo/bin/foo[EXE] ... "#]]) .run(); validate_trackers("foo", "1.0.1", &["foo"]); } #[cargo_test] fn change_features_rebuilds() { Package::new("foo", "1.0.0") .file( "src/main.rs", r#" fn main() { if cfg!(feature = "f1") { println!("f1"); } if cfg!(feature = "f2") { println!("f2"); } } "#, ) .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" [features] f1 = [] f2 = [] default = ["f1"] "#, ) .publish(); cargo_process("install foo").run(); installed_process("foo") .with_stdout_data(str![[r#" f1 "#]]) .run(); cargo_process("install foo --no-default-features").run(); installed_process("foo").with_stdout_data("").run(); cargo_process("install foo --all-features").run(); installed_process("foo") .with_stdout_data(str![[r#" f1 f2 "#]]) .run(); cargo_process("install foo --no-default-features --features=f1").run(); installed_process("foo") .with_stdout_data(str![[r#" f1 "#]]) .run(); } #[cargo_test] fn change_profile_rebuilds() { pkg("foo", "1.0.0"); cargo_process("install foo").run(); cargo_process("install foo --debug") .with_stderr_data(str![[r#" ... [COMPILING] foo v1.0.0 ... [REPLACING] [ROOT]/home/.cargo/bin/foo[EXE] ... "#]]) .run(); cargo_process("install foo --debug") .with_stderr_data(str![[r#" ... [IGNORED] package `foo v1.0.0` is already installed, use --force to override ... "#]]) .run(); } #[cargo_test] fn change_target_rebuilds() { if cross_compile_disabled() { return; } pkg("foo", "1.0.0"); cargo_process("install foo").run(); let target = cross_compile::alternate(); cargo_process("install foo -v --target") .arg(&target) .with_stderr_data(str![[r#" ... [COMPILING] foo v1.0.0 [RUNNING] `rustc [..]--target [ALT_TARGET][..]` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [REPLACING] [ROOT]/home/.cargo/bin/foo[EXE] ... "#]]) .run(); } #[cargo_test] fn change_bin_sets_rebuilds() { // Changing which bins in a multi-bin project should reinstall. Package::new("foo", "1.0.0") .file("src/main.rs", "fn main() { }") .file("src/bin/x.rs", "fn main() { }") .file("src/bin/y.rs", "fn main() { }") .publish(); cargo_process("install foo --bin x").run(); assert!(installed_exe("x").exists()); assert!(!installed_exe("y").exists()); assert!(!installed_exe("foo").exists()); validate_trackers("foo", "1.0.0", &["x"]); cargo_process("install foo --bin y") .with_stderr_data(str![[r#" ... [INSTALLED] package `foo v1.0.0` (executable `y[EXE]`) ... "#]]) .run(); assert!(installed_exe("x").exists()); assert!(installed_exe("y").exists()); assert!(!installed_exe("foo").exists()); validate_trackers("foo", "1.0.0", &["x", "y"]); cargo_process("install foo") .with_stderr_data(str![[r#" ... [INSTALLED] package `foo v1.0.0` (executable `foo[EXE]`) [REPLACED] package `foo v1.0.0` with `foo v1.0.0` (executables `x[EXE]`, `y[EXE]`) ... "#]]) .run(); assert!(installed_exe("x").exists()); assert!(installed_exe("y").exists()); assert!(installed_exe("foo").exists()); validate_trackers("foo", "1.0.0", &["foo", "x", "y"]); } #[cargo_test] fn forwards_compatible() { // Unknown fields should be preserved. pkg("foo", "1.0.0"); pkg("bar", "1.0.0"); cargo_process("install foo").run(); let key = "foo 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)"; let v2 = paths::cargo_home().join(".crates2.json"); let mut data = load_crates2(); data["newfield"] = serde_json::Value::Bool(true); data["installs"][key]["moreinfo"] = serde_json::Value::String("shazam".to_string()); fs::write(&v2, serde_json::to_string(&data).unwrap()).unwrap(); cargo_process("install bar").run(); let data: serde_json::Value = serde_json::from_str(&fs::read_to_string(&v2).unwrap()).unwrap(); assert_eq!(data["newfield"].as_bool().unwrap(), true); assert_eq!( data["installs"][key]["moreinfo"].as_str().unwrap(), "shazam" ); } #[cargo_test] fn v2_syncs() { // V2 inherits the installs from V1. pkg("one", "1.0.0"); pkg("two", "1.0.0"); pkg("three", "1.0.0"); let p = project() .file("src/bin/x.rs", "fn main() {}") .file("src/bin/y.rs", "fn main() {}") .build(); cargo_process("install one").run(); validate_trackers("one", "1.0.0", &["one"]); p.cargo("install --path .").run(); validate_trackers("foo", "1.0.0", &["x", "y"]); // v1 add/remove cargo_process("install two").run(); cargo_process("uninstall one").run(); // This should pick up that `two` was added, `one` was removed. cargo_process("install three").run(); validate_trackers("three", "1.0.0", &["three"]); cargo_process("install --list") .with_stdout_data(str![[r#" foo v0.0.1 ([ROOT]/foo): x[EXE] y[EXE] three v1.0.0: three[EXE] two v1.0.0: two[EXE] "#]]) .run(); cargo_process("install one").run(); installed_process("one") .with_stdout_data(str![[r#" 1.0.0 "#]]) .run(); validate_trackers("one", "1.0.0", &["one"]); cargo_process("install two") .with_stderr_data(str![[r#" ... [IGNORED] package `two v1.0.0` is already installed, use --force to override ... "#]]) .run(); // v1 remove p.cargo("uninstall --bin x").run(); pkg("x", "1.0.0"); pkg("y", "1.0.0"); // This should succeed because `x` was removed in V1. cargo_process("install x").run(); validate_trackers("x", "1.0.0", &["x"]); // This should fail because `y` still exists in a different package. cargo_process("install y") .with_stderr_data(str![[r#" ... [ERROR] binary `y[EXE]` already exists in destination as part of `foo v0.0.1 ([ROOT]/foo)` ... "#]]) .with_status(101) .run(); } #[cargo_test] fn upgrade_git() { let git_project = git::new("foo", |project| project.file("src/main.rs", "fn main() {}")); // install cargo_process("install --git") .arg(git_project.url().to_string()) .run(); // Check install stays fresh. cargo_process("install --git") .arg(git_project.url().to_string()) .with_stderr_data(str![[r#" ... [IGNORED] package `foo v0.0.1 ([ROOTURL]/foo#[..])` is already installed, use --force to override ... "#]]) .run(); // Modify a file. let repo = git2::Repository::open(git_project.root()).unwrap(); git_project.change_file("src/main.rs", r#"fn main() {println!("onomatopoeia");}"#); git::add(&repo); git::commit(&repo); // Install should reinstall. cargo_process("install --git") .arg(git_project.url().to_string()) .with_stderr_data(str![[r#" ... [COMPILING] foo v0.0.1 ([ROOT]/home/.cargo/git/checkouts/foo-[HASH]/[..]) ... [REPLACING] [ROOT]/home/.cargo/bin/foo[EXE] ... "#]]) .run(); installed_process("foo") .with_stdout_data(str![[r#" onomatopoeia "#]]) .run(); // Check install stays fresh. cargo_process("install --git") .arg(git_project.url().to_string()) .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/foo` [IGNORED] package `foo v0.0.1 ([ROOTURL]/foo#[..])` is already installed, use --force to override [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); } #[cargo_test] fn switch_sources() { // Installing what appears to be the same thing, but from different // sources should reinstall. registry::alt_init(); pkg("foo", "1.0.0"); Package::new("foo", "1.0.0") .file("src/main.rs", r#"fn main() { println!("alt"); }"#) .alternative(true) .publish(); let p = project() .at("foo-local") // so it doesn't use the same directory as the git project .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) .file("src/main.rs", r#"fn main() { println!("local"); }"#) .build(); let git_project = git::new("foo", |project| { project.file("src/main.rs", r#"fn main() { println!("git"); }"#) }); cargo_process("install foo").run(); installed_process("foo") .with_stdout_data(str![[r#" 1.0.0 "#]]) .run(); cargo_process("install foo --registry alternative").run(); installed_process("foo") .with_stdout_data(str![[r#" alt "#]]) .run(); p.cargo("install --path .").run(); installed_process("foo") .with_stdout_data(str![[r#" local "#]]) .run(); cargo_process("install --git") .arg(git_project.url().to_string()) .run(); installed_process("foo") .with_stdout_data(str![[r#" git "#]]) .run(); } #[cargo_test] fn multiple_report() { // Testing the full output that indicates installed/ignored/replaced/summary. pkg("one", "1.0.0"); pkg("two", "1.0.0"); fn three(vers: &str) { Package::new("three", vers) .file("src/main.rs", "fn main() { }") .file("src/bin/x.rs", "fn main() { }") .file("src/bin/y.rs", "fn main() { }") .publish(); } three("1.0.0"); cargo_process("install one two three") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] one v1.0.0 (registry `dummy-registry`) [DOWNLOADING] crates ... [DOWNLOADED] two v1.0.0 (registry `dummy-registry`) [DOWNLOADING] crates ... [DOWNLOADED] three v1.0.0 (registry `dummy-registry`) [INSTALLING] one v1.0.0 [COMPILING] one v1.0.0 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/one[EXE] [INSTALLED] package `one v1.0.0` (executable `one[EXE]`) [INSTALLING] two v1.0.0 [COMPILING] two v1.0.0 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/two[EXE] [INSTALLED] package `two v1.0.0` (executable `two[EXE]`) [INSTALLING] three v1.0.0 [COMPILING] three v1.0.0 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/three[EXE] [INSTALLING] [ROOT]/home/.cargo/bin/x[EXE] [INSTALLING] [ROOT]/home/.cargo/bin/y[EXE] [INSTALLED] package `three v1.0.0` (executables `three[EXE]`, `x[EXE]`, `y[EXE]`) [SUMMARY] Successfully installed one, two, three! [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); pkg("foo", "1.0.1"); pkg("bar", "1.0.1"); three("1.0.1"); cargo_process("install one two three") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [IGNORED] package `one v1.0.0` is already installed, use --force to override [IGNORED] package `two v1.0.0` is already installed, use --force to override [DOWNLOADING] crates ... [DOWNLOADED] three v1.0.1 (registry `dummy-registry`) [INSTALLING] three v1.0.1 [COMPILING] three v1.0.1 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [REPLACING] [ROOT]/home/.cargo/bin/three[EXE] [REPLACING] [ROOT]/home/.cargo/bin/x[EXE] [REPLACING] [ROOT]/home/.cargo/bin/y[EXE] [REPLACED] package `three v1.0.0` with `three v1.0.1` (executables `three[EXE]`, `x[EXE]`, `y[EXE]`) [SUMMARY] Successfully installed one, two, three! [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); cargo_process("uninstall three") .with_stderr_data(str![[r#" [REMOVING] [ROOT]/home/.cargo/bin/three[EXE] [REMOVING] [ROOT]/home/.cargo/bin/x[EXE] [REMOVING] [ROOT]/home/.cargo/bin/y[EXE] "#]]) .run(); cargo_process("install three --bin x") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [INSTALLING] three v1.0.1 [COMPILING] three v1.0.1 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/x[EXE] [INSTALLED] package `three v1.0.1` (executable `x[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); cargo_process("install three") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [INSTALLING] three v1.0.1 [COMPILING] three v1.0.1 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/three[EXE] [INSTALLING] [ROOT]/home/.cargo/bin/y[EXE] [REPLACING] [ROOT]/home/.cargo/bin/x[EXE] [INSTALLED] package `three v1.0.1` (executables `three[EXE]`, `y[EXE]`) [REPLACED] package `three v1.0.1` with `three v1.0.1` (executable `x[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); } #[cargo_test] fn no_track() { pkg("foo", "1.0.0"); cargo_process("install --no-track foo").run(); assert!(!v1_path().exists()); assert!(!v2_path().exists()); cargo_process("install --no-track foo") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] binary `foo[EXE]` already exists in destination `[ROOT]/home/.cargo/bin/foo[EXE]` Add --force to overwrite "#]]) .with_status(101) .run(); } #[cargo_test] fn deletes_orphaned() { // When an executable is removed from a project, upgrading should remove it. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" "#, ) .file("src/main.rs", "fn main() {}") .file("src/bin/other.rs", "fn main() {}") .file("examples/ex1.rs", "fn main() {}") .build(); p.cargo("install --path . --bins --examples").run(); assert!(installed_exe("other").exists()); // Remove a binary, add a new one, and bump the version. fs::remove_file(p.root().join("src/bin/other.rs")).unwrap(); p.change_file("examples/ex2.rs", "fn main() {}"); p.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.2.0" "#, ); p.cargo("install --path . --bins --examples") .with_stderr_data(str![[r#" [INSTALLING] foo v0.2.0 ([ROOT]/foo) [COMPILING] foo v0.2.0 ([ROOT]/foo) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/ex2[EXE] [REPLACING] [ROOT]/home/.cargo/bin/ex1[EXE] [REPLACING] [ROOT]/home/.cargo/bin/foo[EXE] [REMOVING] executable `[ROOT]/home/.cargo/bin/other[EXE]` from previous version foo v0.1.0 ([ROOT]/foo) [INSTALLED] package `foo v0.2.0 ([ROOT]/foo)` (executable `ex2[EXE]`) [REPLACED] package `foo v0.1.0 ([ROOT]/foo)` with `foo v0.2.0 ([ROOT]/foo)` (executables `ex1[EXE]`, `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); assert!(!installed_exe("other").exists()); validate_trackers("foo", "0.2.0", &["foo", "ex1", "ex2"]); // 0.1.0 should not have any entries. validate_trackers("foo", "0.1.0", &[]); } #[cargo_test] fn already_installed_exact_does_not_update() { pkg("foo", "1.0.0"); cargo_process("install foo --version=1.0.0").run(); cargo_process("install foo --version=1.0.0") .with_stderr_data(str![[r#" [IGNORED] package `foo v1.0.0` is already installed, use --force to override [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); cargo_process("install foo --version=>=1.0.0") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [IGNORED] package `foo v1.0.0` is already installed, use --force to override [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); pkg("foo", "1.0.1"); cargo_process("install foo --version=>=1.0.0") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] foo v1.0.1 (registry `dummy-registry`) [INSTALLING] foo v1.0.1 [COMPILING] foo v1.0.1 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [REPLACING] [ROOT]/home/.cargo/bin/foo[EXE] [REPLACED] package `foo v1.0.0` with `foo v1.0.1` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); } #[cargo_test] fn already_installed_updates_yank_status_on_upgrade() { pkg("foo", "1.0.0"); pkg_maybe_yanked("foo", "1.0.1", true); cargo_process("install foo --version=1.0.0").run(); cargo_process("install foo --version=1.0.1") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] cannot install package `foo`, it has been yanked from registry `crates-io` "#]]) .run(); pkg_maybe_yanked("foo", "1.0.1", false); pkg("foo", "1.0.1"); cargo_process("install foo --version=1.0.1") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] foo v1.0.1 (registry `dummy-registry`) [INSTALLING] foo v1.0.1 [COMPILING] foo v1.0.1 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [REPLACING] [ROOT]/home/.cargo/bin/foo[EXE] [REPLACED] package `foo v1.0.0` with `foo v1.0.1` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); } #[cargo_test] fn partially_already_installed_does_one_update() { pkg("foo", "1.0.0"); cargo_process("install foo --version=1.0.0").run(); pkg("bar", "1.0.0"); pkg("baz", "1.0.0"); cargo_process("install foo bar baz --version=1.0.0") .with_stderr_data(str![[r#" [IGNORED] package `foo v1.0.0` is already installed, use --force to override [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [DOWNLOADING] crates ... [DOWNLOADED] baz v1.0.0 (registry `dummy-registry`) [INSTALLING] bar v1.0.0 [COMPILING] bar v1.0.0 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/bar[EXE] [INSTALLED] package `bar v1.0.0` (executable `bar[EXE]`) [INSTALLING] baz v1.0.0 [COMPILING] baz v1.0.0 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/baz[EXE] [INSTALLED] package `baz v1.0.0` (executable `baz[EXE]`) [SUMMARY] Successfully installed foo, bar, baz! [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); } cargo-0.91.0/tests/testsuite/jobserver.rs000064400000000000000000000242271046102023000165570ustar 00000000000000//! Tests for the jobserver protocol. use std::env; use std::net::TcpListener; use std::process::Command; use std::thread; use crate::prelude::*; use crate::utils::cargo_exe; use cargo_test_support::basic_bin_manifest; use cargo_test_support::install::assert_has_installed_exe; use cargo_test_support::paths; use cargo_test_support::{project, rustc_host, str}; use cargo_util::is_ci; const EXE_CONTENT: &str = r#" use std::env; fn main() { let var = env::var("CARGO_MAKEFLAGS").expect("no jobserver from env"); let arg = var.split(' ') .find(|p| p.starts_with("--jobserver")) .unwrap(); let val = &arg[arg.find('=').unwrap() + 1..]; validate(val); } #[cfg(unix)] fn validate(s: &str) { use std::fs::{self, File}; use std::io::*; use std::os::unix::prelude::*; if let Some((r, w)) = s.split_once(',') { // `--jobserver-auth=R,W` unsafe { let mut read = File::from_raw_fd(r.parse().unwrap()); let mut write = File::from_raw_fd(w.parse().unwrap()); let mut buf = [0]; assert_eq!(read.read(&mut buf).unwrap(), 1); assert_eq!(write.write(&buf).unwrap(), 1); } } else { // `--jobserver-auth=fifo:PATH` is the default since GNU Make 4.4 let (_, path) = s.split_once(':').expect("fifo:PATH"); assert!(fs::metadata(path).unwrap().file_type().is_fifo()); } } #[cfg(windows)] fn validate(_: &str) { // a little too complicated for a test... } "#; fn make_exe() -> &'static str { if cfg!(windows) { "mingw32-make" } else if cfg!(target_os = "aix") { "gmake" } else { "make" } } #[cargo_test] fn jobserver_exists() { let p = project() .file("build.rs", EXE_CONTENT) .file("src/lib.rs", "") .build(); // Explicitly use `-j2` to ensure that there's eventually going to be a // token to read from `validate` above, since running the build script // itself consumes a token. p.cargo("check -j2").run(); } #[cargo_test] fn external_subcommand_inherits_jobserver() { let make = make_exe(); if Command::new(make).arg("--version").output().is_err() { return; } let name = "cargo-jobserver-check"; let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "{name}" version = "0.0.1" "# ), ) .file("src/main.rs", EXE_CONTENT) .file( "Makefile", "\ all: \t+$(CARGO) jobserver-check ", ) .build(); p.cargo("install --path .").run(); assert_has_installed_exe(paths::cargo_home(), name); p.process(make).env("CARGO", cargo_exe()).arg("-j2").run(); } #[cargo_test] fn runner_inherits_jobserver() { let make = make_exe(); if Command::new(make).arg("--version").output().is_err() { return; } let runner = "runner"; project() .at(runner) .file("Cargo.toml", &basic_bin_manifest(runner)) .file( "src/main.rs", r#" pub fn main() { eprintln!("this is a runner"); let args: Vec = std::env::args().collect(); let status = std::process::Command::new(&args[1]).status().unwrap(); assert!(status.success()); } "#, ) .build() .cargo("install --path .") .run(); // Add .cargo/bin to PATH let mut path: Vec<_> = env::split_paths(&env::var_os("PATH").unwrap_or_default()).collect(); path.push(paths::cargo_home().join("bin")); let path = &env::join_paths(path).unwrap(); assert_has_installed_exe(paths::cargo_home(), runner); let host = rustc_host(); let config_value = &format!("target.{host}.runner = \"{runner}\""); let name = "cargo-jobserver-check"; let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "{name}" version = "0.0.1" edition = "2015" "# ), ) .file( "src/lib.rs", r#" #[test] fn test() { _ = std::env::var("CARGO_MAKEFLAGS").expect("no jobserver from env"); } "#, ) .file("src/main.rs", EXE_CONTENT) .file( "Makefile", &format!( "\ run: \t+$(CARGO) run run-runner: \t+$(CARGO) run --config '{config_value}' test: \t+$(CARGO) test --lib test-runner: \t+$(CARGO) test --lib --config '{config_value}' ", ), ) .build(); // jobserver can be inherited from env p.process(make) .env("CARGO", cargo_exe()) .arg("run") .arg("-j2") .run(); p.process(make) .env("PATH", path) .env("CARGO", cargo_exe()) .arg("run-runner") .arg("-j2") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `runner target/debug/cargo-jobserver-check[EXE]` this is a runner "#]]) .run(); p.process(make) .env("CARGO", cargo_exe()) .arg("test") .arg("-j2") .run(); p.process(make) .env("PATH", path) .env("CARGO", cargo_exe()) .arg("test-runner") .arg("-j2") .with_stderr_data(str![[r#" [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/cargo_jobserver_check-[HASH][EXE]) this is a runner "#]]) .run(); // but not from `-j` flag p.cargo("run -j2") .with_status(101) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/cargo-jobserver-check[EXE]` ... [..]no jobserver from env[..] ... "#]]) .run(); p.cargo("run -j2") .env("PATH", path) .arg("--config") .arg(config_value) .with_status(101) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `runner target/debug/cargo-jobserver-check[EXE]` this is a runner ... [..]no jobserver from env[..] ... "#]]) .run(); p.cargo("test -j2") .with_status(101) .with_stdout_data("...\n[..]no jobserver from env[..]\n...") .run(); p.cargo("test -j2") .env("PATH", path) .arg("--config") .arg(config_value) .with_status(101) .with_stderr_data(str![[r#" [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/cargo_jobserver_check-[HASH][EXE]) this is a runner ... "#]]) .with_stdout_data("...\n[..]no jobserver from env[..]\n...") .run(); } #[cargo_test] fn makes_jobserver_used() { let make = make_exe(); if !is_ci() && Command::new(make).arg("--version").output().is_err() { return; } let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [dependencies] d1 = { path = "d1" } d2 = { path = "d2" } d3 = { path = "d3" } "#, ) .file("src/lib.rs", "") .file( "d1/Cargo.toml", r#" [package] name = "d1" version = "0.0.1" authors = [] build = "../dbuild.rs" "#, ) .file("d1/src/lib.rs", "") .file( "d2/Cargo.toml", r#" [package] name = "d2" version = "0.0.1" authors = [] build = "../dbuild.rs" "#, ) .file("d2/src/lib.rs", "") .file( "d3/Cargo.toml", r#" [package] name = "d3" version = "0.0.1" authors = [] build = "../dbuild.rs" "#, ) .file("d3/src/lib.rs", "") .file( "dbuild.rs", r#" use std::net::TcpStream; use std::env; use std::io::Read; fn main() { let addr = env::var("ADDR").unwrap(); let mut stream = TcpStream::connect(addr).unwrap(); let mut v = Vec::new(); stream.read_to_end(&mut v).unwrap(); } "#, ) .file( "Makefile", "\ all: \t+$(CARGO) build ", ) .build(); let l = TcpListener::bind("127.0.0.1:0").unwrap(); let addr = l.local_addr().unwrap(); let child = thread::spawn(move || { let a1 = l.accept().unwrap(); let a2 = l.accept().unwrap(); l.set_nonblocking(true).unwrap(); for _ in 0..1000 { assert!(l.accept().is_err()); thread::yield_now(); } drop(a1); l.set_nonblocking(false).unwrap(); let a3 = l.accept().unwrap(); drop((a2, a3)); }); p.process(make) .env("CARGO", cargo_exe()) .env("ADDR", addr.to_string()) .arg("-j2") .run(); child.join().unwrap(); } #[cargo_test] fn jobserver_and_j() { let make = make_exe(); if !is_ci() && Command::new(make).arg("--version").output().is_err() { return; } let p = project() .file("src/lib.rs", "") .file( "Makefile", "\ all: \t+$(CARGO) build -j2 ", ) .build(); p.process(make) .env("CARGO", cargo_exe()) .arg("-j2") .with_stderr_data(str![[r#" [WARNING] a `-j` argument was passed to Cargo but Cargo is also configured with an external jobserver in its environment, ignoring the `-j` parameter [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } cargo-0.91.0/tests/testsuite/lints/error/mod.rs000064400000000000000000000013651046102023000176150ustar 00000000000000use crate::prelude::*; use cargo_test_support::str; use cargo_test_support::{file, project}; #[cargo_test] fn case() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["test-dummy-unstable"] [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] im-a-teapot = true [lints.cargo] im_a_teapot = "deny" "#, ) .file("src/lib.rs", "") .build(); snapbox::cmd::Command::cargo_ui() .masquerade_as_nightly_cargo(&["cargo-lints", "test-dummy-unstable"]) .current_dir(p.root()) .arg("check") .arg("-Zcargo-lints") .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); } cargo-0.91.0/tests/testsuite/lints/error/stderr.term.svg000064400000000000000000000032341046102023000214570ustar 00000000000000 error: `im_a_teapot` is specified --> Cargo.toml:9:1 | 9 | im-a-teapot = true | ^^^^^^^^^^^^^^^^^^ | = note: `cargo::im_a_teapot` is set to `deny` in `[lints]` cargo-0.91.0/tests/testsuite/lints/inherited/mod.rs000064400000000000000000000015011046102023000204270ustar 00000000000000use crate::prelude::*; use cargo_test_support::str; use cargo_test_support::{file, project}; #[cargo_test] fn case() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo"] [workspace.lints.cargo] im_a_teapot = { level = "warn", priority = 10 } "#, ) .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lints] workspace = true "#, ) .file("foo/src/lib.rs", "") .build(); snapbox::cmd::Command::cargo_ui() .masquerade_as_nightly_cargo(&["cargo-lints"]) .current_dir(p.root()) .arg("check") .arg("-Zcargo-lints") .assert() .code(101) .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); } cargo-0.91.0/tests/testsuite/lints/inherited/stderr.term.svg000064400000000000000000000055411046102023000223040ustar 00000000000000 error: use of unstable lint `im_a_teapot` --> Cargo.toml:6:1 | 6 | im_a_teapot = { level = "warn", priority = 10 } | ^^^^^^^^^^^ this is behind `test-dummy-unstable`, which is not enabled | note: `cargo::im_a_teapot` was inherited --> foo/Cargo.toml:9:1 | 9 | workspace = true | ---------------- | = help: consider adding `cargo-features = ["test-dummy-unstable"]` to the top of the manifest error: encountered 1 errors(s) while verifying lints cargo-0.91.0/tests/testsuite/lints/mod.rs000064400000000000000000000156741046102023000164740ustar 00000000000000use crate::prelude::*; use cargo_test_support::project; use cargo_test_support::registry::Package; use cargo_test_support::str; mod error; mod inherited; mod unknown_lints; mod warning; #[cargo_test] fn dashes_dont_get_rewritten() { let foo = project() .file( "Cargo.toml", r#" cargo-features = ["test-dummy-unstable"] [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] im-a-teapot = true [lints.cargo] im-a-teapot = "warn" "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check -Zcargo-lints") .masquerade_as_nightly_cargo(&["cargo-lints", "test-dummy-unstable"]) .with_stderr_data(str![[r#" [WARNING] unknown lint: `im-a-teapot` --> Cargo.toml:12:1 | 12 | im-a-teapot = "warn" | ^^^^^^^^^^^ | = [NOTE] `cargo::unknown_lints` is set to `warn` by default = [HELP] there is a lint with a similar name: `im_a_teapot` [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn forbid_not_overridden() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["test-dummy-unstable"] [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] im-a-teapot = true [lints.cargo] im_a_teapot = { level = "warn", priority = 10 } test_dummy_unstable = { level = "forbid", priority = -1 } "#, ) .file("src/lib.rs", "") .build(); p.cargo("check -Zcargo-lints") .masquerade_as_nightly_cargo(&["cargo-lints", "test-dummy-unstable"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] `im_a_teapot` is specified --> Cargo.toml:9:1 | 9 | im-a-teapot = true | ^^^^^^^^^^^^^^^^^^ | = [NOTE] `cargo::im_a_teapot` is set to `forbid` in `[lints]` "#]]) .run(); } #[cargo_test] fn workspace_lints() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["test-dummy-unstable"] [workspace.lints.cargo] im_a_teapot = { level = "warn", priority = 10 } test_dummy_unstable = { level = "forbid", priority = -1 } [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] im-a-teapot = true [lints] workspace = true "#, ) .file("src/lib.rs", "") .build(); p.cargo("check -Zcargo-lints") .masquerade_as_nightly_cargo(&["cargo-lints", "test-dummy-unstable"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] `im_a_teapot` is specified --> Cargo.toml:13:1 | 13 | im-a-teapot = true | ^^^^^^^^^^^^^^^^^^ | = [NOTE] `cargo::im_a_teapot` is set to `forbid` in `[lints]` "#]]) .run(); } #[cargo_test] fn dont_always_inherit_workspace_lints() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo"] [workspace.lints.cargo] im_a_teapot = "warn" "#, ) .file( "foo/Cargo.toml", r#" cargo-features = ["test-dummy-unstable"] [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] im-a-teapot = true "#, ) .file("foo/src/lib.rs", "") .build(); p.cargo("check -Zcargo-lints") .masquerade_as_nightly_cargo(&["cargo-lints"]) .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn cap_lints() { Package::new("baz", "0.1.0").publish(); Package::new("bar", "0.1.0") .file( "Cargo.toml", r#" cargo-features = ["test-dummy-unstable"] [package] name = "bar" version = "0.1.0" edition = "2021" im-a-teapot = true [dependencies] baz = { version = "0.1.0", optional = true } [lints.cargo] im_a_teapot = "warn" "#, ) .file("src/lib.rs", "") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2021" [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check -Zcargo-lints") .masquerade_as_nightly_cargo(&["cargo-lints"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.1.0 (registry `dummy-registry`) [CHECKING] bar v0.1.0 [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn check_feature_gated() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lints.cargo] im_a_teapot = "warn" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check -Zcargo-lints") .masquerade_as_nightly_cargo(&["cargo-lints"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] use of unstable lint `im_a_teapot` --> Cargo.toml:9:1 | 9 | im_a_teapot = "warn" | ^^^^^^^^^^^ this is behind `test-dummy-unstable`, which is not enabled | = [HELP] consider adding `cargo-features = ["test-dummy-unstable"]` to the top of the manifest [ERROR] encountered 1 errors(s) while verifying lints "#]]) .run(); } #[cargo_test] fn check_feature_gated_workspace() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo"] [workspace.lints.cargo] im_a_teapot = { level = "warn", priority = 10 } test_dummy_unstable = { level = "forbid", priority = -1 } "#, ) .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lints] workspace = true "#, ) .file("foo/src/lib.rs", "") .build(); p.cargo("check -Zcargo-lints") .masquerade_as_nightly_cargo(&["cargo-lints"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] use of unstable lint `im_a_teapot` --> Cargo.toml:6:1 | 6 | im_a_teapot = { level = "warn", priority = 10 } | ^^^^^^^^^^^ this is behind `test-dummy-unstable`, which is not enabled | [NOTE] `cargo::im_a_teapot` was inherited --> foo/Cargo.toml:9:1 | 9 | workspace = true | ---------------- | = [HELP] consider adding `cargo-features = ["test-dummy-unstable"]` to the top of the manifest [ERROR] use of unstable lint `test_dummy_unstable` --> Cargo.toml:7:1 | 7 | test_dummy_unstable = { level = "forbid", priority = -1 } | ^^^^^^^^^^^^^^^^^^^ this is behind `test-dummy-unstable`, which is not enabled | [NOTE] `cargo::test_dummy_unstable` was inherited --> foo/Cargo.toml:9:1 | 9 | workspace = true | ---------------- | = [HELP] consider adding `cargo-features = ["test-dummy-unstable"]` to the top of the manifest [ERROR] encountered 2 errors(s) while verifying lints "#]]) .run(); } cargo-0.91.0/tests/testsuite/lints/unknown_lints.rs000064400000000000000000000035571046102023000206220ustar 00000000000000use crate::prelude::*; use cargo_test_support::project; use cargo_test_support::str; #[cargo_test] fn default() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lints.cargo] this-lint-does-not-exist = "warn" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check -Zcargo-lints") .masquerade_as_nightly_cargo(&["cargo-lints"]) .with_stderr_data(str![[r#" [WARNING] unknown lint: `this-lint-does-not-exist` --> Cargo.toml:9:1 | 9 | this-lint-does-not-exist = "warn" | ^^^^^^^^^^^^^^^^^^^^^^^^ | = [NOTE] `cargo::unknown_lints` is set to `warn` by default [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn inherited() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo"] [workspace.lints.cargo] this-lint-does-not-exist = "warn" "#, ) .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lints] workspace = true "#, ) .file("foo/src/lib.rs", "") .build(); p.cargo("check -Zcargo-lints") .masquerade_as_nightly_cargo(&["cargo-lints"]) .with_stderr_data(str![[r#" [WARNING] unknown lint: `this-lint-does-not-exist` --> Cargo.toml:6:1 | 6 | this-lint-does-not-exist = "warn" | ^^^^^^^^^^^^^^^^^^^^^^^^ | [NOTE] `cargo::this-lint-does-not-exist` was inherited --> foo/Cargo.toml:9:1 | 9 | workspace = true | ---------------- | = [NOTE] `cargo::unknown_lints` is set to `warn` by default [CHECKING] foo v0.0.1 ([ROOT]/foo/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } cargo-0.91.0/tests/testsuite/lints/warning/mod.rs000064400000000000000000000013651046102023000201310ustar 00000000000000use crate::prelude::*; use cargo_test_support::str; use cargo_test_support::{file, project}; #[cargo_test] fn case() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["test-dummy-unstable"] [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] im-a-teapot = true [lints.cargo] im_a_teapot = "warn" "#, ) .file("src/lib.rs", "") .build(); snapbox::cmd::Command::cargo_ui() .masquerade_as_nightly_cargo(&["cargo-lints", "test-dummy-unstable"]) .current_dir(p.root()) .arg("check") .arg("-Zcargo-lints") .assert() .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); } cargo-0.91.0/tests/testsuite/lints/warning/stderr.term.svg000064400000000000000000000041171046102023000217740ustar 00000000000000 warning: `im_a_teapot` is specified --> Cargo.toml:9:1 | 9 | im-a-teapot = true | ------------------ | = note: `cargo::im_a_teapot` is set to `warn` in `[lints]` Checking foo v0.0.1 ([ROOT]/foo) Finished `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s cargo-0.91.0/tests/testsuite/lints_table.rs000064400000000000000000000435211046102023000170540ustar 00000000000000//! Tests for `[lints]` use crate::prelude::*; use cargo_test_support::project; use cargo_test_support::registry::Package; use cargo_test_support::str; #[cargo_test] fn dependency_warning_ignored() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar.path = "../bar" "#, ) .file("src/lib.rs", "") .build(); let _bar = project() .at("bar") .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [lints.rust] unsafe_code = "forbid" "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.0.1 ([ROOT]/bar) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn malformed_on_stable() { let foo = project() .file( "Cargo.toml", r#" lints = 20 [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid type: integer `20`, expected a lints table --> Cargo.toml:2:25 | 2 | lints = 20 | ^^ | "#]]) .run(); } #[cargo_test] fn fail_on_invalid_tool() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [workspace.lints.super-awesome-linter] unsafe_code = "forbid" "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check").with_stderr_data(str![[r#" [WARNING] [ROOT]/foo/Cargo.toml: unrecognized lint tool `lints.super-awesome-linter`, specifying unrecognized tools may break in the future. supported tools: cargo, clippy, rust, rustdoc [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn invalid_type_in_lint_value() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [workspace.lints.rust] rust-2018-idioms = -1 "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid type: integer `-1`, expected a string or map --> Cargo.toml:8:36 | 8 | rust-2018-idioms = -1 | ^^ | "#]]) .run(); } #[cargo_test] fn warn_on_unused_key() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [workspace.lints.rust] rust-2018-idioms = { level = "allow", unused = true } [lints.rust] rust-2018-idioms = { level = "allow", unused = true } "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check") .with_stderr_data(str![[r#" [WARNING] [ROOT]/foo/Cargo.toml: unused manifest key: `lints.rust.rust-2018-idioms.unused` [WARNING] [ROOT]/foo/Cargo.toml: unused manifest key: `lints.rust.rust-2018-idioms.unused` [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn fail_on_tool_injection() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [workspace.lints.rust] "clippy::cyclomatic_complexity" = "warn" "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: `lints.rust.clippy::cyclomatic_complexity` is not valid lint name; try `lints.clippy.cyclomatic_complexity` "#]]) .run(); } #[cargo_test] fn fail_on_redundant_tool() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [workspace.lints.rust] "rust::unsafe_code" = "forbid" "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: `lints.rust.rust::unsafe_code` is not valid lint name; try `lints.rust.unsafe_code` "#]]) .run(); } #[cargo_test] fn fail_on_conflicting_tool() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [workspace.lints.rust] "super-awesome-tool::unsafe_code" = "forbid" "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: `lints.rust.super-awesome-tool::unsafe_code` is not a valid lint name "#]]) .run(); } #[cargo_test] fn package_lint_deny() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lints.rust] "unsafe_code" = "deny" "#, ) .file( "src/lib.rs", " pub fn foo(num: i32) -> u32 { unsafe { std::mem::transmute(num) } } ", ) .build(); foo.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [ERROR] usage of an `unsafe` block ... "#]]) .run(); } #[cargo_test] fn workspace_cant_be_false() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lints] workspace = false [workspace.lints.rust] "unsafe_code" = "deny" "#, ) .file( "src/lib.rs", " pub fn foo(num: i32) -> u32 { unsafe { std::mem::transmute(num) } } ", ) .build(); foo.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] `workspace` cannot be false --> Cargo.toml:9:29 | 9 | workspace = false | ^^^^^ | "#]]) .run(); } #[cargo_test] fn workspace_lint_deny() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lints] workspace = true [workspace.lints.rust] "unsafe_code" = "deny" "#, ) .file( "src/lib.rs", " pub fn foo(num: i32) -> u32 { unsafe { std::mem::transmute(num) } } ", ) .build(); foo.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [ERROR] usage of an `unsafe` block ... "#]]) .run(); } #[cargo_test] fn workspace_and_package_lints() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lints] workspace = true [lints.rust] "unsafe_code" = "allow" [workspace.lints.rust] "unsafe_code" = "deny" "#, ) .file( "src/lib.rs", " pub fn foo(num: i32) -> u32 { unsafe { std::mem::transmute(num) } } ", ) .build(); foo.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: cannot override `workspace.lints` in `lints`, either remove the overrides or `lints.workspace = true` and manually specify the lints "#]]) .run(); } #[cargo_test] fn attribute_has_precedence() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lints.rust] "unsafe_code" = "deny" "#, ) .file( "src/lib.rs", " #![allow(unsafe_code)] pub fn foo(num: i32) -> u32 { unsafe { std::mem::transmute(num) } } ", ) .build(); foo.cargo("check") .arg("-v") // Show order of rustflags on failure .run(); } #[cargo_test] fn rustflags_has_precedence() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lints.rust] "unsafe_code" = "deny" "#, ) .file( "src/lib.rs", " pub fn foo(num: i32) -> u32 { unsafe { std::mem::transmute(num) } } ", ) .build(); foo.cargo("check") .arg("-v") // Show order of rustflags on failure .env("RUSTFLAGS", "-Aunsafe_code") .run(); } #[cargo_test] fn profile_rustflags_has_precedence() { let foo = project() .file( "Cargo.toml", r#" cargo-features = ["profile-rustflags"] [package] name = "foo" version = "0.0.1" edition = "2015" [lints.rust] "unsafe_code" = "deny" [profile.dev] rustflags = ["-A", "unsafe_code"] "#, ) .file( "src/lib.rs", " pub fn foo(num: i32) -> u32 { unsafe { std::mem::transmute(num) } } ", ) .build(); foo.cargo("check") .arg("-v") // Show order of rustflags on failure .masquerade_as_nightly_cargo(&["profile-rustflags"]) .run(); } #[cargo_test] fn build_rustflags_has_precedence() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [lints.rust] "unsafe_code" = "deny" "#, ) .file( ".cargo/config.toml", r#" [build] rustflags = ["-A", "unsafe_code"] "#, ) .file( "src/lib.rs", " pub fn foo(num: i32) -> u32 { unsafe { std::mem::transmute(num) } } ", ) .build(); foo.cargo("check") .arg("-v") // Show order of rustflags on failure .run(); } #[cargo_test] fn without_priority() { Package::new("reg-dep", "1.0.0").publish(); let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2018" authors = [] [dependencies] reg-dep = "1.0.0" [lints.rust] "rust-2018-idioms" = "deny" "unused-extern-crates" = "allow" "#, ) .file( "src/lib.rs", " extern crate reg_dep; pub fn foo() -> u32 { 2 } ", ) .build(); foo.cargo("check") .with_status(101) .with_stderr_data(str![[r#" ... [ERROR] unused extern crate ... "#]]) .run(); } #[cargo_test] fn with_priority() { Package::new("reg-dep", "1.0.0").publish(); let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2018" authors = [] [dependencies] reg-dep = "1.0.0" [lints.rust] "rust-2018-idioms" = { level = "deny", priority = -1 } "unused-extern-crates" = "allow" "#, ) .file( "src/lib.rs", " extern crate reg_dep; pub fn foo() -> u32 { 2 } ", ) .build(); foo.cargo("check").run(); } #[cargo_test] fn rustdoc_lint() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lints.rustdoc] broken_intra_doc_links = "deny" "#, ) .file( "src/lib.rs", " /// [`bar`] doesn't exist pub fn foo() -> u32 { } ", ) .build(); foo.cargo("doc") .with_status(101) .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [ERROR] unresolved link to `bar` ... "#]]) .run(); } #[cargo_test] fn doctest_respects_lints() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lints.rust] confusable-idents = 'allow' "#, ) .file( "src/lib.rs", r#" /// Test /// /// [`Foo`] /// /// ``` /// let s = "rust"; /// let s_s = "rust2"; /// ``` pub fn f() {} pub const Ě: i32 = 1; pub const Ĕ: i32 = 2; "#, ) .build(); foo.cargo("check") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); foo.cargo("test --doc") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [DOCTEST] foo "#]]) .run(); } #[cargo_test] fn cargo_lints_nightly_required() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lints.cargo] "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check") .with_stderr_data(str![[r#" [WARNING] unused manifest key `lints.cargo` (may be supported in a future version) this Cargo does not support nightly features, but if you switch to nightly channel you can pass `-Zcargo-lints` to enable this feature. [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn cargo_lints_no_z_flag() { let foo = project() .file( "Cargo.toml", r#" cargo-features = ["test-dummy-unstable"] [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] im-a-teapot = true [lints.cargo] "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check") .masquerade_as_nightly_cargo(&["cargo-lints", "test-dummy-unstable"]) .with_stderr_data(str![[r#" [WARNING] unused manifest key `lints.cargo` (may be supported in a future version) consider passing `-Zcargo-lints` to enable this feature. [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn cargo_lints_success() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["test-dummy-unstable"] [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] im-a-teapot = true [lints.cargo] im_a_teapot = "warn" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check -Zcargo-lints") .masquerade_as_nightly_cargo(&["cargo-lints", "test-dummy-unstable"]) .with_stderr_data(str![[r#" [WARNING] `im_a_teapot` is specified --> Cargo.toml:9:1 | 9 | im-a-teapot = true | ------------------ | = [NOTE] `cargo::im_a_teapot` is set to `warn` in `[lints]` [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } cargo-0.91.0/tests/testsuite/list_availables.rs000064400000000000000000000474541046102023000177230ustar 00000000000000//! Tests for packages/target filter flags giving suggestions on which //! packages/targets are available. use crate::prelude::*; use cargo_test_support::project; use cargo_test_support::str; use snapbox::IntoData; fn list_availables_test(command: &str, expected: ExpectedSnapshots) { let full_project = project() .file("examples/a.rs", "fn main() { }") .file("examples/b.rs", "fn main() { }") .file("benches/bench1.rs", "") .file("benches/bench2.rs", "") .file("tests/test1.rs", "") .file("tests/test2.rs", "") .file("src/main.rs", "fn main() { }") .file("Cargo.lock", "") // for `cargo pkgid` .build(); if let ExpectedSnapshots { example: ProjectExpected { full: Some(example), .. }, .. } = expected { full_project .cargo(&format!("{} --example", command)) .with_stderr_data(example) .with_status(101) .run(); } if let ExpectedSnapshots { bin: ProjectExpected { full: Some(bin), .. }, .. } = expected { full_project .cargo(&format!("{} --bin", command)) .with_stderr_data(bin) .with_status(101) .run(); } if let ExpectedSnapshots { bench: ProjectExpected { full: Some(bench), .. }, .. } = expected { full_project .cargo(&format!("{} --bench", command)) .with_stderr_data(bench) .with_status(101) .run(); } if let ExpectedSnapshots { test: ProjectExpected { full: Some(test), .. }, .. } = expected { full_project .cargo(&format!("{} --test", command)) .with_stderr_data(test) .with_status(101) .run(); } if let ExpectedSnapshots { package: ProjectExpected { full: Some(package), .. }, .. } = expected { full_project .cargo(&format!("{} -p", command)) .with_stderr_data(package) .with_status(101) .run(); } let empty_project = project().file("src/lib.rs", "").build(); if let ExpectedSnapshots { example: ProjectExpected { empty: Some(example), .. }, .. } = expected { empty_project .cargo(&format!("{} --example", command)) .with_stderr_data(example) .with_status(101) .run(); } if let ExpectedSnapshots { bin: ProjectExpected { empty: Some(bin), .. }, .. } = expected { empty_project .cargo(&format!("{} --bin", command)) .with_stderr_data(bin) .with_status(101) .run(); } if let ExpectedSnapshots { bench: ProjectExpected { empty: Some(bench), .. }, .. } = expected { empty_project .cargo(&format!("{} --bench", command)) .with_stderr_data(bench) .with_status(101) .run(); } if let ExpectedSnapshots { test: ProjectExpected { empty: Some(test), .. }, .. } = expected { empty_project .cargo(&format!("{} --test", command)) .with_stderr_data(test) .with_status(101) .run(); } if let ExpectedSnapshots { target: ProjectExpected { empty: Some(target), .. }, .. } = expected { empty_project .cargo(&format!("{} --target", command)) .with_stderr_data(target) .with_status(101) .run(); } } #[cargo_test] fn build_list_availables() { list_availables_test( "build", SnapshotsBuilder::new() .with_example(str![[r#" [ERROR] "--example" takes one argument. Available examples: a b "#]], str![[r#" [ERROR] "--example" takes one argument. No examples available. "#]]) .with_bin(str![[r#" [ERROR] "--bin" takes one argument. Available binaries: foo "#]], str![[r#" [ERROR] "--bin" takes one argument. No binaries available. "#]]) .with_test(str![[r#" [ERROR] "--test" takes one argument. Available test targets: test1 test2 "#]], str![[r#" [ERROR] "--test" takes one argument. No test targets available. "#]]) .with_bench(str![[r#" [ERROR] "--bench" takes one argument. Available bench targets: bench1 bench2 "#]], str![[r#" [ERROR] "--bench" takes one argument. No bench targets available. "#]]) .with_package(str![[r#" [ERROR] "--package " requires a SPEC format value, which can be any package ID specifier in the dependency graph. Run `cargo help pkgid` for more information about SPEC format. Possible packages/workspace members: foo "#]]) .with_target(str![[r#" [ERROR] "--target" takes a target architecture as an argument. Run `[..]` to see possible targets. "#]]) .build(), ); } #[cargo_test] fn check_list_availables() { list_availables_test( "check", SnapshotsBuilder::new() .with_example(str![[r#" [ERROR] "--example" takes one argument. Available examples: a b "#]], str![[r#" [ERROR] "--example" takes one argument. No examples available. "#]]) .with_bin(str![[r#" [ERROR] "--bin" takes one argument. Available binaries: foo "#]], str![[r#" [ERROR] "--bin" takes one argument. No binaries available. "#]]) .with_test(str![[r#" [ERROR] "--test" takes one argument. Available test targets: test1 test2 "#]], str![[r#" [ERROR] "--test" takes one argument. No test targets available. "#]]) .with_bench(str![[r#" [ERROR] "--bench" takes one argument. Available bench targets: bench1 bench2 "#]], str![[r#" [ERROR] "--bench" takes one argument. No bench targets available. "#]]) .with_package(str![[r#" [ERROR] "--package " requires a SPEC format value, which can be any package ID specifier in the dependency graph. Run `cargo help pkgid` for more information about SPEC format. Possible packages/workspace members: foo "#]]) .with_target(str![[r#" [ERROR] "--target" takes a target architecture as an argument. Run `[..]` to see possible targets. "#]]) .build(), ); } #[cargo_test] fn doc_list_availables() { list_availables_test( "doc", SnapshotsBuilder::new() .with_bin(str![[r#" [ERROR] "--bin" takes one argument. Available binaries: foo "#]], str![[r#" [ERROR] "--bin" takes one argument. No binaries available. "#]]) .with_package(str![[r#" [ERROR] "--package " requires a SPEC format value, which can be any package ID specifier in the dependency graph. Run `cargo help pkgid` for more information about SPEC format. Possible packages/workspace members: foo "#]]) .with_target(str![[r#" [ERROR] "--target" takes a target architecture as an argument. Run `[..]` to see possible targets. "#]]) .build(), ); } #[cargo_test] fn fix_list_availables() { list_availables_test( "fix", SnapshotsBuilder::new() .with_example(str![[r#" [ERROR] "--example" takes one argument. Available examples: a b "#]], str![[r#" [ERROR] "--example" takes one argument. No examples available. "#]]) .with_bin(str![[r#" [ERROR] "--bin" takes one argument. Available binaries: foo "#]], str![[r#" [ERROR] "--bin" takes one argument. No binaries available. "#]]) .with_test(str![[r#" [ERROR] "--test" takes one argument. Available test targets: test1 test2 "#]], str![[r#" [ERROR] "--test" takes one argument. No test targets available. "#]]) .with_bench(str![[r#" [ERROR] "--bench" takes one argument. Available bench targets: bench1 bench2 "#]], str![[r#" [ERROR] "--bench" takes one argument. No bench targets available. "#]]) .with_package(str![[r#" [ERROR] "--package " requires a SPEC format value, which can be any package ID specifier in the dependency graph. Run `cargo help pkgid` for more information about SPEC format. Possible packages/workspace members: foo "#]]) .with_target(str![[r#" [ERROR] "--target" takes a target architecture as an argument. Run `[..]` to see possible targets. "#]]) .build(), ); } #[cargo_test] fn run_list_availables() { list_availables_test( "run", SnapshotsBuilder::new() .with_example(str![[r#" [ERROR] "--example" takes one argument. Available examples: a b "#]], str![[r#" [ERROR] "--example" takes one argument. No examples available. "#]]) .with_bin(str![[r#" [ERROR] "--bin" takes one argument. Available binaries: foo "#]], str![[r#" [ERROR] "--bin" takes one argument. No binaries available. "#]]) .with_package(str![[r#" [ERROR] "--package " requires a SPEC format value, which can be any package ID specifier in the dependency graph. Run `cargo help pkgid` for more information about SPEC format. Possible packages/workspace members: foo "#]]) .with_target(str![[r#" [ERROR] "--target" takes a target architecture as an argument. Run `[..]` to see possible targets. "#]]) .build(), ); } #[cargo_test] fn test_list_availables() { list_availables_test( "test", SnapshotsBuilder::new() .with_example(str![[r#" [ERROR] "--example" takes one argument. Available examples: a b "#]], str![[r#" [ERROR] "--example" takes one argument. No examples available. "#]]) .with_bin(str![[r#" [ERROR] "--bin" takes one argument. Available binaries: foo "#]], str![[r#" [ERROR] "--bin" takes one argument. No binaries available. "#]]) .with_test(str![[r#" [ERROR] "--test" takes one argument. Available test targets: test1 test2 "#]], str![[r#" [ERROR] "--test" takes one argument. No test targets available. "#]]) .with_bench(str![[r#" [ERROR] "--bench" takes one argument. Available bench targets: bench1 bench2 "#]], str![[r#" [ERROR] "--bench" takes one argument. No bench targets available. "#]]) .with_package(str![[r#" [ERROR] "--package " requires a SPEC format value, which can be any package ID specifier in the dependency graph. Run `cargo help pkgid` for more information about SPEC format. Possible packages/workspace members: foo "#]]) .with_target(str![[r#" [ERROR] "--target" takes a target architecture as an argument. Run `[..]` to see possible targets. "#]]) .build(), ); } #[cargo_test] fn bench_list_availables() { list_availables_test( "bench", SnapshotsBuilder::new() .with_example(str![[r#" [ERROR] "--example" takes one argument. Available examples: a b "#]], str![[r#" [ERROR] "--example" takes one argument. No examples available. "#]]) .with_bin(str![[r#" [ERROR] "--bin" takes one argument. Available binaries: foo "#]], str![[r#" [ERROR] "--bin" takes one argument. No binaries available. "#]]) .with_test(str![[r#" [ERROR] "--test" takes one argument. Available test targets: test1 test2 "#]], str![[r#" [ERROR] "--test" takes one argument. No test targets available. "#]]) .with_bench(str![[r#" [ERROR] "--bench" takes one argument. Available bench targets: bench1 bench2 "#]], str![[r#" [ERROR] "--bench" takes one argument. No bench targets available. "#]]) .with_package(str![[r#" [ERROR] "--package " requires a SPEC format value, which can be any package ID specifier in the dependency graph. Run `cargo help pkgid` for more information about SPEC format. Possible packages/workspace members: foo "#]]) .with_target(str![[r#" [ERROR] "--target" takes a target architecture as an argument. Run `[..]` to see possible targets. "#]]) .build(), ); } #[cargo_test] fn install_list_availables() { list_availables_test( "install", SnapshotsBuilder::new() .with_example( str![[r#" [ERROR] "--example" takes one argument. Available examples: a b "#]], str![[r#" [ERROR] "--example" takes one argument. No examples available. "#]], ) .with_bin( str![[r#" [ERROR] "--bin" takes one argument. Available binaries: foo "#]], str![[r#" [ERROR] "--bin" takes one argument. No binaries available. "#]], ) .with_target(str![[r#" [ERROR] "--target" takes a target architecture as an argument. Run `[..]` to see possible targets. "#]]) .build(), ); } #[cargo_test] fn rustdoc_list_availables() { list_availables_test( "rustdoc", SnapshotsBuilder::new() .with_example(str![[r#" [ERROR] "--example" takes one argument. Available examples: a b "#]], str![[r#" [ERROR] "--example" takes one argument. No examples available. "#]]) .with_bin(str![[r#" [ERROR] "--bin" takes one argument. Available binaries: foo "#]], str![[r#" [ERROR] "--bin" takes one argument. No binaries available. "#]]) .with_test(str![[r#" [ERROR] "--test" takes one argument. Available test targets: test1 test2 "#]], str![[r#" [ERROR] "--test" takes one argument. No test targets available. "#]]) .with_bench(str![[r#" [ERROR] "--bench" takes one argument. Available bench targets: bench1 bench2 "#]], str![[r#" [ERROR] "--bench" takes one argument. No bench targets available. "#]]) .with_package(str![[r#" [ERROR] "--package " requires a SPEC format value, which can be any package ID specifier in the dependency graph. Run `cargo help pkgid` for more information about SPEC format. Possible packages/workspace members: foo "#]]) .with_target(str![[r#" [ERROR] "--target" takes a target architecture as an argument. Run `[..]` to see possible targets. "#]]) .build(), ); } #[cargo_test] fn rustc_list_availables() { list_availables_test( "rustc", SnapshotsBuilder::new() .with_example(str![[r#" [ERROR] "--example" takes one argument. Available examples: a b "#]], str![[r#" [ERROR] "--example" takes one argument. No examples available. "#]]) .with_bin(str![[r#" [ERROR] "--bin" takes one argument. Available binaries: foo "#]], str![[r#" [ERROR] "--bin" takes one argument. No binaries available. "#]]) .with_test(str![[r#" [ERROR] "--test" takes one argument. Available test targets: test1 test2 "#]], str![[r#" [ERROR] "--test" takes one argument. No test targets available. "#]]) .with_bench(str![[r#" [ERROR] "--bench" takes one argument. Available bench targets: bench1 bench2 "#]], str![[r#" [ERROR] "--bench" takes one argument. No bench targets available. "#]]) .with_package(str![[r#" [ERROR] "--package " requires a SPEC format value, which can be any package ID specifier in the dependency graph. Run `cargo help pkgid` for more information about SPEC format. Possible packages/workspace members: foo "#]]) .with_target(str![[r#" [ERROR] "--target" takes a target architecture as an argument. Run `[..]` to see possible targets. "#]]) .build(), ); } #[cargo_test] fn pkgid_list_availables() { list_availables_test( "pkgid", SnapshotsBuilder::new() .with_package(str![[r#" [ERROR] "--package " requires a SPEC format value, which can be any package ID specifier in the dependency graph. Run `cargo help pkgid` for more information about SPEC format. Possible packages/workspace members: foo "#]]) .build(), ); } #[cargo_test] fn tree_list_availables() { list_availables_test( "tree", SnapshotsBuilder::new() .with_package(str![[r#" [ERROR] "--package " requires a SPEC format value, which can be any package ID specifier in the dependency graph. Run `cargo help pkgid` for more information about SPEC format. Possible packages/workspace members: foo "#]]) .with_target(str![[r#" [ERROR] "--target" takes a target architecture as an argument. Run `[..]` to see possible targets. "#]]) .build(), ) } #[cargo_test] fn clean_list_availables() { list_availables_test( "clean", SnapshotsBuilder::new() .with_package(str![[r#" [ERROR] "--package " requires a SPEC format value, which can be any package ID specifier in the dependency graph. Run `cargo help pkgid` for more information about SPEC format. Possible packages/workspace members: foo "#]]) .with_target(str![[r#" [ERROR] "--target" takes a target architecture as an argument. Run `[..]` to see possible targets. "#]]) .build(), ); } #[cargo_test] fn update_list_availables() { list_availables_test( "update", SnapshotsBuilder::new() .with_package(str![[r#" [ERROR] "--package " requires a SPEC format value, which can be any package ID specifier in the dependency graph. Run `cargo help pkgid` for more information about SPEC format. Possible packages/workspace members: foo "#]]) .build(), ); } struct ExpectedSnapshots { example: ProjectExpected, bin: ProjectExpected, test: ProjectExpected, bench: ProjectExpected, package: ProjectExpected, target: ProjectExpected, } struct ProjectExpected { full: Option, empty: Option, } struct SnapshotsBuilder { example: ProjectExpected, bin: ProjectExpected, test: ProjectExpected, bench: ProjectExpected, package: ProjectExpected, target: ProjectExpected, } impl SnapshotsBuilder { pub fn new() -> Self { Self { example: ProjectExpected { full: None, empty: None, }, bin: ProjectExpected { full: None, empty: None, }, test: ProjectExpected { full: None, empty: None, }, bench: ProjectExpected { full: None, empty: None, }, package: ProjectExpected { full: None, empty: None, }, target: ProjectExpected { full: None, empty: None, }, } } fn with_example(mut self, full: T, empty: T) -> Self { self.example.full = Some(full); self.example.empty = Some(empty); self } fn with_bin(mut self, full: T, empty: T) -> Self { self.bin.full = Some(full); self.bin.empty = Some(empty); self } fn with_test(mut self, full: T, empty: T) -> Self { self.test.full = Some(full); self.test.empty = Some(empty); self } fn with_bench(mut self, full: T, empty: T) -> Self { self.bench.full = Some(full); self.bench.empty = Some(empty); self } fn with_package(mut self, full: T) -> Self { self.package.full = Some(full); self } fn with_target(mut self, empty: T) -> Self { self.target.empty = Some(empty); self } fn build(self) -> ExpectedSnapshots { ExpectedSnapshots { example: self.example, bin: self.bin, test: self.test, bench: self.bench, package: self.package, target: self.target, } } } cargo-0.91.0/tests/testsuite/local_registry.rs000064400000000000000000000325411046102023000175760ustar 00000000000000//! Tests for local-registry sources. use std::fs; use crate::prelude::*; use cargo_test_support::paths; use cargo_test_support::registry::{Package, registry_path}; use cargo_test_support::{basic_manifest, project, str, t}; fn setup() { let root = paths::root(); t!(fs::create_dir(&root.join(".cargo"))); t!(fs::write( root.join(".cargo/config.toml"), r#" [source.crates-io] registry = 'https://wut' replace-with = 'my-awesome-local-registry' [source.my-awesome-local-registry] local-registry = 'registry' "# )); } #[cargo_test] fn simple() { setup(); Package::new("bar", "0.0.1") .local(true) .file("src/lib.rs", "pub fn bar() {}") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.0.1" "#, ) .file( "src/lib.rs", "extern crate bar; pub fn foo() { bar::bar(); }", ) .build(); p.cargo("build") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [UNPACKING] bar v0.0.1 (registry `[ROOT]/registry`) [COMPILING] bar v0.0.1 [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("test").run(); } #[cargo_test] fn not_found() { setup(); // Publish a package so that the directory hierarchy is created. // Note, however, that we declare a dependency on baZ. Package::new("bar", "0.0.1").local(true).publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] baz = "0.0.1" "#, ) .file( "src/lib.rs", "extern crate baz; pub fn foo() { baz::bar(); }", ) .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no matching package named `baz` found location searched: `[ROOT]/registry` index (which is replacing registry `crates-io`) required by package `foo v0.0.1 ([ROOT]/foo)` "#]]) .run(); } #[cargo_test] fn depend_on_yanked() { setup(); Package::new("bar", "0.0.1").local(true).publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.0.1" "#, ) .file("src/lib.rs", "") .build(); // Run cargo to create lock file. p.cargo("check").run(); registry_path().join("index").join("3").rm_rf(); Package::new("bar", "0.0.1") .local(true) .yanked(true) .publish(); p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn multiple_versions() { setup(); Package::new("bar", "0.0.1").local(true).publish(); Package::new("bar", "0.1.0") .local(true) .file("src/lib.rs", "pub fn bar() {}") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "*" "#, ) .file( "src/lib.rs", "extern crate bar; pub fn foo() { bar::bar(); }", ) .build(); p.cargo("check") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [UNPACKING] bar v0.1.0 (registry `[ROOT]/registry`) [CHECKING] bar v0.1.0 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); Package::new("bar", "0.2.0") .local(true) .file("src/lib.rs", "pub fn bar() {}") .publish(); p.cargo("update") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [UPDATING] bar v0.1.0 -> v0.2.0 "#]]) .run(); } #[cargo_test] fn multiple_names() { setup(); Package::new("bar", "0.0.1") .local(true) .file("src/lib.rs", "pub fn bar() {}") .publish(); Package::new("baz", "0.1.0") .local(true) .file("src/lib.rs", "pub fn baz() {}") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "*" baz = "*" "#, ) .file( "src/lib.rs", r#" extern crate bar; extern crate baz; pub fn foo() { bar::bar(); baz::baz(); } "#, ) .build(); p.cargo("check") .with_stderr_data( str![[r#" [LOCKING] 2 packages to latest compatible versions [UNPACKING] bar v0.0.1 (registry `[ROOT]/registry`) [UNPACKING] baz v0.1.0 (registry `[ROOT]/registry`) [CHECKING] bar v0.0.1 [CHECKING] baz v0.1.0 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn interdependent() { setup(); Package::new("bar", "0.0.1") .local(true) .file("src/lib.rs", "pub fn bar() {}") .publish(); Package::new("baz", "0.1.0") .local(true) .dep("bar", "*") .file("src/lib.rs", "extern crate bar; pub fn baz() {}") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "*" baz = "*" "#, ) .file( "src/lib.rs", r#" extern crate bar; extern crate baz; pub fn foo() { bar::bar(); baz::baz(); } "#, ) .build(); p.cargo("check") .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [UNPACKING] bar v0.0.1 (registry `[ROOT]/registry`) [UNPACKING] baz v0.1.0 (registry `[ROOT]/registry`) [CHECKING] bar v0.0.1 [CHECKING] baz v0.1.0 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn path_dep_rewritten() { setup(); Package::new("bar", "0.0.1") .local(true) .file("src/lib.rs", "pub fn bar() {}") .publish(); Package::new("baz", "0.1.0") .local(true) .dep("bar", "*") .file( "Cargo.toml", r#" [package] name = "baz" version = "0.1.0" edition = "2015" authors = [] [dependencies] bar = { path = "bar", version = "*" } "#, ) .file("src/lib.rs", "extern crate bar; pub fn baz() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "pub fn bar() {}") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "*" baz = "*" "#, ) .file( "src/lib.rs", r#" extern crate bar; extern crate baz; pub fn foo() { bar::bar(); baz::baz(); } "#, ) .build(); p.cargo("check") .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [UNPACKING] bar v0.0.1 (registry `[ROOT]/registry`) [UNPACKING] baz v0.1.0 (registry `[ROOT]/registry`) [CHECKING] bar v0.0.1 [CHECKING] baz v0.1.0 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn invalid_dir_bad() { setup(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "*" "#, ) .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [source.crates-io] registry = 'https://wut' replace-with = 'my-awesome-local-directory' [source.my-awesome-local-directory] local-registry = '/path/to/nowhere' "#, ) .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to get `bar` as a dependency of package `foo v0.0.1 ([ROOT]/foo)` Caused by: failed to load source for dependency `bar` Caused by: Unable to update registry `crates-io` Caused by: failed to update replaced source registry `crates-io` Caused by: local registry path is not a directory: [..]path[..]to[..]nowhere "#]]) .run(); } #[cargo_test] fn different_directory_replacing_the_registry_is_bad() { setup(); // Move our test's .cargo/config to a temporary location and publish a // registry package we're going to use first. let config = paths::root().join(".cargo"); let config_tmp = paths::root().join(".cargo-old"); t!(fs::rename(&config, &config_tmp)); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "*" "#, ) .file("src/lib.rs", "") .build(); // Generate a lock file against the crates.io registry Package::new("bar", "0.0.1").publish(); p.cargo("check").run(); // Switch back to our directory source, and now that we're replacing // crates.io make sure that this fails because we're replacing with a // different checksum config.rm_rf(); t!(fs::rename(&config_tmp, &config)); Package::new("bar", "0.0.1") .file("src/lib.rs", "invalid") .local(true) .publish(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] checksum for `bar v0.0.1` changed between lock files this could be indicative of a few possible errors: * the lock file is corrupt * a replacement source in use (e.g., a mirror) returned a different checksum * the source itself may be corrupt in one way or another unable to verify that `bar v0.0.1` is the same as when the lockfile was generated "#]]) .run(); } #[cargo_test] fn crates_io_registry_url_is_optional() { let root = paths::root(); t!(fs::create_dir(&root.join(".cargo"))); t!(fs::write( root.join(".cargo/config.toml"), r#" [source.crates-io] replace-with = 'my-awesome-local-registry' [source.my-awesome-local-registry] local-registry = 'registry' "# )); Package::new("bar", "0.0.1") .local(true) .file("src/lib.rs", "pub fn bar() {}") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.0.1" "#, ) .file( "src/lib.rs", "extern crate bar; pub fn foo() { bar::bar(); }", ) .build(); p.cargo("build") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [UNPACKING] bar v0.0.1 (registry `[ROOT]/registry`) [COMPILING] bar v0.0.1 [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("test").run(); } cargo-0.91.0/tests/testsuite/locate_project.rs000064400000000000000000000045171046102023000175530ustar 00000000000000//! Tests for the `cargo locate-project` command. use crate::prelude::*; use cargo_test_support::project; use cargo_test_support::str; #[cargo_test] fn simple() { let p = project().build(); p.cargo("locate-project") .with_stdout_data( str![[r#" { "root": "[ROOT]/foo/Cargo.toml" } "#]] .is_json(), ) .run(); } #[cargo_test] fn message_format() { let p = project().build(); p.cargo("locate-project --message-format plain") .with_stdout_data(str![[r#" [ROOT]/foo/Cargo.toml "#]]) .run(); p.cargo("locate-project --message-format json") .with_stdout_data( str![[r#" { "root": "[ROOT]/foo/Cargo.toml" } "#]] .is_json(), ) .run(); p.cargo("locate-project --message-format cryptic") .with_stderr_data(str![[r#" [ERROR] invalid value 'cryptic' for '--message-format ' [possible values: json, plain] For more information, try '--help'. "#]]) .with_status(1) .run(); } #[cargo_test] fn workspace() { let p = project() .file( "Cargo.toml", r#" [package] name = "outer" version = "0.0.0" [workspace] members = ["inner"] "#, ) .file("src/main.rs", "fn main() {}") .file( "inner/Cargo.toml", r#" [package] name = "inner" version = "0.0.0" "#, ) .file("inner/src/lib.rs", "") .build(); p.cargo("locate-project") .with_stdout_data( str![[r#" { "root": "[ROOT]/foo/Cargo.toml" } "#]] .is_json(), ) .run(); p.cargo("locate-project") .cwd("inner") .with_stdout_data( str![[r#" { "root": "[ROOT]/foo/inner/Cargo.toml" } "#]] .is_json(), ) .run(); p.cargo("locate-project --workspace") .with_stdout_data( str![[r#" { "root": "[ROOT]/foo/Cargo.toml" } "#]] .is_json(), ) .run(); p.cargo("locate-project --workspace") .cwd("inner") .with_stdout_data( str![[r#" { "root": "[ROOT]/foo/Cargo.toml" } "#]] .is_json(), ) .run(); } cargo-0.91.0/tests/testsuite/lockfile_compat.rs000064400000000000000000000755731046102023000177230ustar 00000000000000//! Tests for supporting older versions of the Cargo.lock file format. use crate::prelude::*; use cargo_test_support::compare::assert_e2e; use cargo_test_support::git; use cargo_test_support::registry::Package; use cargo_test_support::str; use cargo_test_support::{basic_lib_manifest, basic_manifest, project}; #[cargo_test] fn oldest_lockfile_still_works() { let cargo_commands = vec!["build", "update"]; for cargo_command in cargo_commands { oldest_lockfile_still_works_with_command(cargo_command); } } fn oldest_lockfile_still_works_with_command(cargo_command: &str) { Package::new("bar", "0.1.0").publish(); let expected_lockfile = str![[r##" # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "[..]" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar", ] "##]]; let old_lockfile = r#" [root] name = "foo" version = "0.0.1" dependencies = [ "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" "#; let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .file("Cargo.lock", old_lockfile) .build(); p.cargo(cargo_command).run(); let lock = p.read_lockfile(); assert_e2e().eq(&lock, expected_lockfile); } #[cargo_test] fn frozen_flag_preserves_old_lockfile() { let cksum = Package::new("bar", "0.1.0").publish(); let old_lockfile = format!( r#"[root] name = "foo" version = "0.0.1" dependencies = [ "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "{}" "#, cksum, ); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .file("Cargo.lock", &old_lockfile) .build(); p.cargo("check --locked").run(); let lock = p.read_lockfile(); assert_e2e().eq(&lock, &old_lockfile); } #[cargo_test] fn totally_wild_checksums_works() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .file( "Cargo.lock", r#" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum baz 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "checksum" "checksum bar 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "checksum" "#, ); let p = p.build(); p.cargo("check").run(); let lock = p.read_lockfile(); assert_e2e().eq( &lock, str![[r##" # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "[..]" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar", ] "##]], ); } #[cargo_test] fn wrong_checksum_is_an_error() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .file( "Cargo.lock", r#" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "checksum" "#, ); let p = p.build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] checksum for `bar v0.1.0` changed between lock files this could be indicative of a few possible errors: * the lock file is corrupt * a replacement source in use (e.g., a mirror) returned a different checksum * the source itself may be corrupt in one way or another unable to verify that `bar v0.1.0` is the same as when the lockfile was generated "#]]) .run(); } // If the checksum is unlisted in the lock file (e.g., ) yet we can // calculate it (e.g., it's a registry dep), then we should in theory just fill // it in. #[cargo_test] fn unlisted_checksum_is_bad_if_we_calculate() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .file( "Cargo.lock", r#" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "" "#, ); let p = p.build(); p.cargo("fetch").with_status(101).with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] checksum for `bar v0.1.0` was not previously calculated, but a checksum could now be calculated this could be indicative of a few possible situations: * the source `registry `crates-io`` did not previously support checksums, but was replaced with one that does * newer Cargo implementations know how to checksum this source, but this older implementation does not * the lock file is corrupt "#]]).run(); } // If the checksum is listed in the lock file yet we cannot calculate it (e.g., // Git dependencies as of today), then make sure we choke. #[cargo_test] fn listed_checksum_bad_if_we_cannot_compute() { let git = git::new("bar", |p| { p.file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = {{ git = '{}' }} "#, git.url() ), ) .file("src/lib.rs", "") .file( "Cargo.lock", &format!( r#" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar 0.1.0 (git+{0})" ] [[package]] name = "bar" version = "0.1.0" source = "git+{0}" [metadata] "checksum bar 0.1.0 (git+{0})" = "checksum" "#, git.url() ), ); let p = p.build(); p.cargo("fetch").with_status(101).with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/bar` [ERROR] checksum for `bar v0.1.0 ([ROOTURL]/bar)` could not be calculated, but a checksum is listed in the existing lock file this could be indicative of a few possible situations: * the source `[ROOTURL]/bar` supports checksums, but was replaced with one that doesn't * the lock file is corrupt unable to verify that `bar v0.1.0 ([ROOTURL]/bar)` is the same as when the lockfile was generated "#]]).run(); } #[cargo_test] fn current_lockfile_format() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", ""); let p = p.build(); p.cargo("check").run(); let actual = p.read_lockfile(); let expected = str![[r##" # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "[..]" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar", ] "##]]; assert_e2e().eq(&actual, expected); } #[cargo_test] fn lockfile_without_root() { Package::new("bar", "0.1.0").publish(); let lockfile = r#" # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar", ] "#; let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .file("Cargo.lock", lockfile); let p = p.build(); p.cargo("check").run(); let lock = p.read_lockfile(); assert_e2e().eq( &lock, str![[r##" # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "[..]" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar", ] "##]], ); } #[cargo_test] fn locked_correct_error() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", ""); let p = p.build(); p.cargo("check --locked") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] the lock file [ROOT]/foo/Cargo.lock needs to be updated but --locked was passed to prevent this If you want to try to generate the lock file without accessing the network, remove the --locked flag and use --offline instead. "#]]) .run(); } #[cargo_test] fn v2_format_preserved() { let cksum = Package::new("bar", "0.1.0").publish(); let lockfile = format!( r#"# This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "{}" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar", ] "#, cksum ); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .file("Cargo.lock", &lockfile) .build(); p.cargo("fetch").run(); let lock = p.read_lockfile(); assert_e2e().eq(&lock, &lockfile); } #[cargo_test] fn v2_path_and_crates_io() { let cksum010 = Package::new("a", "0.1.0").publish(); let cksum020 = Package::new("a", "0.2.0").publish(); let lockfile = format!( r#"# This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "a" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "{}" [[package]] name = "a" version = "0.2.0" [[package]] name = "a" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "{}" [[package]] name = "foo" version = "0.0.1" dependencies = [ "a 0.1.0", "a 0.2.0", "a 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] "#, cksum010, cksum020, ); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] a = { path = 'a' } b = { version = "0.1", package = 'a' } c = { version = "0.2", package = 'a' } "#, ) .file("src/lib.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.2.0" edition = "2015" "#, ) .file("a/src/lib.rs", "") .file("Cargo.lock", &lockfile) .build(); p.cargo("fetch").run(); p.cargo("fetch").run(); let lock = p.read_lockfile(); assert_e2e().eq(&lock, &lockfile); } #[cargo_test] fn v3_and_git() { let (git_project, repo) = git::new_repo("dep1", |project| { project .file("Cargo.toml", &basic_lib_manifest("dep1")) .file("src/lib.rs", "") }); let head_id = repo.head().unwrap().target().unwrap(); let lockfile = format!( r#"# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "dep1" version = "0.5.0" source = "git+[ROOTURL]/dep1?branch=master#{}" [[package]] name = "foo" version = "0.0.1" dependencies = [ "dep1", ] "#, head_id, ); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" rust-version = "1.81" # ensure it stays in lockfile v3 [dependencies] dep1 = {{ git = '{}', branch = 'master' }} "#, git_project.url(), ), ) .file("src/lib.rs", "") .file("Cargo.lock", "version = 3") .build(); p.cargo("fetch").run(); let lock = p.read_lockfile(); assert_e2e().eq(&lock, &lockfile); } #[cargo_test] fn lock_from_the_future() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] "#, ) .file("src/lib.rs", "") .file("Cargo.lock", "version = 10000000") .build(); p.cargo("fetch").with_stderr_data(str![[r#" [ERROR] failed to parse lock file at: [ROOT]/foo/Cargo.lock Caused by: lock file version `10000000` was found, but this version of Cargo does not understand this lock file, perhaps Cargo needs to be updated? "#]]).with_status(101).run(); } #[cargo_test] fn preserve_old_format_if_no_update_needed() { let cksum = Package::new("bar", "0.1.0").publish(); let lockfile = format!( r#"# This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [metadata] "checksum bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "{}" "#, cksum ); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .file("Cargo.lock", &lockfile) .build(); p.cargo("check --locked").run(); } #[cargo_test] fn same_name_version_different_sources() { let cksum = Package::new("foo", "0.1.0").publish(); let (git_project, repo) = git::new_repo("dep1", |project| { project .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" "#, ) .file("src/lib.rs", "") }); let head_id = repo.head().unwrap().target().unwrap(); // Lockfile was generated with Rust 1.51 let lockfile = format!( r#"# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "foo" version = "0.1.0" dependencies = [ "foo 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "foo 0.1.0 (git+{url})", ] [[package]] name = "foo" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "{cksum}" [[package]] name = "foo" version = "0.1.0" source = "git+{url}#{sha}" "#, sha = head_id, url = git_project.url(), cksum = cksum ); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] foo = "0.1.0" foo2 = {{ git = '{}', package = 'foo' }} "#, git_project.url(), ), ) .file("src/lib.rs", "") .file("Cargo.lock", &lockfile) .build(); p.cargo("check").run(); assert_eq!(p.read_file("Cargo.lock"), lockfile); } #[cargo_test] fn bad_data_in_lockfile_error_meg() { Package::new("bar", "0.0.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "test" version = "0.0.0" edition = "2015" [dependencies] bar = "*" "#, ) .file("src/main.rs", "fn main() {}") .file( "Cargo.lock", r#"# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e1b9346248cf3391ead604c4407258d327c28e37209f6d56127598165165dda" [[package]] name = "test" version = "0.0.0" dependencies = [ "bar", ]"#, ) .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for the requirement `bar = "*"` (locked to 0.1.0) candidate versions found which didn't match: 0.0.1 location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `test v0.0.0 ([ROOT]/foo)` perhaps a crate was updated and forgotten to be re-vendored? "#]]) .run(); } #[cargo_test] fn next_version_is_always_unstable() { let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" "#, ), ) .file("src/lib.rs", "") .file("Cargo.lock", "version = 5") .build(); p.cargo("fetch").with_status(101).with_stderr_data(str![[r#" [ERROR] failed to parse lock file at: [ROOT]/foo/Cargo.lock Caused by: lock file version `5` was found, but this version of Cargo does not understand this lock file, perhaps Cargo needs to be updated? "#]]).run(); // On nightly, let the user know about the `-Z` flag. p.cargo("fetch") .masquerade_as_nightly_cargo(&["-Znext-lockfile-bump"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse lock file at: [ROOT]/foo/Cargo.lock Caused by: lock file version `5` requires `-Znext-lockfile-bump` "#]]) .run(); } fn create_branch(repo: &git2::Repository, branch: &str, head_id: git2::Oid) { repo.branch(branch, &repo.find_commit(head_id).unwrap(), true) .unwrap(); } fn create_tag(repo: &git2::Repository, tag: &str, head_id: git2::Oid) { repo.tag( tag, &repo.find_object(head_id, None).unwrap(), &repo.signature().unwrap(), "make a new tag", false, ) .unwrap(); } fn v3_and_git_url_encoded(ref_kind: &str, f: impl FnOnce(&git2::Repository, &str, git2::Oid)) { let (git_project, repo) = git::new_repo("dep1", |project| { project .file("Cargo.toml", &basic_lib_manifest("dep1")) .file("src/lib.rs", "") }); let url = git_project.url(); let head_id = repo.head().unwrap().target().unwrap(); // Ref name with special characters let git_ref = "a-_+#$)"; let encoded_ref = "a-_%2B%23%24%29"; f(&repo, git_ref, head_id); let lockfile = format!( r#"# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "dep1" version = "0.5.0" source = "git+[ROOTURL]/dep1?{ref_kind}={git_ref}#{head_id}" [[package]] name = "foo" version = "0.0.1" dependencies = [ "dep1", ] "#, ); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" rust-version = "1.81" # ensure it stays in lockfile v3 [dependencies] dep1 = {{ git = '{url}', {ref_kind} = '{git_ref}' }} "#, ), ) .file("src/lib.rs", "") .file("Cargo.lock", "version = 3") .build(); p.cargo("check") .with_stderr_data(format!( "\ [UPDATING] git repository `[ROOTURL]/dep1` [LOCKING] 1 package to latest compatible version [ADDING] dep1 v0.5.0 ([ROOTURL]/dep1?{ref_kind}={encoded_ref}#[..]) [CHECKING] dep1 v0.5.0 ([ROOTURL]/dep1?{ref_kind}={encoded_ref}#[..]) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s " )) .run(); let lock = p.read_lockfile(); assert_e2e().eq(&lock, &lockfile); // v3 doesn't URL-encode URL parameters, but `url` crate does decode as it // was URL-encoded. Therefore Cargo thinks they are from different source // and clones the repository again. p.cargo("check") .with_stderr_data(format!( "\ [UPDATING] git repository `[ROOTURL]/dep1` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s " )) .run(); } #[cargo_test] fn v3_and_git_url_encoded_branch() { v3_and_git_url_encoded("branch", create_branch); } #[cargo_test] fn v3_and_git_url_encoded_tag() { v3_and_git_url_encoded("tag", create_tag); } #[cargo_test] fn v3_and_git_url_encoded_rev() { v3_and_git_url_encoded("rev", create_tag); } fn v4_and_git_url_encoded(ref_kind: &str, f: impl FnOnce(&git2::Repository, &str, git2::Oid)) { let (git_project, repo) = git::new_repo("dep1", |project| { project .file("Cargo.toml", &basic_lib_manifest("dep1")) .file("src/lib.rs", "") }); let url = git_project.url(); let head_id = repo.head().unwrap().target().unwrap(); // Ref name with special characters let git_ref = "a-_+#$)"; let encoded_ref = "a-_%2B%23%24%29"; f(&repo, git_ref, head_id); let lockfile = format!( r#"# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "dep1" version = "0.5.0" source = "git+[ROOTURL]/dep1?{ref_kind}={encoded_ref}#{head_id}" [[package]] name = "foo" version = "0.0.1" dependencies = [ "dep1", ] "#, ); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] dep1 = {{ git = '{url}', {ref_kind} = '{git_ref}' }} "#, ), ) .file("src/lib.rs", "") .file("Cargo.lock", "version = 4") .build(); p.cargo("check") .with_stderr_data(format!( "\ [UPDATING] git repository `[ROOTURL]/dep1` [LOCKING] 1 package to latest compatible version [ADDING] dep1 v0.5.0 ([ROOTURL]/dep1?{ref_kind}={encoded_ref}#[..]) [CHECKING] dep1 v0.5.0 ([ROOTURL]/dep1?{ref_kind}={encoded_ref}#[..]) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s " )) .run(); let lock = p.read_lockfile(); assert_e2e().eq(&lock, &lockfile); // Unlike v3_and_git_url_encoded, v4 encodes URL parameters so no git // repository re-clone happen. p.cargo("check") .with_stderr_data( "\ [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s ", ) .run(); } #[cargo_test] fn v4_and_git_url_encoded_branch() { v4_and_git_url_encoded("branch", create_branch); } #[cargo_test] fn v4_and_git_url_encoded_tag() { v4_and_git_url_encoded("tag", create_tag); } #[cargo_test] fn v4_and_git_url_encoded_rev() { v4_and_git_url_encoded("rev", create_tag) } #[cargo_test] fn with_msrv() { let cksum = Package::new("bar", "0.1.0").publish(); let v3_lockfile = format!( r#"# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "{cksum}" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar", ] "# ); let v2_lockfile = format!( r#"# This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "{cksum}" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar", ] "# ); let v1_lockfile = format!( r#"# This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "foo" version = "0.0.1" dependencies = [ "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [metadata] "checksum bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "{cksum}" "# ); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .build(); let cases = [ // v1 is the default ("1.37", None, 1), ("1.37", Some(1), 1), ("1.37", Some(2), 2), ("1.37", Some(3), 3), ("1.37", Some(4), 4), // v2 introduced ("1.38", None, 1), // last version of v1 as the default ("1.40", None, 1), // v2 is the default ("1.41", None, 2), ("1.41", Some(1), 1), ("1.41", Some(2), 2), ("1.41", Some(3), 3), ("1.41", Some(4), 4), // v3 introduced ("1.47", None, 2), // last version of v2 as the default ("1.48", None, 2), // v3 is the default ("1.53", None, 3), ("1.53", Some(1), 1), ("1.53", Some(2), 2), ("1.53", Some(3), 3), ("1.53", Some(4), 4), // v4 introduced ("1.78", None, 3), // last version of v3 as the default ("1.82", None, 3), // v4 is the default ("1.83", None, 4), ("1.83", Some(1), 1), ("1.83", Some(2), 2), ("1.83", Some(3), 3), ("1.83", Some(4), 4), ]; for (msrv, existing_lockfile, expected_version) in cases { // Clean previous lockfile. _ = std::fs::remove_file(p.root().join("Cargo.lock")); p.change_file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" rust-version = "{msrv}" [dependencies] bar = "0.1.0" "#, ), ); if let Some(existing_lockfile) = existing_lockfile { let existing_lockfile = match existing_lockfile { 1 => v1_lockfile.as_str().into(), 2 => v2_lockfile.as_str().into(), 3 => v3_lockfile.as_str().into(), v => std::borrow::Cow::from(format!("version = {v}")), }; p.change_file("Cargo.lock", &existing_lockfile); } p.cargo("fetch").run(); let lock = p.read_lockfile(); let toml = lock.parse::().unwrap(); // get `version = ` from Cargo.lock let version_field = toml.get("version").and_then(|v| v.as_integer()); let actual_version = if let Some(ver) = version_field { ver } else if lock.find("\nchecksum = ").is_some() { 2 } else { 1 }; assert_eq!( expected_version, actual_version, "msrv: {msrv}, existing lockfile: {existing_lockfile:?}" ); } } cargo-0.91.0/tests/testsuite/lockfile_path.rs000064400000000000000000000344361046102023000173650ustar 00000000000000//! Tests for `lockfile-path` flag use std::fs; use snapbox::str; use crate::prelude::*; use crate::utils::cargo_process; use cargo_test_support::compare::assert_e2e; use cargo_test_support::install::assert_has_installed_exe; use cargo_test_support::registry::{Package, RegistryBuilder}; use cargo_test_support::{ ProjectBuilder, basic_bin_manifest, cargo_test, paths, project, symlink_supported, }; /////////////////////////////// //// Unstable feature tests start /////////////////////////////// #[cargo_test] fn must_have_unstable_options() { let lockfile_path = "mylockfile/is/burried/Cargo.lock"; let p = make_project().build(); p.cargo("generate-lockfile") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("--lockfile-path") .arg(lockfile_path) .with_stderr_data(str![[ r#"[ERROR] the `--lockfile-path` flag is unstable, pass `-Z unstable-options` to enable it See https://github.com/rust-lang/cargo/issues/14421 for more information about the `--lockfile-path` flag. "#]]) .with_status(101) .run(); } #[cargo_test] fn must_be_nightly() { let lockfile_path = "mylockfile/is/burried/Cargo.lock"; let p = make_project().build(); p.cargo("generate-lockfile") .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path) .with_stderr_data(str![[ r#"[ERROR] the `-Z` flag is only accepted on the nightly channel of Cargo, but this is the `stable` channel See https://doc.rust-lang.org/book/appendix-07-nightly-rust.html for more information about Rust release channels. "#]]) .with_status(101) .run(); } /////////////////////////////// //// Unstable feature tests end /////////////////////////////// #[cargo_test] fn basic_lockfile_created() { let lockfile_path = "mylockfile/is/burried/Cargo.lock"; let p = make_project().build(); p.cargo("generate-lockfile") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path) .run(); assert!(!p.root().join("Cargo.lock").exists()); assert!(p.root().join(lockfile_path).is_file()); } #[cargo_test] fn basic_lockfile_read() { let lockfile_path = "mylockfile/Cargo.lock"; let p = make_project().file(lockfile_path, VALID_LOCKFILE).build(); p.cargo("generate-lockfile") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path) .run(); assert!(!p.root().join("Cargo.lock").exists()); assert!(p.root().join(lockfile_path).is_file()); } #[cargo_test] fn basic_lockfile_override() { let lockfile_path = "mylockfile/Cargo.lock"; let p = make_project() .file("Cargo.lock", "This is an invalid lock file!") .build(); p.cargo("generate-lockfile") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path) .run(); assert!(p.root().join(lockfile_path).is_file()); } ////////////////////// ///// Symlink tests ////////////////////// #[cargo_test] fn symlink_in_path() { if !symlink_supported() { return; } let dst = "dst"; let src = "somedir/link"; let lockfile_path = format!("{src}/Cargo.lock"); let p = make_project().symlink_dir(dst, src).build(); fs::create_dir(p.root().join("dst")).unwrap(); assert!(p.root().join(src).is_dir()); p.cargo("generate-lockfile") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path.as_str()) .run(); assert!(p.root().join(lockfile_path).is_file()); assert!(p.root().join(dst).join("Cargo.lock").is_file()); } #[cargo_test] fn symlink_lockfile() { if !symlink_supported() { return; } let lockfile_path = "dst/Cargo.lock"; let src = "somedir/link"; let lock_body = VALID_LOCKFILE; let p = make_project() .file(lockfile_path, lock_body) .symlink(lockfile_path, src) .build(); assert!(p.root().join(src).is_file()); p.cargo("generate-lockfile") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path) .run(); assert!(!p.root().join("Cargo.lock").exists()); } #[cargo_test] fn broken_symlink() { if !symlink_supported() { return; } let invalid_dst = "invalid_path"; let src = "somedir/link"; let lockfile_path = format!("{src}/Cargo.lock"); let p = make_project().symlink_dir(invalid_dst, src).build(); assert!(!p.root().join(src).is_dir()); p.cargo("generate-lockfile") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path) .with_status(101) .with_stderr_data(str![[ r#"[ERROR] failed to create directory `[ROOT]/foo/somedir/link` ... "# ]]) .run(); } #[cargo_test] fn loop_symlink() { if !symlink_supported() { return; } let loop_link = "loop"; let src = "somedir/link"; let lockfile_path = format!("{src}/Cargo.lock"); let p = make_project() .symlink_dir(loop_link, src) .symlink_dir(src, loop_link) .build(); assert!(!p.root().join(src).is_dir()); p.cargo("generate-lockfile") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path) .with_status(101) .with_stderr_data(str![[ r#"[ERROR] failed to create directory `[ROOT]/foo/somedir/link` ... "# ]]) .run(); } ///////////////////////// //// Commands tests ///////////////////////// #[cargo_test] fn add_lockfile_override() { let lockfile_path = "mylockfile/Cargo.lock"; project() .at("bar") .file("Cargo.toml", LIB_TOML) .file("src/main.rs", "fn main() {}") .build(); let p = make_project() .file("Cargo.lock", "This is an invalid lock file!") .build(); p.cargo("add") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path) .arg("--path") .arg("../bar") .run(); assert!(p.root().join(lockfile_path).is_file()); } #[cargo_test] fn clean_lockfile_override() { let lockfile_path = "mylockfile/Cargo.lock"; let p = make_project() .file("Cargo.lock", "This is an invalid lock file!") .build(); p.cargo("clean") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path) .arg("--package") .arg("test_foo") .run(); assert!(p.root().join(lockfile_path).is_file()); } #[cargo_test] fn fix_lockfile_override() { let lockfile_path = "mylockfile/Cargo.lock"; let p = make_project() .file("Cargo.lock", "This is an invalid lock file!") .build(); p.cargo("fix") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path) .arg("--package") .arg("test_foo") .arg("--allow-no-vcs") .run(); assert!(p.root().join(lockfile_path).is_file()); } #[cargo_test] fn publish_lockfile_read() { let lockfile_path = "mylockfile/Cargo.lock"; let p = make_project().file(lockfile_path, VALID_LOCKFILE).build(); let registry = RegistryBuilder::new().http_api().http_index().build(); p.cargo("publish") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path) .replace_crates_io(registry.index_url()) .run(); assert!(!p.root().join("Cargo.lock").exists()); assert!(p.root().join(lockfile_path).is_file()); } #[cargo_test] fn remove_lockfile_override() { let lockfile_path = "mylockfile/Cargo.lock"; let manifest = r#" [package] name = "foo" version = "0.5.0" authors = ["wycats@example.com"] edition = "2015" [[bin]] name = "foo" [dependencies] test_bar = { version = "0.1.0", path = "../bar" } "#; project() .at("bar") .file("Cargo.toml", LIB_TOML) .file("src/main.rs", "fn main() {}") .build(); let p = project() .file("Cargo.toml", &manifest) .file("src/main.rs", "fn main() {}") .file("Cargo.lock", "This is an invalid lock file!") .build(); p.cargo("remove") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path) .arg("test_bar") .run(); assert!(p.root().join(lockfile_path).is_file()); } #[cargo_test] fn assert_respect_pinned_version_from_lockfile_path() { let lockfile_path = "mylockfile/Cargo.lock"; let p = project() .file( "Cargo.toml", r#"# [package] name = "test_foo" version = "0.5.0" authors = ["wycats@example.com"] edition = "2015" [[bin]] name = "test_foo" [dependencies] bar = "0.1.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("bar", "0.1.0").publish(); p.cargo("generate-lockfile") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path) .run(); assert!(!p.root().join("Cargo.lock").exists()); assert!(p.root().join(lockfile_path).is_file()); let lockfile_original = fs::read_to_string(p.root().join(lockfile_path)).unwrap(); Package::new("bar", "0.1.1").publish(); p.cargo("package") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("--lockfile-path") .arg(lockfile_path) .run(); assert!( p.root() .join("target/package/test_foo-0.5.0/Cargo.lock") .is_file() ); let path = p.root().join("target/package/test_foo-0.5.0/Cargo.lock"); let contents = fs::read_to_string(path).unwrap(); assert_e2e().eq(contents, lockfile_original); } #[cargo_test] fn install_respects_lock_file_path() { // `cargo install` will imply --locked when lockfile path is provided Package::new("bar", "0.1.0").publish(); Package::new("bar", "0.1.1") .file("src/lib.rs", "not rust") .publish(); // Publish with lockfile containing bad version of `bar` (0.1.1) Package::new("foo", "0.1.0") .dep("bar", "0.1") .file("src/lib.rs", "") .file( "src/main.rs", "extern crate foo; extern crate bar; fn main() {}", ) .file( "Cargo.lock", r#" [[package]] name = "bar" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "foo" version = "0.1.0" dependencies = [ "bar 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] "#, ) .publish(); cargo_process("install foo --locked") .with_stderr_data(str![[r#" ... [..]not rust[..] ... "#]]) .with_status(101) .run(); // Create lockfile with the good `bar` version (0.1.0) and use it for install project() .file( "Cargo.lock", r#" [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "foo" version = "0.1.0" dependencies = [ "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] "#, ) .build(); cargo_process("install foo -Zunstable-options --lockfile-path foo/Cargo.lock") .masquerade_as_nightly_cargo(&["lockfile-path"]) .run(); assert!(paths::root().join("foo/Cargo.lock").is_file()); assert_has_installed_exe(paths::cargo_home(), "foo"); } #[cargo_test] fn install_lock_file_path_must_present() { // `cargo install` will imply --locked when lockfile path is provided Package::new("bar", "0.1.0").publish(); Package::new("foo", "0.1.0") .dep("bar", "0.1") .file("src/lib.rs", "") .file( "src/main.rs", "extern crate foo; extern crate bar; fn main() {}", ) .publish(); cargo_process("install foo -Zunstable-options --lockfile-path lockfile_dir/Cargo.lock") .masquerade_as_nightly_cargo(&["lockfile-path"]) .with_stderr_data(str![[r#" ... [ERROR] no Cargo.lock file found in the requested path [ROOT]/lockfile_dir/Cargo.lock ... "#]]) .with_status(101) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn run_embed() { let lockfile_path = "mylockfile/Cargo.lock"; let invalid_lockfile = "Cargo.lock"; let p = project() .file("src/main.rs", "fn main() {}") .file("Cargo.lock", "This is an invalid lock file!") .build(); p.cargo("run") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("-Zscript") .arg("--lockfile-path") .arg(lockfile_path) .arg("--manifest-path") .arg("src/main.rs") .run(); assert!(p.root().join(lockfile_path).is_file()); p.cargo("run") .masquerade_as_nightly_cargo(&["lockfile-path"]) .arg("-Zunstable-options") .arg("-Zscript") .arg("--lockfile-path") .arg(invalid_lockfile) .arg("--manifest-path") .arg("src/main.rs") .with_status(101) .with_stderr_data(str![[ r#"[WARNING] `package.edition` is unspecified, defaulting to `2024` [ERROR] failed to parse lock file at: [ROOT]/foo/Cargo.lock ... "# ]]) .run(); } const VALID_LOCKFILE: &str = r#"# Test lockfile version = 4 [[package]] name = "test_foo" version = "0.5.0" "#; const LIB_TOML: &str = r#" [package] name = "test_bar" version = "0.1.0" edition = "2021" "#; fn make_project() -> ProjectBuilder { project() .file("Cargo.toml", &basic_bin_manifest("test_foo")) .file("src/main.rs", "fn main() {}") } cargo-0.91.0/tests/testsuite/login.rs000064400000000000000000000276111046102023000156660ustar 00000000000000//! Tests for the `cargo login` command. use std::fs; use std::path::PathBuf; use crate::prelude::*; use crate::utils::cargo_process; use cargo_test_support::paths; use cargo_test_support::registry::{self, RegistryBuilder}; use cargo_test_support::str; use cargo_test_support::t; const TOKEN: &str = "test-token"; const TOKEN2: &str = "test-token2"; const ORIGINAL_TOKEN: &str = "api-token"; fn credentials_toml() -> PathBuf { paths::home().join(".cargo/credentials.toml") } fn setup_new_credentials() { setup_new_credentials_at(credentials_toml()); } fn setup_new_credentials_at(config: PathBuf) { t!(fs::create_dir_all(config.parent().unwrap())); t!(fs::write( &config, format!(r#"token = "{token}""#, token = ORIGINAL_TOKEN) )); } /// Asserts whether or not the token is set to the given value for the given registry. pub fn check_token(expected_token: Option<&str>, registry: Option<&str>) { let credentials = credentials_toml(); assert!(credentials.is_file()); let contents = fs::read_to_string(&credentials).unwrap(); let toml: toml::Table = contents.parse().unwrap(); let actual_token = match registry { // A registry has been provided, so check that the token exists in a // table for the registry. Some(registry) => toml .get("registries") .and_then(|registries_table| registries_table.get(registry)) .and_then(|registry_table| match registry_table.get("token") { Some(&toml::Value::String(ref token)) => Some(token.as_str().to_string()), _ => None, }), // There is no registry provided, so check the global token instead. None => toml .get("registry") .and_then(|registry_table| registry_table.get("token")) .and_then(|v| match v { toml::Value::String(token) => Some(token.as_str().to_string()), _ => None, }), }; match (actual_token, expected_token) { (None, None) => {} (Some(actual), Some(expected)) => assert_eq!(actual, expected), (None, Some(expected)) => { panic!("expected `{registry:?}` to be `{expected}`, but was not set") } (Some(actual), None) => { panic!("expected `{registry:?}` to be unset, but was set to `{actual}`") } } } #[cargo_test] fn registry_credentials() { let _alternative = RegistryBuilder::new().alternative().build(); let _alternative2 = RegistryBuilder::new() .alternative_named("alternative2") .build(); setup_new_credentials(); let reg = "alternative"; cargo_process("login --registry") .arg(reg) .with_stdin(TOKEN) .run(); // Ensure that we have not updated the default token check_token(Some(ORIGINAL_TOKEN), None); // Also ensure that we get the new token for the registry check_token(Some(TOKEN), Some(reg)); let reg2 = "alternative2"; cargo_process("login --registry") .arg(reg2) .with_stdin(TOKEN2) .run(); // Ensure not overwriting 1st alternate registry token with // 2nd alternate registry token (see rust-lang/cargo#7701). check_token(Some(ORIGINAL_TOKEN), None); check_token(Some(TOKEN), Some(reg)); check_token(Some(TOKEN2), Some(reg2)); } #[cargo_test] fn empty_login_token() { let registry = RegistryBuilder::new() .no_configure_registry() .no_configure_token() .build(); setup_new_credentials(); cargo_process("login") .replace_crates_io(registry.index_url()) .with_stdin("\t\n") .with_stderr_data(str![[r#" [UPDATING] crates.io index please paste the token found on [ROOTURL]/api/me below [ERROR] credential provider `cargo:token` failed action `login` Caused by: please provide a non-empty token "#]]) .with_status(101) .run(); cargo_process("login") .replace_crates_io(registry.index_url()) .with_stdin("") .with_stderr_data(str![[r#" please paste the token found on [ROOTURL]/api/me below [ERROR] credential provider `cargo:token` failed action `login` Caused by: please provide a non-empty token "#]]) .with_status(101) .run(); cargo_process("login") .replace_crates_io(registry.index_url()) .arg("") .with_stdin("") .with_stderr_data(str![[r#" [WARNING] `cargo login ` is deprecated in favor of reading `` from stdin [ERROR] credential provider `cargo:token` failed action `login` Caused by: please provide a non-empty token "#]]) .with_status(101) .run(); } #[cargo_test] fn invalid_login_token() { let registry = RegistryBuilder::new() .no_configure_registry() .no_configure_token() .build(); setup_new_credentials(); let check = |stdin: &str, stderr: &str, status: i32| { cargo_process("login") .replace_crates_io(registry.index_url()) .with_stdin(stdin) .with_stderr_data(stderr) .with_status(status) .run(); }; let invalid = |stdin: &str| { check( stdin, "[ERROR] credential provider `cargo:token` failed action `login` Caused by: token contains invalid characters. Only printable ISO-8859-1 characters are allowed as it is sent in a HTTPS header. ", 101, ) }; let valid = |stdin: &str| { check( stdin, "\ [LOGIN] token for `crates-io` saved ", 0, ) }; // Update config.json so that the rest of the tests don't need to care // whether or not `Updating` is printed. check( "test", "\ [UPDATING] crates.io index [LOGIN] token for `crates-io` saved ", 0, ); invalid("😄"); invalid("\u{0016}"); invalid("\u{0000}"); invalid("你好"); valid("foo\tbar"); valid("foo bar"); valid( r##"!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"##, ); } #[cargo_test] fn bad_asymmetric_token_args() { let registry = RegistryBuilder::new() .credential_provider(&["cargo:paseto"]) .no_configure_token() .build(); // These cases are kept brief as the implementation is covered by clap, so this is only smoke testing that we have clap configured correctly. cargo_process("login -Zasymmetric-token -- --key-subject") .masquerade_as_nightly_cargo(&["asymmetric-token"]) .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [ERROR] credential provider `cargo:paseto --key-subject` failed action `login` Caused by: [ERROR] a value is required for '--key-subject ' but none was supplied For more information, try '--help'. "#]]) .with_status(101) .run(); } #[cargo_test] fn login_with_no_cargo_dir() { // Create a config in the root directory because `login` requires the // index to be updated, and we don't want to hit crates.io. let registry = registry::init(); fs::rename(paths::home().join(".cargo"), paths::root().join(".cargo")).unwrap(); paths::home().rm_rf(); cargo_process("login foo -v") .replace_crates_io(registry.index_url()) .run(); let credentials = fs::read_to_string(credentials_toml()).unwrap(); assert_eq!(credentials, "[registry]\ntoken = \"foo\"\n"); } #[cargo_test] fn login_with_asymmetric_token_and_subject_on_stdin() { let registry = RegistryBuilder::new() .credential_provider(&["cargo:paseto"]) .no_configure_token() .build(); let credentials = credentials_toml(); cargo_process("login -v -Z asymmetric-token -- --key-subject=foo") .masquerade_as_nightly_cargo(&["asymmetric-token"]) .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [CREDENTIAL] cargo:paseto --key-subject=foo login crates-io k3.public.AmDwjlyf8jAV3gm5Z7Kz9xAOcsKslt_Vwp5v-emjFzBHLCtcANzTaVEghTNEMj9PkQ "#]]) .with_stdin("k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36") .run(); let credentials = fs::read_to_string(&credentials).unwrap(); assert!(credentials.starts_with("[registry]\n")); assert!(credentials.contains("secret-key-subject = \"foo\"\n")); assert!(credentials.contains("secret-key = \"k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36\"\n")); } #[cargo_test] fn login_with_differently_sized_token() { // Verify that the configuration file gets properly truncated. let registry = registry::init(); let credentials = credentials_toml(); fs::remove_file(&credentials).unwrap(); cargo_process("login lmaolmaolmao -v") .replace_crates_io(registry.index_url()) .run(); cargo_process("login lmao -v") .replace_crates_io(registry.index_url()) .run(); cargo_process("login lmaolmaolmao -v") .replace_crates_io(registry.index_url()) .run(); let credentials = fs::read_to_string(&credentials).unwrap(); assert_eq!(credentials, "[registry]\ntoken = \"lmaolmaolmao\"\n"); } #[cargo_test] fn login_with_token_on_stdin() { let registry = registry::init(); let credentials = credentials_toml(); fs::remove_file(&credentials).unwrap(); cargo_process("login lmao -v") .replace_crates_io(registry.index_url()) .run(); cargo_process("login") .replace_crates_io(registry.index_url()) .with_stdin("some token") .run(); let credentials = fs::read_to_string(&credentials).unwrap(); assert_eq!(credentials, "[registry]\ntoken = \"some token\"\n"); } #[cargo_test] fn login_with_asymmetric_token_on_stdin() { let _registry = RegistryBuilder::new() .credential_provider(&["cargo:paseto"]) .alternative() .no_configure_token() .build(); let credentials = credentials_toml(); cargo_process("login -v -Z asymmetric-token --registry alternative") .masquerade_as_nightly_cargo(&["asymmetric-token"]) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [CREDENTIAL] cargo:paseto login alternative k3.public.AmDwjlyf8jAV3gm5Z7Kz9xAOcsKslt_Vwp5v-emjFzBHLCtcANzTaVEghTNEMj9PkQ "#]]) .with_stdin("k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36") .run(); let credentials = fs::read_to_string(&credentials).unwrap(); assert_eq!( credentials, "[registries.alternative]\nsecret-key = \"k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36\"\n" ); } #[cargo_test] fn login_with_generate_asymmetric_token() { let _registry = RegistryBuilder::new() .credential_provider(&["cargo:paseto"]) .alternative() .no_configure_token() .build(); let credentials = credentials_toml(); cargo_process("login -Z asymmetric-token --registry alternative") .masquerade_as_nightly_cargo(&["asymmetric-token"]) .with_stderr_data(str![[r#" [UPDATING] `alternative` index k3.public.[..] "#]]) .run(); let credentials = fs::read_to_string(&credentials).unwrap(); assert!(credentials.contains("secret-key = \"k3.secret.")); } #[cargo_test] fn default_registry_configured() { // When registry.default is set, login should use that one when // --registry is not used. let _alternative = RegistryBuilder::new().alternative().build(); let cargo_home = paths::home().join(".cargo"); cargo_util::paths::append( &cargo_home.join("config.toml"), br#" [registry] default = "alternative" "#, ) .unwrap(); cargo_process("login") .with_stdin("a-new-token") .with_stderr_data(str![[r#" [UPDATING] `alternative` index [LOGIN] token for `alternative` saved "#]]) .run(); check_token(None, None); check_token(Some("a-new-token"), Some("alternative")); } cargo-0.91.0/tests/testsuite/logout.rs000064400000000000000000000072511046102023000160650ustar 00000000000000//! Tests for the `cargo logout` command. use super::login::check_token; use crate::prelude::*; use crate::utils::cargo_process; use cargo_test_support::paths; use cargo_test_support::registry::TestRegistry; use cargo_test_support::{registry, str}; fn simple_logout_test(registry: &TestRegistry, reg: Option<&str>, flag: &str, note: &str) { let msg = reg.unwrap_or("crates-io"); check_token(Some(registry.token()), reg); let mut cargo = cargo_process(&format!("logout {}", flag)); if reg.is_none() { cargo.replace_crates_io(registry.index_url()); } cargo .with_stderr_data(&format!( "\ [LOGOUT] token for `{msg}` has been removed from local storage [NOTE] This does not revoke the token on the registry server. If you need to revoke the token, visit {note} and follow the instructions there. " )) .run(); check_token(None, reg); let mut cargo = cargo_process(&format!("logout {}", flag)); if reg.is_none() { cargo.replace_crates_io(registry.index_url()); } cargo .with_stderr_data(&format!( "\ [LOGOUT] not currently logged in to `{msg}` " )) .run(); check_token(None, reg); } #[cargo_test] fn default_registry_unconfigured() { let registry = registry::init(); simple_logout_test(®istry, None, "", ""); } #[cargo_test] fn other_registry() { let registry = registry::alt_init(); simple_logout_test( ®istry, Some("alternative"), "--registry alternative", "the `alternative` website", ); // It should not touch crates.io. check_token(Some("sekrit"), None); } #[cargo_test] fn default_registry_configured() { // When registry.default is set, logout should use that one when // --registry is not used. let cargo_home = paths::home().join(".cargo"); cargo_home.mkdir_p(); cargo_util::paths::write( &cargo_home.join("config.toml"), r#" [registry] default = "dummy-registry" [registries.dummy-registry] index = "https://127.0.0.1/index" "#, ) .unwrap(); cargo_util::paths::write( &cargo_home.join("credentials.toml"), r#" [registry] token = "crates-io-token" [registries.dummy-registry] token = "dummy-token" "#, ) .unwrap(); check_token(Some("dummy-token"), Some("dummy-registry")); check_token(Some("crates-io-token"), None); cargo_process("logout").with_stderr_data(str![[r#" [LOGOUT] token for `dummy-registry` has been removed from local storage [NOTE] This does not revoke the token on the registry server. If you need to revoke the token, visit the `dummy-registry` website and follow the instructions there. "#]]).run(); check_token(None, Some("dummy-registry")); check_token(Some("crates-io-token"), None); cargo_process("logout") .with_stderr_data(str![[r#" [LOGOUT] not currently logged in to `dummy-registry` "#]]) .run(); } #[cargo_test] fn logout_asymmetric() { let _registry = registry::RegistryBuilder::new() .token(cargo_test_support::registry::Token::rfc_key()) .build(); cargo_process("logout --registry crates-io -Zasymmetric-token") .masquerade_as_nightly_cargo(&["asymmetric-token"]) .with_stderr_data(str![[r#" [LOGOUT] secret-key for `crates-io` has been removed from local storage "#]]) .run(); cargo_process("logout --registry crates-io -Zasymmetric-token") .masquerade_as_nightly_cargo(&["asymmetric-token"]) .with_stderr_data(str![[r#" [LOGOUT] not currently logged in to `crates-io` "#]]) .run(); } cargo-0.91.0/tests/testsuite/lto.rs000064400000000000000000000703131046102023000153510ustar 00000000000000use crate::prelude::*; use cargo::core::compiler::Lto; use cargo_test_support::RawOutput; use cargo_test_support::registry::Package; use cargo_test_support::{Project, basic_manifest, project, str}; #[cargo_test] fn with_deps() { Package::new("bar", "0.0.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "test" version = "0.0.0" edition = "2015" [dependencies] bar = "*" [profile.release] lto = true "#, ) .file("src/main.rs", "extern crate bar; fn main() {}") .build(); p.cargo("build -v --release") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) [COMPILING] bar v0.0.1 [RUNNING] `rustc --crate-name bar [..]-C linker-plugin-lto [..]` [COMPILING] test v0.0.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name test [..]-C lto [..]` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn shared_deps() { Package::new("bar", "0.0.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "test" version = "0.0.0" edition = "2015" [dependencies] bar = "*" [build-dependencies] bar = "*" [profile.release] lto = true "#, ) .file("build.rs", "extern crate bar; fn main() {}") .file("src/main.rs", "extern crate bar; fn main() {}") .build(); p.cargo("build -v --release") .with_stderr_data( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) [COMPILING] bar v0.0.1 [RUNNING] `rustc --crate-name bar [..]-C linker-plugin-lto [..]` [RUNNING] `rustc --crate-name bar [..]-C embed-bitcode=no [..]` [COMPILING] test v0.0.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name build_script_build [..]` [RUNNING] `[ROOT]/foo/target/release/build/test-[HASH]/build-script-build` [RUNNING] `rustc --crate-name test [..]-C lto [..]` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn build_dep_not_ltod() { Package::new("bar", "0.0.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "test" version = "0.0.0" edition = "2015" [build-dependencies] bar = "*" [profile.release] lto = true "#, ) .file("build.rs", "extern crate bar; fn main() {}") .file("src/main.rs", "fn main() {}") .build(); p.cargo("build -v --release") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) [COMPILING] bar v0.0.1 [RUNNING] `rustc --crate-name bar [..]-C embed-bitcode=no [..]` [COMPILING] test v0.0.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name build_script_build [..]` [RUNNING] `[ROOT]/foo/target/release/build/test-[HASH]/build-script-build` [RUNNING] `rustc --crate-name test [..]-C lto [..]` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn complicated() { Package::new("dep-shared", "0.0.1") .file("src/lib.rs", "pub fn foo() {}") .publish(); Package::new("dep-normal2", "0.0.1") .file("src/lib.rs", "pub fn foo() {}") .publish(); Package::new("dep-normal", "0.0.1") .dep("dep-shared", "*") .dep("dep-normal2", "*") .file( "src/lib.rs", " pub fn foo() { dep_shared::foo(); dep_normal2::foo(); } ", ) .publish(); Package::new("dep-build2", "0.0.1") .file("src/lib.rs", "pub fn foo() {}") .publish(); Package::new("dep-build", "0.0.1") .dep("dep-shared", "*") .dep("dep-build2", "*") .file( "src/lib.rs", " pub fn foo() { dep_shared::foo(); dep_build2::foo(); } ", ) .publish(); Package::new("dep-proc-macro2", "0.0.1") .file("src/lib.rs", "pub fn foo() {}") .publish(); Package::new("dep-proc-macro", "0.0.1") .proc_macro(true) .dep("dep-shared", "*") .dep("dep-proc-macro2", "*") .file( "src/lib.rs", " extern crate proc_macro; use proc_macro::TokenStream; #[proc_macro_attribute] pub fn foo(_: TokenStream, a: TokenStream) -> TokenStream { dep_shared::foo(); dep_proc_macro2::foo(); a } ", ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "test" version = "0.0.0" edition = "2015" [lib] crate-type = ['cdylib', 'staticlib'] [dependencies] dep-normal = "*" dep-proc-macro = "*" [build-dependencies] dep-build = "*" [profile.release] lto = true # force build deps to share an opt-level with the rest of the # graph so they only get built once. [profile.release.build-override] opt-level = 3 "#, ) .file("build.rs", "fn main() { dep_build::foo() }") .file( "src/bin/foo-bin.rs", "#[dep_proc_macro::foo] fn main() { dep_normal::foo() }", ) .file( "src/lib.rs", "#[dep_proc_macro::foo] pub fn foo() { dep_normal::foo() }", ) .build(); p.cargo("build -v --release") // normal deps and their transitive dependencies do not need object // code, so they should have linker-plugin-lto specified .with_stderr_contains( "[..]`rustc[..]--crate-name dep_normal2 [..]-C linker-plugin-lto[..]`", ) .with_stderr_contains("[..]`rustc[..]--crate-name dep_normal [..]-C linker-plugin-lto[..]`") // build dependencies and their transitive deps don't need any bitcode, // so embedding should be turned off .with_stderr_contains("[..]`rustc[..]--crate-name dep_build2 [..]-C embed-bitcode=no[..]`") .with_stderr_contains("[..]`rustc[..]--crate-name dep_build [..]-C embed-bitcode=no[..]`") .with_stderr_contains( "[..]`rustc[..]--crate-name build_script_build [..]-C embed-bitcode=no[..]`", ) // proc macro deps are the same as build deps here .with_stderr_contains( "[..]`rustc[..]--crate-name dep_proc_macro2 [..]-C embed-bitcode=no[..]`", ) .with_stderr_contains( "[..]`rustc[..]--crate-name dep_proc_macro [..]-C embed-bitcode=no[..]`", ) .with_stderr_contains( "[..]`rustc[..]--crate-name foo_bin [..]--crate-type bin[..]-C lto[..]`", ) .with_stderr_contains( "[..]`rustc[..]--crate-name test [..]--crate-type cdylib[..]-C lto[..]`", ) .with_stderr_contains("[..]`rustc[..]--crate-name dep_shared [..]`") .with_stderr_does_not_contain("[..]--crate-name dep_shared[..]-C lto[..]") .with_stderr_does_not_contain("[..]--crate-name dep_shared[..]-C linker-plugin-lto[..]") .with_stderr_does_not_contain("[..]--crate-name dep_shared[..]-C embed-bitcode[..]") .run(); } #[cargo_test] fn off_in_manifest_works() { Package::new("bar", "0.0.1") .file("src/lib.rs", "pub fn foo() {}") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "test" version = "0.0.0" edition = "2015" [dependencies] bar = "*" [profile.release] lto = "off" "#, ) .file("src/lib.rs", "pub fn foo() {}") .file( "src/main.rs", "fn main() { test::foo(); bar::foo(); }", ) .build(); p.cargo("build -v --release") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) [COMPILING] bar v0.0.1 [RUNNING] `rustc --crate-name bar [..]--crate-type lib [..]-C lto=off [..]-C embed-bitcode=no [..]` [COMPILING] test v0.0.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name test [..]--crate-type lib [..]-C lto=off [..]-C embed-bitcode=no [..]` [RUNNING] `rustc --crate-name test --edition=2015 src/main.rs [..]--crate-type bin [..]-C lto=off [..]` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn between_builds() { let p = project() .file( "Cargo.toml", r#" [package] name = "test" version = "0.0.0" edition = "2015" [profile.release] lto = true "#, ) .file("src/lib.rs", "pub fn foo() {}") .file("src/main.rs", "fn main() { test::foo() }") .build(); p.cargo("build -v --release --lib") .with_stderr_data(str![[r#" [COMPILING] test v0.0.0 ([ROOT]/foo) [RUNNING] `rustc [..]--crate-type lib [..]-C linker-plugin-lto [..]` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build -v --release") .with_stderr_data(str![[r#" [COMPILING] test v0.0.0 ([ROOT]/foo) [RUNNING] `rustc [..]--crate-type bin [..]-C lto [..]` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn test_all() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" [profile.release] lto = true "#, ) .file("src/main.rs", "fn main() {}") .file("tests/a.rs", "") .file("tests/b.rs", "") .build(); p.cargo("test --release -v") .with_stderr_data( str![[r#" [COMPILING] foo v0.0.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name a [..]-C lto [..]` [RUNNING] `rustc --crate-name b [..]-C lto [..]` [RUNNING] `rustc --crate-name foo [..]-C lto [..]--test [..]` [RUNNING] `rustc --crate-name foo [..]--crate-type bin [..]-C lto [..]` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/release/deps/foo-[HASH][EXE]` [RUNNING] `[ROOT]/foo/target/release/deps/a-[HASH][EXE]` [RUNNING] `[ROOT]/foo/target/release/deps/b-[HASH][EXE]` "#]] .unordered(), ) .run(); } #[cargo_test] fn test_all_and_bench() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" [profile.release] lto = true [profile.bench] lto = true "#, ) .file("src/main.rs", "fn main() {}") .file("tests/a.rs", "") .file("tests/b.rs", "") .build(); p.cargo("test --release -v") .with_stderr_data( str![[r#" [COMPILING] foo v0.0.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name a [..]-C lto [..]` [RUNNING] `rustc --crate-name b [..]-C lto [..]` [RUNNING] `rustc --crate-name foo [..]-C lto [..]--test [..]` [RUNNING] `rustc --crate-name foo [..]--crate-type bin [..]-C lto [..]` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/release/deps/foo-[HASH][EXE]` [RUNNING] `[ROOT]/foo/target/release/deps/a-[HASH][EXE]` [RUNNING] `[ROOT]/foo/target/release/deps/b-[HASH][EXE]` "#]] .unordered(), ) .run(); } /// Basic setup: /// /// foo v0.0.0 /// ├── bar v0.0.0 /// │ ├── registry v0.0.1 /// │ └── registry-shared v0.0.1 /// └── registry-shared v0.0.1 /// /// Where `bar` will have the given crate types. fn project_with_dep(crate_types: &str) -> Project { Package::new("registry", "0.0.1") .file("src/lib.rs", r#"pub fn foo() { println!("registry"); }"#) .publish(); Package::new("registry-shared", "0.0.1") .file("src/lib.rs", r#"pub fn foo() { println!("shared"); }"#) .publish(); project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" [workspace] [dependencies] bar = { path = 'bar' } registry-shared = "*" [profile.release] lto = true "#, ) .file( "src/main.rs", " fn main() { bar::foo(); registry_shared::foo(); } ", ) .file( "bar/Cargo.toml", &format!( r#" [package] name = "bar" version = "0.0.0" edition = "2015" [dependencies] registry = "*" registry-shared = "*" [lib] crate-type = [{}] "#, crate_types ), ) .file( "bar/src/lib.rs", r#" pub fn foo() { println!("bar"); registry::foo(); registry_shared::foo(); } "#, ) .file("tests/a.rs", "") .file("bar/tests/b.rs", "") .build() } /// Helper for checking which LTO behavior is used for a specific crate. /// /// `krate_info` is extra compiler flags used to distinguish this if the same /// crate name is being built multiple times. fn verify_lto(output: &RawOutput, krate: &str, krate_info: &str, expected_lto: Lto) { let stderr = std::str::from_utf8(&output.stderr).unwrap(); let mut matches = stderr.lines().filter(|line| { line.contains("Running") && line.contains(&format!("--crate-name {} ", krate)) && line.contains(krate_info) }); let line = matches.next().unwrap_or_else(|| { panic!( "expected to find crate `{}` info: `{}`, not found in output:\n{}", krate, krate_info, stderr ); }); if let Some(line2) = matches.next() { panic!( "found multiple lines matching crate `{}` info: `{}`:\nline1:{}\nline2:{}\noutput:\n{}", krate, krate_info, line, line2, stderr ); } let actual_lto = if let Some((_, line)) = line.split_once("-C lto=") { let mode = line.splitn(2, ' ').next().unwrap(); if mode == "off" { Lto::Off } else { Lto::Run(Some(mode.into())) } } else if line.contains("-C lto") { Lto::Run(None) } else if line.contains("-C linker-plugin-lto") { Lto::OnlyBitcode } else if line.contains("-C embed-bitcode=no") { Lto::OnlyObject } else { Lto::ObjectAndBitcode }; assert_eq!( actual_lto, expected_lto, "did not find expected LTO in line: {}", line ); } #[cargo_test] fn cdylib_and_rlib() { let p = project_with_dep("'cdylib', 'rlib'"); let output = p.cargo("build --release -v").run(); // `registry` is ObjectAndBitcode because it needs Object for the // rlib, and Bitcode for the cdylib (which doesn't support LTO). verify_lto( &output, "registry", "--crate-type lib", Lto::ObjectAndBitcode, ); // Same as `registry` verify_lto( &output, "registry_shared", "--crate-type lib", Lto::ObjectAndBitcode, ); // Same as `registry` verify_lto( &output, "bar", "--crate-type cdylib --crate-type rlib", Lto::ObjectAndBitcode, ); verify_lto(&output, "foo", "--crate-type bin", Lto::Run(None)); p.cargo("test --release -v") .with_stderr_data( str![[r#" [FRESH] registry v0.0.1 [FRESH] registry-shared v0.0.1 [FRESH] bar v0.0.0 ([ROOT]/foo/bar) [COMPILING] foo v0.0.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]-C lto [..]--test [..]` [RUNNING] `rustc --crate-name a [..]-C lto [..]--test [..]` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/release/deps/foo-[HASH][EXE]` [RUNNING] `[ROOT]/foo/target/release/deps/a-[HASH][EXE]` "#]] .unordered(), ) .run(); p.cargo("build --release -v --manifest-path bar/Cargo.toml") .with_stderr_data( str![[r#" [FRESH] registry-shared v0.0.1 [FRESH] registry v0.0.1 [FRESH] bar v0.0.0 ([ROOT]/foo/bar) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); p.cargo("test --release -v --manifest-path bar/Cargo.toml") .with_stderr_data(str![[r#" [FRESH] registry-shared v0.0.1 [FRESH] registry v0.0.1 [COMPILING] bar v0.0.0 ([ROOT]/foo/bar) [RUNNING] `rustc --crate-name bar [..]-C lto [..]--test [..]` [RUNNING] `rustc --crate-name b [..]-C lto [..]--test [..]` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/release/deps/bar-[HASH][EXE]` [RUNNING] `[ROOT]/foo/target/release/deps/b-[HASH][EXE]` [DOCTEST] bar [RUNNING] `rustdoc --edition=2015 --crate-type cdylib --crate-type rlib --color auto --crate-name bar --test [..]-C lto [..] "#]].unordered()) .run(); } #[cargo_test] fn dylib() { let p = project_with_dep("'dylib'"); let output = p.cargo("build --release -v").run(); // `registry` is OnlyObject because rustc doesn't support LTO with dylibs. verify_lto(&output, "registry", "--crate-type lib", Lto::OnlyObject); // `registry_shared` is both because it is needed by both bar (Object) and // foo (Bitcode for LTO). verify_lto( &output, "registry_shared", "--crate-type lib", Lto::ObjectAndBitcode, ); // `bar` is OnlyObject because rustc doesn't support LTO with dylibs. verify_lto(&output, "bar", "--crate-type dylib", Lto::OnlyObject); // `foo` is LTO because it is a binary, and the profile specifies `lto=true`. verify_lto(&output, "foo", "--crate-type bin", Lto::Run(None)); // `cargo test` should not rebuild dependencies. It builds the test // executables with `lto=true` because the tests are built with the // `--release` flag. p.cargo("test --release -v") .with_stderr_data( str![[r#" [FRESH] registry v0.0.1 [FRESH] registry-shared v0.0.1 [FRESH] bar v0.0.0 ([ROOT]/foo/bar) [COMPILING] foo v0.0.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]-C lto [..]--test [..]` [RUNNING] `rustc --crate-name a [..]-C lto [..]--test [..]` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/release/deps/foo-[HASH][EXE]` [RUNNING] `[ROOT]/foo/target/release/deps/a-[HASH][EXE]` "#]] .unordered(), ) .run(); // Building just `bar` causes `registry-shared` to get rebuilt because it // switches to OnlyObject because it is now only being used with a dylib // which does not support LTO. // // `bar` gets rebuilt because `registry_shared` got rebuilt. p.cargo("build --release -v --manifest-path bar/Cargo.toml") .with_stderr_data( str![[r#" [COMPILING] registry-shared v0.0.1 [FRESH] registry v0.0.1 [RUNNING] `rustc --crate-name registry_shared [..]-C embed-bitcode=no [..]` [DIRTY] bar v0.0.0 ([..]): dependency info changed [COMPILING] bar v0.0.0 ([ROOT]/foo/bar) [RUNNING] `rustc --crate-name bar [..]--crate-type dylib [..]-C embed-bitcode=no [..]` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); // Testing just `bar` causes `registry` to get rebuilt because it switches // to needing both Object (for the `bar` dylib) and Bitcode (for the test // built with LTO). // // `bar` the dylib gets rebuilt because `registry` got rebuilt. p.cargo("test --release -v --manifest-path bar/Cargo.toml") .with_stderr_data( str![[r#" [FRESH] registry-shared v0.0.1 [COMPILING] registry v0.0.1 [RUNNING] `rustc --crate-name registry [..]` [DIRTY] bar v0.0.0 ([..]): dependency info changed [COMPILING] bar v0.0.0 ([ROOT]/foo/bar) [RUNNING] `rustc --crate-name bar [..]--crate-type dylib [..]-C embed-bitcode=no [..]` [RUNNING] `rustc --crate-name bar [..]-C lto [..]--test [..]` [RUNNING] `rustc --crate-name b [..]-C lto [..]--test [..]` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/release/deps/bar-[HASH][EXE]` [RUNNING] `[ROOT]/foo/target/release/deps/b-[HASH][EXE]` "#]] .unordered(), ) .run(); } #[cargo_test] // This is currently broken on windows-gnu, see https://github.com/rust-lang/rust/issues/109797 #[cfg_attr( all(target_os = "windows", target_env = "gnu"), ignore = "windows-gnu not working" )] fn test_profile() { Package::new("bar", "0.0.1") .file("src/lib.rs", "pub fn foo() -> i32 { 123 } ") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [profile.test] lto = 'thin' [dependencies] bar = "*" "#, ) .file( "src/lib.rs", r#" #[test] fn t1() { assert_eq!(123, bar::foo()); } "#, ) .build(); p.cargo("test -v") // unordered because the two `foo` builds start in parallel .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) [COMPILING] bar v0.0.1 [RUNNING] `rustc --crate-name bar [..]--crate-type lib [..]` [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]--crate-type lib --emit=dep-info,metadata,link -C linker-plugin-lto [..]` [RUNNING] `rustc --crate-name foo [..]--emit=dep-info,link -C lto=thin [..]--test [..]` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/debug/deps/foo-[HASH][EXE]` [DOCTEST] foo [RUNNING] `rustdoc [..] "#]].unordered()) .run(); } #[cargo_test] fn doctest() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [profile.release] lto = true [dependencies] bar = { path = "bar" } "#, ) .file( "src/lib.rs", r#" /// Foo! /// /// ``` /// foo::foo(); /// ``` pub fn foo() { bar::bar(); } "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file( "bar/src/lib.rs", r#" pub fn bar() { println!("hi!"); } "#, ) .build(); p.cargo("test --doc --release -v") // embed-bitcode should be harmless here .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.1.0 ([ROOT]/foo/bar) [RUNNING] `rustc --crate-name bar [..]--crate-type lib [..]-C linker-plugin-lto [..]` [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]--crate-type lib [..]-C linker-plugin-lto [..]` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [DOCTEST] foo [RUNNING] `rustdoc [..]` "#]]) .run(); // Try with bench profile. p.cargo("test --doc --release -v") .env("CARGO_PROFILE_BENCH_LTO", "true") .with_stderr_data( str![[r#" [DOCTEST] foo [RUNNING] `rustdoc [..]-C lto [..]` [FRESH] bar v0.1.0 ([ROOT]/foo/bar) [FRESH] foo v0.1.0 ([ROOT]/foo) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn dylib_rlib_bin() { // dylib+rlib linked with a binary let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [lib] crate-type = ["dylib", "rlib"] [profile.release] lto = true "#, ) .file("src/lib.rs", "pub fn foo() { println!(\"hi!\"); }") .file("src/bin/ferret.rs", "fn main() { foo::foo(); }") .build(); let output = p.cargo("build --release -v").run(); verify_lto( &output, "foo", "--crate-type dylib --crate-type rlib", Lto::ObjectAndBitcode, ); verify_lto(&output, "ferret", "--crate-type bin", Lto::Run(None)); } #[cargo_test] fn fresh_swapping_commands() { // In some rare cases, different commands end up building dependencies // with different LTO settings. This checks that it doesn't cause the // cache to thrash in that scenario. Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" [profile.release] lto = true "#, ) .file("src/lib.rs", "pub fn foo() { println!(\"hi!\"); }") .build(); p.cargo("build --release -v") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [COMPILING] bar v1.0.0 [RUNNING] `rustc --crate-name bar [..]-C linker-plugin-lto [..]` [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]-C linker-plugin-lto [..]` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("test --release -v") .with_stderr_data( str![[r#" [FRESH] bar v1.0.0 [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]-C lto [..]--test [..]` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/release/deps/foo-[HASH][EXE]` [DOCTEST] foo [RUNNING] `rustdoc [..]-C lto [..]` "#]] .unordered(), ) .run(); p.cargo("build --release -v") .with_stderr_data(str![[r#" [FRESH] bar v1.0.0 [FRESH] foo v0.1.0 ([ROOT]/foo) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("test --release -v --no-run -v") .with_stderr_data(str![[r#" [FRESH] bar v1.0.0 [FRESH] foo v0.1.0 ([ROOT]/foo) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [EXECUTABLE] `[ROOT]/foo/target/release/deps/foo-[HASH][EXE]` "#]]) .run(); } cargo-0.91.0/tests/testsuite/main.rs000064400000000000000000000072361046102023000155030ustar 00000000000000#![allow(clippy::disallowed_methods)] #![allow(clippy::print_stderr)] #![allow(clippy::print_stdout)] mod advanced_env; mod alt_registry; mod artifact_dep; mod artifact_dir; mod bad_config; mod bad_manifest_path; mod bench; mod binary_name; mod build; mod build_dir; mod build_plan; mod build_script; mod build_script_env; mod build_script_extra_link_arg; mod build_scripts_multiple; mod cache_lock; mod cache_messages; mod cargo; mod cargo_add; mod cargo_alias_config; mod cargo_bench; mod cargo_build; mod cargo_check; mod cargo_clean; mod cargo_command; mod cargo_config; mod cargo_doc; mod cargo_env_config; mod cargo_features; mod cargo_fetch; mod cargo_fix; mod cargo_generate_lockfile; mod cargo_git_checkout; mod cargo_help; mod cargo_info; mod cargo_init; mod cargo_install; mod cargo_locate_project; mod cargo_login; mod cargo_logout; mod cargo_metadata; mod cargo_new; mod cargo_owner; mod cargo_package; mod cargo_pkgid; mod cargo_publish; mod cargo_read_manifest; mod cargo_remove; mod cargo_report; mod cargo_run; mod cargo_rustc; mod cargo_rustdoc; mod cargo_search; mod cargo_targets; mod cargo_test; mod cargo_tree; mod cargo_uninstall; mod cargo_update; mod cargo_vendor; mod cargo_verify_project; mod cargo_version; mod cargo_yank; mod cfg; mod check; mod check_cfg; mod clean; mod collisions; mod compile_time_deps; mod concurrent; mod config; mod config_cli; mod config_include; mod corrupt_git; mod credential_process; mod cross_compile; mod cross_publish; mod custom_target; mod death; mod dep_info; mod diagnostics; mod direct_minimal_versions; mod directory; mod doc; mod docscrape; mod edition; mod error; mod feature_unification; mod features; mod features2; mod features_namespaced; mod fetch; mod fix; mod fix_n_times; mod freshness; mod freshness_checksum; mod future_incompat_report; mod generate_lockfile; mod git; mod git_auth; mod git_gc; mod git_shallow; mod glob_targets; mod global_cache_tracker; mod help; mod hints; mod https; mod inheritable_workspace_fields; mod install; mod install_upgrade; mod jobserver; mod lints; mod lints_table; mod list_availables; mod local_registry; mod locate_project; mod lockfile_compat; mod lockfile_path; mod login; mod logout; mod lto; mod member_discovery; mod member_errors; mod message_format; mod messages; mod metabuild; mod metadata; mod minimal_versions; mod multitarget; mod net_config; mod new; mod offline; mod old_cargos; mod open_namespaces; mod owner; mod package; mod package_features; mod package_message_format; mod patch; mod path; mod paths; mod pgo; mod pkgid; mod precise_pre_release; mod proc_macro; mod profile_config; mod profile_custom; mod profile_overrides; mod profile_targets; mod profile_trim_paths; mod profiles; mod progress; mod pub_priv; mod publish; mod publish_lockfile; mod read_manifest; mod registry; mod registry_auth; mod registry_overlay; mod rename_deps; mod replace; mod required_features; mod run; mod rust_version; mod rustc; mod rustc_info_cache; mod rustdoc; mod rustdoc_extern_html; mod rustdocflags; mod rustflags; mod rustup; mod sbom; mod script; mod search; mod shell_quoting; mod source_replacement; mod ssh; mod standard_lib; mod test; mod timings; mod tool_paths; mod unit_graph; mod update; mod utils; mod vendor; mod verify_project; mod version; mod warn_on_failure; mod warning_override; mod weak_dep_features; mod workspaces; mod yank; use crate::prelude::*; pub mod prelude { pub use crate::utils::ext::CargoCommandExt; pub use crate::utils::ext::CargoProjectExt; pub use cargo_test_support::prelude::*; } #[cargo_test] fn aaa_trigger_cross_compile_disabled_check() { // This triggers the cross compile disabled check to run ASAP, see #5141 crate::utils::cross_compile::disabled(); } cargo-0.91.0/tests/testsuite/member_discovery.rs000064400000000000000000000024511046102023000201070ustar 00000000000000//! Tests for workspace member discovery. use crate::prelude::*; use cargo::core::{Shell, Workspace}; use cargo::util::context::GlobalContext; use cargo_test_support::paths; use cargo_test_support::project; use cargo_test_support::registry; /// Tests exclusion of non-directory files from workspace member discovery using glob `*`. #[cargo_test] fn bad_file_member_exclusion() { let p = project() .file( "Cargo.toml", r#" [workspace] members = [ "crates/*" ] "#, ) .file("crates/.DS_Store", "PLACEHOLDER") .file( "crates/bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" authors = [] "#, ) .file("crates/bar/src/main.rs", "fn main() {}") .build(); // Prevent this test from accessing the network by setting up .cargo/config. registry::init(); let gctx = GlobalContext::new( Shell::from_write(Box::new(Vec::new())), paths::cargo_home(), paths::cargo_home(), ); let ws = Workspace::new(&p.root().join("Cargo.toml"), &gctx).unwrap(); assert_eq!(ws.members().count(), 1); assert_eq!(ws.members().next().unwrap().name(), "bar"); } cargo-0.91.0/tests/testsuite/member_errors.rs000064400000000000000000000115701046102023000174160ustar 00000000000000//! Tests for workspace member errors. use crate::prelude::*; use cargo::core::Shell; use cargo::core::Workspace; use cargo::core::compiler::UserIntent; use cargo::core::resolver::ResolveError; use cargo::ops::{self, CompileOptions}; use cargo::util::{context::GlobalContext, errors::ManifestError}; use cargo_test_support::paths; use cargo_test_support::project; use cargo_test_support::registry; use cargo_test_support::str; /// Tests inclusion of a `ManifestError` pointing to a member manifest /// when that manifest fails to deserialize. #[cargo_test] fn toml_deserialize_manifest_error() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" authors = [] [dependencies] bar = { path = "bar" } [workspace] "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" authors = [] [dependencies] foobar == "0.55" "#, ) .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] extra `=`, expected nothing --> bar/Cargo.toml:8:25 | 8 | foobar == "0.55" | ^ | [ERROR] failed to load manifest for dependency `bar` "#]]) .run(); } /// Tests inclusion of a `ManifestError` pointing to a member manifest /// when that manifest has an invalid dependency path. #[cargo_test] fn member_manifest_path_io_error() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" authors = [] [dependencies] bar = { path = "bar" } [workspace] "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" authors = [] [dependencies] foobar = { path = "nosuch" } "#, ) .file("bar/src/main.rs", "fn main() {}") .build(); let root_manifest_path = p.root().join("Cargo.toml"); let member_manifest_path = p.root().join("bar").join("Cargo.toml"); let missing_manifest_path = p.root().join("bar").join("nosuch").join("Cargo.toml"); let error = Workspace::new(&root_manifest_path, &GlobalContext::default().unwrap()).unwrap_err(); eprintln!("{:?}", error); let manifest_err: &ManifestError = error.downcast_ref().expect("Not a ManifestError"); assert_eq!(manifest_err.manifest_path(), &root_manifest_path); let causes: Vec<_> = manifest_err.manifest_causes().collect(); assert_eq!(causes.len(), 2, "{:?}", causes); assert_eq!(causes[0].manifest_path(), &member_manifest_path); assert_eq!(causes[1].manifest_path(), &missing_manifest_path); } /// Tests dependency version errors provide which package failed via a `ResolveError`. #[cargo_test] fn member_manifest_version_error() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" authors = [] [dependencies] bar = { path = "bar" } [workspace] "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" authors = [] [dependencies] i-dont-exist = "0.55" "#, ) .file("bar/src/main.rs", "fn main() {}") .build(); // Prevent this test from accessing the network by setting up .cargo/config. registry::init(); let gctx = GlobalContext::new( Shell::from_write(Box::new(Vec::new())), paths::cargo_home(), paths::cargo_home(), ); let ws = Workspace::new(&p.root().join("Cargo.toml"), &gctx).unwrap(); let compile_options = CompileOptions::new(&gctx, UserIntent::Build).unwrap(); let member_bar = ws.members().find(|m| &*m.name() == "bar").unwrap(); let error = ops::compile(&ws, &compile_options).map(|_| ()).unwrap_err(); eprintln!("{:?}", error); let resolve_err: &ResolveError = error.downcast_ref().expect("Not a ResolveError"); let package_path = resolve_err.package_path(); assert_eq!(package_path.len(), 1, "package_path: {:?}", package_path); assert_eq!(package_path[0], member_bar.package_id()); } cargo-0.91.0/tests/testsuite/message_format.rs000064400000000000000000000077311046102023000175530ustar 00000000000000//! Tests for --message-format flag. use crate::prelude::*; use cargo_test_support::{basic_lib_manifest, basic_manifest, project, str}; #[cargo_test] fn cannot_specify_two() { let p = project() .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("src/main.rs", "fn main() {}") .build(); let formats = ["human", "json", "short"]; for a in formats.iter() { for b in formats.iter() { p.cargo(&format!("build --message-format {},{}", a, b)) .with_status(101) .with_stderr_data(str![[r#" [ERROR] cannot specify two kinds of `message-format` arguments "#]]) .run(); } } } #[cargo_test] fn double_json_works() { let p = project() .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check --message-format json,json-render-diagnostics") .run(); p.cargo("check --message-format json,json-diagnostic-short") .run(); p.cargo("check --message-format json,json-diagnostic-rendered-ansi") .run(); p.cargo("check --message-format json --message-format json-diagnostic-rendered-ansi") .run(); p.cargo("check --message-format json-diagnostic-rendered-ansi") .run(); p.cargo("check --message-format json-diagnostic-short,json-diagnostic-rendered-ansi") .run(); } #[cargo_test] fn cargo_renders() { let p = project() .file( "Cargo.toml", r#" [package] name = 'foo' version = '0.1.0' [dependencies] bar = { path = 'bar' } "#, ) .file("src/main.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "") .build(); p.cargo("check --message-format json-render-diagnostics") .with_status(101) .with_stdout_data( str![[r#" [ { "reason": "compiler-artifact", "...": "{...}" }, { "reason": "build-finished", "success": false } ] "#]] .is_json() .against_jsonlines(), ) .with_stderr_contains( "\ [CHECKING] bar [..] [CHECKING] foo [..] error[..]`main`[..] ", ) .run(); } #[cargo_test] fn cargo_renders_short() { let p = project() .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("src/main.rs", "") .build(); p.cargo("check --message-format json-render-diagnostics,json-diagnostic-short") .with_status(101) .with_stderr_data(str![[r#" [CHECKING] foo v0.1.0 ([ROOT]/foo) error[E0601]: `main` function not found in crate `foo` [ERROR] could not compile `foo` (bin "foo") due to 1 previous error "#]]) .run(); } #[cargo_test] fn cargo_renders_ansi() { let p = project() .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("src/main.rs", "") .build(); p.cargo("check --message-format json-diagnostic-rendered-ansi") .with_status(101) .with_stdout_contains("[..]\\u001b[38;5;9merror[..]") .run(); } #[cargo_test] fn cargo_renders_doctests() { let p = project() .file("Cargo.toml", &basic_lib_manifest("foo")) .file( "src/lib.rs", "\ /// ```rust /// bar() /// ``` pub fn bar() {} ", ) .build(); p.cargo("test --doc --message-format short") .with_status(101) .with_stdout_data(str![[r#" running 1 test test src/lib.rs - bar (line 1) ... FAILED failures: ---- src/lib.rs - bar (line 1) stdout ---- src/lib.rs:2:1: error[E0425]: cannot find function `bar`[..] [ERROR] aborting due to 1 previous error Couldn't compile the test. failures: src/lib.rs - bar (line 1) test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } cargo-0.91.0/tests/testsuite/messages.rs000064400000000000000000000117341046102023000163640ustar 00000000000000//! General tests specifically about diagnostics and other messages. //! //! Tests for message caching can be found in `cache_messages`. use crate::prelude::*; use cargo_test_support::compare::assert_e2e; use cargo_test_support::{Project, process, project}; use cargo_util::ProcessError; /// Captures the actual diagnostics displayed by rustc. This is done to avoid /// relying on the exact message formatting in rustc. pub fn raw_rustc_output(project: &Project, path: &str, extra: &[&str]) -> String { let mut proc = process("rustc"); if cfg!(windows) { // Sanitize in case the caller wants to do direct string comparison with Cargo's output. proc.arg(path.replace('/', "\\")); } else { proc.arg(path); } let rustc_output = match proc .arg("--crate-type=lib") .args(extra) .cwd(project.root()) .exec_with_output() { Ok(output) => output.stderr, Err(e) => e.downcast::().unwrap().stderr.unwrap(), }; // Do a little dance to remove rustc's "warnings emitted" message and the subsequent newline. let stderr = std::str::from_utf8(&rustc_output).expect("utf8"); let mut lines = stderr.lines(); let mut result = String::new(); while let Some(line) = lines.next() { if line.contains("warning emitted") || line.contains("warnings emitted") || line.contains("aborting due to") { // Eat blank line. match lines.next() { None | Some("") => continue, Some(s) => panic!("unexpected str {}", s), } } result.push_str(line); result.push('\n'); } result } fn redact_rustc_message(msg: &str) -> impl IntoData { use snapbox::filter::{Filter, FilterPaths}; let assert = assert_e2e(); let redactions = assert.redactions(); let msg = redactions.redact(msg); FilterPaths.filter(msg.into()) } #[cargo_test] fn deduplicate_messages_basic() { let p = project() .file( "src/lib.rs", r#" pub fn foo() { let x = 1; } "#, ) .build(); let rustc_message = raw_rustc_output(&p, "src/lib.rs", &[]); let expected_output = format!( "{}\ [WARNING] `foo` (lib) generated 1 warning[..] [WARNING] `foo` (lib test) generated 1 warning (1 duplicate) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [EXECUTABLE] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) ", rustc_message ); p.cargo("test --no-run -j1") .with_stderr_data(redact_rustc_message(&format!( "\ [COMPILING] foo v0.0.1 ([ROOT]/foo) {}", expected_output ))) .run(); // Run again, to check for caching behavior. p.cargo("test --no-run -j1") .with_stderr_data(redact_rustc_message(&expected_output)) .run(); } #[cargo_test] fn deduplicate_messages_mismatched_warnings() { // One execution prints 1 warning, the other prints 2 where there is an overlap. let p = project() .file( "src/lib.rs", r#" pub fn foo() { let x = 1; } #[test] fn t1() { let MY_VALUE = 1; assert_eq!(MY_VALUE, 1); } "#, ) .build(); let lib_output = raw_rustc_output(&p, "src/lib.rs", &[]); let mut lib_test_output = raw_rustc_output(&p, "src/lib.rs", &["--test"]); // Remove the duplicate warning. let start = lib_test_output.find(&lib_output).expect("same warning"); lib_test_output.replace_range(start..start + lib_output.len(), ""); let expected_output = format!( "\ {}\ [WARNING] `foo` (lib) generated 1 warning[..] {}\ [WARNING] `foo` (lib test) generated 2 warnings (1 duplicate) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [EXECUTABLE] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) ", lib_output, lib_test_output ); p.cargo("test --no-run -j1") .with_stderr_data(redact_rustc_message(&format!( "\ [COMPILING] foo v0.0.1 ([ROOT]/foo) {}", expected_output ))) .run(); // Run again, to check for caching behavior. p.cargo("test --no-run -j1") .with_stderr_data(redact_rustc_message(&expected_output)) .run(); } #[cargo_test] fn deduplicate_errors() { let p = project() .file( "src/lib.rs", r#" this should not compile "#, ) .build(); let rustc_message = raw_rustc_output(&p, "src/lib.rs", &[]); p.cargo("test -j1") .with_status(101) .with_stderr_data(redact_rustc_message(&format!( "\ [COMPILING] foo v0.0.1 ([ROOT]/foo) {}[ERROR] could not compile `foo` (lib) due to 1 previous error ", rustc_message ))) .run(); } cargo-0.91.0/tests/testsuite/metabuild.rs000064400000000000000000000507341046102023000165260ustar 00000000000000//! Tests for the metabuild feature (declarative build scripts). use std::str; use crate::prelude::*; use cargo_test_support::{ Project, basic_lib_manifest, basic_manifest, is_coarse_mtime, project, registry::Package, rustc_host, str, }; #[cargo_test] fn metabuild_gated() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" metabuild = ["mb"] "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .masquerade_as_nightly_cargo(&["metabuild"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: feature `metabuild` is required The package requires the Cargo feature called `metabuild`, but that feature is not stabilized in this version of Cargo ([..]). Consider adding `cargo-features = ["metabuild"]` to the top of Cargo.toml (above the [package] table) to tell Cargo you are opting in to use this unstable feature. See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#metabuild for more information about the status of this feature. "#]]) .run(); } fn basic_project() -> Project { project() .file( "Cargo.toml", r#" cargo-features = ["metabuild"] [package] name = "foo" version = "0.0.1" edition = "2015" metabuild = ["mb", "mb-other"] [build-dependencies] mb = {path="mb"} mb-other = {path="mb-other"} "#, ) .file("src/lib.rs", "") .file("mb/Cargo.toml", &basic_lib_manifest("mb")) .file( "mb/src/lib.rs", r#"pub fn metabuild() { println!("Hello mb"); }"#, ) .file( "mb-other/Cargo.toml", r#" [package] name = "mb-other" version = "0.0.1" edition = "2015" "#, ) .file( "mb-other/src/lib.rs", r#"pub fn metabuild() { println!("Hello mb-other"); }"#, ) .build() } #[cargo_test] fn metabuild_basic() { let p = basic_project(); p.cargo("check -vv") .masquerade_as_nightly_cargo(&["metabuild"]) .with_stdout_data(str![[r#" [foo 0.0.1] Hello mb [foo 0.0.1] Hello mb-other "#]]) .run(); } #[cargo_test] fn metabuild_error_both() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["metabuild"] [package] name = "foo" version = "0.0.1" edition = "2015" metabuild = "mb" [build-dependencies] mb = {path="mb"} "#, ) .file("src/lib.rs", "") .file("build.rs", r#"fn main() {}"#) .file("mb/Cargo.toml", &basic_lib_manifest("mb")) .file( "mb/src/lib.rs", r#"pub fn metabuild() { println!("Hello mb"); }"#, ) .build(); p.cargo("check -vv") .masquerade_as_nightly_cargo(&["metabuild"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: cannot specify both `metabuild` and `build` "#]]) .run(); } #[cargo_test] fn metabuild_missing_dep() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["metabuild"] [package] name = "foo" version = "0.0.1" edition = "2015" metabuild = "mb" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check -vv") .masquerade_as_nightly_cargo(&["metabuild"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: metabuild package `mb` must be specified in `build-dependencies` "#]]) .run(); } #[cargo_test] fn metabuild_optional_dep() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["metabuild"] [package] name = "foo" version = "0.0.1" edition = "2015" metabuild = "mb" [build-dependencies] mb = {path="mb", optional=true} "#, ) .file("src/lib.rs", "") .file("mb/Cargo.toml", &basic_lib_manifest("mb")) .file( "mb/src/lib.rs", r#"pub fn metabuild() { println!("Hello mb"); }"#, ) .build(); p.cargo("check -vv") .masquerade_as_nightly_cargo(&["metabuild"]) .with_stdout_does_not_contain("[foo 0.0.1] Hello mb") .run(); p.cargo("check -vv --features mb") .masquerade_as_nightly_cargo(&["metabuild"]) .with_stdout_contains("[foo 0.0.1] Hello mb") .run(); } #[cargo_test] fn metabuild_lib_name() { // Test when setting `name` on [lib]. let p = project() .file( "Cargo.toml", r#" cargo-features = ["metabuild"] [package] name = "foo" version = "0.0.1" edition = "2015" metabuild = "mb" [build-dependencies] mb = {path="mb"} "#, ) .file("src/lib.rs", "") .file( "mb/Cargo.toml", r#" [package] name = "mb" version = "0.0.1" edition = "2015" [lib] name = "other" "#, ) .file( "mb/src/lib.rs", r#"pub fn metabuild() { println!("Hello mb"); }"#, ) .build(); p.cargo("check -vv") .masquerade_as_nightly_cargo(&["metabuild"]) .with_stdout_data(str![[r#" [foo 0.0.1] Hello mb "#]]) .run(); } #[cargo_test] fn metabuild_fresh() { if is_coarse_mtime() { // This test doesn't work on coarse mtimes very well. Because the // metabuild script is created at build time, its mtime is almost // always equal to the mtime of the output. The second call to `build` // will then think it needs to be rebuilt when it should be fresh. return; } // Check that rebuild is fresh. let p = project() .file( "Cargo.toml", r#" cargo-features = ["metabuild"] [package] name = "foo" version = "0.0.1" edition = "2015" metabuild = "mb" [build-dependencies] mb = {path="mb"} "#, ) .file("src/lib.rs", "") .file("mb/Cargo.toml", &basic_lib_manifest("mb")) .file( "mb/src/lib.rs", r#"pub fn metabuild() { println!("Hello mb"); }"#, ) .build(); p.cargo("check -vv") .masquerade_as_nightly_cargo(&["metabuild"]) .with_stdout_contains("[foo 0.0.1] Hello mb") .run(); p.cargo("check -vv") .masquerade_as_nightly_cargo(&["metabuild"]) .with_stdout_does_not_contain("[foo 0.0.1] Hello mb") .with_stderr_data(str![[r#" [FRESH] mb v0.5.0 ([ROOT]/foo/mb) [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn metabuild_links() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["metabuild"] [package] name = "foo" version = "0.0.1" edition = "2015" links = "cat" metabuild = "mb" [build-dependencies] mb = {path="mb"} "#, ) .file("src/lib.rs", "") .file("mb/Cargo.toml", &basic_lib_manifest("mb")) .file( "mb/src/lib.rs", r#" pub fn metabuild() { assert_eq!(std::env::var("CARGO_MANIFEST_LINKS"), Ok("cat".to_string())); println!("Hello mb"); } "#, ) .build(); p.cargo("check -vv") .masquerade_as_nightly_cargo(&["metabuild"]) .with_stdout_data(str![[r#" [foo 0.0.1] Hello mb "#]]) .run(); } #[cargo_test] fn metabuild_override() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["metabuild"] [package] name = "foo" version = "0.0.1" edition = "2015" links = "cat" metabuild = "mb" [build-dependencies] mb = {path="mb"} "#, ) .file("src/lib.rs", "") .file("mb/Cargo.toml", &basic_lib_manifest("mb")) .file( "mb/src/lib.rs", r#"pub fn metabuild() { panic!("should not run"); }"#, ) .file( ".cargo/config.toml", &format!( r#" [target.{}.cat] rustc-link-lib = ["a"] "#, rustc_host() ), ) .build(); p.cargo("check -vv") .masquerade_as_nightly_cargo(&["metabuild"]) .run(); } #[cargo_test] fn metabuild_workspace() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["member1", "member2"] "#, ) .file( "member1/Cargo.toml", r#" cargo-features = ["metabuild"] [package] name = "member1" version = "0.0.1" edition = "2015" metabuild = ["mb1", "mb2"] [build-dependencies] mb1 = {path="../../mb1"} mb2 = {path="../../mb2"} "#, ) .file("member1/src/lib.rs", "") .file( "member2/Cargo.toml", r#" cargo-features = ["metabuild"] [package] name = "member2" version = "0.0.1" edition = "2015" metabuild = ["mb1"] [build-dependencies] mb1 = {path="../../mb1"} "#, ) .file("member2/src/lib.rs", "") .build(); project() .at("mb1") .file("Cargo.toml", &basic_lib_manifest("mb1")) .file( "src/lib.rs", r#"pub fn metabuild() { println!("Hello mb1 {}", std::env::var("CARGO_MANIFEST_DIR").unwrap()); }"#, ) .build(); project() .at("mb2") .file("Cargo.toml", &basic_lib_manifest("mb2")) .file( "src/lib.rs", r#"pub fn metabuild() { println!("Hello mb2 {}", std::env::var("CARGO_MANIFEST_DIR").unwrap()); }"#, ) .build(); p.cargo("check -vv --workspace") .masquerade_as_nightly_cargo(&["metabuild"]) .with_stdout_contains("[member1 0.0.1] Hello mb1 [..]member1") .with_stdout_contains("[member1 0.0.1] Hello mb2 [..]member1") .with_stdout_contains("[member2 0.0.1] Hello mb1 [..]member2") .with_stdout_does_not_contain("[member2 0.0.1] Hello mb2 [..]member2") .run(); } #[cargo_test] fn metabuild_metadata() { // The metabuild Target is filtered out of the `metadata` results. let p = basic_project(); let meta = p .cargo("metadata --format-version=1") .masquerade_as_nightly_cargo(&["metabuild"]) .run_json(); let mb_info: Vec<&str> = meta["packages"] .as_array() .unwrap() .iter() .find(|p| p["name"].as_str().unwrap() == "foo") .unwrap()["metabuild"] .as_array() .unwrap() .iter() .map(|s| s.as_str().unwrap()) .collect(); assert_eq!(mb_info, ["mb", "mb-other"]); } #[cargo_test] fn metabuild_build_plan() { let p = basic_project(); p.cargo("build --build-plan -Zunstable-options") .masquerade_as_nightly_cargo(&["metabuild", "build-plan"]) .with_stdout_data( str![[r#" { "inputs": [ "[ROOT]/foo/Cargo.toml", "[ROOT]/foo/mb/Cargo.toml", "[ROOT]/foo/mb-other/Cargo.toml" ], "invocations": [ { "args": "{...}", "compile_mode": "build", "cwd": "[ROOT]/foo", "deps": [], "env": "{...}", "kind": null, "links": {}, "outputs": [ "[ROOT]/foo/target/debug/deps/libmb-[HASH].rlib", "[ROOT]/foo/target/debug/deps/libmb-[HASH].rmeta" ], "package_name": "mb", "package_version": "0.5.0", "program": "rustc", "target_kind": [ "lib" ] }, { "args": "{...}", "compile_mode": "build", "cwd": "[ROOT]/foo", "deps": [], "env": "{...}", "kind": null, "links": {}, "outputs": [ "[ROOT]/foo/target/debug/deps/libmb_other-[HASH].rlib", "[ROOT]/foo/target/debug/deps/libmb_other-[HASH].rmeta" ], "package_name": "mb-other", "package_version": "0.0.1", "program": "rustc", "target_kind": [ "lib" ] }, { "args": "{...}", "compile_mode": "build", "cwd": "[ROOT]/foo", "deps": [ 0, 1 ], "env": "{...}", "kind": null, "links": "{...}", "outputs": "{...}", "package_name": "foo", "package_version": "0.0.1", "program": "rustc", "target_kind": [ "custom-build" ] }, { "args": "{...}", "compile_mode": "run-custom-build", "cwd": "[ROOT]/foo", "deps": [ 2 ], "env": "{...}", "kind": null, "links": {}, "outputs": [], "package_name": "foo", "package_version": "0.0.1", "program": "[ROOT]/foo/target/debug/build/foo-[HASH]/metabuild-foo", "target_kind": [ "custom-build" ] }, { "args": "{...}", "compile_mode": "build", "cwd": "[ROOT]/foo", "deps": [ 3 ], "env": "{...}", "kind": null, "links": "{...}", "outputs": [ "[ROOT]/foo/target/debug/deps/libfoo-[HASH].rlib", "[ROOT]/foo/target/debug/deps/libfoo-[HASH].rmeta" ], "package_name": "foo", "package_version": "0.0.1", "program": "rustc", "target_kind": [ "lib" ] } ] } "#]] .is_json(), ) .run(); assert_eq!(p.glob("target/.metabuild/metabuild-foo-*.rs").count(), 1); } #[cargo_test] fn metabuild_two_versions() { // Two versions of a metabuild dep with the same name. let p = project() .at("ws") .file( "Cargo.toml", r#" [workspace] members = ["member1", "member2"] "#, ) .file( "member1/Cargo.toml", r#" cargo-features = ["metabuild"] [package] name = "member1" version = "0.0.1" edition = "2015" metabuild = ["mb"] [build-dependencies] mb = {path="../../mb1"} "#, ) .file("member1/src/lib.rs", "") .file( "member2/Cargo.toml", r#" cargo-features = ["metabuild"] [package] name = "member2" version = "0.0.1" edition = "2015" metabuild = ["mb"] [build-dependencies] mb = {path="../../mb2"} "#, ) .file("member2/src/lib.rs", "") .build(); project().at("mb1") .file("Cargo.toml", r#" [package] name = "mb" version = "0.0.1" edition = "2015" "#) .file( "src/lib.rs", r#"pub fn metabuild() { println!("Hello mb1 {}", std::env::var("CARGO_MANIFEST_DIR").unwrap()); }"#, ) .build(); project().at("mb2") .file("Cargo.toml", r#" [package] name = "mb" version = "0.0.2" edition = "2015" "#) .file( "src/lib.rs", r#"pub fn metabuild() { println!("Hello mb2 {}", std::env::var("CARGO_MANIFEST_DIR").unwrap()); }"#, ) .build(); p.cargo("check -vv --workspace") .masquerade_as_nightly_cargo(&["metabuild"]) .with_stdout_data( str![[r#" [member2 0.0.1] Hello mb2 [ROOT]/ws/member2 [member1 0.0.1] Hello mb1 [ROOT]/ws/member1 "#]] .unordered(), ) .run(); assert_eq!( p.glob("target/.metabuild/metabuild-member?-*.rs").count(), 2 ); } #[cargo_test] fn metabuild_external_dependency() { Package::new("mb", "1.0.0") .file("Cargo.toml", &basic_manifest("mb", "1.0.0")) .file( "src/lib.rs", r#"pub fn metabuild() { println!("Hello mb"); }"#, ) .publish(); Package::new("dep", "1.0.0") .file( "Cargo.toml", r#" cargo-features = ["metabuild"] [package] name = "dep" version = "1.0.0" edition = "2015" metabuild = ["mb"] [build-dependencies] mb = "1.0" "#, ) .file("src/lib.rs", "") .build_dep("mb", "1.0.0") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] dep = "1.0" "#, ) .file("src/lib.rs", "extern crate dep;") .build(); p.cargo("check -vv") .masquerade_as_nightly_cargo(&["metabuild"]) .with_stdout_data(str![[r#" [dep 1.0.0] Hello mb "#]]) .run(); assert_eq!(p.glob("target/.metabuild/metabuild-dep-*.rs").count(), 1); } #[cargo_test] fn metabuild_json_artifact() { let p = basic_project(); p.cargo("check --message-format=json") .masquerade_as_nightly_cargo(&["metabuild"]) .with_stdout_data( str![[r#" [ "{...}", { "executable": null, "features": [], "filenames": "{...}", "fresh": false, "manifest_path": "[ROOT]/foo/Cargo.toml", "package_id": "path+[ROOTURL]/foo#0.0.1", "profile": "{...}", "reason": "compiler-artifact", "target": { "crate_types": [ "bin" ], "doc": false, "doctest": false, "edition": "2018", "kind": [ "custom-build" ], "name": "metabuild-foo", "src_path": "[ROOT]/foo/target/.metabuild/metabuild-foo-[HASH].rs", "test": false } }, { "cfgs": [], "env": [], "linked_libs": [], "linked_paths": [], "out_dir": "[ROOT]/foo/target/debug/build/foo-[HASH]/out", "package_id": "path+[ROOTURL]/foo#0.0.1", "reason": "build-script-executed" }, "{...}" ] "#]] .is_json() .against_jsonlines(), ) .run(); } #[cargo_test] fn metabuild_failed_build_json() { let p = basic_project(); // Modify the metabuild dep so that it fails to compile. p.change_file("mb/src/lib.rs", ""); p.cargo("check --message-format=json") .masquerade_as_nightly_cargo(&["metabuild"]) .with_status(101) .with_stdout_data( str![[r#" [ "{...}", { "manifest_path": "[ROOT]/foo/Cargo.toml", "message": { "level": "error", "message": "cannot find function `metabuild` in crate `mb`", "...": "{...}" }, "package_id": "path+[ROOTURL]/foo#0.0.1", "reason": "compiler-message", "target": { "crate_types": [ "bin" ], "doc": false, "doctest": false, "edition": "2018", "kind": [ "custom-build" ], "name": "metabuild-foo", "src_path": null, "test": false } }, "{...}" ] "#]] .is_json() .against_jsonlines(), ) .run(); } cargo-0.91.0/tests/testsuite/metadata.rs000064400000000000000000003535771046102023000163530ustar 00000000000000//! Tests for the `cargo metadata` command. use crate::prelude::*; use cargo_test_support::paths; use cargo_test_support::registry::Package; use cargo_test_support::{ basic_bin_manifest, basic_lib_manifest, main_file, project, rustc_host, str, }; use serde_json::json; #[cargo_test] fn cargo_metadata_simple() { let p = project() .file("src/foo.rs", "") .file("Cargo.toml", &basic_bin_manifest("foo")) .build(); p.cargo("metadata") .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [ "wycats@example.com" ], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo#0.5.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/Cargo.toml", "metadata": null, "name": "foo", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "bin" ], "doc": true, "doctest": false, "edition": "2015", "kind": [ "bin" ], "name": "foo", "src_path": "[ROOT]/foo/src/foo.rs", "test": true } ], "version": "0.5.0" } ], "resolve": { "nodes": [ { "dependencies": [], "deps": [], "features": [], "id": "path+[ROOTURL]/foo#0.5.0" } ], "root": "path+[ROOTURL]/foo#0.5.0" }, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo#0.5.0" ], "workspace_members": [ "path+[ROOTURL]/foo#0.5.0" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); } #[cargo_test] fn cargo_metadata_warns_on_implicit_version() { let p = project() .file("src/foo.rs", "") .file("Cargo.toml", &basic_bin_manifest("foo")) .build(); p.cargo("metadata") .with_stderr_data(str![[r#" [WARNING] please specify `--format-version` flag explicitly to avoid compatibility problems "#]]) .run(); p.cargo("metadata --format-version 1") .with_stderr_data("") .run(); } #[cargo_test] fn library_with_several_crate_types() { let p = project() .file("src/lib.rs", "") .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" [lib] crate-type = ["lib", "staticlib"] "#, ) .build(); p.cargo("metadata") .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo#0.5.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/Cargo.toml", "metadata": null, "name": "foo", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "lib", "staticlib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib", "staticlib" ], "name": "foo", "src_path": "[ROOT]/foo/src/lib.rs", "test": true } ], "version": "0.5.0" } ], "resolve": { "nodes": [ { "dependencies": [], "deps": [], "features": [], "id": "path+[ROOTURL]/foo#0.5.0" } ], "root": "path+[ROOTURL]/foo#0.5.0" }, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo#0.5.0" ], "workspace_members": [ "path+[ROOTURL]/foo#0.5.0" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); } #[cargo_test] fn library_with_features() { let p = project() .file("src/lib.rs", "") .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" [features] default = ["default_feat"] default_feat = [] optional_feat = [] "#, ) .build(); p.cargo("metadata") .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": { "default": [ "default_feat" ], "default_feat": [], "optional_feat": [] }, "homepage": null, "id": "path+[ROOTURL]/foo#0.5.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/Cargo.toml", "metadata": null, "name": "foo", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "foo", "src_path": "[ROOT]/foo/src/lib.rs", "test": true } ], "version": "0.5.0" } ], "resolve": { "nodes": [ { "dependencies": [], "deps": [], "features": [ "default", "default_feat" ], "id": "path+[ROOTURL]/foo#0.5.0" } ], "root": "path+[ROOTURL]/foo#0.5.0" }, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo#0.5.0" ], "workspace_members": [ "path+[ROOTURL]/foo#0.5.0" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); } #[cargo_test] fn cargo_metadata_with_deps_and_version() { let p = project() .file("src/foo.rs", "") .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" authors = [] license = "MIT" description = "foo" [[bin]] name = "foo" [dependencies] bar = "*" [dev-dependencies] foobar = "*" "#, ) .build(); Package::new("baz", "0.0.1").publish(); Package::new("foobar", "0.0.1").publish(); Package::new("bar", "0.0.1").dep("baz", "0.0.1").publish(); p.cargo("metadata -q --format-version 1") .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [], "categories": [], "default_run": null, "dependencies": [ { "features": [], "kind": null, "name": "baz", "optional": false, "registry": null, "rename": null, "req": "^0.0.1", "source": "registry+https://github.com/rust-lang/crates.io-index", "target": null, "uses_default_features": true } ], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "registry+https://github.com/rust-lang/crates.io-index#bar@0.0.1", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/bar-0.0.1/Cargo.toml", "metadata": null, "name": "bar", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": "registry+https://github.com/rust-lang/crates.io-index", "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "bar", "src_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/bar-0.0.1/src/lib.rs", "test": true } ], "version": "0.0.1" }, { "authors": [], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "registry+https://github.com/rust-lang/crates.io-index#baz@0.0.1", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/baz-0.0.1/Cargo.toml", "metadata": null, "name": "baz", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": "registry+https://github.com/rust-lang/crates.io-index", "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "baz", "src_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/baz-0.0.1/src/lib.rs", "test": true } ], "version": "0.0.1" }, { "authors": [], "categories": [], "default_run": null, "dependencies": [ { "features": [], "kind": null, "name": "bar", "optional": false, "registry": null, "rename": null, "req": "*", "source": "registry+https://github.com/rust-lang/crates.io-index", "target": null, "uses_default_features": true }, { "features": [], "kind": "dev", "name": "foobar", "optional": false, "registry": null, "rename": null, "req": "*", "source": "registry+https://github.com/rust-lang/crates.io-index", "target": null, "uses_default_features": true } ], "description": "foo", "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo#0.5.0", "keywords": [], "license": "MIT", "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/Cargo.toml", "metadata": null, "name": "foo", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "bin" ], "doc": true, "doctest": false, "edition": "2015", "kind": [ "bin" ], "name": "foo", "src_path": "[ROOT]/foo/src/foo.rs", "test": true } ], "version": "0.5.0" }, { "authors": [], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "registry+https://github.com/rust-lang/crates.io-index#foobar@0.0.1", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/foobar-0.0.1/Cargo.toml", "metadata": null, "name": "foobar", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": "registry+https://github.com/rust-lang/crates.io-index", "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "foobar", "src_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/foobar-0.0.1/src/lib.rs", "test": true } ], "version": "0.0.1" } ], "resolve": { "nodes": [ { "dependencies": [ "registry+https://github.com/rust-lang/crates.io-index#baz@0.0.1" ], "deps": [ { "dep_kinds": [ { "kind": null, "target": null } ], "name": "baz", "pkg": "registry+https://github.com/rust-lang/crates.io-index#baz@0.0.1" } ], "features": [], "id": "registry+https://github.com/rust-lang/crates.io-index#bar@0.0.1" }, { "dependencies": [], "deps": [], "features": [], "id": "registry+https://github.com/rust-lang/crates.io-index#baz@0.0.1" }, { "dependencies": [ "registry+https://github.com/rust-lang/crates.io-index#bar@0.0.1", "registry+https://github.com/rust-lang/crates.io-index#foobar@0.0.1" ], "deps": [ { "dep_kinds": [ { "kind": null, "target": null } ], "name": "bar", "pkg": "registry+https://github.com/rust-lang/crates.io-index#bar@0.0.1" }, { "dep_kinds": [ { "kind": "dev", "target": null } ], "name": "foobar", "pkg": "registry+https://github.com/rust-lang/crates.io-index#foobar@0.0.1" } ], "features": [], "id": "path+[ROOTURL]/foo#0.5.0" }, { "dependencies": [], "deps": [], "features": [], "id": "registry+https://github.com/rust-lang/crates.io-index#foobar@0.0.1" } ], "root": "path+[ROOTURL]/foo#0.5.0" }, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo#0.5.0" ], "workspace_members": [ "path+[ROOTURL]/foo#0.5.0" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); } /// The `public` field should not show up in `cargo metadata` output if `-Zpublic-dependency` /// is not enabled #[cargo_test] fn cargo_metadata_public_private_dependencies_disabled() { let p = project() .file("src/foo.rs", "") .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" authors = [] license = "MIT" description = "foo" [[bin]] name = "foo" [dependencies] bar = { version = "*", public = false } foobar = { version = "*", public = true } baz = "*" "#, ) .build(); Package::new("bar", "0.0.1").publish(); Package::new("foobar", "0.0.2").publish(); Package::new("baz", "0.0.3").publish(); p.cargo("metadata -q --format-version 1") .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "name": "bar", "...": "{...}" }, { "name": "baz", "...": "{...}" }, { "name": "foo", "dependencies": [ { "features": [], "kind": null, "name": "bar", "optional": false, "registry": null, "rename": null, "req": "*", "source": "registry+https://github.com/rust-lang/crates.io-index", "target": null, "uses_default_features": true }, { "features": [], "kind": null, "name": "baz", "optional": false, "registry": null, "rename": null, "req": "*", "source": "registry+https://github.com/rust-lang/crates.io-index", "target": null, "uses_default_features": true }, { "features": [], "kind": null, "name": "foobar", "optional": false, "registry": null, "rename": null, "req": "*", "source": "registry+https://github.com/rust-lang/crates.io-index", "target": null, "uses_default_features": true } ], "...": "{...}" }, { "name": "foobar", "...": "{...}" } ], "...": "{...}" } "#]] .is_json(), ) .run(); } #[cargo_test] fn cargo_metadata_public_private_dependencies_enabled() { let p = project() .file("src/foo.rs", "") .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" authors = [] license = "MIT" description = "foo" [[bin]] name = "foo" [dependencies] bar = { version = "*", public = false } foobar = { version = "*", public = true } baz = "*" "#, ) .build(); Package::new("bar", "0.0.1").publish(); Package::new("foobar", "0.0.2").publish(); Package::new("baz", "0.0.3").publish(); p.cargo("metadata -q --format-version 1 -Zpublic-dependency") .masquerade_as_nightly_cargo(&["public-dependency"]) .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "name": "bar", "...": "{...}" }, { "name": "baz", "...": "{...}" }, { "name": "foo", "dependencies": [ { "features": [], "kind": null, "name": "bar", "optional": false, "public": false, "registry": null, "rename": null, "req": "*", "source": "registry+https://github.com/rust-lang/crates.io-index", "target": null, "uses_default_features": true }, { "features": [], "kind": null, "name": "baz", "optional": false, "public": false, "registry": null, "rename": null, "req": "*", "source": "registry+https://github.com/rust-lang/crates.io-index", "target": null, "uses_default_features": true }, { "features": [], "kind": null, "name": "foobar", "optional": false, "public": true, "registry": null, "rename": null, "req": "*", "source": "registry+https://github.com/rust-lang/crates.io-index", "target": null, "uses_default_features": true } ], "...": "{...}" }, { "name": "foobar", "...": "{...}" } ], "...": "{...}" } "#]] .is_json(), ) .run(); } #[cargo_test] fn example() { let p = project() .file("src/lib.rs", "") .file("examples/ex.rs", "") .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [[example]] name = "ex" "#, ) .build(); p.cargo("metadata") .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo#0.1.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/Cargo.toml", "metadata": null, "name": "foo", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "foo", "src_path": "[ROOT]/foo/src/lib.rs", "test": true }, { "crate_types": [ "bin" ], "doc": false, "doctest": false, "edition": "2015", "kind": [ "example" ], "name": "ex", "src_path": "[ROOT]/foo/examples/ex.rs", "test": false } ], "version": "0.1.0" } ], "resolve": { "nodes": [ { "dependencies": [], "deps": [], "features": [], "id": "path+[ROOTURL]/foo#0.1.0" } ], "root": "path+[ROOTURL]/foo#0.1.0" }, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo#0.1.0" ], "workspace_members": [ "path+[ROOTURL]/foo#0.1.0" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); } #[cargo_test] fn example_lib() { let p = project() .file("src/lib.rs", "") .file("examples/ex.rs", "") .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [[example]] name = "ex" crate-type = ["rlib", "dylib"] "#, ) .build(); p.cargo("metadata") .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo#0.1.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/Cargo.toml", "metadata": null, "name": "foo", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "foo", "src_path": "[ROOT]/foo/src/lib.rs", "test": true }, { "crate_types": [ "rlib", "dylib" ], "doc": false, "doctest": false, "edition": "2015", "kind": [ "example" ], "name": "ex", "src_path": "[ROOT]/foo/examples/ex.rs", "test": false } ], "version": "0.1.0" } ], "resolve": { "nodes": [ { "dependencies": [], "deps": [], "features": [], "id": "path+[ROOTURL]/foo#0.1.0" } ], "root": "path+[ROOTURL]/foo#0.1.0" }, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo#0.1.0" ], "workspace_members": [ "path+[ROOTURL]/foo#0.1.0" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); } #[cargo_test] fn workspace_metadata() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] [workspace.metadata] tool1 = "hello" tool2 = [1, 2, 3] [workspace.metadata.foo] bar = 3 "#, ) .file("bar/Cargo.toml", &basic_lib_manifest("bar")) .file("bar/src/lib.rs", "") .file("baz/Cargo.toml", &basic_lib_manifest("baz")) .file("baz/src/lib.rs", "") .build(); p.cargo("metadata") .with_stdout_data( str![[r#" { "metadata": { "foo": { "bar": 3 }, "tool1": "hello", "tool2": [ 1, 2, 3 ] }, "packages": [ { "authors": [ "wycats@example.com" ], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo/bar#0.5.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/bar/Cargo.toml", "metadata": null, "name": "bar", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "bar", "src_path": "[ROOT]/foo/bar/src/lib.rs", "test": true } ], "version": "0.5.0" }, { "authors": [ "wycats@example.com" ], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo/baz#0.5.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/baz/Cargo.toml", "metadata": null, "name": "baz", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "baz", "src_path": "[ROOT]/foo/baz/src/lib.rs", "test": true } ], "version": "0.5.0" } ], "resolve": { "nodes": [ { "dependencies": [], "deps": [], "features": [], "id": "path+[ROOTURL]/foo/bar#0.5.0" }, { "dependencies": [], "deps": [], "features": [], "id": "path+[ROOTURL]/foo/baz#0.5.0" } ], "root": null }, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo/bar#0.5.0", "path+[ROOTURL]/foo/baz#0.5.0" ], "workspace_members": [ "path+[ROOTURL]/foo/bar#0.5.0", "path+[ROOTURL]/foo/baz#0.5.0" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); } #[cargo_test] fn workspace_metadata_with_dependencies_no_deps() { let p = project() // NOTE that 'artifact' isn't mentioned in the workspace here, yet it shows up as member. .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] "#, ) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.5.0" authors = ["wycats@example.com"] [dependencies] baz = { path = "../baz/" } artifact = { path = "../artifact/", artifact = "bin" } "#, ) .file("bar/src/lib.rs", "") .file("baz/Cargo.toml", &basic_lib_manifest("baz")) .file("baz/src/lib.rs", "") .file("artifact/Cargo.toml", &basic_bin_manifest("artifact")) .file("artifact/src/main.rs", "fn main() {}") .build(); p.cargo("metadata --no-deps -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [ "wycats@example.com" ], "categories": [], "default_run": null, "dependencies": [ { "artifact": { "kinds": [ "bin" ], "lib": false, "target": null }, "features": [], "kind": null, "name": "artifact", "optional": false, "path": "[ROOT]/foo/artifact", "registry": null, "rename": null, "req": "*", "source": null, "target": null, "uses_default_features": true }, { "features": [], "kind": null, "name": "baz", "optional": false, "path": "[ROOT]/foo/baz", "registry": null, "rename": null, "req": "*", "source": null, "target": null, "uses_default_features": true } ], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo/bar#0.5.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/bar/Cargo.toml", "metadata": null, "name": "bar", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "bar", "src_path": "[ROOT]/foo/bar/src/lib.rs", "test": true } ], "version": "0.5.0" }, { "authors": [ "wycats@example.com" ], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo/artifact#0.5.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/artifact/Cargo.toml", "metadata": null, "name": "artifact", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "bin" ], "doc": true, "doctest": false, "edition": "2015", "kind": [ "bin" ], "name": "artifact", "src_path": "[ROOT]/foo/artifact/src/main.rs", "test": true } ], "version": "0.5.0" }, { "authors": [ "wycats@example.com" ], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo/baz#0.5.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/baz/Cargo.toml", "metadata": null, "name": "baz", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "baz", "src_path": "[ROOT]/foo/baz/src/lib.rs", "test": true } ], "version": "0.5.0" } ], "resolve": null, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo/bar#0.5.0", "path+[ROOTURL]/foo/artifact#0.5.0", "path+[ROOTURL]/foo/baz#0.5.0" ], "workspace_members": [ "path+[ROOTURL]/foo/bar#0.5.0", "path+[ROOTURL]/foo/artifact#0.5.0", "path+[ROOTURL]/foo/baz#0.5.0" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); } #[cargo_test] fn workspace_metadata_with_dependencies_and_resolve() { let alt_target = "wasm32-unknown-unknown"; let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "artifact", "non-artifact", "bin-only-artifact"] "#, ) .file( "bar/Cargo.toml", &r#" [package] name = "bar" version = "0.5.0" authors = [] [build-dependencies] artifact = { path = "../artifact/", artifact = "bin", target = "target" } bin-only-artifact = { path = "../bin-only-artifact/", artifact = "bin", target = "$ALT_TARGET" } non-artifact = { path = "../non-artifact" } [dependencies] artifact = { path = "../artifact/", artifact = ["cdylib", "staticlib", "bin:baz-name"], lib = true, target = "$ALT_TARGET" } bin-only-artifact = { path = "../bin-only-artifact/", artifact = "bin:a-name" } non-artifact = { path = "../non-artifact" } [dev-dependencies] artifact = { path = "../artifact/" } non-artifact = { path = "../non-artifact" } bin-only-artifact = { path = "../bin-only-artifact/", artifact = "bin:b-name" } "#.replace("$ALT_TARGET", alt_target), ) .file("bar/src/lib.rs", "") .file("bar/build.rs", "fn main() {}") .file( "artifact/Cargo.toml", r#" [package] name = "artifact" version = "0.5.0" authors = [] [lib] crate-type = ["staticlib", "cdylib", "rlib"] [[bin]] name = "bar-name" [[bin]] name = "baz-name" "#, ) .file("artifact/src/main.rs", "fn main() {}") .file("artifact/src/lib.rs", "") .file( "bin-only-artifact/Cargo.toml", r#" [package] name = "bin-only-artifact" version = "0.5.0" authors = [] [[bin]] name = "a-name" [[bin]] name = "b-name" "#, ) .file("bin-only-artifact/src/main.rs", "fn main() {}") .file("non-artifact/Cargo.toml", r#" [package] name = "non-artifact" version = "0.5.0" authors = [] "#, ) .file("non-artifact/src/lib.rs", "") .build(); p.cargo("metadata -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo/artifact#0.5.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/artifact/Cargo.toml", "metadata": null, "name": "artifact", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "staticlib", "cdylib", "rlib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "staticlib", "cdylib", "rlib" ], "name": "artifact", "src_path": "[ROOT]/foo/artifact/src/lib.rs", "test": true }, { "crate_types": [ "bin" ], "doc": true, "doctest": false, "edition": "2015", "kind": [ "bin" ], "name": "bar-name", "src_path": "[ROOT]/foo/artifact/src/main.rs", "test": true }, { "crate_types": [ "bin" ], "doc": true, "doctest": false, "edition": "2015", "kind": [ "bin" ], "name": "baz-name", "src_path": "[ROOT]/foo/artifact/src/main.rs", "test": true } ], "version": "0.5.0" }, { "authors": [], "categories": [], "default_run": null, "dependencies": [ { "artifact": { "kinds": [ "cdylib", "staticlib", "bin:baz-name" ], "lib": true, "target": "wasm32-unknown-unknown" }, "features": [], "kind": null, "name": "artifact", "optional": false, "path": "[ROOT]/foo/artifact", "registry": null, "rename": null, "req": "*", "source": null, "target": null, "uses_default_features": true }, { "artifact": { "kinds": [ "bin:a-name" ], "lib": false, "target": null }, "features": [], "kind": null, "name": "bin-only-artifact", "optional": false, "path": "[ROOT]/foo/bin-only-artifact", "registry": null, "rename": null, "req": "*", "source": null, "target": null, "uses_default_features": true }, { "features": [], "kind": null, "name": "non-artifact", "optional": false, "path": "[ROOT]/foo/non-artifact", "registry": null, "rename": null, "req": "*", "source": null, "target": null, "uses_default_features": true }, { "features": [], "kind": "dev", "name": "artifact", "optional": false, "path": "[ROOT]/foo/artifact", "registry": null, "rename": null, "req": "*", "source": null, "target": null, "uses_default_features": true }, { "artifact": { "kinds": [ "bin:b-name" ], "lib": false, "target": null }, "features": [], "kind": "dev", "name": "bin-only-artifact", "optional": false, "path": "[ROOT]/foo/bin-only-artifact", "registry": null, "rename": null, "req": "*", "source": null, "target": null, "uses_default_features": true }, { "features": [], "kind": "dev", "name": "non-artifact", "optional": false, "path": "[ROOT]/foo/non-artifact", "registry": null, "rename": null, "req": "*", "source": null, "target": null, "uses_default_features": true }, { "artifact": { "kinds": [ "bin" ], "lib": false, "target": "target" }, "features": [], "kind": "build", "name": "artifact", "optional": false, "path": "[ROOT]/foo/artifact", "registry": null, "rename": null, "req": "*", "source": null, "target": null, "uses_default_features": true }, { "artifact": { "kinds": [ "bin" ], "lib": false, "target": "wasm32-unknown-unknown" }, "features": [], "kind": "build", "name": "bin-only-artifact", "optional": false, "path": "[ROOT]/foo/bin-only-artifact", "registry": null, "rename": null, "req": "*", "source": null, "target": null, "uses_default_features": true }, { "features": [], "kind": "build", "name": "non-artifact", "optional": false, "path": "[ROOT]/foo/non-artifact", "registry": null, "rename": null, "req": "*", "source": null, "target": null, "uses_default_features": true } ], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo/bar#0.5.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/bar/Cargo.toml", "metadata": null, "name": "bar", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "bar", "src_path": "[ROOT]/foo/bar/src/lib.rs", "test": true }, { "crate_types": [ "bin" ], "doc": false, "doctest": false, "edition": "2015", "kind": [ "custom-build" ], "name": "build-script-build", "src_path": "[ROOT]/foo/bar/build.rs", "test": false } ], "version": "0.5.0" }, { "authors": [], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo/bin-only-artifact#0.5.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/bin-only-artifact/Cargo.toml", "metadata": null, "name": "bin-only-artifact", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "bin" ], "doc": true, "doctest": false, "edition": "2015", "kind": [ "bin" ], "name": "a-name", "src_path": "[ROOT]/foo/bin-only-artifact/src/main.rs", "test": true }, { "crate_types": [ "bin" ], "doc": true, "doctest": false, "edition": "2015", "kind": [ "bin" ], "name": "b-name", "src_path": "[ROOT]/foo/bin-only-artifact/src/main.rs", "test": true } ], "version": "0.5.0" }, { "authors": [], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo/non-artifact#0.5.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/non-artifact/Cargo.toml", "metadata": null, "name": "non-artifact", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "non_artifact", "src_path": "[ROOT]/foo/non-artifact/src/lib.rs", "test": true } ], "version": "0.5.0" } ], "resolve": { "nodes": [ { "dependencies": [], "deps": [], "features": [], "id": "path+[ROOTURL]/foo/artifact#0.5.0" }, { "dependencies": [ "path+[ROOTURL]/foo/artifact#0.5.0", "path+[ROOTURL]/foo/bin-only-artifact#0.5.0", "path+[ROOTURL]/foo/non-artifact#0.5.0" ], "deps": [ { "dep_kinds": [ { "extern_name": "artifact", "kind": null, "target": null }, { "artifact": "cdylib", "compile_target": "wasm32-unknown-unknown", "extern_name": "artifact", "kind": null, "target": null }, { "artifact": "staticlib", "compile_target": "wasm32-unknown-unknown", "extern_name": "artifact", "kind": null, "target": null }, { "artifact": "bin", "bin_name": "baz-name", "compile_target": "wasm32-unknown-unknown", "extern_name": "baz_name", "kind": null, "target": null }, { "kind": "dev", "target": null }, { "artifact": "bin", "bin_name": "bar-name", "compile_target": "", "extern_name": "bar_name", "kind": "build", "target": null }, { "artifact": "bin", "bin_name": "baz-name", "compile_target": "", "extern_name": "baz_name", "kind": "build", "target": null } ], "name": "artifact", "pkg": "path+[ROOTURL]/foo/artifact#0.5.0" }, { "dep_kinds": [ { "artifact": "bin", "bin_name": "a-name", "extern_name": "a_name", "kind": null, "target": null }, { "artifact": "bin", "bin_name": "b-name", "extern_name": "b_name", "kind": "dev", "target": null }, { "artifact": "bin", "bin_name": "a-name", "compile_target": "wasm32-unknown-unknown", "extern_name": "a_name", "kind": "build", "target": null }, { "artifact": "bin", "bin_name": "b-name", "compile_target": "wasm32-unknown-unknown", "extern_name": "b_name", "kind": "build", "target": null } ], "name": "", "pkg": "path+[ROOTURL]/foo/bin-only-artifact#0.5.0" }, { "dep_kinds": [ { "kind": null, "target": null }, { "kind": "dev", "target": null }, { "kind": "build", "target": null } ], "name": "non_artifact", "pkg": "path+[ROOTURL]/foo/non-artifact#0.5.0" } ], "features": [], "id": "path+[ROOTURL]/foo/bar#0.5.0" }, { "dependencies": [], "deps": [], "features": [], "id": "path+[ROOTURL]/foo/bin-only-artifact#0.5.0" }, { "dependencies": [], "deps": [], "features": [], "id": "path+[ROOTURL]/foo/non-artifact#0.5.0" } ], "root": null }, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo/bar#0.5.0", "path+[ROOTURL]/foo/artifact#0.5.0", "path+[ROOTURL]/foo/bin-only-artifact#0.5.0", "path+[ROOTURL]/foo/non-artifact#0.5.0" ], "workspace_members": [ "path+[ROOTURL]/foo/bar#0.5.0", "path+[ROOTURL]/foo/artifact#0.5.0", "path+[ROOTURL]/foo/bin-only-artifact#0.5.0", "path+[ROOTURL]/foo/non-artifact#0.5.0" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); } #[cargo_test] fn cargo_metadata_with_invalid_manifest() { let p = project().file("Cargo.toml", "").build(); p.cargo("metadata --format-version 1") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: manifest is missing either a `[package]` or a `[workspace]` "#]]) .run(); } #[cargo_test] fn cargo_metadata_with_invalid_authors_field() { let p = project() .file("src/foo.rs", "") .file( "Cargo.toml", r#" [package] authors = "" "#, ) .build(); p.cargo("metadata") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid type: string "", expected a vector of strings or workspace --> Cargo.toml:3:27 | 3 | authors = "" | ^^ | "#]]) .run(); } #[cargo_test] fn cargo_metadata_with_invalid_version_field() { let p = project() .file("src/foo.rs", "") .file( "Cargo.toml", r#" [package] version = 1 "#, ) .build(); p.cargo("metadata") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid type: integer `1`, expected SemVer version --> Cargo.toml:3:27 | 3 | version = 1 | ^ | "#]]) .run(); } #[cargo_test] fn cargo_metadata_with_invalid_publish_field() { let p = project() .file("src/foo.rs", "") .file( "Cargo.toml", r#" [package] publish = "foo" "#, ) .build(); p.cargo("metadata") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid type: string "foo", expected a boolean, a vector of strings, or workspace --> Cargo.toml:3:27 | 3 | publish = "foo" | ^^^^^ | "#]]) .run(); } #[cargo_test] fn cargo_metadata_with_invalid_artifact_deps() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" [dependencies] artifact = { path = "artifact", artifact = "bin:notfound" } "#, ) .file("src/lib.rs", "") .file("artifact/Cargo.toml", &basic_bin_manifest("artifact")) .file("artifact/src/main.rs", "fn main() {}") .build(); p.cargo("metadata -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_status(101) .with_stderr_data(str![[r#" [WARNING] please specify `--format-version` flag explicitly to avoid compatibility problems [LOCKING] 1 package to latest compatible version [ERROR] dependency `artifact` in package `foo` requires a `bin:notfound` artifact to be present. "#]]) .run(); } #[cargo_test] fn cargo_metadata_with_invalid_duplicate_renamed_deps() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" [dependencies] bar = { path = "bar" } baz = { path = "bar", package = "bar" } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_lib_manifest("bar")) .file("bar/src/lib.rs", "") .build(); p.cargo("metadata") .with_status(101) .with_stderr_data(str![[r#" [WARNING] please specify `--format-version` flag explicitly to avoid compatibility problems [LOCKING] 1 package to latest compatible version [ERROR] the crate `foo v0.5.0 ([ROOT]/foo)` depends on crate `bar v0.5.0 ([ROOT]/foo/bar)` multiple times with different names "#]]) .run(); } #[cargo_test] fn cargo_metadata_no_deps_path_to_cargo_toml_relative() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("metadata --no-deps --manifest-path foo/Cargo.toml") .cwd(p.root().parent().unwrap()) .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [ "wycats@example.com" ], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo#0.5.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/Cargo.toml", "metadata": null, "name": "foo", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "bin" ], "doc": true, "doctest": false, "edition": "2015", "kind": [ "bin" ], "name": "foo", "src_path": "[ROOT]/foo/src/foo.rs", "test": true } ], "version": "0.5.0" } ], "resolve": null, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo#0.5.0" ], "workspace_members": [ "path+[ROOTURL]/foo#0.5.0" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); } #[cargo_test] fn cargo_metadata_no_deps_path_to_cargo_toml_absolute() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("metadata --no-deps --manifest-path") .arg(p.root().join("Cargo.toml")) .cwd(p.root().parent().unwrap()) .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [ "wycats@example.com" ], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo#0.5.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/Cargo.toml", "metadata": null, "name": "foo", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "bin" ], "doc": true, "doctest": false, "edition": "2015", "kind": [ "bin" ], "name": "foo", "src_path": "[ROOT]/foo/src/foo.rs", "test": true } ], "version": "0.5.0" } ], "resolve": null, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo#0.5.0" ], "workspace_members": [ "path+[ROOTURL]/foo#0.5.0" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); } #[cargo_test] fn cargo_metadata_no_deps_path_to_cargo_toml_parent_relative() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("metadata --no-deps --manifest-path foo") .cwd(p.root().parent().unwrap()) .with_status(101) .with_stderr_data(str![[r#" [ERROR] the manifest-path must be a path to a Cargo.toml file "#]]) .run(); } #[cargo_test] fn cargo_metadata_no_deps_path_to_cargo_toml_parent_absolute() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("metadata --no-deps --manifest-path") .arg(p.root()) .cwd(p.root().parent().unwrap()) .with_status(101) .with_stderr_data(str![[r#" [ERROR] the manifest-path must be a path to a Cargo.toml file "#]]) .run(); } #[cargo_test] fn cargo_metadata_no_deps_cwd() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("metadata --no-deps") .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [ "wycats@example.com" ], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo#0.5.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/Cargo.toml", "metadata": null, "name": "foo", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "bin" ], "doc": true, "doctest": false, "edition": "2015", "kind": [ "bin" ], "name": "foo", "src_path": "[ROOT]/foo/src/foo.rs", "test": true } ], "version": "0.5.0" } ], "resolve": null, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo#0.5.0" ], "workspace_members": [ "path+[ROOTURL]/foo#0.5.0" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); } #[cargo_test] fn cargo_metadata_bad_version() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("metadata --no-deps --format-version 2") .with_status(1) .with_stderr_data(str![[r#" [ERROR] invalid value '2' for '--format-version ' [possible values: 1] ... "#]]) .run(); } #[cargo_test] fn multiple_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" authors = [] [features] a = [] b = [] "#, ) .file("src/lib.rs", "") .build(); p.cargo("metadata --features").arg("a b").run(); } #[cargo_test] fn package_metadata() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" authors = ["wycats@example.com"] categories = ["database"] keywords = ["database"] readme = "README.md" repository = "https://github.com/rust-lang/cargo" homepage = "https://rust-lang.org" documentation = "https://doc.rust-lang.org/stable/std/" [package.metadata.bar] baz = "quux" "#, ) .file("README.md", "") .file("src/lib.rs", "") .build(); p.cargo("metadata --no-deps") .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [ "wycats@example.com" ], "categories": [ "database" ], "default_run": null, "dependencies": [], "description": null, "documentation": "https://doc.rust-lang.org/stable/std/", "edition": "2015", "features": {}, "homepage": "https://rust-lang.org", "id": "path+[ROOTURL]/foo#0.1.0", "keywords": [ "database" ], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/Cargo.toml", "metadata": { "bar": { "baz": "quux" } }, "name": "foo", "publish": null, "readme": "README.md", "repository": "https://github.com/rust-lang/cargo", "rust_version": null, "source": null, "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "foo", "src_path": "[ROOT]/foo/src/lib.rs", "test": true } ], "version": "0.1.0" } ], "resolve": null, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo#0.1.0" ], "workspace_members": [ "path+[ROOTURL]/foo#0.1.0" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); } #[cargo_test] fn package_publish() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" authors = ["wycats@example.com"] categories = ["database"] keywords = ["database"] readme = "README.md" repository = "https://github.com/rust-lang/cargo" publish = ["my-registry"] "#, ) .file("README.md", "") .file("src/lib.rs", "") .build(); p.cargo("metadata --no-deps") .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [ "wycats@example.com" ], "categories": [ "database" ], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo#0.1.0", "keywords": [ "database" ], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/Cargo.toml", "metadata": null, "name": "foo", "publish": [ "my-registry" ], "readme": "README.md", "repository": "https://github.com/rust-lang/cargo", "rust_version": null, "source": null, "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "foo", "src_path": "[ROOT]/foo/src/lib.rs", "test": true } ], "version": "0.1.0" } ], "resolve": null, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo#0.1.0" ], "workspace_members": [ "path+[ROOTURL]/foo#0.1.0" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); } #[cargo_test] fn cargo_metadata_path_to_cargo_toml_project() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar"] "#, ) .file("bar/Cargo.toml", &basic_lib_manifest("bar")) .file("bar/src/lib.rs", "") .build(); p.cargo("package --manifest-path") .arg(p.root().join("bar/Cargo.toml")) .cwd(p.root().parent().unwrap()) .run(); p.cargo("metadata --manifest-path") .arg(p.root().join("target/package/bar-0.5.0/Cargo.toml")) .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [ "wycats@example.com" ], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo/target/package/bar-0.5.0#bar@0.5.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/target/package/bar-0.5.0/Cargo.toml", "metadata": null, "name": "bar", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "bar", "src_path": "[ROOT]/foo/target/package/bar-0.5.0/src/lib.rs", "test": true } ], "version": "0.5.0" } ], "resolve": { "nodes": [ { "dependencies": [], "deps": [], "features": [], "id": "path+[ROOTURL]/foo/target/package/bar-0.5.0#bar@0.5.0" } ], "root": "path+[ROOTURL]/foo/target/package/bar-0.5.0#bar@0.5.0" }, "target_directory": "[ROOT]/foo/target/package/bar-0.5.0/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo/target/package/bar-0.5.0#bar@0.5.0" ], "workspace_members": [ "path+[ROOTURL]/foo/target/package/bar-0.5.0#bar@0.5.0" ], "workspace_root": "[ROOT]/foo/target/package/bar-0.5.0" } "#]] .is_json(), ) .run(); } #[cargo_test] fn package_edition_2018() { let p = project() .file("src/lib.rs", "") .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" authors = ["wycats@example.com"] edition = "2018" "#, ) .build(); p.cargo("metadata") .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [ "wycats@example.com" ], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2018", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo#0.1.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/Cargo.toml", "metadata": null, "name": "foo", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2018", "kind": [ "lib" ], "name": "foo", "src_path": "[ROOT]/foo/src/lib.rs", "test": true } ], "version": "0.1.0" } ], "resolve": { "nodes": [ { "dependencies": [], "deps": [], "features": [], "id": "path+[ROOTURL]/foo#0.1.0" } ], "root": "path+[ROOTURL]/foo#0.1.0" }, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo#0.1.0" ], "workspace_members": [ "path+[ROOTURL]/foo#0.1.0" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); } #[cargo_test] fn package_default_run() { let p = project() .file("src/lib.rs", "") .file("src/bin/a.rs", r#"fn main() { println!("hello A"); }"#) .file("src/bin/b.rs", r#"fn main() { println!("hello B"); }"#) .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" authors = ["wycats@example.com"] edition = "2018" default-run = "a" "#, ) .build(); let json = p.cargo("metadata").run_json(); assert_eq!(json["packages"][0]["default_run"], json!("a")); } #[cargo_test] fn package_rust_version() { let p = project() .file("src/lib.rs", "") .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" authors = ["wycats@example.com"] edition = "2018" rust-version = "1.56" "#, ) .build(); let json = p.cargo("metadata").run_json(); assert_eq!(json["packages"][0]["rust_version"], json!("1.56")); } #[cargo_test] fn target_edition_2018() { let p = project() .file("src/lib.rs", "") .file("src/main.rs", "") .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" authors = ["wycats@example.com"] edition = "2015" [lib] edition = "2018" "#, ) .build(); p.cargo("metadata") .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [ "wycats@example.com" ], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo#0.1.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/Cargo.toml", "metadata": null, "name": "foo", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2018", "kind": [ "lib" ], "name": "foo", "src_path": "[ROOT]/foo/src/lib.rs", "test": true }, { "crate_types": [ "bin" ], "doc": true, "doctest": false, "edition": "2015", "kind": [ "bin" ], "name": "foo", "src_path": "[ROOT]/foo/src/main.rs", "test": true } ], "version": "0.1.0" } ], "resolve": { "nodes": [ { "dependencies": [], "deps": [], "features": [], "id": "path+[ROOTURL]/foo#0.1.0" } ], "root": "path+[ROOTURL]/foo#0.1.0" }, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo#0.1.0" ], "workspace_members": [ "path+[ROOTURL]/foo#0.1.0" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); } #[cargo_test] fn rename_dependency() { Package::new("bar", "0.1.0").publish(); Package::new("bar", "0.2.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [dependencies] bar = { version = "0.1.0" } baz = { version = "0.2.0", package = "bar" } "#, ) .file("src/lib.rs", "extern crate bar; extern crate baz;") .build(); p.cargo("metadata") .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "registry+https://github.com/rust-lang/crates.io-index#bar@0.1.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/bar-0.1.0/Cargo.toml", "metadata": null, "name": "bar", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": "registry+https://github.com/rust-lang/crates.io-index", "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "bar", "src_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/bar-0.1.0/src/lib.rs", "test": true } ], "version": "0.1.0" }, { "authors": [], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "registry+https://github.com/rust-lang/crates.io-index#bar@0.2.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/bar-0.2.0/Cargo.toml", "metadata": null, "name": "bar", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": "registry+https://github.com/rust-lang/crates.io-index", "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "bar", "src_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/bar-0.2.0/src/lib.rs", "test": true } ], "version": "0.2.0" }, { "authors": [], "categories": [], "default_run": null, "dependencies": [ { "features": [], "kind": null, "name": "bar", "optional": false, "registry": null, "rename": null, "req": "^0.1.0", "source": "registry+https://github.com/rust-lang/crates.io-index", "target": null, "uses_default_features": true }, { "features": [], "kind": null, "name": "bar", "optional": false, "registry": null, "rename": "baz", "req": "^0.2.0", "source": "registry+https://github.com/rust-lang/crates.io-index", "target": null, "uses_default_features": true } ], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo#0.0.1", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/Cargo.toml", "metadata": null, "name": "foo", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "foo", "src_path": "[ROOT]/foo/src/lib.rs", "test": true } ], "version": "0.0.1" } ], "resolve": { "nodes": [ { "dependencies": [], "deps": [], "features": [], "id": "registry+https://github.com/rust-lang/crates.io-index#bar@0.1.0" }, { "dependencies": [], "deps": [], "features": [], "id": "registry+https://github.com/rust-lang/crates.io-index#bar@0.2.0" }, { "dependencies": [ "registry+https://github.com/rust-lang/crates.io-index#bar@0.1.0", "registry+https://github.com/rust-lang/crates.io-index#bar@0.2.0" ], "deps": [ { "dep_kinds": [ { "kind": null, "target": null } ], "name": "bar", "pkg": "registry+https://github.com/rust-lang/crates.io-index#bar@0.1.0" }, { "dep_kinds": [ { "kind": null, "target": null } ], "name": "baz", "pkg": "registry+https://github.com/rust-lang/crates.io-index#bar@0.2.0" } ], "features": [], "id": "path+[ROOTURL]/foo#0.0.1" } ], "root": "path+[ROOTURL]/foo#0.0.1" }, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo#0.0.1" ], "workspace_members": [ "path+[ROOTURL]/foo#0.0.1" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); } #[cargo_test] fn metadata_links() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" links = "a" "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .build(); p.cargo("metadata") .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo#0.5.0", "keywords": [], "license": null, "license_file": null, "links": "a", "manifest_path": "[ROOT]/foo/Cargo.toml", "metadata": null, "name": "foo", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "foo", "src_path": "[ROOT]/foo/src/lib.rs", "test": true }, { "crate_types": [ "bin" ], "doc": false, "doctest": false, "edition": "2015", "kind": [ "custom-build" ], "name": "build-script-build", "src_path": "[ROOT]/foo/build.rs", "test": false } ], "version": "0.5.0" } ], "resolve": { "nodes": [ { "dependencies": [], "deps": [], "features": [], "id": "path+[ROOTURL]/foo#0.5.0" } ], "root": "path+[ROOTURL]/foo#0.5.0" }, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo#0.5.0" ], "workspace_members": [ "path+[ROOTURL]/foo#0.5.0" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); } #[cargo_test] fn deps_with_bin_only() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] bdep = { path = "bdep" } "#, ) .file("src/lib.rs", "") .file("bdep/Cargo.toml", &basic_bin_manifest("bdep")) .file("bdep/src/main.rs", "fn main() {}") .build(); p.cargo("metadata") .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [], "categories": [], "default_run": null, "dependencies": [ { "features": [], "kind": null, "name": "bdep", "optional": false, "path": "[ROOT]/foo/bdep", "registry": null, "rename": null, "req": "*", "source": null, "target": null, "uses_default_features": true } ], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo#0.1.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/Cargo.toml", "metadata": null, "name": "foo", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "foo", "src_path": "[ROOT]/foo/src/lib.rs", "test": true } ], "version": "0.1.0" } ], "resolve": { "nodes": [ { "dependencies": [], "deps": [], "features": [], "id": "path+[ROOTURL]/foo#0.1.0" } ], "root": "path+[ROOTURL]/foo#0.1.0" }, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo#0.1.0" ], "workspace_members": [ "path+[ROOTURL]/foo#0.1.0" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); } #[cargo_test] fn filter_platform() { // Testing the --filter-platform flag. Package::new("normal-dep", "0.0.1").publish(); Package::new("host-dep", "0.0.1").publish(); Package::new("alt-dep", "0.0.1").publish(); Package::new("cfg-dep", "0.0.1").publish(); // Just needs to be a valid target that is different from host. // Presumably nobody runs these tests on wasm. 🙃 let alt_target = "wasm32-unknown-unknown"; let host_target = rustc_host(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [dependencies] normal-dep = "0.0.1" [target.{}.dependencies] host-dep = "0.0.1" [target.{}.dependencies] alt-dep = "0.0.1" [target.'cfg(foobar)'.dependencies] cfg-dep = "0.0.1" "#, host_target, alt_target ), ) .file("src/lib.rs", "") .build(); // We're going to be checking that we don't download excessively, // so we need to ensure that downloads will happen. let clear = || { paths::cargo_home().join("registry/cache").rm_rf(); paths::cargo_home().join("registry/src").rm_rf(); p.build_dir().rm_rf(); }; // Normal metadata, no filtering, returns *everything*. p.cargo("metadata") .with_stderr_data( str![[r#" [WARNING] please specify `--format-version` flag explicitly to avoid compatibility problems [UPDATING] `dummy-registry` index [LOCKING] 4 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] normal-dep v0.0.1 (registry `dummy-registry`) [DOWNLOADED] host-dep v0.0.1 (registry `dummy-registry`) [DOWNLOADED] cfg-dep v0.0.1 (registry `dummy-registry`) [DOWNLOADED] alt-dep v0.0.1 (registry `dummy-registry`) "#]] .unordered(), ) .with_stdout_data( str![[r#" { "packages": [ { "name": "alt-dep", "dependencies": [], "...": "{...}" }, { "name": "cfg-dep", "dependencies": [], "...": "{...}" }, { "name": "foo", "dependencies": [ { "name": "normal-dep", "target": null, "...": "{...}" }, { "name": "cfg-dep", "target": "cfg(foobar)", "...": "{...}" }, { "name": "alt-dep", "target": "wasm32-unknown-unknown", "...": "{...}" }, { "name": "host-dep", "target": "[HOST_TARGET]", "...": "{...}" } ], "...": "{...}" }, { "name": "host-dep", "dependencies": [], "...": "{...}" }, { "name": "normal-dep", "dependencies": [], "...": "{...}" } ], "resolve": { "nodes": [ { "dependencies": [], "deps": [], "features": [], "id": "registry+https://github.com/rust-lang/crates.io-index#alt-dep@0.0.1" }, { "dependencies": [], "deps": [], "features": [], "id": "registry+https://github.com/rust-lang/crates.io-index#cfg-dep@0.0.1" }, { "dependencies": [ "registry+https://github.com/rust-lang/crates.io-index#alt-dep@0.0.1", "registry+https://github.com/rust-lang/crates.io-index#cfg-dep@0.0.1", "registry+https://github.com/rust-lang/crates.io-index#host-dep@0.0.1", "registry+https://github.com/rust-lang/crates.io-index#normal-dep@0.0.1" ], "deps": [ { "dep_kinds": [ { "kind": null, "target": "wasm32-unknown-unknown" } ], "name": "alt_dep", "pkg": "registry+https://github.com/rust-lang/crates.io-index#alt-dep@0.0.1" }, { "dep_kinds": [ { "kind": null, "target": "cfg(foobar)" } ], "name": "cfg_dep", "pkg": "registry+https://github.com/rust-lang/crates.io-index#cfg-dep@0.0.1" }, { "dep_kinds": [ { "kind": null, "target": "[HOST_TARGET]" } ], "name": "host_dep", "pkg": "registry+https://github.com/rust-lang/crates.io-index#host-dep@0.0.1" }, { "dep_kinds": [ { "kind": null, "target": null } ], "name": "normal_dep", "pkg": "registry+https://github.com/rust-lang/crates.io-index#normal-dep@0.0.1" } ], "features": [], "id": "path+[ROOTURL]/foo#0.1.0" }, { "dependencies": [], "deps": [], "features": [], "id": "registry+https://github.com/rust-lang/crates.io-index#host-dep@0.0.1" }, { "dependencies": [], "deps": [], "features": [], "id": "registry+https://github.com/rust-lang/crates.io-index#normal-dep@0.0.1" } ], "root": "path+[ROOTURL]/foo#0.1.0" }, "...": "{...}" } "#]] .is_json() .unordered(), ) .run(); clear(); // Filter on alternate, removes cfg and host. p.cargo("metadata --filter-platform") .arg(alt_target) .with_stderr_data( str![[r#" [WARNING] please specify `--format-version` flag explicitly to avoid compatibility problems [DOWNLOADING] crates ... [DOWNLOADED] normal-dep v0.0.1 (registry `dummy-registry`) [DOWNLOADED] host-dep v0.0.1 (registry `dummy-registry`) [DOWNLOADED] alt-dep v0.0.1 (registry `dummy-registry`) "#]] .unordered(), ) .with_stdout_data( str![[r#" { "packages": [ { "name": "alt-dep", "dependencies": [], "...": "{...}" }, { "name": "foo", "dependencies": [ { "name": "normal-dep", "target": null, "...": "{...}" }, { "name": "cfg-dep", "target": "cfg(foobar)", "...": "{...}" }, { "name": "alt-dep", "target": "wasm32-unknown-unknown", "...": "{...}" }, { "name": "host-dep", "target": "[HOST_TARGET]", "...": "{...}" } ], "...": "{...}" }, { "name": "normal-dep", "dependencies": [], "...": "{...}" } ], "resolve": { "nodes": [ { "dependencies": [], "deps": [], "features": [], "id": "registry+https://github.com/rust-lang/crates.io-index#alt-dep@0.0.1" }, { "dependencies": [ "registry+https://github.com/rust-lang/crates.io-index#alt-dep@0.0.1", "registry+https://github.com/rust-lang/crates.io-index#normal-dep@0.0.1" ], "deps": [ { "dep_kinds": [ { "kind": null, "target": "wasm32-unknown-unknown" } ], "name": "alt_dep", "pkg": "registry+https://github.com/rust-lang/crates.io-index#alt-dep@0.0.1" }, { "dep_kinds": [ { "kind": null, "target": null } ], "name": "normal_dep", "pkg": "registry+https://github.com/rust-lang/crates.io-index#normal-dep@0.0.1" } ], "features": [], "id": "path+[ROOTURL]/foo#0.1.0" }, { "dependencies": [], "deps": [], "features": [], "id": "registry+https://github.com/rust-lang/crates.io-index#normal-dep@0.0.1" } ], "root": "path+[ROOTURL]/foo#0.1.0" }, "...": "{...}" } "#]] .is_json() .unordered(), ) .run(); clear(); // Filter on host, removes alt and cfg. p.cargo("metadata --filter-platform") .arg(&host_target) .with_stderr_data( str![[r#" [WARNING] please specify `--format-version` flag explicitly to avoid compatibility problems [DOWNLOADING] crates ... [DOWNLOADED] normal-dep v0.0.1 (registry `dummy-registry`) [DOWNLOADED] host-dep v0.0.1 (registry `dummy-registry`) "#]] .unordered(), ) .with_stdout_data( str![[r#" { "packages": [ { "name": "foo", "dependencies": [ { "name": "normal-dep", "target": null, "...": "{...}" }, { "name": "cfg-dep", "target": "cfg(foobar)", "...": "{...}" }, { "name": "alt-dep", "target": "wasm32-unknown-unknown", "...": "{...}" }, { "name": "host-dep", "target": "[HOST_TARGET]", "...": "{...}" } ], "...": "{...}" }, { "name": "host-dep", "dependencies": [], "...": "{...}" }, { "name": "normal-dep", "dependencies": [], "...": "{...}" } ], "resolve": { "nodes": [ { "dependencies": [ "registry+https://github.com/rust-lang/crates.io-index#host-dep@0.0.1", "registry+https://github.com/rust-lang/crates.io-index#normal-dep@0.0.1" ], "deps": [ { "dep_kinds": [ { "kind": null, "target": "[HOST_TARGET]" } ], "name": "host_dep", "pkg": "registry+https://github.com/rust-lang/crates.io-index#host-dep@0.0.1" }, { "dep_kinds": [ { "kind": null, "target": null } ], "name": "normal_dep", "pkg": "registry+https://github.com/rust-lang/crates.io-index#normal-dep@0.0.1" } ], "features": [], "id": "path+[ROOTURL]/foo#0.1.0" }, { "dependencies": [], "deps": [], "features": [], "id": "registry+https://github.com/rust-lang/crates.io-index#host-dep@0.0.1" }, { "dependencies": [], "deps": [], "features": [], "id": "registry+https://github.com/rust-lang/crates.io-index#normal-dep@0.0.1" } ], "root": "path+[ROOTURL]/foo#0.1.0" }, "...": "{...}" } "#]] .is_json() .unordered(), ) .run(); clear(); // Filter host with cfg, removes alt only p.cargo("metadata --filter-platform") .arg(&host_target) .env("RUSTFLAGS", "--cfg=foobar") .with_stderr_data( str![[r#" [WARNING] please specify `--format-version` flag explicitly to avoid compatibility problems [DOWNLOADING] crates ... [DOWNLOADED] normal-dep v0.0.1 (registry `dummy-registry`) [DOWNLOADED] host-dep v0.0.1 (registry `dummy-registry`) [DOWNLOADED] cfg-dep v0.0.1 (registry `dummy-registry`) "#]] .unordered(), ) .with_stdout_data( str![[r#" { "packages": [ { "name": "cfg-dep", "dependencies": [], "...": "{...}" }, { "name": "foo", "dependencies": [ { "name": "normal-dep", "target": null, "...": "{...}" }, { "name": "cfg-dep", "target": "cfg(foobar)", "...": "{...}" }, { "name": "alt-dep", "target": "wasm32-unknown-unknown", "...": "{...}" }, { "name": "host-dep", "target": "[HOST_TARGET]", "...": "{...}" } ], "...": "{...}" }, { "name": "host-dep", "dependencies": [], "...": "{...}" }, { "name": "normal-dep", "dependencies": [], "...": "{...}" } ], "resolve": { "nodes": [ { "dependencies": [], "deps": [], "features": [], "id": "registry+https://github.com/rust-lang/crates.io-index#cfg-dep@0.0.1" }, { "dependencies": [ "registry+https://github.com/rust-lang/crates.io-index#cfg-dep@0.0.1", "registry+https://github.com/rust-lang/crates.io-index#host-dep@0.0.1", "registry+https://github.com/rust-lang/crates.io-index#normal-dep@0.0.1" ], "deps": [ { "dep_kinds": [ { "kind": null, "target": "cfg(foobar)" } ], "name": "cfg_dep", "pkg": "registry+https://github.com/rust-lang/crates.io-index#cfg-dep@0.0.1" }, { "dep_kinds": [ { "kind": null, "target": "[HOST_TARGET]" } ], "name": "host_dep", "pkg": "registry+https://github.com/rust-lang/crates.io-index#host-dep@0.0.1" }, { "dep_kinds": [ { "kind": null, "target": null } ], "name": "normal_dep", "pkg": "registry+https://github.com/rust-lang/crates.io-index#normal-dep@0.0.1" } ], "features": [], "id": "path+[ROOTURL]/foo#0.1.0" }, { "dependencies": [], "deps": [], "features": [], "id": "registry+https://github.com/rust-lang/crates.io-index#host-dep@0.0.1" }, { "dependencies": [], "deps": [], "features": [], "id": "registry+https://github.com/rust-lang/crates.io-index#normal-dep@0.0.1" } ], "root": "path+[ROOTURL]/foo#0.1.0" }, "...": "{...}" } "#]] .is_json() .unordered(), ) .run(); } #[cargo_test] fn dep_kinds() { Package::new("bar", "0.1.0").publish(); Package::new("winapi", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] bar = "0.1" [dev-dependencies] bar = "0.1" [build-dependencies] bar = "0.1" [target.'cfg(windows)'.dependencies] winapi = "0.1" "#, ) .file("src/lib.rs", "") .build(); p.cargo("metadata") .with_stdout_data( str![[r#" { "metadata": null, "packages": "{...}", "resolve": { "nodes": [ { "dependencies": [], "deps": [], "features": [], "id": "registry+https://github.com/rust-lang/crates.io-index#bar@0.1.0" }, { "dependencies": [ "registry+https://github.com/rust-lang/crates.io-index#bar@0.1.0", "registry+https://github.com/rust-lang/crates.io-index#winapi@0.1.0" ], "deps": [ { "dep_kinds": [ { "kind": null, "target": null }, { "kind": "dev", "target": null }, { "kind": "build", "target": null } ], "name": "bar", "pkg": "registry+https://github.com/rust-lang/crates.io-index#bar@0.1.0" }, { "dep_kinds": [ { "kind": null, "target": "cfg(windows)" } ], "name": "winapi", "pkg": "registry+https://github.com/rust-lang/crates.io-index#winapi@0.1.0" } ], "features": [], "id": "path+[ROOTURL]/foo#0.1.0" }, { "dependencies": [], "deps": [], "features": [], "id": "registry+https://github.com/rust-lang/crates.io-index#winapi@0.1.0" } ], "root": "path+[ROOTURL]/foo#0.1.0" }, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo#0.1.0" ], "workspace_members": [ "path+[ROOTURL]/foo#0.1.0" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); } #[cargo_test] fn dep_kinds_workspace() { // Check for bug with duplicate dep kinds in a workspace. // If different members select different features for the same package, // they show up multiple times in the resolver `deps`. // // Here: // foo -> dep // bar -> foo[feat1] -> dep let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [features] feat1 = [] [dependencies] dep = { path="dep" } [workspace] members = ["bar"] "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" [dependencies] foo = { path="..", features=["feat1"] } "#, ) .file("bar/src/lib.rs", "") .file("dep/Cargo.toml", &basic_lib_manifest("dep")) .file("dep/src/lib.rs", "") .build(); p.cargo("metadata") .with_stdout_data( str![[r#" { "metadata": null, "packages": "{...}", "resolve": { "nodes": [ { "dependencies": [ "path+[ROOTURL]/foo#0.1.0" ], "deps": [ { "dep_kinds": [ { "kind": null, "target": null } ], "name": "foo", "pkg": "path+[ROOTURL]/foo#0.1.0" } ], "features": [], "id": "path+[ROOTURL]/foo/bar#0.1.0" }, { "dependencies": [], "deps": [], "features": [], "id": "path+[ROOTURL]/foo/dep#0.5.0" }, { "dependencies": [ "path+[ROOTURL]/foo/dep#0.5.0" ], "deps": [ { "dep_kinds": [ { "kind": null, "target": null } ], "name": "dep", "pkg": "path+[ROOTURL]/foo/dep#0.5.0" } ], "features": [ "feat1" ], "id": "path+[ROOTURL]/foo#0.1.0" } ], "root": "path+[ROOTURL]/foo#0.1.0" }, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo#0.1.0" ], "workspace_members": [ "path+[ROOTURL]/foo/bar#0.1.0", "path+[ROOTURL]/foo#0.1.0", "path+[ROOTURL]/foo/dep#0.5.0" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); } #[cargo_test] fn build_dir() { let p = project() .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) .file( ".cargo/config.toml", r#" [build] build-dir = "build-dir" "#, ) .build(); p.cargo("metadata -Z build-dir") .masquerade_as_nightly_cargo(&["build-dir"]) .with_stdout_data( str![[r#" { "build_directory": "[ROOT]/foo/build-dir", "metadata": null, "packages": "{...}", "resolve": "{...}", "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo#0.0.1" ], "workspace_members": [ "path+[ROOTURL]/foo#0.0.1" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); } // Creating non-utf8 path is an OS-specific pain, so let's run this only on // linux, where arbitrary bytes work. #[cfg(target_os = "linux")] #[cargo_test] fn cargo_metadata_non_utf8() { use std::ffi::OsString; use std::os::unix::ffi::OsStringExt; use std::path::PathBuf; let base = PathBuf::from(OsString::from_vec(vec![255])); let p = project() .no_manifest() .file(base.join("./src/lib.rs"), "") .file(base.join("./Cargo.toml"), &basic_lib_manifest("foo")) .build(); p.cargo("metadata") .cwd(p.root().join(base)) .arg("--format-version") .arg("1") .with_stderr_data(str![[r#" [ERROR] path contains invalid UTF-8 characters "#]]) .with_status(101) .run(); } // TODO: Consider using this test instead of the version without the 'artifact' suffix or merge them because they should be pretty much the same. #[cargo_test] fn workspace_metadata_with_dependencies_no_deps_artifact() { let p = project() // NOTE that 'artifact' isn't mentioned in the workspace here, yet it shows up as member. .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] "#, ) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.5.0" authors = ["wycats@example.com"] [dependencies] baz = { path = "../baz/" } baz-renamed = { path = "../baz/" } artifact = { path = "../artifact/", artifact = "bin" } "#, ) .file("bar/src/lib.rs", "") .file("baz/Cargo.toml", &basic_lib_manifest("baz")) .file("baz/src/lib.rs", "") .file("artifact/Cargo.toml", &basic_bin_manifest("artifact")) .file("artifact/src/main.rs", "fn main() {}") .build(); p.cargo("metadata --no-deps -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [ "wycats@example.com" ], "categories": [], "default_run": null, "dependencies": [ { "artifact": { "kinds": [ "bin" ], "lib": false, "target": null }, "features": [], "kind": null, "name": "artifact", "optional": false, "path": "[ROOT]/foo/artifact", "registry": null, "rename": null, "req": "*", "source": null, "target": null, "uses_default_features": true }, { "features": [], "kind": null, "name": "baz", "optional": false, "path": "[ROOT]/foo/baz", "registry": null, "rename": null, "req": "*", "source": null, "target": null, "uses_default_features": true }, { "features": [], "kind": null, "name": "baz-renamed", "optional": false, "path": "[ROOT]/foo/baz", "registry": null, "rename": null, "req": "*", "source": null, "target": null, "uses_default_features": true } ], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo/bar#0.5.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/bar/Cargo.toml", "metadata": null, "name": "bar", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "bar", "src_path": "[ROOT]/foo/bar/src/lib.rs", "test": true } ], "version": "0.5.0" }, { "authors": [ "wycats@example.com" ], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo/artifact#0.5.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/artifact/Cargo.toml", "metadata": null, "name": "artifact", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "bin" ], "doc": true, "doctest": false, "edition": "2015", "kind": [ "bin" ], "name": "artifact", "src_path": "[ROOT]/foo/artifact/src/main.rs", "test": true } ], "version": "0.5.0" }, { "authors": [ "wycats@example.com" ], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo/baz#0.5.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/baz/Cargo.toml", "metadata": null, "name": "baz", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "baz", "src_path": "[ROOT]/foo/baz/src/lib.rs", "test": true } ], "version": "0.5.0" } ], "resolve": null, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo/bar#0.5.0", "path+[ROOTURL]/foo/artifact#0.5.0", "path+[ROOTURL]/foo/baz#0.5.0" ], "workspace_members": [ "path+[ROOTURL]/foo/bar#0.5.0", "path+[ROOTURL]/foo/artifact#0.5.0", "path+[ROOTURL]/foo/baz#0.5.0" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); } #[cargo_test] fn versionless_packages() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] "#, ) .file( "bar/Cargo.toml", r#" [package] name = "bar" [dependencies] foobar = "0.0.1" baz = { path = "../baz/" } "#, ) .file("bar/src/lib.rs", "") .file( "baz/Cargo.toml", r#" [package] name = "baz" [dependencies] foobar = "0.0.1" "#, ) .file("baz/src/lib.rs", "") .build(); Package::new("foobar", "0.0.1").publish(); p.cargo("metadata -q --format-version 1") .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [], "categories": [], "default_run": null, "dependencies": [ { "features": [], "kind": null, "name": "baz", "optional": false, "path": "[ROOT]/foo/baz", "registry": null, "rename": null, "req": "*", "source": null, "target": null, "uses_default_features": true }, { "features": [], "kind": null, "name": "foobar", "optional": false, "registry": null, "rename": null, "req": "^0.0.1", "source": "registry+https://github.com/rust-lang/crates.io-index", "target": null, "uses_default_features": true } ], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo/bar#0.0.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/bar/Cargo.toml", "metadata": null, "name": "bar", "publish": [], "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "bar", "src_path": "[ROOT]/foo/bar/src/lib.rs", "test": true } ], "version": "0.0.0" }, { "authors": [], "categories": [], "default_run": null, "dependencies": [ { "features": [], "kind": null, "name": "foobar", "optional": false, "registry": null, "rename": null, "req": "^0.0.1", "source": "registry+https://github.com/rust-lang/crates.io-index", "target": null, "uses_default_features": true } ], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo/baz#0.0.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/baz/Cargo.toml", "metadata": null, "name": "baz", "publish": [], "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "baz", "src_path": "[ROOT]/foo/baz/src/lib.rs", "test": true } ], "version": "0.0.0" }, { "authors": [], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "registry+https://github.com/rust-lang/crates.io-index#foobar@0.0.1", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/foobar-0.0.1/Cargo.toml", "metadata": null, "name": "foobar", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": "registry+https://github.com/rust-lang/crates.io-index", "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "foobar", "src_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/foobar-0.0.1/src/lib.rs", "test": true } ], "version": "0.0.1" } ], "resolve": { "nodes": [ { "dependencies": [ "path+[ROOTURL]/foo/baz#0.0.0", "registry+https://github.com/rust-lang/crates.io-index#foobar@0.0.1" ], "deps": [ { "dep_kinds": [ { "kind": null, "target": null } ], "name": "baz", "pkg": "path+[ROOTURL]/foo/baz#0.0.0" }, { "dep_kinds": [ { "kind": null, "target": null } ], "name": "foobar", "pkg": "registry+https://github.com/rust-lang/crates.io-index#foobar@0.0.1" } ], "features": [], "id": "path+[ROOTURL]/foo/bar#0.0.0" }, { "dependencies": [ "registry+https://github.com/rust-lang/crates.io-index#foobar@0.0.1" ], "deps": [ { "dep_kinds": [ { "kind": null, "target": null } ], "name": "foobar", "pkg": "registry+https://github.com/rust-lang/crates.io-index#foobar@0.0.1" } ], "features": [], "id": "path+[ROOTURL]/foo/baz#0.0.0" }, { "dependencies": [], "deps": [], "features": [], "id": "registry+https://github.com/rust-lang/crates.io-index#foobar@0.0.1" } ], "root": null }, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo/bar#0.0.0", "path+[ROOTURL]/foo/baz#0.0.0" ], "workspace_members": [ "path+[ROOTURL]/foo/bar#0.0.0", "path+[ROOTURL]/foo/baz#0.0.0" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); } /// Record how TOML-specific types are deserialized by `toml` so we can make sure we know if these change and /// can have a conversation about what should be done. #[cargo_test] fn cargo_metadata_toml_types() { let p = project() .file("src/lib.rs", "") .file( "Cargo.toml", " [package] name = 'foo' edition = '2015' [package.metadata] offset-datetime = 1979-05-27T07:32:00Z local-datetime = 1979-05-27T07:32:00 local-date = 1979-05-27 local-time = 1979-05-27 ", ) .build(); p.cargo("metadata") .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo#0.0.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/Cargo.toml", "metadata": { "local-date": { "$__toml_private_datetime": "1979-05-27" }, "local-datetime": { "$__toml_private_datetime": "1979-05-27T07:32:00" }, "local-time": { "$__toml_private_datetime": "1979-05-27" }, "offset-datetime": { "$__toml_private_datetime": "1979-05-27T07:32:00Z" } }, "name": "foo", "publish": [], "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "foo", "src_path": "[ROOT]/foo/src/lib.rs", "test": true } ], "version": "0.0.0" } ], "resolve": { "nodes": [ { "dependencies": [], "deps": [], "features": [], "id": "path+[ROOTURL]/foo#0.0.0" } ], "root": "path+[ROOTURL]/foo#0.0.0" }, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo#0.0.0" ], "workspace_members": [ "path+[ROOTURL]/foo#0.0.0" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); } #[cargo_test] fn metadata_ignores_build_target_configuration() -> anyhow::Result<()> { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" [target.'cfg(something)'.dependencies] foobar = "0.0.1" "#, ) .file("src/lib.rs", "") .build(); Package::new("foobar", "0.0.1").publish(); let output1 = p .cargo("metadata -q --format-version 1") .exec_with_output()?; let output2 = p .cargo("metadata -q --format-version 1") .env("CARGO_BUILD_TARGET", rustc_host()) .exec_with_output()?; assert!( output1.stdout == output2.stdout, "metadata should not change when `CARGO_BUILD_TARGET` is set", ); Ok(()) } cargo-0.91.0/tests/testsuite/minimal_versions.rs000064400000000000000000000023001046102023000201200ustar 00000000000000//! Tests for minimal-version resolution. //! //! Note: Some tests are located in the resolver-tests package. use crate::prelude::*; use cargo_test_support::project; use cargo_test_support::registry::Package; use cargo_test_support::str; // Ensure that the "-Z minimal-versions" CLI option works and the minimal // version of a dependency ends up in the lock file. #[cargo_test] fn minimal_version_cli() { Package::new("dep", "1.0.0").publish(); Package::new("dep", "1.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.1" [dependencies] dep = "1.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("generate-lockfile -Zminimal-versions") .masquerade_as_nightly_cargo(&["minimal-versions"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to earliest compatible version [ADDING] dep v1.0.0 (available: v1.1.0) "#]]) .run(); let lock = p.read_lockfile(); assert!(!lock.contains("1.1.0")); } cargo-0.91.0/tests/testsuite/multitarget.rs000064400000000000000000000143631046102023000171170ustar 00000000000000//! Tests for multiple `--target` flags to subcommands use crate::prelude::*; use crate::utils::cross_compile::{ can_run_on_host as cross_compile_can_run_on_host, disabled as cross_compile_disabled, }; use cargo_test_support::{basic_manifest, cross_compile, project, rustc_host, str}; #[cargo_test] fn simple_build() { if cross_compile_disabled() { return; } let t1 = cross_compile::alternate(); let t2 = rustc_host(); let p = project() .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build") .arg("--target") .arg(&t1) .arg("--target") .arg(&t2) .run(); assert!(p.target_bin(t1, "foo").is_file()); assert!(p.target_bin(t2, "foo").is_file()); } #[cargo_test] fn simple_build_with_config() { if cross_compile_disabled() { return; } let t1 = cross_compile::alternate(); let t2 = rustc_host(); let p = project() .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) .file("src/main.rs", "fn main() {}") .file( ".cargo/config.toml", &format!( r#" [build] target = ["{t1}", "{t2}"] "# ), ) .build(); p.cargo("build").run(); assert!(p.target_bin(t1, "foo").is_file()); assert!(p.target_bin(t2, "foo").is_file()); } #[cargo_test] fn simple_test() { if !cross_compile_can_run_on_host() { return; } let t1 = cross_compile::alternate(); let t2 = rustc_host(); let p = project() .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) .file("src/lib.rs", "fn main() {}") .build(); p.cargo("test") .arg("--target") .arg(&t1) .arg("--target") .arg(&t2) .with_stderr_data( str![[r#" [RUNNING] unittests src/lib.rs (target/[ALT_TARGET]/debug/deps/foo-[HASH][EXE]) [RUNNING] unittests src/lib.rs (target/[HOST_TARGET]/debug/deps/foo-[HASH][EXE]) ... "#]] .unordered(), ) .run(); } #[cargo_test] fn simple_run() { let p = project() .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) .file("src/main.rs", "fn main() {}") .build(); p.cargo("run --target a --target b") .with_stderr_data(str![[r#" [ERROR] only one `--target` argument is supported "#]]) .with_status(101) .run(); } #[cargo_test] fn simple_doc() { if cross_compile_disabled() { return; } let t1 = cross_compile::alternate(); let t2 = rustc_host(); let p = project() .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) .file("src/lib.rs", "//! empty lib") .build(); p.cargo("doc") .arg("--target") .arg(&t1) .arg("--target") .arg(&t2) .run(); assert!(p.build_dir().join(&t1).join("doc/foo/index.html").is_file()); assert!(p.build_dir().join(&t2).join("doc/foo/index.html").is_file()); } #[cargo_test] fn simple_doc_open() { if cross_compile_disabled() { return; } let t1 = cross_compile::alternate(); let t2 = rustc_host(); let p = project() .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) .file("src/lib.rs", "//! empty lib") .build(); p.cargo("doc") .arg("--open") .arg("--target") .arg(&t1) .arg("--target") .arg(&t2) .with_stderr_data(str![[r#" [DOCUMENTING] foo v1.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [ERROR] only one `--target` argument is supported "#]]) .with_status(101) .run(); } #[cargo_test] fn simple_check() { if cross_compile_disabled() { return; } let t1 = cross_compile::alternate(); let t2 = rustc_host(); let p = project() .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .arg("--target") .arg(&t1) .arg("--target") .arg(&t2) .run(); } #[cargo_test] fn same_value_twice() { if cross_compile_disabled() { return; } let t = rustc_host(); let p = project() .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build") .arg("--target") .arg(&t) .arg("--target") .arg(&t) .run(); assert!(p.target_bin(t, "foo").is_file()); } #[cargo_test] fn same_value_twice_with_config() { if cross_compile_disabled() { return; } let t = rustc_host(); let p = project() .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) .file("src/main.rs", "fn main() {}") .file( ".cargo/config.toml", &format!( r#" [build] target = ["{t}", "{t}"] "# ), ) .build(); p.cargo("build").run(); assert!(p.target_bin(t, "foo").is_file()); } #[cargo_test] fn works_with_config_in_both_string_or_list() { if cross_compile_disabled() { return; } let t = rustc_host(); let p = project() .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) .file("src/main.rs", "fn main() {}") .file( ".cargo/config.toml", &format!( r#" [build] target = "{t}" "# ), ) .build(); p.cargo("build").run(); assert!(p.target_bin(t, "foo").is_file()); p.cargo("clean").run(); p.change_file( ".cargo/config.toml", &format!( r#" [build] target = ["{t}"] "# ), ); p.cargo("build").run(); assert!(p.target_bin(t, "foo").is_file()); } #[cargo_test] fn works_with_env() { let t = rustc_host(); let p = project() .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build").env("CARGO_BUILD_TARGET", t).run(); assert!(p.target_bin(t, "foo").is_file()); } cargo-0.91.0/tests/testsuite/net_config.rs000064400000000000000000000031751046102023000166700ustar 00000000000000//! Tests for network configuration. use crate::prelude::*; use cargo_test_support::project; use cargo_test_support::str; #[cargo_test] fn net_retry_loads_from_config() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [dependencies.bar] git = "http://127.0.0.1:11/foo/bar" "#, ) .file("src/main.rs", "") .file( ".cargo/config.toml", r#" [net] retry=1 [http] timeout=1 "#, ) .build(); p.cargo("check -v") .with_status(101) .with_stderr_data(str![[r#" ... [WARNING] spurious network error (1 try remaining): [..] ... "#]]) .run(); } #[cargo_test] fn net_retry_git_outputs_warning() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [dependencies.bar] git = "http://127.0.0.1:11/foo/bar" "#, ) .file( ".cargo/config.toml", r#" [http] timeout=1 "#, ) .file("src/main.rs", "") .build(); p.cargo("check -v -j 1") .with_status(101) .with_stderr_data(str![[r#" ... [WARNING] spurious network error (2 tries remaining): [..] [WARNING] spurious network error (1 try remaining): [..] ... "#]]) .run(); } cargo-0.91.0/tests/testsuite/new.rs000064400000000000000000000440111046102023000153400ustar 00000000000000//! Tests for the `cargo new` command. use std::env; use std::fs::{self, File}; use crate::prelude::*; use crate::utils::cargo_process; use cargo_test_support::paths; use cargo_test_support::str; fn create_default_gitconfig() { // This helps on Windows where libgit2 is very aggressive in attempting to // find a git config file. let gitconfig = paths::home().join(".gitconfig"); File::create(gitconfig).unwrap(); // If we're running this under a user account that has a different default branch set up // then tests that assume the default branch is master will fail. We set the default branch // to master explicitly so that tests that rely on this behavior still pass. fs::write( paths::home().join(".gitconfig"), r#" [init] defaultBranch = master "#, ) .unwrap(); } #[cargo_test] fn simple_lib() { cargo_process("new --lib foo --vcs none --edition 2015") .with_stderr_data(str![[r#" [CREATING] library `foo` package [NOTE] see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html "#]]) .run(); assert!(paths::root().join("foo").is_dir()); assert!(paths::root().join("foo/Cargo.toml").is_file()); assert!(paths::root().join("foo/src/lib.rs").is_file()); assert!(!paths::root().join("foo/.gitignore").is_file()); let lib = paths::root().join("foo/src/lib.rs"); let contents = fs::read_to_string(&lib).unwrap(); assert_eq!( contents, r#"pub fn add(left: u64, right: u64) -> u64 { left + right } #[cfg(test)] mod tests { use super::*; #[test] fn it_works() { let result = add(2, 2); assert_eq!(result, 4); } } "# ); cargo_process("build").cwd(&paths::root().join("foo")).run(); } #[cargo_test] fn simple_bin() { cargo_process("new --bin foo --edition 2015") .with_stderr_data(str![[r#" [CREATING] binary (application) `foo` package [NOTE] see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html "#]]) .run(); assert!(paths::root().join("foo").is_dir()); assert!(paths::root().join("foo/Cargo.toml").is_file()); assert!(paths::root().join("foo/src/main.rs").is_file()); cargo_process("build").cwd(&paths::root().join("foo")).run(); assert!( paths::root() .join(&format!("foo/target/debug/foo{}", env::consts::EXE_SUFFIX)) .is_file() ); } #[cargo_test] fn both_lib_and_bin() { cargo_process("new --lib --bin foo") .with_status(101) .with_stderr_data(str![[r#" [ERROR] can't specify both lib and binary outputs "#]]) .run(); } #[cargo_test] fn simple_git() { cargo_process("new --lib foo --edition 2015").run(); assert!(paths::root().is_dir()); assert!(paths::root().join("foo/Cargo.toml").is_file()); assert!(paths::root().join("foo/src/lib.rs").is_file()); assert!(paths::root().join("foo/.git").is_dir()); assert!(paths::root().join("foo/.gitignore").is_file()); let fp = paths::root().join("foo/.gitignore"); let contents = fs::read_to_string(&fp).unwrap(); assert_eq!(contents, "/target\n",); cargo_process("build").cwd(&paths::root().join("foo")).run(); } #[cargo_test(requires = "hg")] fn simple_hg() { cargo_process("new --lib foo --edition 2015 --vcs hg").run(); assert!(paths::root().is_dir()); assert!(paths::root().join("foo/Cargo.toml").is_file()); assert!(paths::root().join("foo/src/lib.rs").is_file()); assert!(paths::root().join("foo/.hg").is_dir()); assert!(paths::root().join("foo/.hgignore").is_file()); let fp = paths::root().join("foo/.hgignore"); let contents = fs::read_to_string(&fp).unwrap(); assert_eq!(contents, "^target$\n",); cargo_process("build").cwd(&paths::root().join("foo")).run(); } #[cargo_test] fn no_argument() { cargo_process("new") .with_status(1) .with_stderr_data(str![[r#" [ERROR] the following required arguments were not provided: ... "#]]) .run(); } #[cargo_test] fn existing() { let dst = paths::root().join("foo"); fs::create_dir(&dst).unwrap(); cargo_process("new foo") .with_status(101) .with_stderr_data(str![[r#" [CREATING] binary (application) `foo` package [ERROR] destination `[ROOT]/foo` already exists Use `cargo init` to initialize the directory "#]]) .run(); } #[cargo_test] fn invalid_characters() { cargo_process("new foo.rs") .with_status(101) .with_stderr_data(str![[r#" [CREATING] binary (application) `foo.rs` package [ERROR] invalid character `.` in package name: `foo.rs`, characters must be Unicode XID characters (numbers, `-`, `_`, or most letters) If you need a package name to not match the directory name, consider using --name flag. If you need a binary with the name "foo.rs", use a valid package name, and set the binary name to be different from the package. This can be done by setting the binary filename to `src/bin/foo.rs.rs` or change the name in Cargo.toml with: [[bin]] name = "foo.rs" path = "src/main.rs" "#]]) .run(); } #[cargo_test] fn reserved_name() { cargo_process("new test") .with_status(101) .with_stderr_data(str![[r#" [CREATING] binary (application) `test` package [ERROR] the name `test` cannot be used as a package name, it conflicts with Rust's built-in test library If you need a package name to not match the directory name, consider using --name flag. If you need a binary with the name "test", use a valid package name, and set the binary name to be different from the package. This can be done by setting the binary filename to `src/bin/test.rs` or change the name in Cargo.toml with: [[bin]] name = "test" path = "src/main.rs" "#]]) .run(); } #[cargo_test] fn reserved_binary_name() { cargo_process("new --bin incremental") .with_status(101) .with_stderr_data(str![[r#" [CREATING] binary (application) `incremental` package [ERROR] the name `incremental` cannot be used as a package name, it conflicts with cargo's build directory names If you need a package name to not match the directory name, consider using --name flag. "#]]) .run(); cargo_process("new --lib incremental") .with_stderr_data(str![[r#" [CREATING] library `incremental` package [WARNING] the name `incremental` will not support binary executables with that name, it conflicts with cargo's build directory names [NOTE] see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html "#]]) .run(); } #[cargo_test] fn keyword_name() { cargo_process("new pub") .with_status(101) .with_stderr_data(str![[r#" [CREATING] binary (application) `pub` package [ERROR] the name `pub` cannot be used as a package name, it is a Rust keyword If you need a package name to not match the directory name, consider using --name flag. If you need a binary with the name "pub", use a valid package name, and set the binary name to be different from the package. This can be done by setting the binary filename to `src/bin/pub.rs` or change the name in Cargo.toml with: [[bin]] name = "pub" path = "src/main.rs" "#]]) .run(); } #[cargo_test] fn std_name() { cargo_process("new core").with_stderr_data(str![[r#" [CREATING] binary (application) `core` package [WARNING] the name `core` is part of Rust's standard library It is recommended to use a different name to avoid problems. If you need a package name to not match the directory name, consider using --name flag. If you need a binary with the name "core", use a valid package name, and set the binary name to be different from the package. This can be done by setting the binary filename to `src/bin/core.rs` or change the name in Cargo.toml with: [[bin]] name = "core" path = "src/main.rs" [NOTE] see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html "#]]).run(); } #[cargo_test] fn git_prefers_command_line() { let root = paths::root(); fs::create_dir(&root.join(".cargo")).unwrap(); fs::write( &root.join(".cargo/config.toml"), r#" [cargo-new] vcs = "none" name = "foo" email = "bar" "#, ) .unwrap(); cargo_process("new foo --vcs git").run(); assert!(paths::root().join("foo/.gitignore").exists()); assert!( !fs::read_to_string(paths::root().join("foo/Cargo.toml")) .unwrap() .contains("authors =") ); } #[cargo_test] fn subpackage_no_git() { cargo_process("new foo").run(); assert!(paths::root().join("foo/.git").is_dir()); assert!(paths::root().join("foo/.gitignore").is_file()); let subpackage = paths::root().join("foo").join("components"); fs::create_dir(&subpackage).unwrap(); cargo_process("new foo/components/subcomponent").run(); assert!( !paths::root() .join("foo/components/subcomponent/.git") .is_file() ); assert!( !paths::root() .join("foo/components/subcomponent/.gitignore") .is_file() ); } #[cargo_test] fn subpackage_git_with_gitignore() { cargo_process("new foo").run(); assert!(paths::root().join("foo/.git").is_dir()); assert!(paths::root().join("foo/.gitignore").is_file()); let gitignore = paths::root().join("foo/.gitignore"); fs::write(gitignore, b"components").unwrap(); let subpackage = paths::root().join("foo/components"); fs::create_dir(&subpackage).unwrap(); cargo_process("new foo/components/subcomponent").run(); assert!( paths::root() .join("foo/components/subcomponent/.git") .is_dir() ); assert!( paths::root() .join("foo/components/subcomponent/.gitignore") .is_file() ); } #[cargo_test] fn subpackage_git_with_vcs_arg() { cargo_process("new foo").run(); let subpackage = paths::root().join("foo").join("components"); fs::create_dir(&subpackage).unwrap(); cargo_process("new foo/components/subcomponent --vcs git").run(); assert!( paths::root() .join("foo/components/subcomponent/.git") .is_dir() ); assert!( paths::root() .join("foo/components/subcomponent/.gitignore") .is_file() ); } #[cargo_test] fn unknown_flags() { cargo_process("new foo --flag") .with_status(1) .with_stderr_data(str![[r#" [ERROR] unexpected argument '--flag' found ... "#]]) .run(); } #[cargo_test] fn explicit_invalid_name_not_suggested() { cargo_process("new --name 10-invalid a") .with_status(101) .with_stderr_data(str![[r#" [CREATING] binary (application) `10-invalid` package [ERROR] invalid character `1` in package name: `10-invalid`, the name cannot start with a digit If you need a binary with the name "10-invalid", use a valid package name, and set the binary name to be different from the package. This can be done by setting the binary filename to `src/bin/10-invalid.rs` or change the name in Cargo.toml with: [[bin]] name = "10-invalid" path = "src/main.rs" "#]]) .run(); } #[cargo_test] fn explicit_project_name() { cargo_process("new --lib foo --name bar") .with_stderr_data(str![[r#" [CREATING] library `bar` package [NOTE] see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html "#]]) .run(); } #[cargo_test] fn new_with_edition_2015() { cargo_process("new --edition 2015 foo").run(); let manifest = fs::read_to_string(paths::root().join("foo/Cargo.toml")).unwrap(); assert!(manifest.contains("edition = \"2015\"")); } #[cargo_test] fn new_with_edition_2018() { cargo_process("new --edition 2018 foo").run(); let manifest = fs::read_to_string(paths::root().join("foo/Cargo.toml")).unwrap(); assert!(manifest.contains("edition = \"2018\"")); } #[cargo_test] fn new_default_edition() { cargo_process("new foo").run(); let manifest = fs::read_to_string(paths::root().join("foo/Cargo.toml")).unwrap(); assert!(manifest.contains("edition = \"2024\"")); } #[cargo_test] fn new_with_bad_edition() { cargo_process("new --edition something_else foo") .with_stderr_data(str![[r#" [ERROR] invalid value 'something_else' for '--edition ' ... "#]]) .with_status(1) .run(); } #[cargo_test] fn lockfile_constant_during_new() { cargo_process("new foo").run(); cargo_process("build").cwd(&paths::root().join("foo")).run(); let before = fs::read_to_string(paths::root().join("foo/Cargo.lock")).unwrap(); cargo_process("build").cwd(&paths::root().join("foo")).run(); let after = fs::read_to_string(paths::root().join("foo/Cargo.lock")).unwrap(); assert_eq!(before, after); } #[cargo_test] fn restricted_windows_name() { if cfg!(windows) { cargo_process("new nul") .with_status(101) .with_stderr_data(str![[r#" [CREATING] binary (application) `nul` package [ERROR] cannot use name `nul`, it is a reserved Windows filename If you need a package name to not match the directory name, consider using --name flag. "#]]) .run(); } else { cargo_process("new nul").with_stderr_data(str![[r#" [CREATING] binary (application) `nul` package [WARNING] the name `nul` is a reserved Windows filename This package will not work on Windows platforms. [NOTE] see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html "#]]).run(); } } #[cargo_test] fn non_ascii_name() { cargo_process("new Привет").with_stderr_data(str![[r#" [CREATING] binary (application) `Привет` package [WARNING] the name `Привет` contains non-ASCII characters Non-ASCII crate names are not supported by Rust. [WARNING] the name `Привет` is not snake_case or kebab-case which is recommended for package names, consider `привет` [NOTE] see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html "#]]).run(); } #[cargo_test] fn non_ascii_name_invalid() { // These are alphanumeric characters, but not Unicode XID. cargo_process("new ⒶⒷⒸ") .with_status(101) .with_stderr_data(str![[r#" [CREATING] binary (application) `ⒶⒷⒸ` package [ERROR] invalid character `Ⓐ` in package name: `ⒶⒷⒸ`, the first character must be a Unicode XID start character (most letters or `_`) If you need a package name to not match the directory name, consider using --name flag. If you need a binary with the name "ⒶⒷⒸ", use a valid package name, and set the binary name to be different from the package. This can be done by setting the binary filename to `src/bin/ⒶⒷⒸ.rs` or change the name in Cargo.toml with: [[bin]] name = "ⒶⒷⒸ" path = "src/main.rs" "#]]) .run(); cargo_process("new a¼") .with_status(101) .with_stderr_data(str![[r#" [CREATING] binary (application) `a¼` package [ERROR] invalid character `¼` in package name: `a¼`, characters must be Unicode XID characters (numbers, `-`, `_`, or most letters) If you need a package name to not match the directory name, consider using --name flag. If you need a binary with the name "a¼", use a valid package name, and set the binary name to be different from the package. This can be done by setting the binary filename to `src/bin/a¼.rs` or change the name in Cargo.toml with: [[bin]] name = "a¼" path = "src/main.rs" "#]]) .run(); } #[cargo_test] fn non_snake_case_name() { cargo_process("new UPPERcase_name") .with_stderr_data(str![[r#" [CREATING] binary (application) `UPPERcase_name` package [WARNING] the name `UPPERcase_name` is not snake_case or kebab-case which is recommended for package names, consider `uppercase_name` [NOTE] see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html "#]]) .run(); } #[cargo_test] fn kebab_case_name_is_accepted() { cargo_process("new kebab-case-is-valid") .with_stderr_data(str![[r#" [CREATING] binary (application) `kebab-case-is-valid` package [NOTE] see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html "#]]) .run(); } #[cargo_test] fn git_default_branch() { // Check for init.defaultBranch support. create_default_gitconfig(); cargo_process("new foo").run(); let repo = git2::Repository::open(paths::root().join("foo")).unwrap(); let head = repo.find_reference("HEAD").unwrap(); assert_eq!(head.symbolic_target().unwrap(), "refs/heads/master"); fs::write( paths::home().join(".gitconfig"), r#" [init] defaultBranch = hello "#, ) .unwrap(); cargo_process("new bar").run(); let repo = git2::Repository::open(paths::root().join("bar")).unwrap(); let head = repo.find_reference("HEAD").unwrap(); assert_eq!(head.symbolic_target().unwrap(), "refs/heads/hello"); } #[cargo_test] fn non_utf8_str_in_ignore_file() { let gitignore = paths::home().join(".gitignore"); File::create(gitignore).unwrap(); fs::write(paths::home().join(".gitignore"), &[0xFF, 0xFE]).unwrap(); cargo_process(&format!("init {} --vcs git", paths::home().display())) .with_status(101) .with_stderr_data(str![[r#" [CREATING] binary (application) package [ERROR] Failed to create package `home` at `[ROOT]/home` Caused by: Character at line 0 is invalid. Cargo only supports UTF-8. "#]]) .run(); } #[cfg(unix)] #[cargo_test] fn path_with_invalid_character() { cargo_process("new --name testing test:ing") .with_stderr_data(str![[r#" [CREATING] binary (application) `testing` package [WARNING] the path `[ROOT]/test:ing` contains invalid PATH characters (usually `:`, `;`, or `"`) It is recommended to use a different name to avoid problems. [NOTE] see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html "#]]) .run(); } cargo-0.91.0/tests/testsuite/offline.rs000064400000000000000000000510261046102023000161750ustar 00000000000000//! Tests for --offline flag. use std::fs; use crate::prelude::*; use cargo_test_support::{ Execs, basic_manifest, git, main_file, project, registry::{Package, RegistryBuilder}, str, }; #[cargo_test] fn offline_unused_target_dep() { // --offline with a target dependency that is not used and not downloaded. Package::new("unused_dep", "1.0.0").publish(); Package::new("used_dep", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] used_dep = "1.0" [target.'cfg(unused)'.dependencies] unused_dep = "1.0" "#, ) .file("src/lib.rs", "") .build(); // Do a build that downloads only what is necessary. p.cargo("check") .with_stderr_data(str![[r#" ... [DOWNLOADED] used_dep v1.0.0 (registry `dummy-registry`) ... "#]]) .with_stderr_does_not_contain("[DOWNLOADED] unused_dep [..]") .run(); p.cargo("clean").run(); // Build offline, make sure it works. p.cargo("check --offline").run(); } #[cargo_test] fn offline_missing_optional() { Package::new("opt_dep", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] opt_dep = { version = "1.0", optional = true } "#, ) .file("src/lib.rs", "") .build(); // Do a build that downloads only what is necessary. p.cargo("check") .with_stderr_does_not_contain("[DOWNLOADED] opt_dep [..]") .run(); p.cargo("clean").run(); // Build offline, make sure it works. p.cargo("check --offline").run(); p.cargo("check --offline --features=opt_dep") .with_stderr_data(str![[r#" [ERROR] failed to download `opt_dep v1.0.0` Caused by: attempting to make an HTTP request, but --offline was specified "#]]) .with_status(101) .run(); } #[cargo_test] fn cargo_compile_path_with_offline() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "bar" "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "") .build(); p.cargo("check --offline").run(); } #[cargo_test] fn cargo_compile_with_downloaded_dependency_with_offline() { Package::new("present_dep", "1.2.3") .file("Cargo.toml", &basic_manifest("present_dep", "1.2.3")) .file("src/lib.rs", "") .publish(); // make package downloaded let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] present_dep = "1.2.3" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check").run(); let p2 = project() .at("bar") .file( "Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" [dependencies] present_dep = "1.2.3" "#, ) .file("src/lib.rs", "") .build(); p2.cargo("check --offline") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [CHECKING] present_dep v1.2.3 [CHECKING] bar v0.1.0 ([ROOT]/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn cargo_compile_offline_not_try_update() { // When --offline needs to download the registry, provide a reasonable // error hint to run without --offline. let p = project() .at("bar") .file( "Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" [dependencies] not_cached_dep = "1.2.5" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check --offline") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no matching package named `not_cached_dep` found location searched: crates.io index required by package `bar v0.1.0 ([ROOT]/bar)` As a reminder, you're using offline mode (--offline) which can sometimes cause surprising resolution failures, if this error is too confusing you may wish to retry without `--offline`. "#]]) .run(); // While we're here, also check the config works. p.change_file(".cargo/config.toml", "net.offline = true"); p.cargo("check").with_status(101).with_stderr_data(str![[r#" [ERROR] no matching package named `not_cached_dep` found location searched: crates.io index required by package `bar v0.1.0 ([ROOT]/bar)` As a reminder, you're using offline mode (--offline) which can sometimes cause surprising resolution failures, if this error is too confusing you may wish to retry without `--offline`. "#]]).run(); } #[cargo_test] fn compile_offline_without_maxvers_cached() { Package::new("present_dep", "1.2.1").publish(); Package::new("present_dep", "1.2.2").publish(); Package::new("present_dep", "1.2.3") .file("Cargo.toml", &basic_manifest("present_dep", "1.2.3")) .file( "src/lib.rs", r#"pub fn get_version()->&'static str {"1.2.3"}"#, ) .publish(); Package::new("present_dep", "1.2.5") .file("Cargo.toml", &basic_manifest("present_dep", "1.2.5")) .file("src/lib.rs", r#"pub fn get_version(){"1.2.5"}"#) .publish(); // make package cached let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] present_dep = "=1.2.3" "#, ) .file("src/lib.rs", "") .build(); p.cargo("build").run(); let p2 = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] present_dep = "1.2" "#, ) .file( "src/main.rs", "\ extern crate present_dep; fn main(){ println!(\"{}\", present_dep::get_version()); }", ) .build(); p2.cargo("run --offline") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] present_dep v1.2.3 [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .with_stdout_data(str![[r#" 1.2.3 "#]]) .run(); } #[cargo_test] fn cargo_compile_forbird_git_httpsrepo_offline() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["chabapok@example.com"] [dependencies.dep1] git = 'https://github.com/some_user/dep1.git' "#, ) .file("src/main.rs", "") .build(); p.cargo("check --offline").with_status(101).with_stderr_data(str![[r#" [ERROR] failed to get `dep1` as a dependency of package `foo v0.5.0 ([ROOT]/foo)` Caused by: failed to load source for dependency `dep1` Caused by: Unable to update https://github.com/some_user/dep1.git Caused by: can't checkout from 'https://github.com/some_user/dep1.git': you are in the offline mode (--offline) "#]]).run(); } #[cargo_test] fn compile_offline_while_transitive_dep_not_cached() { let baz = Package::new("baz", "1.0.0"); let baz_path = baz.archive_dst(); baz.publish(); let baz_content = fs::read(&baz_path).unwrap(); // Truncate the file to simulate a download failure. fs::write(&baz_path, &[]).unwrap(); Package::new("bar", "0.1.0").dep("baz", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = "0.1.0" "#, ) .file("src/main.rs", "fn main(){}") .build(); // simulate download bar, but fail to download baz p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" ... Caused by: failed to verify the checksum of `baz v1.0.0 (registry `dummy-registry`)` "#]]) .run(); // Restore the file contents. fs::write(&baz_path, &baz_content).unwrap(); p.cargo("check --offline") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to download `bar v0.1.0` Caused by: attempting to make an HTTP request, but --offline was specified "#]]) .run(); } fn update_offline_not_cached() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "*" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("update --offline") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no matching package named `bar` found location searched: [..] required by package `foo v0.0.1 ([ROOT]/foo)` As a reminder, you're using offline mode (--offline) which can sometimes cause surprising resolution failures, if this error is too confusing you may wish to retry without `--offline`. "#]]) .run(); } #[cargo_test] fn update_offline_not_cached_sparse() { let _registry = RegistryBuilder::new().http_index().build(); update_offline_not_cached() } #[cargo_test] fn update_offline_not_cached_git() { update_offline_not_cached() } #[cargo_test] fn cargo_compile_offline_with_cached_git_dep() { compile_offline_with_cached_git_dep(false) } #[cargo_test] fn gitoxide_cargo_compile_offline_with_cached_git_dep_shallow_dep() { compile_offline_with_cached_git_dep(true) } fn compile_offline_with_cached_git_dep(shallow: bool) { let git_project = git::new("dep1", |project| { project .file("Cargo.toml", &basic_manifest("dep1", "0.5.0")) .file( "src/lib.rs", r#" pub static COOL_STR:&str = "cached git repo rev1"; "#, ) }); let repo = git2::Repository::open(&git_project.root()).unwrap(); let rev1 = repo.revparse_single("HEAD").unwrap().id(); // Commit the changes and make sure we trigger a recompile git_project.change_file( "src/lib.rs", r#"pub static COOL_STR:&str = "cached git repo rev2";"#, ); git::add(&repo); let rev2 = git::commit(&repo); // cache to registry rev1 and rev2 let prj = project() .at("cache_git_dep") .file( "Cargo.toml", &format!( r#" [package] name = "cache_git_dep" version = "0.5.0" edition = "2015" [dependencies.dep1] git = '{}' rev = "{}" "#, git_project.url(), rev1 ), ) .file("src/main.rs", "fn main(){}") .build(); let maybe_use_shallow = |mut cargo: Execs| -> Execs { if shallow { cargo .arg("-Zgitoxide=fetch") .arg("-Zgit=shallow-deps") .masquerade_as_nightly_cargo(&[ "unstable features must be available for -Z gitoxide and -Z git", ]); } cargo }; maybe_use_shallow(prj.cargo("build")).run(); prj.change_file( "Cargo.toml", &format!( r#" [package] name = "cache_git_dep" version = "0.5.0" edition = "2015" [dependencies.dep1] git = '{}' rev = "{}" "#, git_project.url(), rev2 ), ); maybe_use_shallow(prj.cargo("build")).run(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" [dependencies.dep1] git = '{}' "#, git_project.url() ), ) .file( "src/main.rs", &main_file(r#""hello from {}", dep1::COOL_STR"#, &["dep1"]), ) .build(); let mut cargo = p.cargo("build --offline"); cargo.with_stderr_data(format!( "\ [LOCKING] 1 package to latest compatible version [COMPILING] dep1 v0.5.0 ([ROOTURL]/dep1#[..]) [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s ", )); maybe_use_shallow(cargo).run(); assert!(p.bin("foo").is_file()); p.process(&p.bin("foo")) .with_stdout_data("hello from cached git repo rev2\n") .run(); p.change_file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" [dependencies.dep1] git = '{}' rev = "{}" "#, git_project.url(), rev1 ), ); maybe_use_shallow(p.cargo("build --offline")).run(); p.process(&p.bin("foo")) .with_stdout_data("hello from cached git repo rev1\n") .run(); } #[cargo_test] fn offline_resolve_optional_fail() { // Example where resolve fails offline. // // This happens if at least 1 version of an optional dependency is // available, but none of them satisfy the requirements. The current logic // that handles this is `RegistryIndex::query_inner`, and it doesn't know // if the package being queried is an optional one. This is not ideal, it // would be best if it just ignored optional (unselected) dependencies. Package::new("dep", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] dep = { version = "1.0", optional = true } "#, ) .file("src/lib.rs", "") .build(); p.cargo("fetch").run(); // Change dep to 2.0. p.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] dep = { version = "2.0", optional = true } "#, ); p.cargo("check --offline") .with_status(101) .with_stderr_data( str![[r#" [ERROR] failed to select a version for the requirement `dep = "^2.0"` candidate versions found which didn't match: 1.0.0 location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.1.0 ([ROOT]/foo)` perhaps a crate was updated and forgotten to be re-vendored? As a reminder, you're using offline mode (--offline) which can sometimes cause surprising resolution failures, if this error is too confusing you may wish to retry without `--offline`. "#]] ) .run(); } #[cargo_test] fn offline_with_all_patched() { // Offline works if everything is patched. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] dep = "1.0" [patch.crates-io] dep = {path = "dep"} "#, ) .file("src/lib.rs", "pub fn f() { dep::foo(); }") .file("dep/Cargo.toml", &basic_manifest("dep", "1.0.0")) .file("dep/src/lib.rs", "pub fn foo() {}") .build(); p.cargo("check --offline").run(); } #[cargo_test] fn update_offline_cached() { // Cache a few versions to update against let p = project().file("src/lib.rs", "").build(); let versions = ["1.2.3", "1.2.5", "1.2.9"]; for vers in versions.iter() { Package::new("present_dep", vers) .file("Cargo.toml", &basic_manifest("present_dep", vers)) .file( "src/lib.rs", format!(r#"pub fn get_version()->&'static str {{ "{}" }}"#, vers).as_str(), ) .publish(); // make package cached p.change_file( "Cargo.toml", format!( r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] present_dep = "={}" "#, vers ) .as_str(), ); p.cargo("build").run(); } let p2 = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] present_dep = "1.2" "#, ) .file( "src/main.rs", "\ extern crate present_dep; fn main(){ println!(\"{}\", present_dep::get_version()); }", ) .build(); p2.cargo("build --offline") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] present_dep v1.2.9 [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p2.rename_run("foo", "with_1_2_9") .with_stdout_data(str![[r#" 1.2.9 "#]]) .run(); // updates happen without updating the index p2.cargo("update present_dep --precise 1.2.3 --offline") .with_status(0) .with_stderr_data(str![[r#" [DOWNGRADING] present_dep v1.2.9 -> v1.2.3 "#]]) .run(); p2.cargo("build --offline") .with_stderr_data(str![[r#" [COMPILING] present_dep v1.2.3 [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p2.rename_run("foo", "with_1_2_3") .with_stdout_data(str![[r#" 1.2.3 "#]]) .run(); // Offline update should only print package details and not index updating p2.cargo("update --offline") .with_status(0) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [UPDATING] present_dep v1.2.3 -> v1.2.9 "#]]) .run(); // No v1.2.8 loaded into the cache so expect failure. p2.cargo("update present_dep --precise 1.2.8 --offline") .with_status(101) .with_stderr_data( str![[r#" [ERROR] no matching package named `present_dep` found location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.1.0 ([ROOT]/foo)` As a reminder, you're using offline mode (--offline) which can sometimes cause surprising resolution failures, if this error is too confusing you may wish to retry without `--offline`. "#]] ) .run(); } #[cargo_test] fn offline_and_frozen_and_no_lock() { let p = project().file("src/lib.rs", "").build(); p.cargo("check --frozen --offline") .with_status(101) .with_stderr_data(str![[r#" [ERROR] the lock file [ROOT]/foo/Cargo.lock needs to be updated but --frozen was passed to prevent this If you want to try to generate the lock file without accessing the network, remove the --frozen flag and use --offline instead. "#]]) .run(); } #[cargo_test] fn offline_and_locked_and_no_frozen() { let p = project().file("src/lib.rs", "").build(); p.cargo("check --locked --offline") .with_status(101) .with_stderr_data(str![[r#" [ERROR] the lock file [ROOT]/foo/Cargo.lock needs to be updated but --locked was passed to prevent this If you want to try to generate the lock file without accessing the network, remove the --locked flag and use --offline instead. "#]]) .run(); } cargo-0.91.0/tests/testsuite/old_cargos.rs000064400000000000000000000643251046102023000166750ustar 00000000000000//! Tests for checking behavior of old cargos. //! //! These tests are ignored because it is intended to be run on a developer //! system with a bunch of toolchains installed. This requires `rustup` to be //! installed. It will iterate over installed toolchains, and run some tests //! over each one, producing a report at the end. As of this writing, I have //! tested 1.0 to 1.51. Run this with: //! //! ```console //! cargo test --test testsuite -- old_cargos --nocapture --ignored //! ``` use std::fs; use crate::prelude::*; use crate::utils::cargo_exe; use cargo::CargoResult; use cargo_test_support::registry::{self, Dependency, Package}; use cargo_test_support::{execs, paths, process, project, rustc_host, str}; use cargo_util::{ProcessBuilder, ProcessError}; use semver::Version; fn tc_process(cmd: &str, toolchain: &str) -> ProcessBuilder { let mut p = if toolchain == "this" { if cmd == "cargo" { process(&cargo_exe()) } else { process(cmd) } } else { let mut cmd = process(cmd); cmd.arg(format!("+{}", toolchain)); cmd }; // Reset PATH since `process` modifies it to remove rustup. p.env("PATH", std::env::var_os("PATH").unwrap()); p } /// Returns a sorted list of all toolchains. /// /// The returned value includes the parsed version, and the rustup toolchain /// name as a string. fn collect_all_toolchains() -> Vec<(Version, String)> { let rustc_version = |tc| { let mut cmd = tc_process("rustc", tc); cmd.arg("-V"); let output = cmd.exec_with_output().expect("rustc installed"); let version = std::str::from_utf8(&output.stdout).unwrap(); let parts: Vec<_> = version.split_whitespace().collect(); assert_eq!(parts[0], "rustc"); assert!(parts[1].starts_with("1.")); Version::parse(parts[1]).expect("valid version") }; // Provide a way to override the list. if let Ok(tcs) = std::env::var("OLD_CARGO") { return tcs .split(',') .map(|tc| (rustc_version(tc), tc.to_string())) .collect(); } let host = rustc_host(); // I tend to have lots of toolchains installed, but I don't want to test // all of them (like dated nightlies, or toolchains for non-host targets). let valid_names = &[ format!("stable-{}", host), format!("beta-{}", host), format!("nightly-{}", host), ]; let output = ProcessBuilder::new("rustup") .args(&["toolchain", "list"]) .exec_with_output() .expect("rustup should be installed"); let stdout = std::str::from_utf8(&output.stdout).unwrap(); let mut toolchains: Vec<_> = stdout .lines() .map(|line| { // Some lines say things like (default), just get the version. line.split_whitespace().next().expect("non-empty line") }) .filter(|line| { line.ends_with(&host) && (line.starts_with("1.") || valid_names.iter().any(|name| name == line)) }) .map(|line| (rustc_version(line), line.to_string())) .collect(); toolchains.sort_by(|a, b| a.0.cmp(&b.0)); toolchains } /// Returns whether the default toolchain is the stable version. fn default_toolchain_is_stable() -> bool { let default = tc_process("rustc", "this").arg("-V").exec_with_output(); let stable = tc_process("rustc", "stable").arg("-V").exec_with_output(); match (default, stable) { (Ok(d), Ok(s)) => d.stdout == s.stdout, _ => false, } } // This is a test for exercising the behavior of older versions of cargo with // the new feature syntax. // // The test involves a few dependencies with different feature requirements: // // * `bar` 1.0.0 is the base version that does not use the new syntax. // * `bar` 1.0.1 has a feature with the new syntax, but the feature is unused. // The optional dependency `new-baz-dep` should not be activated. // * `bar` 1.0.2 has a dependency on `baz` that *requires* the new feature // syntax. #[ignore = "must be run manually, requires old cargo installations"] #[cargo_test] fn new_features() { let registry = registry::init(); if std::process::Command::new("rustup").output().is_err() { panic!("old_cargos requires rustup to be installed"); } Package::new("new-baz-dep", "1.0.0").publish(); Package::new("baz", "1.0.0").publish(); let baz101_cksum = Package::new("baz", "1.0.1") .add_dep(Dependency::new("new-baz-dep", "1.0").optional(true)) .feature("new-feat", &["dep:new-baz-dep"]) .publish(); let bar100_cksum = Package::new("bar", "1.0.0") .add_dep(Dependency::new("baz", "1.0").optional(true)) .feature("feat", &["baz"]) .publish(); let bar101_cksum = Package::new("bar", "1.0.1") .add_dep(Dependency::new("baz", "1.0").optional(true)) .feature("feat", &["dep:baz"]) .publish(); let bar102_cksum = Package::new("bar", "1.0.2") .add_dep(Dependency::new("baz", "1.0").enable_features(&["new-feat"])) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); let lock_bar_to = |toolchain_version: &Version, bar_version| { let lock = if toolchain_version < &Version::new(1, 12, 0) { let url = registry.index_url(); match bar_version { 100 => format!( r#" [root] name = "foo" version = "0.1.0" dependencies = [ "bar 1.0.0 (registry+{url})", ] [[package]] name = "bar" version = "1.0.0" source = "registry+{url}" "#, url = url ), 101 => format!( r#" [root] name = "foo" version = "0.1.0" dependencies = [ "bar 1.0.1 (registry+{url})", ] [[package]] name = "bar" version = "1.0.1" source = "registry+{url}" "#, url = url ), 102 => format!( r#" [root] name = "foo" version = "0.1.0" dependencies = [ "bar 1.0.2 (registry+{url})", ] [[package]] name = "bar" version = "1.0.2" source = "registry+{url}" dependencies = [ "baz 1.0.1 (registry+{url})", ] [[package]] name = "baz" version = "1.0.1" source = "registry+{url}" "#, url = url ), _ => panic!("unexpected version"), } } else { match bar_version { 100 => format!( r#" [root] name = "foo" version = "0.1.0" dependencies = [ "bar 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bar" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum bar 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "{}" "#, bar100_cksum ), 101 => format!( r#" [root] name = "foo" version = "0.1.0" dependencies = [ "bar 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bar" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum bar 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "{}" "#, bar101_cksum ), 102 => format!( r#" [root] name = "foo" version = "0.1.0" dependencies = [ "bar 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bar" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "baz 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "baz" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum bar 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "{bar102_cksum}" "checksum baz 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "{baz101_cksum}" "#, bar102_cksum = bar102_cksum, baz101_cksum = baz101_cksum ), _ => panic!("unexpected version"), } }; p.change_file("Cargo.lock", &lock); }; let toolchains = collect_all_toolchains(); let config_path = paths::home().join(".cargo/config"); let lock_path = p.root().join("Cargo.lock"); struct ToolchainBehavior { bar: Option, baz: Option, new_baz_dep: Option, } // Collect errors to print at the end. One entry per toolchain, a list of // strings to print. let mut unexpected_results: Vec> = Vec::new(); for (version, toolchain) in &toolchains { if version >= &Version::new(1, 15, 0) && version < &Version::new(1, 18, 0) { // These versions do not stay within the sandbox, and chokes on // Cargo's own `Cargo.toml`. continue; } let mut tc_result = Vec::new(); // Write a config appropriate for this version. if version < &Version::new(1, 12, 0) { fs::write( &config_path, format!( r#" [registry] index = "{}" "#, registry.index_url() ), ) .unwrap(); } else { fs::write( &config_path, format!( " [source.crates-io] registry = 'https://wut' # only needed by 1.12 replace-with = 'dummy-registry' [source.dummy-registry] registry = '{}' ", registry.index_url() ), ) .unwrap(); } // Fetches the version of a package in the lock file. let pkg_version = |pkg| -> Option { let output = tc_process("cargo", toolchain) .args(&["pkgid", pkg]) .cwd(p.root()) .exec_with_output() .ok()?; let stdout = std::str::from_utf8(&output.stdout).unwrap(); let version = stdout .trim() .rsplitn(2, ['@', ':']) .next() .expect("version after colon"); Some(Version::parse(version).expect("parseable version")) }; // Runs `cargo build` and returns the versions selected in the lock. let run_cargo = || -> CargoResult { match tc_process("cargo", toolchain) .args(&["build", "--verbose"]) .cwd(p.root()) .exec_with_output() { Ok(_output) => { eprintln!("{} ok", toolchain); let bar = pkg_version("bar"); let baz = pkg_version("baz"); let new_baz_dep = pkg_version("new-baz-dep"); Ok(ToolchainBehavior { bar, baz, new_baz_dep, }) } Err(e) => { eprintln!("{} err {}", toolchain, e); Err(e) } } }; macro_rules! check_lock { ($tc_result:ident, $pkg:expr, $which:expr, $actual:expr, None) => { check_lock!(= $tc_result, $pkg, $which, $actual, None); }; ($tc_result:ident, $pkg:expr, $which:expr, $actual:expr, $expected:expr) => { check_lock!(= $tc_result, $pkg, $which, $actual, Some(Version::parse($expected).unwrap())); }; (= $tc_result:ident, $pkg:expr, $which:expr, $actual:expr, $expected:expr) => { let exp: Option = $expected; if $actual != $expected { $tc_result.push(format!( "{} for {} saw {:?} but expected {:?}", $which, $pkg, $actual, exp )); } }; } let check_err_contains = |tc_result: &mut Vec<_>, err: anyhow::Error, contents| { if let Some(ProcessError { stderr: Some(stderr), .. }) = err.downcast_ref::() { let stderr = std::str::from_utf8(stderr).unwrap(); if !stderr.contains(contents) { tc_result.push(format!( "{} expected to see error contents:\n{}\nbut saw:\n{}", toolchain, contents, stderr )); } } else { panic!("{} unexpected error {}", toolchain, err); } }; // Unlocked behavior. let which = "unlocked"; lock_path.rm_rf(); p.build_dir().rm_rf(); match run_cargo() { Ok(behavior) => { if version < &Version::new(1, 51, 0) { check_lock!(tc_result, "bar", which, behavior.bar, "1.0.2"); check_lock!(tc_result, "baz", which, behavior.baz, "1.0.1"); check_lock!(tc_result, "new-baz-dep", which, behavior.new_baz_dep, None); } else if version >= &Version::new(1, 51, 0) && version <= &Version::new(1, 59, 0) { check_lock!(tc_result, "bar", which, behavior.bar, "1.0.0"); check_lock!(tc_result, "baz", which, behavior.baz, None); check_lock!(tc_result, "new-baz-dep", which, behavior.new_baz_dep, None); } // Starting with 1.60, namespaced-features has been stabilized. else { check_lock!(tc_result, "bar", which, behavior.bar, "1.0.2"); check_lock!(tc_result, "baz", which, behavior.baz, "1.0.1"); check_lock!( tc_result, "new-baz-dep", which, behavior.new_baz_dep, "1.0.0" ); } } Err(e) => { if version < &Version::new(1, 49, 0) { // Old versions don't like the dep: syntax. check_err_contains( &mut tc_result, e, "which is neither a dependency nor another feature", ); } else if version >= &Version::new(1, 49, 0) && version < &Version::new(1, 51, 0) { check_err_contains( &mut tc_result, e, "requires the `-Z namespaced-features` flag", ); } else { tc_result.push(format!("unlocked build failed: {}", e)); } } } let which = "locked bar 1.0.0"; lock_bar_to(version, 100); match run_cargo() { Ok(behavior) => { check_lock!(tc_result, "bar", which, behavior.bar, "1.0.0"); check_lock!(tc_result, "baz", which, behavior.baz, None); check_lock!(tc_result, "new-baz-dep", which, behavior.new_baz_dep, None); } Err(e) => { tc_result.push(format!("bar 1.0.0 locked build failed: {}", e)); } } let which = "locked bar 1.0.1"; lock_bar_to(version, 101); match run_cargo() { Ok(behavior) => { check_lock!(tc_result, "bar", which, behavior.bar, "1.0.1"); check_lock!(tc_result, "baz", which, behavior.baz, None); check_lock!(tc_result, "new-baz-dep", which, behavior.new_baz_dep, None); } Err(e) => { if version < &Version::new(1, 49, 0) { // Old versions don't like the dep: syntax. check_err_contains( &mut tc_result, e, "which is neither a dependency nor another feature", ); } else if version >= &Version::new(1, 49, 0) && version < &Version::new(1, 51, 0) { check_err_contains( &mut tc_result, e, "requires the `-Z namespaced-features` flag", ); } else { // When version >= 1.51 and <= 1.59, // 1.0.1 can't be used without -Znamespaced-features // It gets filtered out of the index. check_err_contains( &mut tc_result, e, "candidate versions found which didn't match: 1.0.2, 1.0.0", ); } } } let which = "locked bar 1.0.2"; lock_bar_to(version, 102); match run_cargo() { Ok(behavior) => { if version <= &Version::new(1, 59, 0) { check_lock!(tc_result, "bar", which, behavior.bar, "1.0.2"); check_lock!(tc_result, "baz", which, behavior.baz, "1.0.1"); check_lock!(tc_result, "new-baz-dep", which, behavior.new_baz_dep, None); } // Starting with 1.60, namespaced-features has been stabilized. else { check_lock!(tc_result, "bar", which, behavior.bar, "1.0.2"); check_lock!(tc_result, "baz", which, behavior.baz, "1.0.1"); check_lock!( tc_result, "new-baz-dep", which, behavior.new_baz_dep, "1.0.0" ); } } Err(e) => { if version < &Version::new(1, 49, 0) { // Old versions don't like the dep: syntax. check_err_contains( &mut tc_result, e, "which is neither a dependency nor another feature", ); } else if version >= &Version::new(1, 49, 0) && version < &Version::new(1, 51, 0) { check_err_contains( &mut tc_result, e, "requires the `-Z namespaced-features` flag", ); } else { // When version >= 1.51 and <= 1.59, // baz can't lock to 1.0.1, it requires -Znamespaced-features check_err_contains( &mut tc_result, e, "candidate versions found which didn't match: 1.0.0", ); } } } unexpected_results.push(tc_result); } // Generate a report. let mut has_err = false; for ((tc_vers, tc_name), errs) in toolchains.iter().zip(unexpected_results) { if errs.is_empty() { continue; } eprintln!("error: toolchain {} (version {}):", tc_name, tc_vers); for err in errs { eprintln!(" {}", err); } has_err = true; } if has_err { panic!("at least one toolchain did not run as expected"); } } #[cargo_test] #[ignore = "must be run manually, requires old cargo installations"] fn index_cache_rebuild() { // Checks that the index cache gets rebuilt. // // 1.48 will not cache entries with features with the same name as a // dependency. If the cache does not get rebuilt, then running with // `-Znamespaced-features` would prevent the new cargo from seeing those // entries. The index cache version was changed to prevent this from // happening, and switching between versions should work correctly // (although it will thrash the cash, that's better than not working // correctly. let registry = registry::init(); Package::new("baz", "1.0.0").publish(); Package::new("bar", "1.0.0").publish(); Package::new("bar", "1.0.1") .add_dep(Dependency::new("baz", "1.0").optional(true)) .feature("baz", &["dep:baz"]) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .file( ".cargo/config.toml", &format!( r#" [source.crates-io] replace-with = 'dummy-registry' [source.dummy-registry] registry = '{}' "#, registry.index_url() ), ) .build(); // This version of Cargo errors on index entries that have overlapping // feature names, so 1.0.1 will be missing. execs() .with_process_builder(tc_process("cargo", "1.48.0")) .arg("check") .cwd(p.root()) .with_stderr_data(str![[r#" [UPDATING] `[ROOT]/registry` index [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `[ROOT]/registry`) [CHECKING] bar v1.0.0 [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] dev [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); fs::remove_file(p.root().join("Cargo.lock")).unwrap(); // This should rebuild the cache and use 1.0.1. p.cargo("check") .with_stderr_data(str![[r#" [WARNING] no edition set: defaulting to the 2015 edition while the latest is [..] [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.1 (registry `dummy-registry`) [CHECKING] bar v1.0.1 [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); fs::remove_file(p.root().join("Cargo.lock")).unwrap(); // Verify 1.48 can still resolve, and is at 1.0.0. execs() .with_process_builder(tc_process("cargo", "1.48.0")) .arg("tree") .cwd(p.root()) .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── bar v1.0.0 "#]]) .run(); } #[cargo_test] #[ignore = "must be run manually, requires old cargo installations"] fn avoids_split_debuginfo_collision() { // Test needs two different toolchains. // If the default toolchain is stable, then it won't work. if default_toolchain_is_stable() { return; } // Checks for a bug where .o files were being incorrectly shared between // different toolchains using incremental and split-debuginfo on macOS. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [profile.dev] split-debuginfo = "unpacked" "#, ) .file("src/main.rs", "fn main() {}") .build(); execs() .with_process_builder(tc_process("cargo", "stable")) .arg("build") .env("CARGO_INCREMENTAL", "1") .cwd(p.root()) .with_stderr_data(str![[r#" [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build") .env("CARGO_INCREMENTAL", "1") .with_stderr_data(str![[r#" [WARNING] no edition set: defaulting to the 2015 edition while the latest is [..] [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); execs() .with_process_builder(tc_process("cargo", "stable")) .arg("build") .env("CARGO_INCREMENTAL", "1") .cwd(p.root()) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } cargo-0.91.0/tests/testsuite/open_namespaces.rs000064400000000000000000000246041046102023000177150ustar 00000000000000use crate::prelude::*; use cargo_test_support::project; use cargo_test_support::str; #[cargo_test] fn within_namespace_requires_feature() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo::bar" version = "0.0.1" edition = "2015" "#, ) .file("src/lib.rs", "") .build(); p.cargo("read-manifest") .masquerade_as_nightly_cargo(&["open-namespaces"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: feature `open-namespaces` is required The package requires the Cargo feature called `open-namespaces`, but that feature is not stabilized in this version of Cargo ([..]). Consider adding `cargo-features = ["open-namespaces"]` to the top of Cargo.toml (above the [package] table) to tell Cargo you are opting in to use this unstable feature. See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#open-namespaces for more information about the status of this feature. "#]]) .run(); } #[cargo_test] fn implicit_lib_within_namespace() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["open-namespaces"] [package] name = "foo::bar" version = "0.0.1" edition = "2015" "#, ) .file("src/lib.rs", "") .build(); p.cargo("read-manifest") .masquerade_as_nightly_cargo(&["open-namespaces"]) .with_stdout_data( str![[r#" { "authors": [], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo#foo::bar@0.0.1", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/Cargo.toml", "metadata": null, "name": "foo::bar", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "foo::bar", "src_path": "[ROOT]/foo/src/lib.rs", "test": true } ], "version": "0.0.1" } "#]] .is_json(), ) .with_stderr_data("") .run(); } #[cargo_test] fn implicit_bin_within_namespace() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["open-namespaces"] [package] name = "foo::bar" version = "0.0.1" edition = "2015" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("read-manifest") .masquerade_as_nightly_cargo(&["open-namespaces"]) .with_stdout_data( str![[r#" { "authors": [], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo#foo::bar@0.0.1", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/Cargo.toml", "metadata": null, "name": "foo::bar", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "bin" ], "doc": true, "doctest": false, "edition": "2015", "kind": [ "bin" ], "name": "foo::bar", "src_path": "[ROOT]/foo/src/main.rs", "test": true } ], "version": "0.0.1" } "#]] .is_json(), ) .with_stderr_data("") .run(); } #[cargo_test] fn explicit_bin_within_namespace() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["open-namespaces"] [package] name = "foo::bar" version = "0.0.1" edition = "2015" [[bin]] name = "foo-bar" "#, ) .file("src/lib.rs", "") .file("src/bin/foo-bar/main.rs", "fn main() {}") .build(); p.cargo("read-manifest") .masquerade_as_nightly_cargo(&["open-namespaces"]) .with_stdout_data( str![[r#" { "authors": [], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo#foo::bar@0.0.1", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/Cargo.toml", "metadata": null, "name": "foo::bar", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "foo::bar", "src_path": "[ROOT]/foo/src/lib.rs", "test": true }, { "crate_types": [ "bin" ], "doc": true, "doctest": false, "edition": "2015", "kind": [ "bin" ], "name": "foo-bar", "src_path": "[ROOT]/foo/src/bin/foo-bar/main.rs", "test": true } ], "version": "0.0.1" } "#]] .is_json(), ) .with_stderr_data("") .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] #[cfg(unix)] fn namespaced_script_name() { let p = cargo_test_support::project() .file( "foo::bar.rs", r#"--- cargo-features = ["open-namespaces"] package.edition = "2021" --- fn main() {} "#, ) .build(); p.cargo("read-manifest -Zscript --manifest-path foo::bar.rs") .masquerade_as_nightly_cargo(&["script", "open-namespaces"]) .with_stdout_data( str![[r#" { "authors": [], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2021", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo/foo::bar.rs#foo::bar@0.0.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/foo::bar.rs", "metadata": null, "name": "foo::bar", "publish": [], "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "bin" ], "doc": true, "doctest": false, "edition": "2021", "kind": [ "bin" ], "name": "foo::bar", "src_path": "[ROOT]/foo/foo::bar.rs", "test": true } ], "version": "0.0.0" } "#]] .is_json(), ) .with_stderr_data("") .run(); } #[cargo_test] fn generate_pkgid_with_namespace() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["open-namespaces"] [package] name = "foo::bar" version = "0.0.1" edition = "2015" "#, ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile") .masquerade_as_nightly_cargo(&["open-namespaces"]) .run(); p.cargo("pkgid") .masquerade_as_nightly_cargo(&["open-namespaces"]) .with_stdout_data(str![[r#" path+[ROOTURL]/foo#foo::bar@0.0.1 "#]]) .with_stderr_data("") .run(); } #[cargo_test] fn update_spec_accepts_namespaced_name() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["open-namespaces"] [package] name = "foo::bar" version = "0.0.1" edition = "2015" "#, ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile") .masquerade_as_nightly_cargo(&["open-namespaces"]) .run(); p.cargo("update foo::bar") .masquerade_as_nightly_cargo(&["open-namespaces"]) .with_stdout_data(str![""]) .with_stderr_data(str![[r#" [LOCKING] 0 packages to latest compatible versions "#]]) .run(); } #[cargo_test] fn update_spec_accepts_namespaced_pkgid() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["open-namespaces"] [package] name = "foo::bar" version = "0.0.1" edition = "2015" "#, ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile") .masquerade_as_nightly_cargo(&["open-namespaces"]) .run(); p.cargo(&format!("update path+{}#foo::bar@0.0.1", p.url())) .masquerade_as_nightly_cargo(&["open-namespaces"]) .with_stdout_data(str![""]) .with_stderr_data(str![[r#" [LOCKING] 0 packages to latest compatible versions "#]]) .run(); } #[cargo_test] #[cfg(unix)] // until we get proper packaging support fn publish_namespaced() { use cargo_test_support::registry::RegistryBuilder; let registry = RegistryBuilder::new().http_api().http_index().build(); let p = project() .file( "Cargo.toml", r#" cargo-features = ["open-namespaces"] [package] name = "foo::bar" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" "#, ) .file("src/lib.rs", "fn main() {}") .build(); p.cargo("publish") .masquerade_as_nightly_cargo(&["script", "open-namespaces"]) .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo::bar v0.0.1 ([ROOT]/foo) [ERROR] failed to prepare local package for uploading Caused by: cannot publish with `open-namespaces` "#]]) .run(); } cargo-0.91.0/tests/testsuite/owner.rs000064400000000000000000000114021046102023000156770ustar 00000000000000//! Tests for the `cargo owner` command. use std::fs; use crate::prelude::*; use cargo_test_support::project; use cargo_test_support::registry::{self, api_path}; use cargo_test_support::str; fn setup(name: &str, content: Option<&str>) { let dir = api_path().join(format!("api/v1/crates/{}", name)); dir.mkdir_p(); if let Some(body) = content { fs::write(dir.join("owners"), body).unwrap(); } } #[cargo_test] fn simple_list() { let registry = registry::init(); let content = r#"{ "users": [ { "id": 70, "login": "github:rust-lang:core", "name": "Core" }, { "id": 123, "login": "octocat" } ] }"#; setup("foo", Some(content)); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] license = "MIT" description = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("owner -l") .replace_crates_io(registry.index_url()) .with_stdout_data(str![[r#" github:rust-lang:core (Core) octocat "#]]) .run(); } #[cargo_test] fn simple_add() { let registry = registry::init(); setup("foo", None); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] license = "MIT" description = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("owner -a username") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] crates.io index [ERROR] failed to invite owners to crate `foo` on registry at [ROOTURL]/api Caused by: EOF while parsing a value at line 1 column 0 "#]]) .run(); } #[cargo_test] fn simple_add_with_asymmetric() { let registry = registry::RegistryBuilder::new() .http_api() .token(cargo_test_support::registry::Token::rfc_key()) .build(); setup("foo", None); let p = project() .file( "Cargo.toml", r#" [project] name = "foo" version = "0.0.1" authors = [] license = "MIT" description = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build(); // The http_api server will check that the authorization is correct. // If the authorization was not sent then we would get an unauthorized error. p.cargo("owner -a username") .arg("-Zasymmetric-token") .masquerade_as_nightly_cargo(&["asymmetric-token"]) .replace_crates_io(registry.index_url()) .with_status(0) .run(); } #[cargo_test] fn simple_remove() { let registry = registry::init(); setup("foo", None); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] license = "MIT" description = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("owner -r username") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] crates.io index [OWNER] removing ["username"] from crate foo [ERROR] failed to remove owners from crate `foo` on registry at [ROOTURL]/api Caused by: EOF while parsing a value at line 1 column 0 "#]]) .run(); } #[cargo_test] fn simple_remove_with_asymmetric() { let registry = registry::RegistryBuilder::new() .http_api() .token(cargo_test_support::registry::Token::rfc_key()) .build(); setup("foo", None); let p = project() .file( "Cargo.toml", r#" [project] name = "foo" version = "0.0.1" authors = [] license = "MIT" description = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build(); // The http_api server will check that the authorization is correct. // If the authorization was not sent then we would get an unauthorized error. p.cargo("owner -r username") .arg("-Zasymmetric-token") .replace_crates_io(registry.index_url()) .masquerade_as_nightly_cargo(&["asymmetric-token"]) .with_status(0) .run(); } cargo-0.91.0/tests/testsuite/package.rs000064400000000000000000006355461046102023000161650ustar 00000000000000//! Tests for the `cargo package` command. use std::fs::{self, File, read_to_string}; use std::path::Path; use crate::prelude::*; use crate::utils::cargo_process; use cargo_test_support::publish::validate_crate_contents; use cargo_test_support::registry::{self, Package}; use cargo_test_support::{ Project, ProjectBuilder, basic_manifest, git, paths, project, rustc_host, str, symlink_supported, t, }; use flate2::read::GzDecoder; use tar::Archive; #[cargo_test] fn simple() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] exclude = ["*.txt"] license = "MIT" description = "foo" "#, ) .file("src/main.rs", r#"fn main() { println!("hello"); }"#) .file("src/bar.txt", "") // should be ignored when packaging .build(); p.cargo("package") .with_stderr_data(str![[r#" [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert!(p.root().join("target/package/foo-0.0.1.crate").is_file()); p.cargo("package -l") .with_stdout_data(str![[r#" Cargo.lock Cargo.toml Cargo.toml.orig src/main.rs "#]]) .run(); p.cargo("package") .with_stderr_data(str![[r#" [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", &[ "Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs", "Cargo.lock", ], (), ); } #[cargo_test] fn metadata_warning() { let p = project().file("src/main.rs", "fn main() {}").build(); p.cargo("package") .with_stderr_data(str![[r#" [WARNING] manifest has no description, license, license-file, documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("package") .with_stderr_data(str![[r#" [WARNING] manifest has no description, documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" repository = "bar" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("package") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn package_verbose() { let root = paths::root().join("all"); let repo = git::repo(&root) .file("Cargo.toml", &basic_manifest("foo", "0.0.1")) .file("src/main.rs", "fn main() {}") .file("a/a/Cargo.toml", &basic_manifest("a", "0.0.1")) .file("a/a/src/lib.rs", "") .build(); cargo_process("build").cwd(repo.root()).run(); println!("package main repo"); cargo_process("package -v --no-verify") .cwd(repo.root()) .with_stderr_data(str![[r#" [WARNING] manifest has no description, license, license-file, documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/all) [ARCHIVING] .cargo_vcs_info.json [ARCHIVING] Cargo.lock [ARCHIVING] Cargo.toml [ARCHIVING] Cargo.toml.orig [ARCHIVING] src/main.rs [PACKAGED] 5 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) "#]]) .run(); let f = File::open(&repo.root().join("target/package/foo-0.0.1.crate")).unwrap(); let vcs_contents = format!( r#"{{ "git": {{ "sha1": "{}" }}, "path_in_vcs": "" }}"#, repo.revparse_head() ); validate_crate_contents( f, "foo-0.0.1.crate", &[ "Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs", ".cargo_vcs_info.json", ], [(".cargo_vcs_info.json", &vcs_contents)], ); println!("package sub-repo"); cargo_process("package -v --no-verify") .cwd(repo.root().join("a/a")) .with_stderr_data(str![[r#" [WARNING] manifest has no description, license, license-file, documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] a v0.0.1 ([ROOT]/all/a/a) [ARCHIVING] .cargo_vcs_info.json [ARCHIVING] Cargo.lock [ARCHIVING] Cargo.toml [ARCHIVING] Cargo.toml.orig [ARCHIVING] src/lib.rs [PACKAGED] 5 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) "#]]) .run(); let f = File::open(&repo.root().join("a/a/target/package/a-0.0.1.crate")).unwrap(); let vcs_contents = format!( r#"{{ "git": {{ "sha1": "{}" }}, "path_in_vcs": "a/a" }}"#, repo.revparse_head() ); validate_crate_contents( f, "a-0.0.1.crate", &[ "Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/lib.rs", ".cargo_vcs_info.json", ], [(".cargo_vcs_info.json", &vcs_contents)], ); } #[cargo_test] fn package_verification() { let p = project().file("src/main.rs", "fn main() {}").build(); p.cargo("build").run(); p.cargo("package") .with_stderr_data(str![[r#" [WARNING] manifest has no description, license, license-file, documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn vcs_file_collision() { let p = project().build(); let _ = git::repo(&paths::root().join("foo")) .file( "Cargo.toml", r#" [package] name = "foo" description = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" documentation = "foo" homepage = "foo" repository = "foo" exclude = ["*.no-existe"] "#, ) .file( "src/main.rs", r#" fn main() {} "#, ) .file(".cargo_vcs_info.json", "foo") .build(); p.cargo("package") .arg("--no-verify") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid inclusion of reserved file name .cargo_vcs_info.json in package source "#]]) .run(); } #[cargo_test] fn orig_file_collision() { let p = project().build(); let _ = git::repo(&paths::root().join("foo")) .file( "Cargo.toml", r#" [package] name = "foo" description = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" documentation = "foo" homepage = "foo" repository = "foo" exclude = ["*.no-existe"] "#, ) .file( "src/main.rs", r#" fn main() {} "#, ) .file("Cargo.toml.orig", "oops") .build(); p.cargo("package") .arg("--no-verify") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid inclusion of reserved file name Cargo.toml.orig in package source "#]]) .run(); } #[cargo_test] fn path_dependency_no_version() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" [dependencies.bar] path = "bar" "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "") .build(); p.cargo("package") .with_status(101) .with_stderr_data(str![[r#" [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [ERROR] failed to verify manifest at `[ROOT]/foo/Cargo.toml` Caused by: all dependencies must have a version requirement specified when packaging. dependency `bar` does not specify a version Note: The packaged dependency will use the version from crates.io, the `path` specification will be removed from the dependency declaration. "#]]) .run(); } #[cargo_test] fn git_dependency_no_version() { registry::init(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" [dependencies.foo] git = "git://path/to/nowhere" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("package") .with_status(101) .with_stderr_data(str![[r#" [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [ERROR] failed to verify manifest at `[ROOT]/foo/Cargo.toml` Caused by: all dependencies must have a version requirement specified when packaging. dependency `foo` does not specify a version Note: The packaged dependency will use the version from crates.io, the `git` specification will be removed from the dependency declaration. "#]]) .run(); } #[cargo_test] fn exclude() { let root = paths::root().join("exclude"); let repo = git::repo(&root) .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] exclude = [ "*.txt", # file in root "file_root_1", # NO_CHANGE (ignored) "/file_root_2", # CHANGING (packaged -> ignored) "file_root_3/", # NO_CHANGE (packaged) "file_root_4/*", # NO_CHANGE (packaged) "file_root_5/**", # NO_CHANGE (packaged) # file in sub-dir "file_deep_1", # CHANGING (packaged -> ignored) "/file_deep_2", # NO_CHANGE (packaged) "file_deep_3/", # NO_CHANGE (packaged) "file_deep_4/*", # NO_CHANGE (packaged) "file_deep_5/**", # NO_CHANGE (packaged) # dir in root "dir_root_1", # CHANGING (packaged -> ignored) "/dir_root_2", # CHANGING (packaged -> ignored) "dir_root_3/", # CHANGING (packaged -> ignored) "dir_root_4/*", # NO_CHANGE (ignored) "dir_root_5/**", # NO_CHANGE (ignored) # dir in sub-dir "dir_deep_1", # CHANGING (packaged -> ignored) "/dir_deep_2", # NO_CHANGE "dir_deep_3/", # CHANGING (packaged -> ignored) "dir_deep_4/*", # CHANGING (packaged -> ignored) "dir_deep_5/**", # CHANGING (packaged -> ignored) ] "#, ) .file("src/main.rs", r#"fn main() { println!("hello"); }"#) .file("bar.txt", "") .file("src/bar.txt", "") // File in root. .file("file_root_1", "") .file("file_root_2", "") .file("file_root_3", "") .file("file_root_4", "") .file("file_root_5", "") // File in sub-dir. .file("some_dir/file_deep_1", "") .file("some_dir/file_deep_2", "") .file("some_dir/file_deep_3", "") .file("some_dir/file_deep_4", "") .file("some_dir/file_deep_5", "") // Dir in root. .file("dir_root_1/some_dir/file", "") .file("dir_root_2/some_dir/file", "") .file("dir_root_3/some_dir/file", "") .file("dir_root_4/some_dir/file", "") .file("dir_root_5/some_dir/file", "") // Dir in sub-dir. .file("some_dir/dir_deep_1/some_dir/file", "") .file("some_dir/dir_deep_2/some_dir/file", "") .file("some_dir/dir_deep_3/some_dir/file", "") .file("some_dir/dir_deep_4/some_dir/file", "") .file("some_dir/dir_deep_5/some_dir/file", "") .build(); cargo_process("package --no-verify -v") .cwd(repo.root()) .with_stdout_data("") .with_stderr_data(str![[r#" [WARNING] manifest has no description, license, license-file, documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/exclude) [ARCHIVING] .cargo_vcs_info.json [ARCHIVING] Cargo.lock [ARCHIVING] Cargo.toml [ARCHIVING] Cargo.toml.orig [ARCHIVING] file_root_3 [ARCHIVING] file_root_4 [ARCHIVING] file_root_5 [ARCHIVING] some_dir/dir_deep_2/some_dir/file [ARCHIVING] some_dir/dir_deep_4/some_dir/file [ARCHIVING] some_dir/dir_deep_5/some_dir/file [ARCHIVING] some_dir/file_deep_2 [ARCHIVING] some_dir/file_deep_3 [ARCHIVING] some_dir/file_deep_4 [ARCHIVING] some_dir/file_deep_5 [ARCHIVING] src/main.rs [PACKAGED] 15 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) "#]]) .run(); assert!(repo.root().join("target/package/foo-0.0.1.crate").is_file()); cargo_process("package -l") .cwd(repo.root()) .with_stdout_data(str![[r#" .cargo_vcs_info.json Cargo.lock Cargo.toml Cargo.toml.orig file_root_3 file_root_4 file_root_5 some_dir/dir_deep_2/some_dir/file some_dir/dir_deep_4/some_dir/file some_dir/dir_deep_5/some_dir/file some_dir/file_deep_2 some_dir/file_deep_3 some_dir/file_deep_4 some_dir/file_deep_5 src/main.rs "#]]) .run(); } #[cargo_test] fn include() { let root = paths::root().join("include"); let repo = git::repo(&root) .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] exclude = ["*.txt"] include = ["foo.txt", "**/*.rs", "Cargo.toml", ".dotfile"] "#, ) .file("foo.txt", "") .file("src/main.rs", r#"fn main() { println!("hello"); }"#) .file(".dotfile", "") // Should be ignored when packaging. .file("src/bar.txt", "") .build(); cargo_process("package --no-verify -v") .cwd(repo.root()) .with_stderr_data(str![[r#" [WARNING] manifest has no description, license, license-file, documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [WARNING] both package.include and package.exclude are specified; the exclude list will be ignored [PACKAGING] foo v0.0.1 ([ROOT]/include) [ARCHIVING] .cargo_vcs_info.json [ARCHIVING] .dotfile [ARCHIVING] Cargo.lock [ARCHIVING] Cargo.toml [ARCHIVING] Cargo.toml.orig [ARCHIVING] foo.txt [ARCHIVING] src/main.rs [PACKAGED] 7 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) "#]]) .run(); } #[cargo_test] fn package_lib_with_bin() { let p = project() .file("src/main.rs", "extern crate foo; fn main() {}") .file("src/lib.rs", "") .build(); p.cargo("package -v").run(); } #[cargo_test] fn package_git_submodule() { let project = git::new("foo", |project| { project .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = ["foo@example.com"] license = "MIT" description = "foo" repository = "foo" "#, ) .file("src/lib.rs", "pub fn foo() {}") }); let library = git::new("bar", |library| { library.no_manifest().file("Makefile", "all:") }); let repository = git2::Repository::open(&project.root()).unwrap(); let url = library.root().to_url().to_string(); git::add_submodule(&repository, &url, Path::new("bar")); git::commit(&repository); let repository = git2::Repository::open(&project.root().join("bar")).unwrap(); repository .reset( &repository.revparse_single("HEAD").unwrap(), git2::ResetType::Hard, None, ) .unwrap(); project .cargo("package --no-verify -v") .with_stderr_data(str![[r#" ... [ARCHIVING] bar/Makefile ... "#]]) .run(); } #[cargo_test] /// Tests if a symlink to a git submodule is properly handled. /// /// This test requires you to be able to make symlinks. /// For windows, this may require you to enable developer mode. fn package_symlink_to_submodule() { #[cfg(unix)] use std::os::unix::fs::symlink; #[cfg(windows)] use std::os::windows::fs::symlink_dir as symlink; if !symlink_supported() { return; } let project = git::new("foo", |project| { project.file("src/lib.rs", "pub fn foo() {}") }); let library = git::new("submodule", |library| { library.no_manifest().file("Makefile", "all:") }); let repository = git2::Repository::open(&project.root()).unwrap(); let url = library.root().to_url().to_string(); git::add_submodule(&repository, &url, Path::new("submodule")); t!(symlink( &project.root().join("submodule"), &project.root().join("submodule-link") )); git::add(&repository); git::commit(&repository); let repository = git2::Repository::open(&project.root().join("submodule")).unwrap(); repository .reset( &repository.revparse_single("HEAD").unwrap(), git2::ResetType::Hard, None, ) .unwrap(); project .cargo("package --no-verify -v") .with_stderr_contains("[ARCHIVING] submodule/Makefile") .with_stderr_does_not_contain("[ARCHIVING] submodule-link/.git/config") .run(); } #[cargo_test] fn no_duplicates_from_modified_tracked_files() { let p = git::new("all", |p| p.file("src/main.rs", "fn main() {}")); p.change_file("src/main.rs", r#"fn main() { println!("A change!"); }"#); p.cargo("build").run(); p.cargo("package --list --allow-dirty") .with_stdout_data(str![[r#" .cargo_vcs_info.json Cargo.lock Cargo.toml Cargo.toml.orig src/main.rs "#]]) .run(); } #[cargo_test] fn ignore_nested() { let cargo_toml = r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" homepage = "https://example.com/" "#; let main_rs = r#" fn main() { println!("hello"); } "#; let p = project() .file("Cargo.toml", cargo_toml) .file("src/main.rs", main_rs) // If a project happens to contain a copy of itself, we should // ignore it. .file("a_dir/foo/Cargo.toml", cargo_toml) .file("a_dir/foo/src/main.rs", main_rs) .build(); p.cargo("package") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert!(p.root().join("target/package/foo-0.0.1.crate").is_file()); p.cargo("package -l") .with_stdout_data(str![[r#" Cargo.lock Cargo.toml Cargo.toml.orig src/main.rs "#]]) .run(); p.cargo("package") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], (), ); } // Windows doesn't allow these characters in filenames. #[cfg(unix)] #[cargo_test] fn package_weird_characters() { let p = project() .file("src/main.rs", r#"fn main() { println!("hello"); }"#) .file("src/:foo", "") .build(); p.cargo("package") .with_status(101) .with_stderr_data(str![[r#" [WARNING] manifest has no description, license, license-file, documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [ERROR] cannot package a filename with a special character `:`: src/:foo "#]]) .run(); } #[cargo_test] fn repackage_on_source_change() { let p = project() .file("src/main.rs", r#"fn main() { println!("hello"); }"#) .build(); p.cargo("package").run(); // Add another source file p.change_file("src/foo.rs", r#"fn main() { println!("foo"); }"#); // Check that cargo rebuilds the tarball p.cargo("package") .with_stderr_data(str![[r#" [WARNING] manifest has no description, license, license-file, documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 5 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Check that the tarball contains the added file let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", &[ "Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs", "src/foo.rs", ], (), ); } #[cargo_test] /// Tests if a broken symlink is properly handled when packaging. /// /// This test requires you to be able to make symlinks. /// For windows, this may require you to enable developer mode. fn broken_symlink() { #[cfg(unix)] use std::os::unix::fs::symlink; #[cfg(windows)] use std::os::windows::fs::symlink_dir as symlink; if !symlink_supported() { return; } let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = 'foo' documentation = 'foo' homepage = 'foo' repository = 'foo' "#, ) .file("src/main.rs", r#"fn main() { println!("hello"); }"#) .build(); t!(symlink("nowhere", &p.root().join("src/foo.rs"))); p.cargo("package -v") .with_status(101) .with_stderr_data(str![[r#" ... [ERROR] failed to prepare local package for uploading Caused by: failed to open for archiving: `[ROOT]/foo/src/foo.rs` Caused by: [NOT_FOUND] "#]]) .run(); } #[cargo_test] /// Tests if a broken but excluded symlink is ignored. /// See issue rust-lang/cargo#10917 /// /// This test requires you to be able to make symlinks. /// For windows, this may require you to enable developer mode. fn broken_but_excluded_symlink() { #[cfg(unix)] use std::os::unix::fs::symlink; #[cfg(windows)] use std::os::windows::fs::symlink_dir as symlink; if !symlink_supported() { return; } let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = 'foo' documentation = 'foo' homepage = 'foo' repository = 'foo' exclude = ["src/foo.rs"] "#, ) .file("src/main.rs", r#"fn main() { println!("hello"); }"#) .build(); t!(symlink("nowhere", &p.root().join("src/foo.rs"))); p.cargo("package -v --list") // `src/foo.rs` is excluded. .with_stdout_data(str![[r#" Cargo.lock Cargo.toml Cargo.toml.orig src/main.rs "#]]) .run(); } #[cargo_test] #[cfg(not(windows))] // https://github.com/libgit2/libgit2/issues/6250 /// Test that /dir and /dir/ matches symlinks to directories. fn gitignore_symlink_dir() { if !symlink_supported() { return; } let (p, _repo) = git::new_repo("foo", |p| { p.file("src/main.rs", r#"fn main() { println!("hello"); }"#) .symlink_dir("src", "src1") .symlink_dir("src", "src2") .symlink_dir("src", "src3") .symlink_dir("src", "src4") .file(".gitignore", "/src1\n/src2/\nsrc3\nsrc4/") }); p.cargo("package -l --no-metadata") .with_stderr_data("") .with_stdout_data(str![[r#" .cargo_vcs_info.json .gitignore Cargo.lock Cargo.toml Cargo.toml.orig src/main.rs "#]]) .run(); } #[cargo_test] #[cfg(not(windows))] // https://github.com/libgit2/libgit2/issues/6250 /// Test that /dir and /dir/ matches symlinks to directories in dirty working directory. fn gitignore_symlink_dir_dirty() { if !symlink_supported() { return; } let (p, _repo) = git::new_repo("foo", |p| { p.file("src/main.rs", r#"fn main() { println!("hello"); }"#) .file(".gitignore", "/src1\n/src2/\nsrc3\nsrc4/") }); p.symlink("src", "src1"); p.symlink("src", "src2"); p.symlink("src", "src3"); p.symlink("src", "src4"); p.cargo("package -l --no-metadata") .with_stderr_data("") .with_stdout_data(str![[r#" .cargo_vcs_info.json .gitignore Cargo.lock Cargo.toml Cargo.toml.orig src/main.rs "#]]) .run(); p.cargo("package -l --no-metadata --allow-dirty") .with_stderr_data("") .with_stdout_data(str![[r#" .cargo_vcs_info.json .gitignore Cargo.lock Cargo.toml Cargo.toml.orig src/main.rs "#]]) .run(); } #[cargo_test] /// Tests if a symlink to a directory is properly included. /// /// This test requires you to be able to make symlinks. /// For windows, this may require you to enable developer mode. fn package_symlink_to_dir() { if !symlink_supported() { return; } project() .file("src/main.rs", r#"fn main() { println!("hello"); }"#) .file("bla/Makefile", "all:") .symlink_dir("bla", "foo") .build() .cargo("package -v") .with_stderr_data(str![[r#" ... [ARCHIVING] foo/Makefile ... "#]]) .run(); } #[cargo_test] /// Tests if a symlink to ancestor causes filesystem loop error. /// /// This test requires you to be able to make symlinks. /// For windows, this may require you to enable developer mode. fn filesystem_loop() { if !symlink_supported() { return; } project() .file("src/main.rs", r#"fn main() { println!("hello"); }"#) .symlink_dir("a/b", "a/b/c/d/foo") .build() .cargo("package -v") .with_stderr_data(str![[r#" ... [WARNING] File system loop found: [ROOT]/foo/a/b/c/d/foo points to an ancestor [ROOT]/foo/a/b ... "#]]) .run(); } #[cargo_test] fn do_not_package_if_repository_is_dirty() { let p = project().build(); // Create a Git repository containing a minimal Rust project. let _ = git::repo(&paths::root().join("foo")) .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" license = "MIT" description = "foo" documentation = "foo" homepage = "foo" repository = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build(); // Modify Cargo.toml without committing the change. p.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" license = "MIT" description = "foo" documentation = "foo" homepage = "foo" repository = "foo" # change "#, ); p.cargo("package") .with_status(101) .with_stderr_data(str![[r#" [ERROR] 1 files in the working directory contain changes that were not yet committed into git: Cargo.toml to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag "#]]) .run(); // cd to `src` and cargo report relative paths. p.cargo("package") .cwd(p.root().join("src")) .with_status(101) .with_stderr_data(str![[r#" [ERROR] 1 files in the working directory contain changes that were not yet committed into git: ../Cargo.toml to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag "#]]) .run(); } #[cargo_test] fn dirty_ignored() { // Cargo warns about an ignored file that will be published. let (p, repo) = git::new_repo("foo", |p| { p.file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" description = "foo" license = "foo" documentation = "foo" include = ["src", "build"] "#, ) .file("src/lib.rs", "") .file(".gitignore", "build") }); // Example of adding a file that is confusingly ignored by an overzealous // gitignore rule. p.change_file("src/build/mod.rs", ""); p.cargo("package --list") .with_status(101) .with_stderr_data(str![[r#" [ERROR] 1 files in the working directory contain changes that were not yet committed into git: src/build/mod.rs to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag "#]]) .run(); // Add the ignored file and make sure it is included. let mut index = t!(repo.index()); t!(index.add_path(Path::new("src/build/mod.rs"))); t!(index.write()); git::commit(&repo); p.cargo("package --list") .with_stderr_data("") .with_stdout_data(str![[r#" .cargo_vcs_info.json Cargo.lock Cargo.toml Cargo.toml.orig src/build/mod.rs src/lib.rs "#]]) .run(); } #[cargo_test] fn vcs_status_check_for_each_workspace_member() { // Cargo checks VCS status separately for each workspace member. // This ensure one file changed in a package won't affect the other. // Since the dirty bit in .cargo_vcs_info.json is just for advisory purpose, // We may change the meaning of it in the future. let (p, repo) = git::new_repo("foo", |p| { p.file( "Cargo.toml", r#" [workspace] members = ["isengard", "mordor"] "#, ) .file("hobbit", "...") .file( "isengard/Cargo.toml", r#" [package] name = "isengard" edition = "2015" homepage = "saruman" description = "saruman" license = "MIT" "#, ) .file("isengard/src/lib.rs", "") .file( "mordor/Cargo.toml", r#" [package] name = "mordor" edition = "2015" homepage = "sauron" description = "sauron" license = "MIT" "#, ) .file("mordor/src/lib.rs", "") }); git::commit(&repo); // Dirty file outside won't affect packaging. p.change_file("hobbit", "changed!"); p.change_file("mordor/src/lib.rs", "changed!"); p.change_file("mordor/src/main.rs", "fn main() {}"); // Ensure dirty files be reported only for one affected package. p.cargo("package --workspace --no-verify") .with_status(101) .with_stderr_data(str![[r#" [PACKAGING] isengard v0.0.0 ([ROOT]/foo/isengard) [PACKAGED] 5 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [ERROR] 2 files in the working directory contain changes that were not yet committed into git: mordor/src/lib.rs mordor/src/main.rs to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag "#]]) .run(); // Ensure only dirty package be recorded as dirty. p.cargo("package --workspace --no-verify --allow-dirty") .with_stderr_data(str![[r#" [PACKAGING] isengard v0.0.0 ([ROOT]/foo/isengard) [PACKAGED] 5 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [PACKAGING] mordor v0.0.0 ([ROOT]/foo/mordor) [PACKAGED] 6 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) "#]]) .run(); let f = File::open(&p.root().join("target/package/isengard-0.0.0.crate")).unwrap(); validate_crate_contents( f, "isengard-0.0.0.crate", &[ ".cargo_vcs_info.json", "Cargo.toml", "Cargo.toml.orig", "src/lib.rs", "Cargo.lock", ], [( ".cargo_vcs_info.json", // No change within `isengard/`, so not dirty at all. str![[r#" { "git": { "sha1": "[..]" }, "path_in_vcs": "isengard" } "#]] .is_json(), )], ); let f = File::open(&p.root().join("target/package/mordor-0.0.0.crate")).unwrap(); validate_crate_contents( f, "mordor-0.0.0.crate", &[ ".cargo_vcs_info.json", "Cargo.toml", "Cargo.toml.orig", "src/lib.rs", "src/main.rs", "Cargo.lock", ], [( ".cargo_vcs_info.json", // Dirty bit is recorded. str![[r#" { "git": { "dirty": true, "sha1": "[..]" }, "path_in_vcs": "mordor" } "#]] .is_json(), )], ); } #[cargo_test] fn dirty_file_outside_pkg_root_considered_dirty() { if !symlink_supported() { return; } let main_outside_pkg_root = paths::root().join("main.rs"); let (p, repo) = git::new_repo("foo", |p| { p.file( "Cargo.toml", r#" [workspace] members = ["isengard"] resolver = "2" [workspace.package] edition = "2015" "#, ) .file("lib.rs", r#"compile_error!("you shall not pass")"#) .file("LICENSE", "before") .file("README.md", "before") .file( "isengard/Cargo.toml", r#" [package] name = "isengard" edition.workspace = true homepage = "saruman" description = "saruman" license-file = "../LICENSE" "#, ) .file("original-dir/file", "before") .symlink("lib.rs", "isengard/src/lib.rs") .symlink("README.md", "isengard/README.md") .file(&main_outside_pkg_root, "fn main() {}") .symlink(&main_outside_pkg_root, "isengard/src/main.rs") .symlink_dir("original-dir", "isengard/symlink-dir") }); git::commit(&repo); // Changing files outside pkg root under situations below should be treated // as dirty. `cargo package` is expected to fail on VCS status check. // // * Changes in files outside package root that source files symlink to p.change_file("README.md", "after"); p.change_file("lib.rs", "pub fn after() {}"); p.change_file("original-dir/file", "after"); // * Changes in files outside pkg root that `license-file`/`readme` point to p.change_file("LICENSE", "after"); // * When workspace root manifest has changed, // no matter whether workspace inheritance is involved. p.change_file( "Cargo.toml", r#" [workspace] members = ["isengard"] resolver = "2" [workspace.package] edition = "2021" "#, ); // Changes in files outside git workdir won't affect VCS status check p.change_file( &main_outside_pkg_root, r#"fn main() { eprintln!("after"); }"#, ); // Ensure dirty files be reported. p.cargo("package --workspace --no-verify") .with_status(101) .with_stderr_data(str![[r#" [ERROR] 5 files in the working directory contain changes that were not yet committed into git: Cargo.toml LICENSE README.md lib.rs original-dir/file to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag "#]]) .run(); p.cargo("package --workspace --no-verify --allow-dirty") .with_stderr_data(str![[r#" [PACKAGING] isengard v0.0.0 ([ROOT]/foo/isengard) [PACKAGED] 9 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) "#]]) .run(); let cargo_toml = str![[r##" ... [package] edition = "2021" ... "##]]; let f = File::open(&p.root().join("target/package/isengard-0.0.0.crate")).unwrap(); validate_crate_contents( f, "isengard-0.0.0.crate", &[ ".cargo_vcs_info.json", "Cargo.toml", "Cargo.toml.orig", "src/lib.rs", "src/main.rs", "symlink-dir/file", "Cargo.lock", "LICENSE", "README.md", ], [ ("src/lib.rs", str!["pub fn after() {}"]), ("src/main.rs", str![r#"fn main() { eprintln!("after"); }"#]), ("symlink-dir/file", str!["after"]), ("README.md", str!["after"]), ("LICENSE", str!["after"]), ("Cargo.toml", cargo_toml), ], ); } #[cargo_test] fn dirty_file_outside_pkg_root_inside_submodule() { if !symlink_supported() { return; } let (p, repo) = git::new_repo("foo", |p| { p.file( "Cargo.toml", r#" [workspace] members = ["isengard"] resolver = "2" "#, ) .file( "isengard/Cargo.toml", r#" [package] name = "isengard" edition = "2015" homepage = "saruman" description = "saruman" license = "ISC" "#, ) .file("isengard/src/lib.rs", "") }); let submodule = git::new("submodule", |p| { p.no_manifest().file("file.txt", "from-submodule") }); git::add_submodule( &repo, submodule.root().to_url().as_ref(), Path::new("submodule"), ); p.symlink("submodule/file.txt", "isengard/src/file.txt"); git::add(&repo); git::commit(&repo); p.change_file("submodule/file.txt", "changed"); p.cargo("package --workspace --no-verify") .with_status(101) .with_stderr_data(str![[r#" [ERROR] 1 files in the working directory contain changes that were not yet committed into git: isengard/src/file.txt to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag "#]]) .run(); } #[cargo_test] fn issue_13695_allow_dirty_vcs_info() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" description = "foo" license = "foo" documentation = "foo" "#, ) .file("src/lib.rs", "") .build(); let repo = git::init(&p.root()); // Initial commit, with no files added. git::commit(&repo); // Allowing a dirty worktree results in the vcs file still being included. p.cargo("package --allow-dirty").run(); let f = File::open(&p.root().join("target/package/foo-0.1.0.crate")).unwrap(); validate_crate_contents( f, "foo-0.1.0.crate", &[ ".cargo_vcs_info.json", "Cargo.toml", "Cargo.toml.orig", "src/lib.rs", "Cargo.lock", ], [( ".cargo_vcs_info.json", str![[r#" { "git": { "dirty": true, "sha1": "[..]" }, "path_in_vcs": "" } "#]] .is_json(), )], ); // Listing provides a consistent result. p.cargo("package --list --allow-dirty") .with_stderr_data("") .with_stdout_data(str![[r#" .cargo_vcs_info.json Cargo.lock Cargo.toml Cargo.toml.orig src/lib.rs "#]]) .run(); } #[cargo_test] fn issue_13695_allowing_dirty_vcs_info_but_clean() { let p = project().build(); let _ = git::repo(&paths::root().join("foo")) .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" description = "foo" license = "foo" documentation = "foo" "#, ) .file("src/lib.rs", "") .build(); // Allowing a dirty worktree despite it being clean. p.cargo("package --allow-dirty").run(); let f = File::open(&p.root().join("target/package/foo-0.1.0.crate")).unwrap(); validate_crate_contents( f, "foo-0.1.0.crate", &[ ".cargo_vcs_info.json", "Cargo.toml", "Cargo.toml.orig", "src/lib.rs", "Cargo.lock", ], [( ".cargo_vcs_info.json", str![[r#" { "git": { "sha1": "[..]" }, "path_in_vcs": "" } "#]] .is_json(), )], ); } #[cargo_test] fn issue_14354_allowing_dirty_bare_commit() { let p = project().build(); // Init a bare commit git repo let _ = git::repo(&paths::root().join("foo")) .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" description = "foo" license = "foo" documentation = "foo" "#, ) .file("src/lib.rs", ""); p.cargo("package --allow-dirty").run(); let f = File::open(&p.root().join("target/package/foo-0.1.0.crate")).unwrap(); validate_crate_contents( f, "foo-0.1.0.crate", &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs", "Cargo.lock"], (), ); } #[cargo_test] fn generated_manifest() { registry::alt_init(); Package::new("abc", "1.0.0").publish(); Package::new("def", "1.0.0").alternative(true).publish(); Package::new("ghi", "1.0.0").publish(); Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] exclude = ["*.txt"] license = "MIT" description = "foo" [package.metadata] foo = 'bar' [workspace] [dependencies] bar = { path = "bar", version = "0.1" } def = { version = "1.0", registry = "alternative" } ghi = "1.0" abc = "1.0" "#, ) .file("src/main.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "") .build(); p.cargo("package --no-verify").run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); let rewritten_toml = str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "foo" version = "0.0.1" authors = [] build = false exclude = ["*.txt"] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" readme = false license = "MIT" [package.metadata] foo = "bar" [[bin]] name = "foo" path = "src/main.rs" [dependencies.abc] version = "1.0" [dependencies.bar] version = "0.1" [dependencies.def] version = "1.0" registry-index = "[ROOTURL]/alternative-registry" [dependencies.ghi] version = "1.0" "##]]; validate_crate_contents( f, "foo-0.0.1.crate", &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], [("Cargo.toml", rewritten_toml)], ); } #[cargo_test] fn ignore_workspace_specifier() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [workspace] [dependencies] bar = { path = "bar", version = "0.1" } "#, ) .file("src/main.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] workspace = ".." "#, ) .file("bar/src/lib.rs", "") .build(); p.cargo("package --no-verify").cwd("bar").run(); let f = File::open(&p.root().join("target/package/bar-0.1.0.crate")).unwrap(); let rewritten_toml = str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "bar" version = "0.1.0" authors = [] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false readme = false [lib] name = "bar" path = "src/lib.rs" "##]]; validate_crate_contents( f, "bar-0.1.0.crate", &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs", "Cargo.lock"], [("Cargo.toml", rewritten_toml)], ); } #[cargo_test] fn package_two_kinds_of_deps() { Package::new("other", "1.0.0").publish(); Package::new("other1", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] other = "1.0" other1 = { version = "1.0" } "#, ) .file("src/main.rs", "") .build(); p.cargo("package --no-verify").run(); } #[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")] fn package_public_dep() { Package::new("bar", "1.0.0").publish(); Package::new("baz", "1.0.0").publish(); let p = project() .file( "Cargo.toml", &format! { r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = {{ version = "1.0.0", public = true }} [target.{host}.dependencies] baz = {{ version = "1.0.0", public = true }} "#, host = rustc_host() }, ) .file("src/main.rs", "fn main() {}") .build(); let rewritten_toml = str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "foo" version = "0.0.1" build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false readme = false [[bin]] name = "foo" path = "src/main.rs" [dependencies.bar] version = "1.0.0" [target.[HOST_TARGET].dependencies.baz] version = "1.0.0" "##]]; verify(&p, "package", rewritten_toml); let rewritten_toml = str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "foo" version = "0.0.1" build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false readme = false [[bin]] name = "foo" path = "src/main.rs" [dependencies.bar] version = "1.0.0" public = true [target.[HOST_TARGET].dependencies.baz] version = "1.0.0" public = true "##]]; verify(&p, "package -Zpublic-dependency", rewritten_toml); fn verify(p: &cargo_test_support::Project, cmd: &str, rewritten_toml: impl IntoData) { p.cargo(cmd) .masquerade_as_nightly_cargo(&["public-dependency"]) .run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", &["Cargo.toml", "Cargo.toml.orig", "Cargo.lock", "src/main.rs"], [("Cargo.toml", rewritten_toml)], ); } } #[cargo_test] fn test_edition() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["edition"] [package] name = "foo" version = "0.0.1" authors = [] edition = "2018" "#, ) .file("src/lib.rs", r#" "#) .build(); p.cargo("check -v") .with_stderr_data(str![[r#" ... [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..]--edition=2018 [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn edition_with_metadata() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] edition = "2018" [package.metadata.docs.rs] features = ["foobar"] "#, ) .file("src/lib.rs", "") .build(); p.cargo("package").run(); } #[cargo_test] fn test_edition_malformed() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] edition = "chicken" "#, ) .file("src/lib.rs", r#" "#) .build(); p.cargo("check -v") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: failed to parse the `edition` key Caused by: supported edition values are `2015`, `2018`, `2021`, or `2024`, but `chicken` is unknown "#]]) .run(); } #[cargo_test] fn test_edition_from_the_future() { let p = project() .file( "Cargo.toml", r#"[package] edition = "2038" name = "foo" version = "99.99.99" authors = [] "#, ) .file("src/main.rs", r#""#) .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: failed to parse the `edition` key Caused by: this version of Cargo is older than the `2038` edition, and only supports `2015`, `2018`, `2021`, and `2024` editions. "#]]) .run(); } #[cargo_test] fn do_not_package_if_src_was_modified() { let p = project() .file("src/main.rs", r#"fn main() { println!("hello"); }"#) .file("dir/foo.txt", "") .file("bar.txt", "") .file( "build.rs", r#" use std::fs; fn main() { fs::write("src/generated.txt", "Hello, world of generated files." ).expect("failed to create file"); fs::remove_file("dir/foo.txt").expect("failed to remove file"); fs::remove_dir("dir").expect("failed to remove dir"); fs::write("bar.txt", "updated content").expect("failed to update"); fs::create_dir("new-dir").expect("failed to create dir"); } "#, ) .build(); p.cargo("package") .with_status(101) .with_stderr_data(str![[r#" ... [ERROR] failed to verify package tarball Caused by: Source directory was modified by build.rs during cargo publish. Build scripts should not modify anything outside of OUT_DIR. Changed: [ROOT]/foo/target/package/foo-0.0.1/bar.txt Added: [ROOT]/foo/target/package/foo-0.0.1/new-dir [ROOT]/foo/target/package/foo-0.0.1/src/generated.txt Removed: [ROOT]/foo/target/package/foo-0.0.1/dir [ROOT]/foo/target/package/foo-0.0.1/dir/foo.txt To proceed despite this, pass the `--no-verify` flag. "#]]) .run(); p.cargo("package --no-verify").run(); } #[cargo_test] fn package_with_select_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" [features] required = [] optional = [] "#, ) .file( "src/main.rs", "#[cfg(not(feature = \"required\"))] compile_error!(\"This crate requires `required` feature!\"); fn main() {}", ) .build(); p.cargo("package --features required").run(); } #[cargo_test] fn package_with_all_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" [features] required = [] optional = [] "#, ) .file( "src/main.rs", "#[cfg(not(feature = \"required\"))] compile_error!(\"This crate requires `required` feature!\"); fn main() {}", ) .build(); p.cargo("package --all-features").run(); } #[cargo_test] fn package_no_default_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" [features] default = ["required"] required = [] "#, ) .file( "src/main.rs", "#[cfg(not(feature = \"required\"))] compile_error!(\"This crate requires `required` feature!\"); fn main() {}", ) .build(); p.cargo("package --no-default-features") .with_stderr_data(str![[r#" ... [ERROR] This crate requires `required` feature! ... "#]]) .with_status(101) .run(); } #[cargo_test] fn include_cargo_toml_implicit() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" include = ["src/lib.rs"] "#, ) .file("src/lib.rs", "") .build(); p.cargo("package --list") .with_stdout_data(str![[r#" Cargo.lock Cargo.toml Cargo.toml.orig src/lib.rs "#]]) .run(); } fn include_exclude_test(include: &str, exclude: &str, files: &[&str], expected: &str) { let mut pb = project().file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] license = "MIT" description = "foo" documentation = "foo" homepage = "foo" repository = "foo" include = {} exclude = {} "#, include, exclude ), ); for file in files { pb = pb.file(file, ""); } let p = pb.build(); p.cargo("package --list") .with_stderr_data("") .with_stdout_data(expected) .run(); p.root().rm_rf(); } #[cargo_test] fn package_include_ignore_only() { // Test with a gitignore pattern that fails to parse with glob. // This is a somewhat nonsense pattern, but is an example of something git // allows and glob does not. assert!(glob::Pattern::new("src/abc**").is_err()); include_exclude_test( r#"["Cargo.toml", "src/abc**", "src/lib.rs"]"#, "[]", &["src/lib.rs", "src/abc1.rs", "src/abc2.rs", "src/abc/mod.rs"], "Cargo.lock\n\ Cargo.toml\n\ Cargo.toml.orig\n\ src/abc/mod.rs\n\ src/abc1.rs\n\ src/abc2.rs\n\ src/lib.rs\n\ ", ) } #[cargo_test] fn gitignore_patterns() { include_exclude_test( r#"["Cargo.toml", "foo"]"#, // include "[]", &["src/lib.rs", "foo", "a/foo", "a/b/foo", "x/foo/y", "bar"], "Cargo.lock\n\ Cargo.toml\n\ Cargo.toml.orig\n\ a/b/foo\n\ a/foo\n\ foo\n\ x/foo/y\n\ ", ); include_exclude_test( r#"["Cargo.toml", "/foo"]"#, // include "[]", &["src/lib.rs", "foo", "a/foo", "a/b/foo", "x/foo/y", "bar"], "Cargo.lock\n\ Cargo.toml\n\ Cargo.toml.orig\n\ foo\n\ ", ); include_exclude_test( "[]", r#"["foo/"]"#, // exclude &["src/lib.rs", "foo", "a/foo", "x/foo/y", "bar"], "Cargo.lock\n\ Cargo.toml\n\ Cargo.toml.orig\n\ a/foo\n\ bar\n\ foo\n\ src/lib.rs\n\ ", ); include_exclude_test( "[]", r#"["*.txt", "[ab]", "[x-z]"]"#, // exclude &[ "src/lib.rs", "foo.txt", "bar/foo.txt", "other", "a", "b", "c", "x", "y", "z", ], "Cargo.lock\n\ Cargo.toml\n\ Cargo.toml.orig\n\ c\n\ other\n\ src/lib.rs\n\ ", ); include_exclude_test( r#"["Cargo.toml", "**/foo/bar"]"#, // include "[]", &["src/lib.rs", "a/foo/bar", "foo", "bar"], "Cargo.lock\n\ Cargo.toml\n\ Cargo.toml.orig\n\ a/foo/bar\n\ ", ); include_exclude_test( r#"["Cargo.toml", "foo/**"]"#, // include "[]", &["src/lib.rs", "a/foo/bar", "foo/x/y/z"], "Cargo.lock\n\ Cargo.toml\n\ Cargo.toml.orig\n\ foo/x/y/z\n\ ", ); include_exclude_test( r#"["Cargo.toml", "a/**/b"]"#, // include "[]", &["src/lib.rs", "a/b", "a/x/b", "a/x/y/b"], "Cargo.lock\n\ Cargo.toml\n\ Cargo.toml.orig\n\ a/b\n\ a/x/b\n\ a/x/y/b\n\ ", ); } #[cargo_test] fn gitignore_negate() { include_exclude_test( r#"["Cargo.toml", "*.rs", "!foo.rs", "\\!important"]"#, // include "[]", &["src/lib.rs", "foo.rs", "!important"], "!important\n\ Cargo.lock\n\ Cargo.toml\n\ Cargo.toml.orig\n\ src/lib.rs\n\ ", ); // NOTE: This is unusual compared to git. Git treats `src/` as a // short-circuit which means rules like `!src/foo.rs` would never run. // However, because Cargo only works by iterating over *files*, it doesn't // short-circuit. include_exclude_test( r#"["Cargo.toml", "src/", "!src/foo.rs"]"#, // include "[]", &["src/lib.rs", "src/foo.rs"], "Cargo.lock\n\ Cargo.toml\n\ Cargo.toml.orig\n\ src/lib.rs\n\ ", ); include_exclude_test( r#"["Cargo.toml", "src/*.rs", "!foo.rs"]"#, // include "[]", &["src/lib.rs", "foo.rs", "src/foo.rs", "src/bar/foo.rs"], "Cargo.lock\n\ Cargo.toml\n\ Cargo.toml.orig\n\ src/lib.rs\n\ ", ); include_exclude_test( "[]", r#"["*.rs", "!foo.rs", "\\!important"]"#, // exclude &["src/lib.rs", "foo.rs", "!important"], "Cargo.lock\n\ Cargo.toml\n\ Cargo.toml.orig\n\ foo.rs\n\ ", ); } #[cargo_test] fn exclude_dot_files_and_directories_by_default() { include_exclude_test( "[]", "[]", &["src/lib.rs", ".dotfile", ".dotdir/file"], "Cargo.lock\n\ Cargo.toml\n\ Cargo.toml.orig\n\ src/lib.rs\n\ ", ); include_exclude_test( r#"["Cargo.toml", "src/lib.rs", ".dotfile", ".dotdir/file"]"#, "[]", &["src/lib.rs", ".dotfile", ".dotdir/file"], ".dotdir/file\n\ .dotfile\n\ Cargo.lock\n\ Cargo.toml\n\ Cargo.toml.orig\n\ src/lib.rs\n\ ", ); } #[cargo_test] fn empty_readme_path() { // fail if `readme` is empty. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" edition = "2015" readme = "" license = "MIT" description = "foo" homepage = "foo" "#, ) .file("src/lib.rs", "") .build(); p.cargo("package --no-verify") .with_status(101) .with_stderr_data(str![[r#" [ERROR] readme `` does not appear to exist (relative to `[ROOT]/foo`). Please update the readme setting in the manifest at `[ROOT]/foo/Cargo.toml`. "#]]) .run(); } #[cargo_test] fn invalid_readme_path() { // fail if `readme` path is invalid. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" edition = "2015" readme = "DOES-NOT-EXIST" license = "MIT" description = "foo" homepage = "foo" "#, ) .file("src/lib.rs", "") .build(); p.cargo("package --no-verify") .with_status(101) .with_stderr_data(str![[r#" [ERROR] readme `DOES-NOT-EXIST` does not appear to exist (relative to `[ROOT]/foo`). Please update the readme setting in the manifest at `[ROOT]/foo/Cargo.toml`. "#]]) .run(); } #[cargo_test] fn readme_or_license_file_is_dir() { // Test error when `readme` or `license-file` is a directory, not a file. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" edition = "2015" readme = "./src" license-file = "./src" description = "foo" homepage = "foo" "#, ) .file("src/lib.rs", "") .build(); p.cargo("package --no-verify") .with_status(101) .with_stderr_data(str![[r#" [ERROR] license-file `./src` does not appear to exist (relative to `[ROOT]/foo`). Please update the license-file setting in the manifest at `[ROOT]/foo/Cargo.toml`. readme `./src` does not appear to exist (relative to `[ROOT]/foo`). Please update the readme setting in the manifest at `[ROOT]/foo/Cargo.toml`. "#]]) .run(); } #[cargo_test] fn empty_license_file_path() { // fail if license-file is empty. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" edition = "2015" license-file = "" description = "foo" homepage = "foo" "#, ) .file("src/lib.rs", "") .build(); p.cargo("package --no-verify") .with_status(101) .with_stderr_data(str![[r#" [WARNING] manifest has no license or license-file. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [ERROR] license-file `` does not appear to exist (relative to `[ROOT]/foo`). Please update the license-file setting in the manifest at `[ROOT]/foo/Cargo.toml`. "#]]) .run(); } #[cargo_test] fn invalid_license_file_path() { // Test warning when license-file points to a non-existent file. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" edition = "2015" license-file = "does-not-exist" description = "foo" homepage = "foo" "#, ) .file("src/lib.rs", "") .build(); p.cargo("package --no-verify") .with_status(101) .with_stderr_data(str![[r#" [ERROR] license-file `does-not-exist` does not appear to exist (relative to `[ROOT]/foo`). Please update the license-file setting in the manifest at `[ROOT]/foo/Cargo.toml`. "#]]) .run(); } #[cargo_test] fn license_file_implicit_include() { // license-file should be automatically included even if not listed. let p = git::new("foo", |p| { p.file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" edition = "2015" license-file = "subdir/LICENSE" description = "foo" homepage = "foo" include = ["src"] "#, ) .file("src/lib.rs", "") .file("subdir/LICENSE", "license text") }); p.cargo("package --list") .with_stdout_data(str![[r#" .cargo_vcs_info.json Cargo.lock Cargo.toml Cargo.toml.orig src/lib.rs subdir/LICENSE "#]]) .with_stderr_data("") .run(); p.cargo("package --no-verify -v") .with_stderr_data(str![[r#" [PACKAGING] foo v1.0.0 ([ROOT]/foo) [ARCHIVING] .cargo_vcs_info.json [ARCHIVING] Cargo.lock [ARCHIVING] Cargo.toml [ARCHIVING] Cargo.toml.orig [ARCHIVING] src/lib.rs [ARCHIVING] subdir/LICENSE [PACKAGED] 6 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) "#]]) .run(); let f = File::open(&p.root().join("target/package/foo-1.0.0.crate")).unwrap(); validate_crate_contents( f, "foo-1.0.0.crate", &[ ".cargo_vcs_info.json", "Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "subdir/LICENSE", "src/lib.rs", ], [("subdir/LICENSE", "license text")], ); } #[cargo_test] fn relative_license_included() { // license-file path outside of package will copy into root. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" edition = "2015" license-file = "../LICENSE" description = "foo" homepage = "foo" "#, ) .file("src/lib.rs", "") .file("../LICENSE", "license text") .build(); p.cargo("package --list") .with_stdout_data(str![[r#" Cargo.lock Cargo.toml Cargo.toml.orig LICENSE src/lib.rs "#]]) .with_stderr_data("") .run(); p.cargo("package") .with_stderr_data(str![[r#" [PACKAGING] foo v1.0.0 ([ROOT]/foo) [PACKAGED] 5 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v1.0.0 ([ROOT]/foo) [COMPILING] foo v1.0.0 ([ROOT]/foo/target/package/foo-1.0.0) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let f = File::open(&p.root().join("target/package/foo-1.0.0.crate")).unwrap(); validate_crate_contents( f, "foo-1.0.0.crate", &[ "Cargo.toml", "Cargo.toml.orig", "LICENSE", "src/lib.rs", "Cargo.lock", ], [("LICENSE", "license text")], ); let manifest = std::fs::read_to_string(p.root().join("target/package/foo-1.0.0/Cargo.toml")).unwrap(); assert!(manifest.contains("license-file = \"LICENSE\"")); let orig = std::fs::read_to_string(p.root().join("target/package/foo-1.0.0/Cargo.toml.orig")).unwrap(); assert!(orig.contains("license-file = \"../LICENSE\"")); } #[cargo_test] fn relative_license_include_collision() { // Can't copy a relative license-file if there is a file with that name already. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" edition = "2015" license-file = "../LICENSE" description = "foo" homepage = "foo" "#, ) .file("src/lib.rs", "") .file("../LICENSE", "outer license") .file("LICENSE", "inner license") .build(); p.cargo("package --list") .with_stdout_data(str![[r#" Cargo.lock Cargo.toml Cargo.toml.orig LICENSE src/lib.rs "#]]) .with_stderr_data(str![[r#" [WARNING] license-file `../LICENSE` appears to be a path outside of the package, but there is already a file named `LICENSE` in the root of the package. The archived crate will contain the copy in the root of the package. Update the license-file to point to the path relative to the root of the package to remove this warning. "#]]) .run(); p.cargo("package").with_stderr_data(str![[r#" [WARNING] license-file `../LICENSE` appears to be a path outside of the package, but there is already a file named `LICENSE` in the root of the package. The archived crate will contain the copy in the root of the package. Update the license-file to point to the path relative to the root of the package to remove this warning. [PACKAGING] foo v1.0.0 ([ROOT]/foo) [PACKAGED] 5 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v1.0.0 ([ROOT]/foo) [COMPILING] foo v1.0.0 ([ROOT]/foo/target/package/foo-1.0.0) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); let f = File::open(&p.root().join("target/package/foo-1.0.0.crate")).unwrap(); validate_crate_contents( f, "foo-1.0.0.crate", &[ "Cargo.toml", "Cargo.toml.orig", "LICENSE", "src/lib.rs", "Cargo.lock", ], [("LICENSE", "inner license")], ); let manifest = read_to_string(p.root().join("target/package/foo-1.0.0/Cargo.toml")).unwrap(); assert!(manifest.contains("license-file = \"LICENSE\"")); let orig = read_to_string(p.root().join("target/package/foo-1.0.0/Cargo.toml.orig")).unwrap(); assert!(orig.contains("license-file = \"../LICENSE\"")); } #[cargo_test] #[cfg(not(windows))] // Don't want to create invalid files on Windows. fn package_restricted_windows() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" license = "MIT" description = "foo" homepage = "foo" "#, ) .file("src/lib.rs", "pub mod con;\npub mod aux;") .file("src/con.rs", "pub fn f() {}") .file("src/aux/mod.rs", "pub fn f() {}") .build(); p.cargo("package") // use unordered here because the order of the warning is different on each platform. .with_stderr_data( str![[r#" [WARNING] file src/con.rs is a reserved Windows filename, it will not work on Windows platforms [WARNING] file src/aux/mod.rs is a reserved Windows filename, it will not work on Windows platforms [PACKAGING] foo v0.1.0 ([ROOT]/foo) [PACKAGED] 6 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.1.0 ([ROOT]/foo) [COMPILING] foo v0.1.0 ([ROOT]/foo/target/package/foo-0.1.0) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn finds_git_in_parent() { // Test where `Cargo.toml` is not in the root of the git repo. let repo_path = paths::root().join("repo"); fs::create_dir(&repo_path).unwrap(); let p = project() .at("repo/foo") .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("src/lib.rs", "") .build(); let repo = git::init(&repo_path); git::add(&repo); git::commit(&repo); p.change_file("ignoreme", ""); p.change_file("ignoreme2", ""); p.cargo("package --list --allow-dirty") .with_stdout_data(str![[r#" .cargo_vcs_info.json Cargo.lock Cargo.toml Cargo.toml.orig ignoreme ignoreme2 src/lib.rs "#]]) .run(); p.change_file(".gitignore", "ignoreme"); p.cargo("package --list --allow-dirty") .with_stdout_data(str![[r#" .cargo_vcs_info.json .gitignore Cargo.lock Cargo.toml Cargo.toml.orig ignoreme2 src/lib.rs "#]]) .run(); fs::write(repo_path.join(".gitignore"), "ignoreme2").unwrap(); p.cargo("package --list --allow-dirty") .with_stdout_data(str![[r#" .cargo_vcs_info.json .gitignore Cargo.lock Cargo.toml Cargo.toml.orig src/lib.rs "#]]) .run(); } #[cargo_test] #[cfg(windows)] fn reserved_windows_name() { // If we are running on a version of Windows that allows these reserved filenames, // skip this test. if paths::windows_reserved_names_are_allowed() { return; } Package::new("bar", "1.0.0") .file("src/lib.rs", "pub mod aux;") .file("src/aux.rs", "") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" [dependencies] bar = "1.0.0" "#, ) .file("src/main.rs", "extern crate bar;\nfn main() { }") .build(); p.cargo("package") .with_status(101) .with_stderr_data(str![[r#" ... [ERROR] failed to verify package tarball Caused by: failed to download replaced source registry `crates-io` Caused by: failed to unpack package `bar v1.0.0 (registry `dummy-registry`)` Caused by: failed to unpack entry at `bar-1.0.0/src/aux.rs` Caused by: `bar-1.0.0/src/aux.rs` appears to contain a reserved Windows path, it cannot be extracted on Windows Caused by: failed to unpack `[ROOT]/home/.cargo/registry/src/-[HASH]/bar-1.0.0/src/aux.rs` Caused by: failed to unpack `bar-1.0.0/src/aux.rs` into `[ROOT]/home/.cargo/registry/src/-[HASH]/bar-1.0.0/src/aux.rs` Caused by: [NOT_FOUND] "#]]) .run(); } #[cargo_test] fn list_with_path_and_lock() { // Allow --list even for something that isn't packageable. // Init an empty registry because a versionless path dep will search for // the package on crates.io. registry::init(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" license = "MIT" description = "foo" homepage = "foo" [dependencies] bar = {path="bar"} "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "") .build(); p.cargo("package --list") .with_stdout_data(str![[r#" Cargo.lock Cargo.toml Cargo.toml.orig src/main.rs "#]]) .run(); p.cargo("package") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to verify manifest at `[ROOT]/foo/Cargo.toml` Caused by: all dependencies must have a version requirement specified when packaging. dependency `bar` does not specify a version Note: The packaged dependency will use the version from crates.io, the `path` specification will be removed from the dependency declaration. "#]]) .run(); } #[cargo_test] fn long_file_names() { // Filenames over 100 characters require a GNU extension tarfile. // See #8453. registry::init(); let long_name = concat!( "012345678901234567890123456789012345678901234567890123456789", "012345678901234567890123456789012345678901234567890123456789", "012345678901234567890123456789012345678901234567890123456789" ); if cfg!(windows) { // Long paths on Windows require a special registry entry that is // disabled by default (even on Windows 10). // https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file // If the directory where Cargo runs happens to be more than 80 characters // long, then it will bump into this limit. // // First create a directory to account for various paths Cargo will // be using in the target directory (such as "target/package/foo-0.1.0"). let test_path = paths::root().join("test-dir-probe-long-path-support"); test_path.mkdir_p(); let test_path = test_path.join(long_name); if let Err(e) = File::create(&test_path) { // write to stderr directly to avoid output from being captured // and always display text, even without --nocapture use std::io::Write; writeln!( std::io::stderr(), "\nSkipping long_file_names test, this OS or filesystem does not \ appear to support long file paths: {:?}\n{:?}", e, test_path ) .unwrap(); return; } } let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" license = "MIT" description = "foo" homepage = "foo" [dependencies] "#, ) .file(long_name, "something") .file("src/main.rs", "fn main() {}") .build(); p.cargo("package").run(); p.cargo("package --list").with_stdout_data(str![[r#" 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 Cargo.lock Cargo.toml Cargo.toml.orig src/main.rs "#]]).run(); } #[cargo_test] fn reproducible_output() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] exclude = ["*.txt"] license = "MIT" description = "foo" "#, ) .file("src/main.rs", r#"fn main() { println!("hello"); }"#) .build(); p.cargo("package").run(); assert!(p.root().join("target/package/foo-0.0.1.crate").is_file()); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); let decoder = GzDecoder::new(f); let mut archive = Archive::new(decoder); for ent in archive.entries().unwrap() { let ent = ent.unwrap(); println!("checking {:?}", ent.path()); let header = ent.header(); assert_eq!(header.mode().unwrap(), 0o644); assert!(header.mtime().unwrap() != 0); assert_eq!(header.username().unwrap().unwrap(), ""); assert_eq!(header.groupname().unwrap().unwrap(), ""); } } #[cargo_test] fn package_with_resolver_and_metadata() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] resolver = '2' [package.metadata.docs.rs] all-features = true "#, ) .file("src/lib.rs", "") .build(); p.cargo("package").run(); } #[cargo_test] fn deleted_git_working_tree() { // When deleting a file, but not staged, cargo should ignore the file. let (p, repo) = git::new_repo("foo", |p| { p.file("src/lib.rs", "").file("src/main.rs", "fn main() {}") }); p.root().join("src/lib.rs").rm_rf(); p.cargo("package --allow-dirty --list") .with_stdout_data(str![[r#" .cargo_vcs_info.json Cargo.lock Cargo.toml Cargo.toml.orig src/main.rs "#]]) .run(); p.cargo("package --allow-dirty").run(); let mut index = t!(repo.index()); t!(index.remove(Path::new("src/lib.rs"), 0)); t!(index.write()); p.cargo("package --allow-dirty --list") .with_stdout_data(str![[r#" .cargo_vcs_info.json Cargo.lock Cargo.toml Cargo.toml.orig src/main.rs "#]]) .run(); p.cargo("package --allow-dirty").run(); } #[cargo_test] fn package_in_workspace_not_found() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "bar" "#, ) .file("bar/src/main.rs", "fn main() {}") .file( "baz/Cargo.toml", r#" [package] name = "baz" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "baz" "#, ) .file("baz/src/main.rs", "fn main() {}") .build(); p.cargo("package -p doesnt-exist") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package ID specification `doesnt-exist` did not match any packages "#]]) .run(); } #[cargo_test] fn in_workspace() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" [workspace] members = ["bar"] "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "bar" workspace = ".." "#, ) .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("package --workspace") .with_stderr_data(str![[r#" [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] bar v0.0.1 ([ROOT]/foo/bar) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] bar v0.0.1 ([ROOT]/foo/bar) [COMPILING] bar v0.0.1 ([ROOT]/foo/target/package/bar-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert!(p.root().join("target/package/foo-0.0.1.crate").is_file()); assert!(p.root().join("target/package/bar-0.0.1.crate").is_file()); } #[cargo_test] fn workspace_noconflict_readme() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar"] "#, ) .file("README.md", "workspace readme") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" repository = "https://github.com/bar/bar" authors = [] license = "MIT" description = "bar" readme = "../README.md" workspace = ".." "#, ) .file("bar/src/main.rs", "fn main() {}") .file("bar/example/README.md", "# example readmdBar") .build(); p.cargo("package") .with_stderr_data(str![[r#" [PACKAGING] bar v0.0.1 ([ROOT]/foo/bar) [PACKAGED] 6 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] bar v0.0.1 ([ROOT]/foo/bar) [COMPILING] bar v0.0.1 ([ROOT]/foo/target/package/bar-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn workspace_conflict_readme() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar"] "#, ) .file("README.md", "workspace readme") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" repository = "https://github.com/bar/bar" authors = [] license = "MIT" description = "bar" readme = "../README.md" workspace = ".." "#, ) .file("bar/src/main.rs", "fn main() {}") .file("bar/README.md", "# workspace member: Bar") .build(); p.cargo("package").with_stderr_data(str![[r#" [WARNING] readme `../README.md` appears to be a path outside of the package, but there is already a file named `README.md` in the root of the package. The archived crate will contain the copy in the root of the package. Update the readme to point to the path relative to the root of the package to remove this warning. [PACKAGING] bar v0.0.1 ([ROOT]/foo/bar) [PACKAGED] 5 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] bar v0.0.1 ([ROOT]/foo/bar) [COMPILING] bar v0.0.1 ([ROOT]/foo/target/package/bar-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn workspace_overrides_resolver() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] "#, ) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2021" "#, ) .file("bar/src/lib.rs", "") .file( "baz/Cargo.toml", r#" [package] name = "baz" version = "0.1.0" edition = "2015" "#, ) .file("baz/src/lib.rs", "") .build(); p.cargo("package --no-verify -p bar -p baz").run(); let f = File::open(&p.root().join("target/package/bar-0.1.0.crate")).unwrap(); let rewritten_toml = str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "bar" version = "0.1.0" build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false readme = false resolver = "1" [lib] name = "bar" path = "src/lib.rs" "##]]; validate_crate_contents( f, "bar-0.1.0.crate", &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs", "Cargo.lock"], [("Cargo.toml", rewritten_toml)], ); // When the crate has the same implicit resolver as the workspace it is not overridden let f = File::open(&p.root().join("target/package/baz-0.1.0.crate")).unwrap(); let rewritten_toml = str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "baz" version = "0.1.0" build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false readme = false [lib] name = "baz" path = "src/lib.rs" "##]]; validate_crate_contents( f, "baz-0.1.0.crate", &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs", "Cargo.lock"], [("Cargo.toml", rewritten_toml)], ); } fn verify_packaged_status_line( output: cargo_test_support::RawOutput, num_files: usize, uncompressed_size: u64, compressed_size: u64, ) { use cargo::util::HumanBytes; let stderr = String::from_utf8(output.stderr).unwrap(); let mut packaged_lines = stderr .lines() .filter(|line| line.trim().starts_with("Packaged")); let packaged_line = packaged_lines .next() .expect("`Packaged` status line should appear in stderr"); assert!( packaged_lines.next().is_none(), "Only one `Packaged` status line should appear in stderr" ); let size_info = packaged_line.trim().trim_start_matches("Packaged").trim(); let uncompressed = HumanBytes(uncompressed_size); let compressed = HumanBytes(compressed_size); let expected = format!("{num_files} files, {uncompressed:.1} ({compressed:.1} compressed)"); assert_eq!(size_info, expected); } #[cargo_test] fn basic_filesizes() { let cargo_toml_orig_contents = r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] exclude = ["*.txt"] license = "MIT" description = "foo" homepage = "https://example.com/" "#; let main_rs_contents = r#"fn main() { println!("🦀"); }"#; let cargo_toml_contents = format!( r#"{} [package] edition = "2015" name = "foo" version = "0.0.1" authors = [] build = false exclude = ["*.txt"] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" homepage = "https://example.com/" readme = false license = "MIT" [[bin]] name = "foo" path = "src/main.rs" "#, cargo::core::manifest::MANIFEST_PREAMBLE ); let cargo_lock_contents = r#"# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "foo" version = "0.0.1" "#; let p = project() .file("Cargo.toml", cargo_toml_orig_contents) .file("src/main.rs", main_rs_contents) .file("src/bar.txt", "Ignored text file contents") // should be ignored when packaging .build(); let uncompressed_size = (cargo_toml_orig_contents.len() + main_rs_contents.len() + cargo_toml_contents.len() + cargo_lock_contents.len()) as u64; let output = p.cargo("package").run(); assert!(p.root().join("target/package/foo-0.0.1.crate").is_file()); p.cargo("package -l") .with_stdout_data(str![[r#" Cargo.lock Cargo.toml Cargo.toml.orig src/main.rs "#]]) .run(); p.cargo("package") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); let compressed_size = f.metadata().unwrap().len(); verify_packaged_status_line(output, 4, uncompressed_size, compressed_size); validate_crate_contents( f, "foo-0.0.1.crate", &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], [ ("Cargo.lock", cargo_lock_contents), ("Cargo.toml", &cargo_toml_contents), ("Cargo.toml.orig", cargo_toml_orig_contents), ("src/main.rs", main_rs_contents), ], ); } #[cargo_test] fn larger_filesizes() { let cargo_toml_orig_contents = r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" documentation = "https://example.com/" "#; let lots_of_crabs = "🦀".repeat(1337); let main_rs_contents = format!(r#"fn main() {{ println!("{}"); }}"#, lots_of_crabs); let bar_txt_contents = "This file is relatively uncompressible, to increase the compressed package size beyond 1KiB. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; let cargo_toml_contents = format!( r#"{} [package] edition = "2015" name = "foo" version = "0.0.1" authors = [] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "https://example.com/" readme = false license = "MIT" [[bin]] name = "foo" path = "src/main.rs" "#, cargo::core::manifest::MANIFEST_PREAMBLE ); let cargo_lock_contents = r#"# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "foo" version = "0.0.1" "#; let p = project() .file("Cargo.toml", cargo_toml_orig_contents) .file("src/main.rs", &main_rs_contents) .file("src/bar.txt", bar_txt_contents) .build(); let uncompressed_size = (cargo_toml_orig_contents.len() + main_rs_contents.len() + cargo_toml_contents.len() + cargo_lock_contents.len() + bar_txt_contents.len()) as u64; let output = p.cargo("package").run(); assert!(p.root().join("target/package/foo-0.0.1.crate").is_file()); p.cargo("package -l") .with_stdout_data(str![[r#" Cargo.lock Cargo.toml Cargo.toml.orig src/bar.txt src/main.rs "#]]) .run(); p.cargo("package") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 5 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); let compressed_size = f.metadata().unwrap().len(); verify_packaged_status_line(output, 5, uncompressed_size, compressed_size); validate_crate_contents( f, "foo-0.0.1.crate", &[ "Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/bar.txt", "src/main.rs", ], [ ("Cargo.lock", cargo_lock_contents), ("Cargo.toml", &cargo_toml_contents), ("Cargo.toml.orig", cargo_toml_orig_contents), ("src/bar.txt", bar_txt_contents), ("src/main.rs", &main_rs_contents), ], ); } #[cargo_test] fn symlink_filesizes() { if !symlink_supported() { return; } let cargo_toml_orig_contents = r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" homepage = "https://example.com/" "#; let lots_of_crabs = "🦀".repeat(1337); let main_rs_contents = format!(r#"fn main() {{ println!("{}"); }}"#, lots_of_crabs); let bar_txt_contents = "This file is relatively uncompressible, to increase the compressed package size beyond 1KiB. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; let cargo_toml_contents = format!( r#"{} [package] edition = "2015" name = "foo" version = "0.0.1" authors = [] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" homepage = "https://example.com/" readme = false license = "MIT" [[bin]] name = "foo" path = "src/main.rs" "#, cargo::core::manifest::MANIFEST_PREAMBLE ); let cargo_lock_contents = r#"# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "foo" version = "0.0.1" "#; let p = project() .file("Cargo.toml", cargo_toml_orig_contents) .file("src/main.rs", &main_rs_contents) .file("bla/bar.txt", bar_txt_contents) .symlink("src/main.rs", "src/main.rs.bak") .symlink_dir("bla", "foo") .build(); let uncompressed_size = (cargo_toml_orig_contents.len() + main_rs_contents.len() * 2 + cargo_toml_contents.len() + cargo_lock_contents.len() + bar_txt_contents.len() * 2) as u64; let output = p.cargo("package").run(); assert!(p.root().join("target/package/foo-0.0.1.crate").is_file()); p.cargo("package -l") .with_stdout_data(str![[r#" Cargo.lock Cargo.toml Cargo.toml.orig bla/bar.txt foo/bar.txt src/main.rs src/main.rs.bak "#]]) .run(); p.cargo("package") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 7 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); let compressed_size = f.metadata().unwrap().len(); verify_packaged_status_line(output, 7, uncompressed_size, compressed_size); validate_crate_contents( f, "foo-0.0.1.crate", &[ "Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "bla/bar.txt", "foo/bar.txt", "src/main.rs", "src/main.rs.bak", ], [ ("Cargo.lock", cargo_lock_contents), ("Cargo.toml", &cargo_toml_contents), ("Cargo.toml.orig", cargo_toml_orig_contents), ("bla/bar.txt", bar_txt_contents), ("foo/bar.txt", bar_txt_contents), ("src/main.rs", &main_rs_contents), ("src/main.rs.bak", &main_rs_contents), ], ); } #[cargo_test] #[cfg(windows)] // windows is the platform that is most consistently configured for case insensitive filesystems fn normalize_case() { let p = project() .file("Build.rs", r#"fn main() { println!("hello"); }"#) .file("src/Main.rs", r#"fn main() { println!("hello"); }"#) .file("src/lib.rs", "") .file("src/bar.txt", "") // should be ignored when packaging .file("Examples/ExampleFoo.rs", "") .file("Tests/ExplicitPath.rs", "") .build(); // Workaround `project()` making a `Cargo.toml` on our behalf std::fs::remove_file(p.root().join("Cargo.toml")).unwrap(); std::fs::write( p.root().join("cargo.toml"), r#" [package] name = "foo" version = "0.0.1" edition = "2018" authors = [] exclude = ["*.txt"] license = "MIT" description = "foo" [[test]] name = "explicitpath" path = "tests/explicitpath.rs" "#, ) .unwrap(); p.cargo("package").with_stderr_data(str![[r#" [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [WARNING] ignoring `package.build` entry `build.rs` as it is not included in the published package [WARNING] ignoring binary `foo` as `src/main.rs` is not included in the published package [WARNING] ignoring example `ExampleFoo` as `examples/ExampleFoo.rs` is not included in the published package [WARNING] ignoring test `ExplicitPath` as `tests/ExplicitPath.rs` is not included in the published package [WARNING] ignoring test `explicitpath` as `tests/explicitpath.rs` is not included in the published package [PACKAGED] 8 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); assert!(p.root().join("target/package/foo-0.0.1.crate").is_file()); p.cargo("package -l") .with_stdout_data(str![[r#" Build.rs Cargo.lock Cargo.toml Cargo.toml.orig Examples/ExampleFoo.rs Tests/ExplicitPath.rs src/Main.rs src/lib.rs "#]]) .run(); p.cargo("package").with_stderr_data(str![[r#" [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [WARNING] ignoring `package.build` entry `build.rs` as it is not included in the published package [WARNING] ignoring binary `foo` as `src/main.rs` is not included in the published package [WARNING] ignoring example `ExampleFoo` as `examples/ExampleFoo.rs` is not included in the published package [WARNING] ignoring test `ExplicitPath` as `tests/ExplicitPath.rs` is not included in the published package [WARNING] ignoring test `explicitpath` as `tests/explicitpath.rs` is not included in the published package [PACKAGED] 8 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", &[ "Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "Build.rs", "src/Main.rs", "src/lib.rs", "Examples/ExampleFoo.rs", "Tests/ExplicitPath.rs", ], [( "Cargo.toml", str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "foo" version = "0.0.1" authors = [] build = false exclude = ["*.txt"] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" readme = false license = "MIT" [lib] name = "foo" path = "src/lib.rs" "##]], )], ); } #[cargo_test] #[cfg(target_os = "linux")] // linux is generally configured to be case sensitive fn mixed_case() { let manifest = r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] exclude = ["*.txt"] license = "MIT" description = "foo" "#; let p = project() .file("Cargo.toml", manifest) .file("cargo.toml", manifest) .file("src/main.rs", r#"fn main() { println!("hello"); }"#) .file("src/bar.txt", "") // should be ignored when packaging .build(); p.cargo("package") .with_stderr_data(str![[r#" [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert!(p.root().join("target/package/foo-0.0.1.crate").is_file()); p.cargo("package -l") .with_stdout_data(str![[r#" Cargo.lock Cargo.toml Cargo.toml.orig src/main.rs "#]]) .run(); p.cargo("package") .with_stderr_data(str![[r#" [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], (), ); } #[cargo_test] fn versionless_package() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" description = "foo" edition = "2015" "#, ) .file("src/main.rs", r#"fn main() { println!("hello"); }"#) .build(); p.cargo("package") .with_stderr_data(str![[r#" [WARNING] manifest has no license, license-file, documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.0 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.0 ([ROOT]/foo) [COMPILING] foo v0.0.0 ([ROOT]/foo/target/package/foo-0.0.0) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let f = File::open(&p.root().join("target/package/foo-0.0.0.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.0.crate", &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], (), ); } #[cargo_test] fn include_files_called_target_project() { // https://github.com/rust-lang/cargo/issues/12790 // files and folders called "target" should be included, unless they're the actual target directory let p = init_and_add_inner_target(project()) .file("target/foo.txt", "") .build(); p.cargo("package -l") .with_stdout_data(str![[r#" Cargo.lock Cargo.toml Cargo.toml.orig data/not_target data/target derp/not_target/foo.txt derp/target/foo.txt src/main.rs "#]]) .run(); } #[cargo_test] fn include_files_called_target_git() { // https://github.com/rust-lang/cargo/issues/12790 // files and folders called "target" should be included, unless they're the actual target directory let (p, repo) = git::new_repo("foo", |p| init_and_add_inner_target(p)); // add target folder but not committed. _ = fs::create_dir(p.build_dir()).unwrap(); _ = fs::write(p.build_dir().join("foo.txt"), "").unwrap(); p.cargo("package -l") .with_stdout_data(str![[r#" .cargo_vcs_info.json Cargo.lock Cargo.toml Cargo.toml.orig data/not_target data/target derp/not_target/foo.txt derp/target/foo.txt src/main.rs "#]]) .run(); // if target is committed, it should be included. git::add(&repo); git::commit(&repo); p.cargo("package -l") .with_stdout_data(str![[r#" .cargo_vcs_info.json Cargo.lock Cargo.toml Cargo.toml.orig data/not_target data/target derp/not_target/foo.txt derp/target/foo.txt src/main.rs target/foo.txt "#]]) .run(); // Untracked files shouldn't be included, if they are also ignored. _ = fs::write(repo.workdir().unwrap().join(".gitignore"), "target/").unwrap(); git::add(&repo); git::commit(&repo); _ = fs::write(p.build_dir().join("untracked.txt"), "").unwrap(); p.cargo("package -l") .with_stdout_data(str![[r#" .cargo_vcs_info.json .gitignore Cargo.lock Cargo.toml Cargo.toml.orig data/not_target data/target derp/not_target/foo.txt derp/target/foo.txt src/main.rs target/foo.txt "#]]) .run(); } fn init_and_add_inner_target(p: ProjectBuilder) -> ProjectBuilder { p.file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" "#, ) .file("src/main.rs", r#"fn main() { println!("hello"); }"#) // file called target, should be included .file("data/target", "") .file("data/not_target", "") // folder called target, should be included .file("derp/target/foo.txt", "") .file("derp/not_target/foo.txt", "") } #[cargo_test] fn build_script_outside_pkg_root() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" license = "MIT" description = "foo" authors = [] build = "../t_custom_build/custom_build.rs" "#, ) .file("src/main.rs", "fn main() {}") .build(); // custom_build.rs does not exist p.cargo("package -l") .with_status(101) .with_stderr_data(str![[r#" [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [ERROR] the source file of build script doesn't appear to exist. This may cause issue during packaging, as modules resolution and resources included via macros are often relative to the path of source files. Please update the `build` setting in the manifest at `[ROOT]/foo/Cargo.toml` and point to a path inside the root of the package. "#]]) .run(); // custom_build.rs outside the package root let custom_build_root = paths::root().join("t_custom_build"); _ = fs::create_dir(&custom_build_root).unwrap(); _ = fs::write(&custom_build_root.join("custom_build.rs"), "fn main() {}"); p.cargo("package -l") .with_status(101) .with_stderr_data(str![[r#" [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [ERROR] the source file of build script doesn't appear to be a path inside of the package. It is at `[ROOT]/t_custom_build/custom_build.rs`, whereas the root the package is `[ROOT]/foo`. This may cause issue during packaging, as modules resolution and resources included via macros are often relative to the path of source files. Please update the `build` setting in the manifest at `[ROOT]/foo/Cargo.toml` and point to a path inside the root of the package. "#]]) .run(); } #[cargo_test] fn symlink_manifest_path() { // Test `cargo install --manifest-path` pointing through a symlink. if !symlink_supported() { return; } let p = git::new("foo", |p| { p.file("Cargo.toml", &basic_manifest("foo", "1.0.0")) .file("src/main.rs", "fn main() {}") // Triggers discover_git_and_list_files for detecting changed files. .file("build.rs", "fn main() {}") }); #[cfg(unix)] use std::os::unix::fs::symlink; #[cfg(windows)] use std::os::windows::fs::symlink_dir as symlink; let foo_symlink = paths::root().join("foo-symlink"); t!(symlink(p.root(), &foo_symlink)); cargo_process("package --no-verify --manifest-path") .arg(foo_symlink.join("Cargo.toml")) .with_stderr_data(str![[r#" [WARNING] manifest has no description, license, license-file, documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v1.0.0 ([ROOT]/foo-symlink) [PACKAGED] 6 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) "#]]) .run(); } #[cargo_test] #[cfg(windows)] fn normalize_paths() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" description = "foo" documentation = "docs.rs/foo" authors = [] readme = ".\\docs\\README.md" license-file = ".\\docs\\LICENSE" build = ".\\src\\build.rs" [lib] path = ".\\src\\lib.rs" [[bin]] name = "foo" path = ".\\src\\bin\\foo\\main.rs" [[example]] name = "example_foo" path = ".\\examples\\example_foo.rs" [[test]] name = "test_foo" path = ".\\tests\\test_foo.rs" [[bench]] name = "bench_foo" path = ".\\benches\\bench_foo.rs" "#, ) .file("src/lib.rs", "") .file("docs/README.md", "") .file("docs/LICENSE", "") .file("src/build.rs", "fn main() {}") .file("src/bin/foo/main.rs", "fn main() {}") .file("examples/example_foo.rs", "fn main() {}") .file("tests/test_foo.rs", "fn main() {}") .file("benches/bench_foo.rs", "fn main() {}") .build(); p.cargo("package") .with_stdout_data("") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 11 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", &[ "Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/lib.rs", "docs/README.md", "docs/LICENSE", "src/build.rs", "src/bin/foo/main.rs", "examples/example_foo.rs", "tests/test_foo.rs", "benches/bench_foo.rs", ], [( "Cargo.toml", str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "foo" version = "0.0.1" authors = [] build = "src/build.rs" autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "docs.rs/foo" readme = "docs/README.md" license-file = "docs/LICENSE" [lib] name = "foo" path = "src/lib.rs" [[bin]] name = "foo" path = "src/bin/foo/main.rs" [[example]] name = "example_foo" path = "examples/example_foo.rs" [[test]] name = "test_foo" path = "tests/test_foo.rs" [[bench]] name = "bench_foo" path = "benches/bench_foo.rs" "##]], )], ); } #[cargo_test] fn discovery_inferred_build_rs_included() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" license = "MIT" description = "foo" documentation = "docs.rs/foo" authors = [] include = ["src/lib.rs", "build.rs"] "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .build(); p.cargo("package") .with_stdout_data("") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 5 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", &[ "Cargo.toml", "Cargo.toml.orig", "src/lib.rs", "build.rs", "Cargo.lock", ], [( "Cargo.toml", str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "foo" version = "0.0.1" authors = [] build = "build.rs" include = [ "src/lib.rs", "build.rs", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "docs.rs/foo" readme = false license = "MIT" [lib] name = "foo" path = "src/lib.rs" "##]], )], ); } #[cargo_test] fn discovery_inferred_build_rs_excluded() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" license = "MIT" description = "foo" documentation = "docs.rs/foo" authors = [] include = ["src/lib.rs"] "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .build(); p.cargo("package") .with_stdout_data("") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [WARNING] ignoring `package.build` entry `build.rs` as it is not included in the published package [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs", "Cargo.lock"], [( "Cargo.toml", str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "foo" version = "0.0.1" authors = [] build = false include = ["src/lib.rs"] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "docs.rs/foo" readme = false license = "MIT" [lib] name = "foo" path = "src/lib.rs" "##]], )], ); } #[cargo_test] fn discovery_explicit_build_rs_included() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" license = "MIT" description = "foo" documentation = "docs.rs/foo" authors = [] include = ["src/lib.rs", "build.rs"] build = "build.rs" "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .build(); p.cargo("package") .with_stdout_data("") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 5 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", &[ "Cargo.toml", "Cargo.toml.orig", "src/lib.rs", "build.rs", "Cargo.lock", ], [( "Cargo.toml", str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "foo" version = "0.0.1" authors = [] build = "build.rs" include = [ "src/lib.rs", "build.rs", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "docs.rs/foo" readme = false license = "MIT" [lib] name = "foo" path = "src/lib.rs" "##]], )], ); } #[cargo_test] fn discovery_explicit_build_rs_excluded() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" license = "MIT" description = "foo" documentation = "docs.rs/foo" authors = [] include = ["src/lib.rs"] build = "build.rs" "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .build(); p.cargo("package") .with_stdout_data("") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [WARNING] ignoring `package.build` entry `build.rs` as it is not included in the published package [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs", "Cargo.lock"], [( "Cargo.toml", str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "foo" version = "0.0.1" authors = [] build = false include = ["src/lib.rs"] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "docs.rs/foo" readme = false license = "MIT" [lib] name = "foo" path = "src/lib.rs" "##]], )], ); } #[cargo_test] fn discovery_inferred_lib_included() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" license = "MIT" description = "foo" documentation = "docs.rs/foo" authors = [] include = ["src/main.rs", "src/lib.rs"] "#, ) .file("src/main.rs", "fn main() {}") .file("src/lib.rs", "") .build(); p.cargo("package") .with_stdout_data("") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 5 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", &[ "Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs", "src/lib.rs", ], [( "Cargo.toml", str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "foo" version = "0.0.1" authors = [] build = false include = [ "src/main.rs", "src/lib.rs", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "docs.rs/foo" readme = false license = "MIT" [lib] name = "foo" path = "src/lib.rs" [[bin]] name = "foo" path = "src/main.rs" "##]], )], ); } #[cargo_test] fn discovery_inferred_lib_excluded() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" license = "MIT" description = "foo" documentation = "docs.rs/foo" authors = [] include = ["src/main.rs"] "#, ) .file("src/main.rs", "fn main() {}") .file("src/lib.rs", "") .build(); p.cargo("package") .with_stdout_data("") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [WARNING] ignoring library `foo` as `src/lib.rs` is not included in the published package [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], [( "Cargo.toml", str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "foo" version = "0.0.1" authors = [] build = false include = ["src/main.rs"] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "docs.rs/foo" readme = false license = "MIT" [[bin]] name = "foo" path = "src/main.rs" "##]], )], ); } #[cargo_test] fn discovery_explicit_lib_included() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" license = "MIT" description = "foo" documentation = "docs.rs/foo" authors = [] include = ["src/main.rs", "src/lib.rs"] [lib] path = "src/lib.rs" "#, ) .file("src/main.rs", "fn main() {}") .file("src/lib.rs", "") .build(); p.cargo("package") .with_stdout_data("") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 5 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", &[ "Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs", "src/lib.rs", ], [( "Cargo.toml", str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "foo" version = "0.0.1" authors = [] build = false include = [ "src/main.rs", "src/lib.rs", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "docs.rs/foo" readme = false license = "MIT" [lib] name = "foo" path = "src/lib.rs" [[bin]] name = "foo" path = "src/main.rs" "##]], )], ); } #[cargo_test] fn discovery_explicit_lib_excluded() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" license = "MIT" description = "foo" documentation = "docs.rs/foo" authors = [] include = ["src/main.rs"] [lib] path = "src/lib.rs" "#, ) .file("src/main.rs", "fn main() {}") .file("src/lib.rs", "") .build(); p.cargo("package") .with_stdout_data("") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [WARNING] ignoring library `foo` as `src/lib.rs` is not included in the published package [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], [( "Cargo.toml", str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "foo" version = "0.0.1" authors = [] build = false include = ["src/main.rs"] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "docs.rs/foo" readme = false license = "MIT" [[bin]] name = "foo" path = "src/main.rs" "##]], )], ); } #[cargo_test] fn discovery_inferred_other_included() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" license = "MIT" description = "foo" documentation = "docs.rs/foo" authors = [] include = ["src/lib.rs", "src/bin/foo/main.rs", "examples/example_foo.rs", "tests/test_foo.rs", "benches/bench_foo.rs"] "#, ) .file("src/lib.rs", "") .file("src/bin/foo/main.rs", "fn main() {}") .file("examples/example_foo.rs", "fn main() {}") .file("tests/test_foo.rs", "fn main() {}") .file("benches/bench_foo.rs", "fn main() {}") .build(); p.cargo("package") .with_stdout_data("") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 8 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", &[ "Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/lib.rs", "src/bin/foo/main.rs", "examples/example_foo.rs", "tests/test_foo.rs", "benches/bench_foo.rs", ], [( "Cargo.toml", str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "foo" version = "0.0.1" authors = [] build = false include = [ "src/lib.rs", "src/bin/foo/main.rs", "examples/example_foo.rs", "tests/test_foo.rs", "benches/bench_foo.rs", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "docs.rs/foo" readme = false license = "MIT" [lib] name = "foo" path = "src/lib.rs" [[bin]] name = "foo" path = "src/bin/foo/main.rs" [[example]] name = "example_foo" path = "examples/example_foo.rs" [[test]] name = "test_foo" path = "tests/test_foo.rs" [[bench]] name = "bench_foo" path = "benches/bench_foo.rs" "##]], )], ); } #[cargo_test] fn discovery_inferred_other_excluded() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" license = "MIT" description = "foo" documentation = "docs.rs/foo" authors = [] include = ["src/lib.rs"] "#, ) .file("src/lib.rs", "") .file("src/bin/foo/main.rs", "fn main() {}") .file("examples/example_foo.rs", "fn main() {}") .file("tests/test_foo.rs", "fn main() {}") .file("benches/bench_foo.rs", "fn main() {}") .build(); p.cargo("package") .with_stdout_data("") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [WARNING] ignoring binary `foo` as `src/bin/foo/main.rs` is not included in the published package [WARNING] ignoring example `example_foo` as `examples/example_foo.rs` is not included in the published package [WARNING] ignoring test `test_foo` as `tests/test_foo.rs` is not included in the published package [WARNING] ignoring benchmark `bench_foo` as `benches/bench_foo.rs` is not included in the published package [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/lib.rs"], [( "Cargo.toml", str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "foo" version = "0.0.1" authors = [] build = false include = ["src/lib.rs"] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "docs.rs/foo" readme = false license = "MIT" [lib] name = "foo" path = "src/lib.rs" "##]], )], ); } #[cargo_test] fn discovery_explicit_other_included() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" license = "MIT" description = "foo" documentation = "docs.rs/foo" authors = [] include = ["src/lib.rs", "src/bin/foo/main.rs", "examples/example_foo.rs", "tests/test_foo.rs", "benches/bench_foo.rs"] [[bin]] name = "foo" [[example]] name = "example_foo" [[test]] name = "test_foo" [[bench]] name = "bench_foo" "#, ) .file("src/lib.rs", "") .file("src/bin/foo/main.rs", "fn main() {}") .file("examples/example_foo.rs", "fn main() {}") .file("tests/test_foo.rs", "fn main() {}") .file("benches/bench_foo.rs", "fn main() {}") .build(); p.cargo("package") .with_stdout_data("") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 8 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", &[ "Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/lib.rs", "src/bin/foo/main.rs", "examples/example_foo.rs", "tests/test_foo.rs", "benches/bench_foo.rs", ], [( "Cargo.toml", str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "foo" version = "0.0.1" authors = [] build = false include = [ "src/lib.rs", "src/bin/foo/main.rs", "examples/example_foo.rs", "tests/test_foo.rs", "benches/bench_foo.rs", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "docs.rs/foo" readme = false license = "MIT" [lib] name = "foo" path = "src/lib.rs" [[bin]] name = "foo" path = "src/bin/foo/main.rs" [[example]] name = "example_foo" path = "examples/example_foo.rs" [[test]] name = "test_foo" path = "tests/test_foo.rs" [[bench]] name = "bench_foo" path = "benches/bench_foo.rs" "##]], )], ); } #[cargo_test] fn discovery_explicit_other_excluded() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" license = "MIT" description = "foo" documentation = "docs.rs/foo" authors = [] include = ["src/lib.rs"] [[main]] name = "foo" [[example]] name = "example_foo" [[test]] name = "test_foo" [[bench]] name = "bench_foo" "#, ) .file("src/lib.rs", "") .file("src/bin/foo/main.rs", "fn main() {}") .file("examples/example_foo.rs", "fn main() {}") .file("tests/test_foo.rs", "fn main() {}") .file("benches/bench_foo.rs", "fn main() {}") .build(); p.cargo("package") .with_stdout_data("") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [WARNING] ignoring binary `foo` as `src/bin/foo/main.rs` is not included in the published package [WARNING] ignoring example `example_foo` as `examples/example_foo.rs` is not included in the published package [WARNING] ignoring test `test_foo` as `tests/test_foo.rs` is not included in the published package [WARNING] ignoring benchmark `bench_foo` as `benches/bench_foo.rs` is not included in the published package [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/lib.rs"], [( "Cargo.toml", str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "foo" version = "0.0.1" authors = [] build = false include = ["src/lib.rs"] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "docs.rs/foo" readme = false license = "MIT" [lib] name = "foo" path = "src/lib.rs" "##]], )], ); } #[cargo_test] fn deterministic_build_targets() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2021" license = "MIT" description = "foo" documentation = "docs.rs/foo" authors = [] [[example]] name = "c" [[example]] name = "b" [[example]] name = "a" "#, ) .file("src/lib.rs", "") .file("examples/z.rs", "fn main() {}") .file("examples/y.rs", "fn main() {}") .file("examples/x.rs", "fn main() {}") .file("examples/c.rs", "fn main() {}") .file("examples/b.rs", "fn main() {}") .file("examples/a.rs", "fn main() {}") .build(); p.cargo("package") .with_stdout_data("") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 10 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", &[ "Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/lib.rs", "examples/a.rs", "examples/b.rs", "examples/c.rs", "examples/x.rs", "examples/y.rs", "examples/z.rs", ], [( "Cargo.toml", str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "foo" version = "0.0.1" authors = [] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "docs.rs/foo" readme = false license = "MIT" [lib] name = "foo" path = "src/lib.rs" [[example]] name = "a" path = "examples/a.rs" [[example]] name = "b" path = "examples/b.rs" [[example]] name = "c" path = "examples/c.rs" [[example]] name = "x" path = "examples/x.rs" [[example]] name = "y" path = "examples/y.rs" [[example]] name = "z" path = "examples/z.rs" "##]], )], ); } // A workspace with three projects that depend on one another (level1 -> level2 -> level3). // level1 is a binary package, to test lockfile generation. fn workspace_with_local_deps_project() -> Project { project() .file( "Cargo.toml", r#" [workspace] members = ["level1", "level2", "level3"] [workspace.dependencies] level2 = { path = "level2", version = "0.0.1" } "# ) .file( "level1/Cargo.toml", r#" [package] name = "level1" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "level1" repository = "bar" [dependencies] # Let one dependency also specify features, for the added test coverage when generating package files. level2 = { workspace = true, features = ["foo"] } "#, ) .file("level1/src/main.rs", "fn main() {}") .file( "level2/Cargo.toml", r#" [package] name = "level2" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "level2" repository = "bar" [features] foo = [] [dependencies] level3 = { path = "../level3", version = "0.0.1" } "# ) .file("level2/src/lib.rs", "") .file( "level3/Cargo.toml", r#" [package] name = "level3" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "level3" repository = "bar" "#, ) .file("level3/src/lib.rs", "") .build() } #[cargo_test] fn workspace_with_local_deps() { let crates_io = registry::init(); let p = workspace_with_local_deps_project(); p.cargo("package") .replace_crates_io(crates_io.index_url()) .with_stdout_data("") .with_stderr_data(str![[r#" [PACKAGING] level3 v0.0.1 ([ROOT]/foo/level3) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [PACKAGING] level2 v0.0.1 ([ROOT]/foo/level2) [UPDATING] crates.io index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [PACKAGING] level1 v0.0.1 ([ROOT]/foo/level1) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] level3 v0.0.1 ([ROOT]/foo/level3) [COMPILING] level3 v0.0.1 ([ROOT]/foo/target/package/level3-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [VERIFYING] level2 v0.0.1 ([ROOT]/foo/level2) [UNPACKING] level3 v0.0.1 (registry `[ROOT]/foo/target/package/tmp-registry`) [COMPILING] level3 v0.0.1 [COMPILING] level2 v0.0.1 ([ROOT]/foo/target/package/level2-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [VERIFYING] level1 v0.0.1 ([ROOT]/foo/level1) [UNPACKING] level2 v0.0.1 (registry `[ROOT]/foo/target/package/tmp-registry`) [COMPILING] level3 v0.0.1 [COMPILING] level2 v0.0.1 [COMPILING] level1 v0.0.1 ([ROOT]/foo/target/package/level1-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let generated_lock = str![[r##" # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "level1" version = "0.0.1" dependencies = [ "level2", ] [[package]] name = "level2" version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "[..]" dependencies = [ "level3", ] [[package]] name = "level3" version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "[..]" "##]]; let generated_manifest = str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "level1" version = "0.0.1" authors = [] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "level1" readme = false license = "MIT" repository = "bar" [[bin]] name = "level1" path = "src/main.rs" [dependencies.level2] version = "0.0.1" features = ["foo"] "##]]; let mut f = File::open(&p.root().join("target/package/level1-0.0.1.crate")).unwrap(); validate_crate_contents( &mut f, "level1-0.0.1.crate", &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], [ ("Cargo.lock", generated_lock), ("Cargo.toml", generated_manifest), ], ); } #[cargo_test] fn workspace_with_local_dev_deps() { let crates_io = registry::init(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["main", "dev_dep"] resolver = "3" [workspace.dependencies] dev_dep = { path = "dev_dep", version = "0.0.1" } "#, ) .file( "main/Cargo.toml", r#" [package] name = "main" version = "0.0.1" edition = "2024" authors = [] license = "MIT" description = "main" [dev-dependencies] dev_dep.workspace = true "#, ) .file( "dev_dep/Cargo.toml", r#" [package] name = "dev_dep" version = "0.0.1" edition = "2024" authors = [] license = "MIT" description = "main" "#, ) .file("main/src/lib.rs", "") .file("dev_dep/src/lib.rs", "") .build(); p.cargo("package") .replace_crates_io(crates_io.index_url()) .with_stdout_data("") .with_stderr_data(str![[r#" [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] dev_dep v0.0.1 ([ROOT]/foo/dev_dep) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] main v0.0.1 ([ROOT]/foo/main) [UPDATING] crates.io index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] dev_dep v0.0.1 ([ROOT]/foo/dev_dep) [COMPILING] dev_dep v0.0.1 ([ROOT]/foo/target/package/dev_dep-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [VERIFYING] main v0.0.1 ([ROOT]/foo/main) [COMPILING] main v0.0.1 ([ROOT]/foo/target/package/main-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } fn workspace_with_local_deps_packaging_one_fails_project() -> Project { project() .file( "Cargo.toml", r#" [workspace] members = ["level1", "level2"] "#, ) .file( "level1/Cargo.toml", r#" [package] name = "level1" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "level1" repository = "bar" [dependencies] level2 = { path = "../level2", version = "0.0.1" } "#, ) .file("level1/src/lib.rs", "") .file( "level2/Cargo.toml", r#" [package] name = "level2" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "level2" repository = "bar" "#, ) .file("level2/src/lib.rs", "") .build() } #[cargo_test] fn workspace_with_local_deps_packaging_one_fails() { let crates_io = registry::init(); let p = workspace_with_local_deps_packaging_one_fails_project(); // We can't package just level1, because there's a dependency on level2. p.cargo("package -p level1") .replace_crates_io(crates_io.index_url()) .with_status(101) .with_stdout_data("") .with_stderr_data(str![[r#" [PACKAGING] level1 v0.0.1 ([ROOT]/foo/level1) [UPDATING] crates.io index [ERROR] failed to prepare local package for uploading Caused by: no matching package named `level2` found location searched: crates.io index required by package `level1 v0.0.1 ([ROOT]/foo/level1)` "#]]) .run(); } // Same as workspace_with_local_deps_packaging_one_fails except that we're // packaging a bin. This fails during lock-file generation instead of during verification. #[cargo_test] fn workspace_with_local_deps_packaging_one_bin_fails() { let crates_io = registry::init(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["level1", "level2"] "#, ) .file( "level1/Cargo.toml", r#" [package] name = "level1" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "level1" repository = "bar" [dependencies] level2 = { path = "../level2", version = "0.0.1" } "#, ) .file("level1/src/main.rs", "fn main() {}") .file( "level2/Cargo.toml", r#" [package] name = "level2" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "level2" repository = "bar" "#, ) .file("level2/src/lib.rs", "") .build(); // We can't package just level1, because there's a dependency on level2. p.cargo("package -p level1") .replace_crates_io(crates_io.index_url()) .with_status(101) .with_stdout_data("") .with_stderr_data(str![[r#" [PACKAGING] level1 v0.0.1 ([ROOT]/foo/level1) [UPDATING] crates.io index [ERROR] failed to prepare local package for uploading Caused by: no matching package named `level2` found location searched: crates.io index required by package `level1 v0.0.1 ([ROOT]/foo/level1)` "#]]) .run(); } // Here we don't package the whole workspace, but it succeeds because we package a // dependency-closed subset. #[cargo_test] fn workspace_with_local_deps_packaging_one_with_needed_deps() { let crates_io = registry::init(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["level1", "level2", "level3"] "#, ) .file( "level1/Cargo.toml", r#" [package] name = "level1" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "level1" repository = "bar" [dependencies] level2 = { path = "../level2", version = "0.0.1" } "#, ) .file("level1/src/main.rs", "fn main() {}") .file( "level2/Cargo.toml", r#" [package] name = "level2" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "level2" repository = "bar" [dependencies] level3 = { path = "../level3", version = "0.0.1" } "#, ) .file("level2/src/lib.rs", "") .file( "level3/Cargo.toml", r#" [package] name = "level3" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "level3" repository = "bar" "#, ) .file("level3/src/lib.rs", "") .build(); p.cargo("package -p level2 -p level3") .replace_crates_io(crates_io.index_url()) .with_stdout_data("") .with_stderr_data(str![[r#" [PACKAGING] level3 v0.0.1 ([ROOT]/foo/level3) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [PACKAGING] level2 v0.0.1 ([ROOT]/foo/level2) [UPDATING] crates.io index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] level3 v0.0.1 ([ROOT]/foo/level3) [COMPILING] level3 v0.0.1 ([ROOT]/foo/target/package/level3-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [VERIFYING] level2 v0.0.1 ([ROOT]/foo/level2) [UNPACKING] level3 v0.0.1 (registry `[ROOT]/foo/target/package/tmp-registry`) [COMPILING] level3 v0.0.1 [COMPILING] level2 v0.0.1 ([ROOT]/foo/target/package/level2-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } // package --list in a workspace lists all the files in all the packages. // The output is not very good, though. See https://github.com/rust-lang/cargo/issues/13953 #[cargo_test] fn workspace_with_local_deps_list() { let crates_io = registry::init(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["level1", "level2"] "#, ) .file( "level1/Cargo.toml", r#" [package] name = "level1" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "level1" repository = "bar" [dependencies] level2 = { path = "../level2", version = "0.0.1" } "#, ) .file("level1/src/main.rs", "fn main() {}") .file( "level2/Cargo.toml", r#" [package] name = "level2" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "level2" repository = "bar" "#, ) .file("level2/src/lib.rs", "") .build(); p.cargo("package --list") .replace_crates_io(crates_io.index_url()) .with_stdout_data(str![[r#" Cargo.lock Cargo.toml Cargo.toml.orig src/lib.rs Cargo.lock Cargo.toml Cargo.toml.orig src/main.rs "#]]) .with_stderr_data("") .run(); } #[cargo_test] fn workspace_with_local_deps_index_mismatch() { registry::init(); let alt_reg = registry::RegistryBuilder::new() .http_api() .http_index() .alternative() .build(); // We're publishing to an alternate index, but the manifests don't specify it. // The intra-workspace deps won't be found. let p = project() .file( "Cargo.toml", r#" [workspace] members = ["level1", "level2"] "#, ) .file( "level1/Cargo.toml", r#" [package] name = "level1" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "level1" repository = "bar" [dependencies] level2 = { path = "../level2", version = "0.0.1" } "#, ) .file("level1/src/main.rs", "fn main() {}") .file( "level2/Cargo.toml", r#" [package] name = "level2" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "level2" repository = "bar" "#, ) .file("level2/src/lib.rs", "") .build(); p.cargo(&format!("package --index {}", alt_reg.index_url())) .with_status(101) .with_stdout_data("") .with_stderr_data(str![[r#" [PACKAGING] level2 v0.0.1 ([ROOT]/foo/level2) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [PACKAGING] level1 v0.0.1 ([ROOT]/foo/level1) [UPDATING] `dummy-registry` index [ERROR] failed to prepare local package for uploading Caused by: no matching package named `level2` found location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `level1 v0.0.1 ([ROOT]/foo/level1)` "#]]) .run(); } #[cargo_test] fn workspace_with_local_deps_alternative_index() { let alt_reg = registry::RegistryBuilder::new() .http_api() .http_index() .alternative() .build(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["level1", "level2"] "#, ) .file( "level1/Cargo.toml", r#" [package] name = "level1" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "level1" repository = "bar" [dependencies] level2 = { path = "../level2", version = "0.0.1", registry = "alternative" } "#, ) .file("level1/src/main.rs", "fn main() {}") .file( "level2/Cargo.toml", r#" [package] name = "level2" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "level2" repository = "bar" "#, ) .file("level2/src/lib.rs", "") .build(); p.cargo(&format!("package --index {}", alt_reg.index_url())) .with_stdout_data("") .with_stderr_data(str![[r#" [PACKAGING] level2 v0.0.1 ([ROOT]/foo/level2) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [PACKAGING] level1 v0.0.1 ([ROOT]/foo/level1) [UPDATING] `alternative` index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] level2 v0.0.1 ([ROOT]/foo/level2) [COMPILING] level2 v0.0.1 ([ROOT]/foo/target/package/level2-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [VERIFYING] level1 v0.0.1 ([ROOT]/foo/level1) [UPDATING] `alternative` index [UNPACKING] level2 v0.0.1 (registry `[ROOT]/foo/target/package/tmp-registry`) [COMPILING] level2 v0.0.1 (registry `alternative`) [COMPILING] level1 v0.0.1 ([ROOT]/foo/target/package/level1-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let index = alt_reg.index_url(); let generated_lock = format!( r#"# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "level1" version = "0.0.1" dependencies = [ "level2", ] [[package]] name = "level2" version = "0.0.1" source = "{index}" checksum = "[..]" "# ); let mut f = File::open(&p.root().join("target/package/level1-0.0.1.crate")).unwrap(); validate_crate_contents( &mut f, "level1-0.0.1.crate", &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], [("Cargo.lock", generated_lock)], ); } fn workspace_with_local_dep_already_published_project() -> Project { Package::new("dep", "0.1.0").publish(); project() .file( "Cargo.toml", r#" [workspace] members = ["dep", "main"] "#, ) .file( "main/Cargo.toml", r#" [package] name = "main" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "main" repository = "bar" [dependencies] dep = { path = "../dep", version = "0.1.0" } "#, ) .file("main/src/main.rs", "fn main() {}") .file( "dep/Cargo.toml", r#" [package] name = "dep" version = "0.1.0" edition = "2015" authors = [] license = "MIT" description = "dep" repository = "bar" "#, ) .file("dep/src/lib.rs", "") .build() } #[cargo_test] fn workspace_with_local_dep_already_published() { let reg = registry::init(); let p = workspace_with_local_dep_already_published_project(); p.cargo("package") .replace_crates_io(reg.index_url()) .with_stderr_data( str![[r#" [PACKAGING] dep v0.1.0 ([ROOT]/foo/dep) [PACKAGING] main v0.0.1 ([ROOT]/foo/main) [UPDATING] crates.io index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] dep v0.1.0 ([ROOT]/foo/dep) [COMPILING] dep v0.1.0 ([ROOT]/foo/target/package/dep-0.1.0) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [VERIFYING] main v0.0.1 ([ROOT]/foo/main) [UNPACKING] dep v0.1.0 (registry `[ROOT]/foo/target/package/tmp-registry`) [COMPILING] dep v0.1.0 [COMPILING] main v0.0.1 ([ROOT]/foo/target/package/main-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn workspace_with_local_and_remote_deps() { let reg = registry::init(); Package::new("dep", "0.0.1").publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["dep", "main"] "#, ) .file( "main/Cargo.toml", r#" [package] name = "main" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "main" repository = "bar" [dependencies] dep = { path = "../dep", version = "0.1.0" } old_dep = { package = "dep", version = "0.0.1" } "#, ) .file("main/src/main.rs", "fn main() {}") .file( "dep/Cargo.toml", r#" [package] name = "dep" version = "0.1.0" edition = "2015" authors = [] license = "MIT" description = "dep" repository = "bar" "#, ) .file("dep/src/lib.rs", "") .build(); p.cargo("package") .replace_crates_io(reg.index_url()) .with_stderr_data( str![[r#" [PACKAGING] dep v0.1.0 ([ROOT]/foo/dep) [PACKAGING] main v0.0.1 ([ROOT]/foo/main) [UPDATING] crates.io index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] dep v0.1.0 ([ROOT]/foo/dep) [COMPILING] dep v0.1.0 ([ROOT]/foo/target/package/dep-0.1.0) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [VERIFYING] main v0.0.1 ([ROOT]/foo/main) [DOWNLOADING] crates ... [UNPACKING] dep v0.1.0 (registry `[ROOT]/foo/target/package/tmp-registry`) [DOWNLOADED] dep v0.0.1 [COMPILING] dep v0.0.1 [COMPILING] dep v0.1.0 [COMPILING] main v0.0.1 ([ROOT]/foo/target/package/main-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) "#]] .unordered(), ) .run(); } #[cargo_test] fn workspace_with_capitalized_member() { let reg = registry::init(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["dep", "main"] "#, ) .file( "main/Cargo.toml", r#" [package] name = "main" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "main" repository = "bar" [dependencies] DEP = { path = "../dep", version = "0.1.0" } "#, ) .file("main/src/main.rs", "fn main() {}") .file( "dep/Cargo.toml", r#" [package] name = "DEP" version = "0.1.0" edition = "2015" authors = [] license = "MIT" description = "dep" repository = "bar" "#, ) .file("dep/src/lib.rs", "") .build(); p.cargo("package --no-verify") .replace_crates_io(reg.index_url()) .with_stderr_data( str![[r#" [PACKAGING] main v0.0.1 ([ROOT]/foo/main) [UPDATING] crates.io index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [PACKAGING] DEP v0.1.0 ([ROOT]/foo/dep) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) "#]] .unordered(), ) .run(); } #[cargo_test] fn workspace_with_renamed_member() { let reg = registry::init(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["crates/*"] "#, ) .file( "crates/val-json/Cargo.toml", r#" [package] name = "obeli-sk-val-json" version = "0.16.2" edition = "2015" authors = [] license = "MIT" description = "main" repository = "bar" [dependencies] "#, ) .file("crates/val-json/src/lib.rs", "pub fn foo() {}") .file( "crates/concepts/Cargo.toml", r#" [package] name = "obeli-sk-concepts" version = "0.16.2" edition = "2015" authors = [] license = "MIT" description = "main" repository = "bar" [dependencies] val-json = { package = "obeli-sk-val-json", path = "../val-json", version = "0.16.2" } "#, ) .file( "crates/concepts/src/lib.rs", "pub fn foo() { val_json::foo() }", ) .file( "crates/utils/Cargo.toml", r#" [package] name = "obeli-sk-utils" version = "0.16.2" edition = "2015" authors = [] license = "MIT" description = "main" repository = "bar" [dependencies] concepts = { package = "obeli-sk-concepts", path = "../concepts", version = "0.16.2" } val-json = { package = "obeli-sk-val-json", path = "../val-json", version = "0.16.2" } "#, ) .file( "crates/utils/src/lib.rs", "pub fn foo() { val_json::foo(); concepts::foo(); }", ) .build(); p.cargo("package") .replace_crates_io(reg.index_url()) .with_stderr_data( str![[r#" [UPDATING] crates.io index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [PACKAGING] obeli-sk-val-json v0.16.2 ([ROOT]/foo/crates/val-json) [PACKAGING] obeli-sk-concepts v0.16.2 ([ROOT]/foo/crates/concepts) [PACKAGING] obeli-sk-utils v0.16.2 ([ROOT]/foo/crates/utils) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] obeli-sk-val-json v0.16.2 ([ROOT]/foo/crates/val-json) [COMPILING] obeli-sk-val-json v0.16.2 ([ROOT]/foo/target/package/obeli-sk-val-json-0.16.2) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [VERIFYING] obeli-sk-concepts v0.16.2 ([ROOT]/foo/crates/concepts) [UNPACKING] obeli-sk-val-json v0.16.2 (registry `[ROOT]/foo/target/package/tmp-registry`) [COMPILING] obeli-sk-val-json v0.16.2 [COMPILING] obeli-sk-concepts v0.16.2 ([ROOT]/foo/target/package/obeli-sk-concepts-0.16.2) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [VERIFYING] obeli-sk-utils v0.16.2 ([ROOT]/foo/crates/utils) [UNPACKING] obeli-sk-concepts v0.16.2 (registry `[ROOT]/foo/target/package/tmp-registry`) [COMPILING] obeli-sk-val-json v0.16.2 [COMPILING] obeli-sk-concepts v0.16.2 [COMPILING] obeli-sk-utils v0.16.2 ([ROOT]/foo/target/package/obeli-sk-utils-0.16.2) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn workspace_with_dot_rs_dir() { let reg = registry::init(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["crates/*"] "#, ) .file( "crates/foo.rs/Cargo.toml", r#" [package] name = "foo" version = "0.16.2" edition = "2015" authors = [] license = "MIT" description = "main" repository = "bar" [dependencies] "#, ) .file("crates/foo.rs/src/lib.rs", "pub fn foo() {}") .file( "crates/bar.rs/Cargo.toml", r#" [package] name = "bar" version = "0.16.2" edition = "2015" authors = [] license = "MIT" description = "main" repository = "bar" [dependencies] foo = { path = "../foo.rs", version = "0.16.2" } "#, ) .file("crates/bar.rs/src/lib.rs", "pub fn foo() {}") .build(); p.cargo("package") .replace_crates_io(reg.index_url()) .with_stderr_data( str![[r#" [PACKAGING] foo v0.16.2 ([ROOT]/foo/crates/foo.rs) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [PACKAGING] bar v0.16.2 ([ROOT]/foo/crates/bar.rs) [UPDATING] crates.io index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.16.2 ([ROOT]/foo/crates/foo.rs) [COMPILING] foo v0.16.2 ([ROOT]/foo/target/package/foo-0.16.2) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [VERIFYING] bar v0.16.2 ([ROOT]/foo/crates/bar.rs) [UNPACKING] foo v0.16.2 (registry `[ROOT]/foo/target/package/tmp-registry`) [COMPILING] foo v0.16.2 [COMPILING] bar v0.16.2 ([ROOT]/foo/target/package/bar-0.16.2) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn registry_not_in_publish_list() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" publish = [ "test" ] "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("package --registry alternative") .with_status(101) .with_stderr_data(str![[r#" [ERROR] `foo` cannot be packaged. The registry `alternative` is not listed in the `package.publish` value in Cargo.toml. "#]]) .run(); } #[cargo_test] fn registry_inferred_from_unique_option() { let _registry = registry::RegistryBuilder::new() .http_api() .http_index() .alternative() .build(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["dep", "main"] "#, ) .file( "main/Cargo.toml", r#" [package] name = "main" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "main" repository = "bar" publish = ["alternative"] [dependencies] dep = { path = "../dep", version = "0.1.0", registry = "alternative" } "#, ) .file("main/src/main.rs", "fn main() {}") .file( "dep/Cargo.toml", r#" [package] name = "dep" version = "0.1.0" edition = "2015" authors = [] license = "MIT" description = "dep" repository = "bar" publish = ["alternative"] "#, ) .file("dep/src/lib.rs", "") .build(); p.cargo("package") .with_stderr_data(str![[r#" [PACKAGING] dep v0.1.0 ([ROOT]/foo/dep) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [PACKAGING] main v0.0.1 ([ROOT]/foo/main) [UPDATING] `alternative` index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] dep v0.1.0 ([ROOT]/foo/dep) [COMPILING] dep v0.1.0 ([ROOT]/foo/target/package/dep-0.1.0) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [VERIFYING] main v0.0.1 ([ROOT]/foo/main) [UPDATING] `alternative` index [UNPACKING] dep v0.1.0 (registry `[ROOT]/foo/target/package/tmp-registry`) [COMPILING] dep v0.1.0 (registry `alternative`) [COMPILING] main v0.0.1 ([ROOT]/foo/target/package/main-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn registry_not_inferred_because_of_conflict() { let alt_reg = registry::RegistryBuilder::new() .http_api() .http_index() .alternative() .build(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["dep", "main"] "#, ) .file( "main/Cargo.toml", r#" [package] name = "main" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "main" repository = "bar" publish = ["alternative"] [dependencies] dep = { path = "../dep", version = "0.1.0", registry = "alternative" } "#, ) .file("main/src/main.rs", "fn main() {}") .file( "dep/Cargo.toml", r#" [package] name = "dep" version = "0.1.0" edition = "2015" authors = [] license = "MIT" description = "dep" repository = "bar" publish = ["alternative2"] "#, ) .file("dep/src/lib.rs", "") .build(); p.cargo("package") .with_status(101) .with_stderr_data(str![[r#" [ERROR] conflicts between `package.publish` fields in the selected packages "#]]) .run(); p.cargo("package --exclude-lockfile") .with_status(101) .with_stderr_data(str![[r#" [ERROR] conflicts between `package.publish` fields in the selected packages "#]]) .run(); p.cargo("package --no-verify") .with_status(101) .with_stderr_data(str![[r#" [ERROR] conflicts between `package.publish` fields in the selected packages "#]]) .run(); p.cargo("package --exclude-lockfile --no-verify") .with_stderr_data(str![[r#" [PACKAGING] dep v0.1.0 ([ROOT]/foo/dep) [PACKAGED] 3 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [PACKAGING] main v0.0.1 ([ROOT]/foo/main) [PACKAGED] 3 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) "#]]) .run(); p.cargo("package --registry=alternative") .with_status(101) .with_stderr_data(str![[r#" [ERROR] `dep` cannot be packaged. The registry `alternative` is not listed in the `package.publish` value in Cargo.toml. "#]]) .run(); p.cargo(&format!("package --index {}", alt_reg.index_url())) .with_stderr_data(str![[r#" [PACKAGING] dep v0.1.0 ([ROOT]/foo/dep) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [PACKAGING] main v0.0.1 ([ROOT]/foo/main) [UPDATING] `alternative` index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] dep v0.1.0 ([ROOT]/foo/dep) [COMPILING] dep v0.1.0 ([ROOT]/foo/target/package/dep-0.1.0) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [VERIFYING] main v0.0.1 ([ROOT]/foo/main) [UPDATING] `alternative` index [UNPACKING] dep v0.1.0 (registry `[ROOT]/foo/target/package/tmp-registry`) [COMPILING] dep v0.1.0 (registry `alternative`) [COMPILING] main v0.0.1 ([ROOT]/foo/target/package/main-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn registry_inference_ignores_unpublishable() { let _alt_reg = registry::RegistryBuilder::new() .http_api() .http_index() .alternative() .build(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["dep", "main"] "#, ) .file( "main/Cargo.toml", r#" [package] name = "main" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "main" repository = "bar" publish = false [dependencies] dep = { path = "../dep", version = "0.1.0", registry = "alternative" } "#, ) .file("main/src/main.rs", "fn main() {}") .file( "dep/Cargo.toml", r#" [package] name = "dep" version = "0.1.0" edition = "2015" authors = [] license = "MIT" description = "dep" repository = "bar" publish = ["alternative"] "#, ) .file("dep/src/lib.rs", "") .build(); p.cargo("package") .with_stderr_data(str![[r#" [PACKAGING] dep v0.1.0 ([ROOT]/foo/dep) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [PACKAGING] main v0.0.1 ([ROOT]/foo/main) [UPDATING] `alternative` index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] dep v0.1.0 ([ROOT]/foo/dep) [COMPILING] dep v0.1.0 ([ROOT]/foo/target/package/dep-0.1.0) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [VERIFYING] main v0.0.1 ([ROOT]/foo/main) [UPDATING] `alternative` index [UNPACKING] dep v0.1.0 (registry `[ROOT]/foo/target/package/tmp-registry`) [COMPILING] dep v0.1.0 (registry `alternative`) [COMPILING] main v0.0.1 ([ROOT]/foo/target/package/main-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("package --registry=alternative") .with_stderr_data(str![[r#" [PACKAGING] dep v0.1.0 ([ROOT]/foo/dep) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [PACKAGING] main v0.0.1 ([ROOT]/foo/main) [UPDATING] `alternative` index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] dep v0.1.0 ([ROOT]/foo/dep) [COMPILING] dep v0.1.0 ([ROOT]/foo/target/package/dep-0.1.0) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [VERIFYING] main v0.0.1 ([ROOT]/foo/main) [UPDATING] `alternative` index [COMPILING] dep v0.1.0 (registry `alternative`) [COMPILING] main v0.0.1 ([ROOT]/foo/target/package/main-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn registry_not_inferred_because_of_multiple_options() { let _alt_reg = registry::RegistryBuilder::new() .http_api() .http_index() .alternative() .build(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["dep", "main"] "#, ) .file( "main/Cargo.toml", r#" [package] name = "main" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "main" repository = "bar" publish = ["alternative", "alternative2"] [dependencies] dep = { path = "../dep", version = "0.1.0", registry = "alternative" } "#, ) .file("main/src/main.rs", "fn main() {}") .file( "dep/Cargo.toml", r#" [package] name = "dep" version = "0.1.0" edition = "2015" authors = [] license = "MIT" description = "dep" repository = "bar" publish = ["alternative", "alternative2"] "#, ) .file("dep/src/lib.rs", "") .build(); p.cargo("package") .with_status(101) .with_stderr_data(str![[r#" [ERROR] --registry is required to disambiguate between "alternative" or "alternative2" registries "#]]) .run(); p.cargo("package --exclude-lockfile") .with_status(101) .with_stderr_data(str![[r#" [ERROR] --registry is required to disambiguate between "alternative" or "alternative2" registries "#]]) .run(); p.cargo("package --no-verify") .with_status(101) .with_stderr_data(str![[r#" [ERROR] --registry is required to disambiguate between "alternative" or "alternative2" registries "#]]) .run(); p.cargo("package --exclude-lockfile --no-verify") .with_stderr_data(str![[r#" [PACKAGING] dep v0.1.0 ([ROOT]/foo/dep) [PACKAGED] 3 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [PACKAGING] main v0.0.1 ([ROOT]/foo/main) [PACKAGED] 3 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) "#]]) .run(); p.cargo("package --registry=alternative") .with_stderr_data(str![[r#" [PACKAGING] dep v0.1.0 ([ROOT]/foo/dep) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [PACKAGING] main v0.0.1 ([ROOT]/foo/main) [UPDATING] `alternative` index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] dep v0.1.0 ([ROOT]/foo/dep) [COMPILING] dep v0.1.0 ([ROOT]/foo/target/package/dep-0.1.0) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [VERIFYING] main v0.0.1 ([ROOT]/foo/main) [UPDATING] `alternative` index [UNPACKING] dep v0.1.0 (registry `[ROOT]/foo/target/package/tmp-registry`) [COMPILING] dep v0.1.0 (registry `alternative`) [COMPILING] main v0.0.1 ([ROOT]/foo/target/package/main-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn registry_not_inferred_because_of_mismatch() { let _alt_reg = registry::RegistryBuilder::new() .http_api() .http_index() .alternative() .build(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["dep", "main"] "#, ) .file( "main/Cargo.toml", r#" [package] name = "main" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "main" repository = "bar" publish = ["alternative"] [dependencies] dep = { path = "../dep", version = "0.1.0", registry = "alternative" } "#, ) .file("main/src/main.rs", "fn main() {}") // No `publish` field means "any registry", but the presence of this package // will stop us from inferring a registry. .file( "dep/Cargo.toml", r#" [package] name = "dep" version = "0.1.0" edition = "2015" authors = [] license = "MIT" description = "dep" repository = "bar" "#, ) .file("dep/src/lib.rs", "") .build(); p.cargo("package") .with_status(101) .with_stderr_data(str![[r#" [ERROR] --registry is required because not all `package.publish` settings agree "#]]) .run(); p.cargo("package --exclude-lockfile") .with_status(101) .with_stderr_data(str![[r#" [ERROR] --registry is required because not all `package.publish` settings agree "#]]) .run(); p.cargo("package --no-verify") .with_status(101) .with_stderr_data(str![[r#" [ERROR] --registry is required because not all `package.publish` settings agree "#]]) .run(); p.cargo("package --exclude-lockfile --no-verify") .with_stderr_data(str![[r#" [PACKAGING] dep v0.1.0 ([ROOT]/foo/dep) [PACKAGED] 3 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [PACKAGING] main v0.0.1 ([ROOT]/foo/main) [PACKAGED] 3 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) "#]]) .run(); p.cargo("package --registry=alternative") .with_stderr_data(str![[r#" [PACKAGING] dep v0.1.0 ([ROOT]/foo/dep) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [PACKAGING] main v0.0.1 ([ROOT]/foo/main) [UPDATING] `alternative` index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] dep v0.1.0 ([ROOT]/foo/dep) [COMPILING] dep v0.1.0 ([ROOT]/foo/target/package/dep-0.1.0) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [VERIFYING] main v0.0.1 ([ROOT]/foo/main) [UPDATING] `alternative` index [UNPACKING] dep v0.1.0 (registry `[ROOT]/foo/target/package/tmp-registry`) [COMPILING] dep v0.1.0 (registry `alternative`) [COMPILING] main v0.0.1 ([ROOT]/foo/target/package/main-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn unpublishable_dependency() { let _alt_reg = registry::RegistryBuilder::new() .http_api() .http_index() .alternative() .build(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["dep", "main"] "#, ) .file( "main/Cargo.toml", r#" [package] name = "main" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "main" repository = "bar" [dependencies] dep = { path = "../dep", version = "0.1.0", registry = "alternative" } "#, ) .file("main/src/main.rs", "fn main() {}") .file( "dep/Cargo.toml", r#" [package] name = "dep" version = "0.1.0" edition = "2015" authors = [] license = "MIT" description = "dep" repository = "bar" publish = false "#, ) .file("dep/src/lib.rs", "") .build(); p.cargo("package") .with_status(101) .with_stderr_data(str![[r#" [PACKAGING] dep v0.1.0 ([ROOT]/foo/dep) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [PACKAGING] main v0.0.1 ([ROOT]/foo/main) [UPDATING] `alternative` index [ERROR] failed to prepare local package for uploading Caused by: no matching package named `dep` found location searched: `alternative` index required by package `main v0.0.1 ([ROOT]/foo/main)` "#]]) .run(); } #[cargo_test] fn in_package_workspace_with_members_with_features_old() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [workspace] members = ["li"] "#, ) .file("src/main.rs", "fn main() {}") .file( "li/Cargo.toml", r#" [package] name = "li" version = "0.0.1" edition = "2015" rust-version = "1.69" description = "li" license = "MIT" "#, ) .file("li/src/main.rs", "fn main() {}") .build(); p.cargo("package -p li --no-verify") .with_stderr_data(str![[r#" [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] li v0.0.1 ([ROOT]/foo/li) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) "#]]) .run(); } #[cargo_test] #[cfg(unix)] fn simple_with_fifo() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" "#, ) .file("src/main.rs", "fn main() {}") .build(); std::process::Command::new("mkfifo") .current_dir(p.root()) .arg(p.root().join("blocks-when-read")) .status() .expect("a FIFO can be created"); // Avoid actual blocking even in case of failure, assuming that what it lists here // would also be read eventually. p.cargo("package -l") .with_stdout_data(str![[r#" Cargo.lock Cargo.toml Cargo.toml.orig src/main.rs "#]]) .run(); } #[cargo_test] fn git_core_symlinks_false() { if !symlink_supported() { return; } let git_project = git::new("bar", |p| { p.file( "Cargo.toml", r#" [package] name = "bar" description = "bar" license = "MIT" edition = "2021" documentation = "foo" "#, ) .file("src/lib.rs", "//! This is a module") .symlink("src/lib.rs", "symlink-lib.rs") .symlink_dir("src", "symlink-dir") }); let url = git_project.root().to_url().to_string(); let p = project().build(); let root = p.root(); // Remove the default project layout, // so we can git-fetch from git_project under the same directory fs::remove_dir_all(&root).unwrap(); fs::create_dir_all(&root).unwrap(); let repo = git::init(&root); let mut cfg = repo.config().unwrap(); cfg.set_bool("core.symlinks", false).unwrap(); // let's fetch from git_project so it respects our core.symlinks=false config. repo.remote_anonymous(&url) .unwrap() .fetch(&["HEAD"], None, None) .unwrap(); let rev = repo .find_reference("FETCH_HEAD") .unwrap() .peel_to_commit() .unwrap(); repo.reset(rev.as_object(), git2::ResetType::Hard, None) .unwrap(); p.cargo("package --allow-dirty") .with_stderr_data(str![[r#" [WARNING] found symbolic links that may be checked out as regular files for git repo at `[ROOT]/foo/` This might cause the `.crate` file to include incorrect or incomplete files [NOTE] to avoid this, set the Git config `core.symlinks` to `true` ... [PACKAGING] bar v0.0.0 ([ROOT]/foo) [PACKAGED] 7 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] bar v0.0.0 ([ROOT]/foo) [COMPILING] bar v0.0.0 ([ROOT]/foo/target/package/bar-0.0.0) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let f = File::open(&p.root().join("target/package/bar-0.0.0.crate")).unwrap(); validate_crate_contents( f, "bar-0.0.0.crate", &[ "Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/lib.rs", // We're missing symlink-dir/lib.rs in the `.crate` file. "symlink-dir", "symlink-lib.rs", ".cargo_vcs_info.json", ], [ // And their contents are incorrect. ("symlink-dir", str!["[ROOT]/bar/src"]), ("symlink-lib.rs", str!["[ROOT]/bar/src/lib.rs"]), ], ); } #[cargo_test] fn exclude_lockfile() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" documentation = "foo" "#, ) .file("src/lib.rs", "") .build(); p.cargo("package --list --exclude-lockfile") .with_stdout_data(str![[r#" Cargo.toml Cargo.toml.orig src/lib.rs "#]]) .with_stderr_data("") .run(); p.cargo("package --exclude-lockfile") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 3 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs"], (), ); } // A failing case from #[cargo_test] fn unpublished_cyclic_dev_dependencies() { registry::init(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" documentation = "foo" [dev-dependencies] foo = { path = ".", version = "0.0.1" } "#, ) .file("src/lib.rs", "") .build(); p.cargo("package --no-verify --exclude-lockfile") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 3 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) "#]]) .run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", // no Cargo.lock &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs"], (), ); } // A failing case from #[cargo_test] fn unpublished_dependency() { registry::init(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" documentation = "foo" [dependencies] dep = { path = "./dep", version = "0.0.1" } "#, ) .file("src/lib.rs", "") .file( "dep/Cargo.toml", r#" [package] name = "dep" version = "0.0.1" edition = "2015" "#, ) .file("src/lib.rs", "") .build(); p.cargo("package --no-verify -p foo --exclude-lockfile") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 3 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) "#]]) .run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", // no Cargo.lock &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs"], (), ); } // This is a companion to `publish::checksum_changed`, but because this one // is packaging without dry-run, it should fail. #[cargo_test] fn checksum_changed() { let registry = registry::RegistryBuilder::new() .http_api() .http_index() .build(); Package::new("dep", "1.0.0").publish(); Package::new("transitive", "1.0.0") .dep("dep", "1.0.0") .publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["dep"] [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" documentation = "foo" [dependencies] dep = { path = "./dep", version = "1.0.0" } transitive = "1.0.0" "#, ) .file("src/lib.rs", "") .file( "dep/Cargo.toml", r#" [package] name = "dep" version = "1.0.0" edition = "2015" "#, ) .file("dep/src/lib.rs", "") .build(); p.cargo("check").run(); p.cargo("package --workspace") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" [WARNING] manifest has no description, license, license-file, documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] dep v1.0.0 ([ROOT]/foo/dep) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [PACKAGING] foo v0.0.1 ([ROOT]/foo) [ERROR] failed to prepare local package for uploading Caused by: checksum for `dep v1.0.0` changed between lock files this could be indicative of a few possible errors: * the lock file is corrupt * a replacement source in use (e.g., a mirror) returned a different checksum * the source itself may be corrupt in one way or another unable to verify that `dep v1.0.0` is the same as when the lockfile was generated "#]]) .run(); } cargo-0.91.0/tests/testsuite/package_features.rs000064400000000000000000000604741046102023000200530ustar 00000000000000//! Tests for feature selection on the command-line. use std::fmt::Write; use crate::prelude::*; use cargo_test_support::registry::{Dependency, Package}; use cargo_test_support::{basic_manifest, project, str}; use super::features2::switch_to_resolver_2; #[cargo_test] fn virtual_no_default_features() { // --no-default-features in root of virtual workspace. Package::new("dep1", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" [dependencies] dep1 = {version = "1.0", optional = true} [features] default = ["dep1"] "#, ) .file("a/src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" edition = "2015" [features] default = ["f1"] f1 = [] "#, ) .file( "b/src/lib.rs", r#" #[cfg(feature = "f1")] compile_error!{"expected f1 off"} "#, ) .build(); p.cargo("check --no-default-features") .with_stderr_data( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [CHECKING] a v0.1.0 ([ROOT]/foo/a) [CHECKING] b v0.1.0 ([ROOT]/foo/b) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); p.cargo("check --features foo") .with_status(101) .with_stderr_data(str![[r#" [ERROR] none of the selected packages contains this feature: foo selected packages: a, b [HELP] there is a similarly named feature: f1 "#]]) .run(); p.cargo("check --features a/dep1,b/f1,b/f2,f2") .with_status(101) .with_stderr_data(str![[r#" [ERROR] none of the selected packages contains these features: b/f2, f2 selected packages: a, b [HELP] there is a similarly named feature: f1 "#]]) .run(); p.cargo("check --features a/dep,b/f1,b/f2,f2") .with_status(101) .with_stderr_data(str![[r#" [ERROR] none of the selected packages contains these features: a/dep, b/f2, f2 selected packages: a, b [HELP] there are similarly named features: a/dep1, f1 "#]]) .run(); p.cargo("check --features a/dep,a/dep1") .with_status(101) .with_stderr_data(str![[r#" [ERROR] none of the selected packages contains this feature: a/dep selected packages: a, b [HELP] there is a similarly named feature: b/f1 "#]]) .run(); p.cargo("check -p b --features=dep1") .with_status(101) .with_stderr_data(str![[r#" [ERROR] the package 'b' does not contain this feature: dep1 [HELP] package with the missing feature: a "#]]) .run(); } #[cargo_test] fn virtual_typo_member_feature() { project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" resolver = "2" [features] deny-warnings = [] "#, ) .file("src/lib.rs", "") .build() .cargo("check --features a/deny-warning") .with_status(101) .with_stderr_data(str![[r#" [ERROR] the package 'a' does not contain this feature: a/deny-warning [HELP] there is a similarly named feature: a/deny-warnings "#]]) .run(); } #[cargo_test] fn virtual_features() { // --features in root of virtual workspace. let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" [features] f1 = [] "#, ) .file( "a/src/lib.rs", r#" #[cfg(not(feature = "f1"))] compile_error!{"f1 is missing"} "#, ) .file("b/Cargo.toml", &basic_manifest("b", "0.1.0")) .file("b/src/lib.rs", "") .build(); p.cargo("check --features f1") .with_stderr_data( str![[r#" [CHECKING] a v0.1.0 ([ROOT]/foo/a) [CHECKING] b v0.1.0 ([ROOT]/foo/b) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn virtual_with_specific() { // -p flags with --features in root of virtual. let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" [features] f1 = [] f2 = [] "#, ) .file( "a/src/lib.rs", r#" #[cfg(not(feature = "f1"))] compile_error!{"f1 is missing"} #[cfg(not(feature = "f2"))] compile_error!{"f2 is missing"} "#, ) .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" edition = "2015" [features] f2 = [] f3 = [] "#, ) .file( "b/src/lib.rs", r#" #[cfg(not(feature = "f2"))] compile_error!{"f2 is missing"} #[cfg(not(feature = "f3"))] compile_error!{"f3 is missing"} "#, ) .build(); p.cargo("check -p a -p b --features f1,f2,f3") .with_stderr_data( str![[r#" [CHECKING] a v0.1.0 ([ROOT]/foo/a) [CHECKING] b v0.1.0 ([ROOT]/foo/b) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn other_member_from_current() { // -p for another member while in the current directory. let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar"] [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { path="bar", features=["f3"] } [features] f1 = ["bar/f4"] "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" [features] f1 = [] f2 = [] f3 = [] f4 = [] "#, ) .file("bar/src/lib.rs", "") .file( "bar/src/main.rs", r#" fn main() { if cfg!(feature = "f1") { print!("f1"); } if cfg!(feature = "f2") { print!("f2"); } if cfg!(feature = "f3") { print!("f3"); } if cfg!(feature = "f4") { print!("f4"); } println!(); } "#, ) .build(); // Old behavior. p.cargo("run -p bar --features f1") .with_stdout_data(str![[r#" f3f4 "#]]) .run(); p.cargo("run -p bar --features f1,f2") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package `foo v0.1.0 ([ROOT]/foo)` does not have the feature `f2` [HELP] a feature with a similar name exists: `f1` "#]]) .run(); p.cargo("run -p bar --features bar/f1") .with_stdout_data(str![[r#" f1f3 "#]]) .run(); // New behavior. switch_to_resolver_2(&p); p.cargo("run -p bar --features f1") .with_stdout_data(str![[r#" f1 "#]]) .run(); p.cargo("run -p bar --features f1,f2") .with_stdout_data(str![[r#" f1f2 "#]]) .run(); p.cargo("run -p bar --features bar/f1") .with_stdout_data(str![[r#" f1 "#]]) .run(); } #[cargo_test] fn feature_default_resolver() { let p = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" [features] test = [] "#, ) .file( "src/main.rs", r#" fn main() { if cfg!(feature = "test") { println!("feature set"); } } "#, ) .build(); p.cargo("check --features testt") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package `a v0.1.0 ([ROOT]/foo)` does not have the feature `testt` [HELP] a feature with a similar name exists: `test` "#]]) .run(); p.cargo("run --features test") .with_status(0) .with_stdout_data(str![[r#" feature set "#]]) .run(); p.cargo("run --features a/test") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package `a v0.1.0 ([ROOT]/foo)` does not have a dependency named `a` "#]]) .run(); } #[cargo_test] fn command_line_optional_dep() { // Enabling a dependency used as a `dep:` errors helpfully Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" [features] foo = ["dep:bar"] [dependencies] bar = { version = "1.0.0", optional = true } "#, ) .file("src/lib.rs", r#""#) .build(); p.cargo("check --features bar") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [ERROR] package `a v0.1.0 ([ROOT]/foo)` does not have feature `bar` [HELP] an optional dependency with that name exists, but the `features` table includes it with the "dep:" syntax so it does not have an implicit feature with that name Dependency `bar` would be enabled by these features: - `foo` "#]]) .run(); } #[cargo_test] fn command_line_optional_dep_three_options() { // Trying to enable an optional dependency used as a `dep:` errors helpfully, when there are three features which would enable the dependency Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" [features] f1 = ["dep:bar"] f2 = ["dep:bar"] f3 = ["dep:bar"] [dependencies] bar = { version = "1.0.0", optional = true } "#, ) .file("src/lib.rs", r#""#) .build(); p.cargo("check --features bar") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [ERROR] package `a v0.1.0 ([ROOT]/foo)` does not have feature `bar` [HELP] an optional dependency with that name exists, but the `features` table includes it with the "dep:" syntax so it does not have an implicit feature with that name Dependency `bar` would be enabled by these features: - `f1` - `f2` - `f3` "#]]) .run(); } #[cargo_test] fn command_line_optional_dep_many_options() { // Trying to enable an optional dependency used as a `dep:` errors helpfully, when there are many features which would enable the dependency Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" [features] f1 = ["dep:bar"] f2 = ["dep:bar"] f3 = ["dep:bar"] f4 = ["dep:bar"] [dependencies] bar = { version = "1.0.0", optional = true } "#, ) .file("src/lib.rs", r#""#) .build(); p.cargo("check --features bar") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [ERROR] package `a v0.1.0 ([ROOT]/foo)` does not have feature `bar` [HELP] an optional dependency with that name exists, but the `features` table includes it with the "dep:" syntax so it does not have an implicit feature with that name Dependency `bar` would be enabled by these features: - `f1` - `f2` - `f3` ... "#]]) .run(); } #[cargo_test] fn command_line_optional_dep_many_paths() { // Trying to enable an optional dependency used as a `dep:` errors helpfully, when a features would enable the dependency in multiple ways Package::new("bar", "1.0.0") .feature("a", &[]) .feature("b", &[]) .feature("c", &[]) .feature("d", &[]) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" [features] f1 = ["dep:bar", "bar/a", "bar/b"] # Remove the implicit feature f2 = ["bar/b", "bar/c"] # Overlaps with previous f3 = ["bar/d"] # No overlap with previous [dependencies] bar = { version = "1.0.0", optional = true } "#, ) .file("src/lib.rs", r#""#) .build(); p.cargo("check --features bar") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [ERROR] package `a v0.1.0 ([ROOT]/foo)` does not have feature `bar` [HELP] an optional dependency with that name exists, but the `features` table includes it with the "dep:" syntax so it does not have an implicit feature with that name Dependency `bar` would be enabled by these features: - `f1` - `f2` - `f3` "#]]) .run(); } #[cargo_test] fn virtual_member_slash() { // member slash feature syntax let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a"] "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" [dependencies] b = {path="../b", optional=true} [features] default = ["f1"] f1 = [] f2 = [] "#, ) .file( "a/src/lib.rs", r#" #[cfg(feature = "f1")] compile_error!{"f1 is set"} #[cfg(feature = "f2")] compile_error!{"f2 is set"} #[cfg(feature = "b")] compile_error!{"b is set"} "#, ) .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" edition = "2015" [features] bfeat = [] "#, ) .file( "b/src/lib.rs", r#" #[cfg(feature = "bfeat")] compile_error!{"bfeat is set"} "#, ) .build(); p.cargo("check -p a") .with_status(101) .with_stderr_data(str![[r#" ... [ERROR] f1 is set ... "#]]) .with_stderr_does_not_contain("[..]f2 is set[..]") .with_stderr_does_not_contain("[..]b is set[..]") .run(); p.cargo("check -p a --features a/f1") .with_status(101) .with_stderr_data(str![[r#" ... [ERROR] f1 is set ... "#]]) .with_stderr_does_not_contain("[..]f2 is set[..]") .with_stderr_does_not_contain("[..]b is set[..]") .run(); p.cargo("check -p a --features a/f2") .with_status(101) .with_stderr_data(str![[r#" ... [ERROR] f1 is set ... [ERROR] f2 is set ... "#]]) .with_stderr_does_not_contain("[..]b is set[..]") .run(); p.cargo("check -p a --features b/bfeat") .with_status(101) .with_stderr_data(str![[r#" ... [ERROR] bfeat is set ... "#]]) .run(); p.cargo("check -p a --no-default-features").run(); p.cargo("check -p a --no-default-features --features b") .with_status(101) .with_stderr_data(str![[r#" ... [ERROR] b is set ... "#]]) .run(); } #[cargo_test] fn non_member() { // -p for a non-member Package::new("dep", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" resolver = "2" [dependencies] dep = "1.0" [features] f1 = [] "#, ) .file("src/lib.rs", "") .build(); p.cargo("check -p dep --features f1") .with_status(101) .with_stderr_data(str![[r#" [ERROR] cannot specify features for packages outside of workspace "#]]) .run(); p.cargo("check -p dep --all-features") .with_status(101) .with_stderr_data(str![[r#" [ERROR] cannot specify features for packages outside of workspace "#]]) .run(); p.cargo("check -p dep --no-default-features") .with_status(101) .with_stderr_data(str![[r#" [ERROR] cannot specify features for packages outside of workspace "#]]) .run(); p.cargo("check -p dep") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] dep v1.0.0 (registry `dummy-registry`) [CHECKING] dep v1.0.0 [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn resolver1_member_features() { // --features member-name/feature-name with resolver="1" let p = project() .file( "Cargo.toml", r#" [workspace] members = ["member1", "member2"] "#, ) .file( "member1/Cargo.toml", r#" [package] name = "member1" version = "0.1.0" edition = "2015" [features] m1-feature = [] "#, ) .file( "member1/src/main.rs", r#" fn main() { if cfg!(feature = "m1-feature") { println!("m1-feature set"); } } "#, ) .file("member2/Cargo.toml", &basic_manifest("member2", "0.1.0")) .file("member2/src/lib.rs", "") .build(); p.cargo("run -p member1 --features member1/m1-feature") .cwd("member2") .with_stdout_data(str![[r#" m1-feature set "#]]) .run(); p.cargo("check -p member1 --features member1/m2-feature") .cwd("member2") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package `member1 v0.1.0 ([ROOT]/foo/member1)` does not have the feature `m2-feature` [HELP] a feature with a similar name exists: `m1-feature` "#]]) .run(); } #[cargo_test] fn non_member_feature() { // --features for a non-member Package::new("jazz", "1.0.0").publish(); Package::new("bar", "1.0.0") .add_dep(Dependency::new("jazz", "1.0").optional(true)) .publish(); let make_toml = |resolver, optional| { let mut s = String::new(); write!( s, r#" [package] name = "foo" version = "0.1.0" edition = "2015" resolver = "{}" [dependencies] "#, resolver ) .unwrap(); if optional { s.push_str(r#"bar = { version = "1.0", optional = true } "#); } else { s.push_str(r#"bar = "1.0""#) } s.push('\n'); s }; let p = project() .file("Cargo.toml", &make_toml("1", false)) .file("src/lib.rs", "") .build(); p.cargo("fetch").run(); ///////////////////////// V1 non-optional eprintln!("V1 non-optional"); p.cargo("check -p bar") .with_stderr_data(str![[r#" [CHECKING] bar v1.0.0 [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // TODO: This should not be allowed (future warning?) p.cargo("check --features bar/jazz") .with_stderr_data(str![[r#" [DOWNLOADING] crates ... [DOWNLOADED] jazz v1.0.0 (registry `dummy-registry`) [CHECKING] jazz v1.0.0 [CHECKING] bar v1.0.0 [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // TODO: This should not be allowed (future warning?) p.cargo("check -p bar --features bar/jazz -v") .with_stderr_data(str![[r#" [FRESH] jazz v1.0.0 [FRESH] bar v1.0.0 [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); ///////////////////////// V1 optional eprintln!("V1 optional"); p.change_file("Cargo.toml", &make_toml("1", true)); // This error isn't great, but is probably unlikely to be common in // practice, so I'm not going to put much effort into improving it. p.cargo("check -p bar") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package ID specification `bar` did not match any packages [HELP] a package with a similar name exists: `foo` "#]]) .run(); p.cargo("check -p bar --features bar -v") .with_stderr_data(str![[r#" [FRESH] bar v1.0.0 [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // TODO: This should not be allowed (future warning?) p.cargo("check -p bar --features bar/jazz -v") .with_stderr_data(str![[r#" [FRESH] jazz v1.0.0 [FRESH] bar v1.0.0 [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); ///////////////////////// V2 non-optional eprintln!("V2 non-optional"); p.change_file("Cargo.toml", &make_toml("2", false)); // TODO: This should not be allowed (future warning?) p.cargo("check --features bar/jazz -v") .with_stderr_data(str![[r#" [FRESH] jazz v1.0.0 [FRESH] bar v1.0.0 [FRESH] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check -p bar -v") .with_stderr_data(str![[r#" [FRESH] bar v1.0.0 [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check -p bar --features bar/jazz") .with_status(101) .with_stderr_data(str![[r#" [ERROR] cannot specify features for packages outside of workspace "#]]) .run(); ///////////////////////// V2 optional eprintln!("V2 optional"); p.change_file("Cargo.toml", &make_toml("2", true)); p.cargo("check -p bar") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package ID specification `bar` did not match any packages [HELP] a package with a similar name exists: `foo` "#]]) .run(); // New --features behavior does not look at cwd. p.cargo("check -p bar --features bar") .with_status(101) .with_stderr_data(str![[r#" [ERROR] cannot specify features for packages outside of workspace "#]]) .run(); p.cargo("check -p bar --features bar/jazz") .with_status(101) .with_stderr_data(str![[r#" [ERROR] cannot specify features for packages outside of workspace "#]]) .run(); p.cargo("check -p bar --features foo/bar") .with_status(101) .with_stderr_data(str![[r#" [ERROR] cannot specify features for packages outside of workspace "#]]) .run(); } cargo-0.91.0/tests/testsuite/package_message_format.rs000064400000000000000000000170661046102023000212300ustar 00000000000000//! Tests for the `--message-format` flag for `cargo package`. use crate::prelude::*; use cargo_test_support::project; use cargo_test_support::str; #[cargo_test] fn gated() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" edition = "2015" license = "MIT" description = "foo" documentation = "foo" "#, ) .file("src/lib.rs", "") .build(); p.cargo("package --list --message-format json") .masquerade_as_nightly_cargo(&["package --message-format"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] the `--message-format` flag is unstable, pass `-Z unstable-options` to enable it See https://github.com/rust-lang/cargo/issues/15353 for more information about the `--message-format` flag. "#]]) .run(); } #[cargo_test] fn requires_list() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" edition = "2015" license = "MIT" description = "foo" documentation = "foo" "#, ) .file("src/lib.rs", "") .build(); p.cargo("package --message-format json -Zunstable-options") .masquerade_as_nightly_cargo(&["package --message-format"]) .with_status(1) .with_stderr_data(str![[r#" [ERROR] the following required arguments were not provided: --list Usage: cargo[EXE] package --list --message-format -Z For more information, try '--help'. "#]]) .run(); } #[cargo_test] fn human() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" edition = "2015" license = "MIT" description = "foo" documentation = "foo" "#, ) .file("src/lib.rs", "") .build(); p.cargo("package --list --message-format human -Zunstable-options") .masquerade_as_nightly_cargo(&["package --message-format"]) .with_stderr_data(str![""]) .with_stdout_data(str![[r#" Cargo.lock Cargo.toml Cargo.toml.orig src/lib.rs "#]]) .run(); } #[cargo_test] fn single_package() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" edition = "2015" license = "MIT" description = "foo" documentation = "foo" "#, ) .file("src/lib.rs", "") .build(); p.cargo("package --list --message-format json -Zunstable-options") .masquerade_as_nightly_cargo(&["package --message-format"]) .with_stderr_data(str![""]) .with_stdout_data( str![[r#" [ { "files": { "Cargo.lock": { "kind": "generate" }, "Cargo.toml": { "kind": "generate", "path": "[ROOT]/foo/Cargo.toml" }, "Cargo.toml.orig": { "kind": "copy", "path": "[ROOT]/foo/Cargo.toml" }, "src/lib.rs": { "kind": "copy", "path": "[ROOT]/foo/src/lib.rs" } }, "id": "path+[ROOTURL]/foo#0.0.0" } ] "#]] .is_json() .against_jsonlines(), ) .run(); // has existing lockfile p.cargo("generate-lockfile").run(); p.cargo("package --list --message-format json -Zunstable-options") .masquerade_as_nightly_cargo(&["package --message-format"]) .with_stderr_data(str![""]) .with_stdout_data( str![[r#" [ { "files": { "Cargo.lock": { "kind": "generate", "path": "[ROOT]/foo/Cargo.lock" }, "Cargo.toml": { "kind": "generate", "path": "[ROOT]/foo/Cargo.toml" }, "Cargo.toml.orig": { "kind": "copy", "path": "[ROOT]/foo/Cargo.toml" }, "src/lib.rs": { "kind": "copy", "path": "[ROOT]/foo/src/lib.rs" } }, "id": "path+[ROOTURL]/foo#0.0.0" } ] "#]] .is_json() .against_jsonlines(), ) .run(); } #[cargo_test] fn workspace() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["gondor", "rohan"] "#, ) .file( "gondor/Cargo.toml", r#" [package] name = "gondor" edition = "2015" license = "MIT" description = "foo" documentation = "foo" "#, ) .file("gondor/src/lib.rs", "") .file( "rohan/Cargo.toml", r#" [package] name = "rohan" edition = "2015" license = "MIT" description = "foo" documentation = "foo" "#, ) .file("rohan/src/lib.rs", "") .build(); p.cargo("package --list --message-format json -Zunstable-options") .masquerade_as_nightly_cargo(&["package --message-format"]) .with_stderr_data(str![""]) .with_stdout_data( str![[r#" [ { "files": { "Cargo.lock": { "kind": "generate" }, "Cargo.toml": { "kind": "generate", "path": "[ROOT]/foo/gondor/Cargo.toml" }, "Cargo.toml.orig": { "kind": "copy", "path": "[ROOT]/foo/gondor/Cargo.toml" }, "src/lib.rs": { "kind": "copy", "path": "[ROOT]/foo/gondor/src/lib.rs" } }, "id": "path+[ROOTURL]/foo/gondor#0.0.0" }, { "files": { "Cargo.lock": { "kind": "generate" }, "Cargo.toml": { "kind": "generate", "path": "[ROOT]/foo/rohan/Cargo.toml" }, "Cargo.toml.orig": { "kind": "copy", "path": "[ROOT]/foo/rohan/Cargo.toml" }, "src/lib.rs": { "kind": "copy", "path": "[ROOT]/foo/rohan/src/lib.rs" } }, "id": "path+[ROOTURL]/foo/rohan#0.0.0" } ] "#]] .is_json() .against_jsonlines(), ) .run(); // has existing lockfile p.cargo("generate-lockfile").run(); p.cargo("package --list --message-format json -Zunstable-options") .masquerade_as_nightly_cargo(&["package --message-format"]) .with_stderr_data(str![""]) .with_stdout_data( str![[r#" [ { "files": { "Cargo.lock": { "kind": "generate", "path": "[ROOT]/foo/Cargo.lock" }, "Cargo.toml": { "kind": "generate", "path": "[ROOT]/foo/gondor/Cargo.toml" }, "Cargo.toml.orig": { "kind": "copy", "path": "[ROOT]/foo/gondor/Cargo.toml" }, "src/lib.rs": { "kind": "copy", "path": "[ROOT]/foo/gondor/src/lib.rs" } }, "id": "path+[ROOTURL]/foo/gondor#0.0.0" }, { "files": { "Cargo.lock": { "kind": "generate", "path": "[ROOT]/foo/Cargo.lock" }, "Cargo.toml": { "kind": "generate", "path": "[ROOT]/foo/rohan/Cargo.toml" }, "Cargo.toml.orig": { "kind": "copy", "path": "[ROOT]/foo/rohan/Cargo.toml" }, "src/lib.rs": { "kind": "copy", "path": "[ROOT]/foo/rohan/src/lib.rs" } }, "id": "path+[ROOTURL]/foo/rohan#0.0.0" } ] "#]] .is_json() .against_jsonlines(), ) .run(); } cargo-0.91.0/tests/testsuite/patch.rs000064400000000000000000002502251046102023000156540ustar 00000000000000//! Tests for `[patch]` table source replacement. use std::fs; use crate::prelude::*; use cargo_test_support::git; use cargo_test_support::paths; use cargo_test_support::registry::{self, Package}; use cargo_test_support::{basic_manifest, project, str}; #[cargo_test] fn replace() { Package::new("bar", "0.1.0").publish(); Package::new("baz", "0.1.0") .file( "src/lib.rs", "extern crate bar; pub fn baz() { bar::bar(); }", ) .dep("bar", "0.1.0") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" baz = "0.1.0" [patch.crates-io] bar = { path = "bar" } "#, ) .file( "src/lib.rs", " extern crate bar; extern crate baz; pub fn bar() { bar::bar(); baz::baz(); } ", ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] baz v0.1.0 (registry `dummy-registry`) [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [CHECKING] baz v0.1.0 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn from_config() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file( ".cargo/config.toml", r#" [patch.crates-io] bar = { path = 'bar' } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1")) .file("bar/src/lib.rs", r#""#) .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.1.1 ([ROOT]/foo/bar) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn from_config_relative() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file( "../.cargo/config.toml", r#" [patch.crates-io] bar = { path = 'foo/bar' } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1")) .file("bar/src/lib.rs", r#""#) .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.1.1 ([ROOT]/foo/bar) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn from_config_precedence() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" [patch.crates-io] bar = { path = 'no-such-path' } "#, ) .file( ".cargo/config.toml", r#" [patch.crates-io] bar = { path = 'bar' } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1")) .file("bar/src/lib.rs", r#""#) .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.1.1 ([ROOT]/foo/bar) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn nonexistent() { Package::new("baz", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" [patch.crates-io] bar = { path = "bar" } "#, ) .file( "src/lib.rs", "extern crate bar; pub fn foo() { bar::bar(); }", ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn patch_git() { let bar = git::repo(&paths::root().join("override")) .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "") .build(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = {{ git = '{}' }} [patch.'{0}'] bar = {{ path = "bar" }} "#, bar.url() ), ) .file( "src/lib.rs", "extern crate bar; pub fn foo() { bar::bar(); }", ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/override` [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn patch_to_git() { let bar = git::repo(&paths::root().join("override")) .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "pub fn bar() {}") .build(); Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1" [patch.crates-io] bar = {{ git = '{}' }} "#, bar.url() ), ) .file( "src/lib.rs", "extern crate bar; pub fn foo() { bar::bar(); }", ) .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/override` [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.1.0 ([ROOTURL]/override#[..]) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn unused() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" [patch.crates-io] bar = { path = "bar" } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.2.0")) .file("bar/src/lib.rs", "not rust code") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [WARNING] Patch `bar v0.2.0 ([ROOT]/foo/bar)` was not used in the crate graph. Check that the patched package version and available features are compatible with the dependency requirements. If the patch has a different version from what is locked in the Cargo.lock file, run `cargo update` to use the new version. This may also occur with an optional dependency that is not enabled. [LOCKING] 1 package to latest compatible version [ADDING] bar v0.1.0 (available: v0.2.0) [DOWNLOADING] crates ... [DOWNLOADED] bar v0.1.0 (registry `dummy-registry`) [CHECKING] bar v0.1.0 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [WARNING] Patch `bar v0.2.0 ([ROOT]/foo/bar)` was not used in the crate graph. Check that the patched package version and available features are compatible with the dependency requirements. If the patch has a different version from what is locked in the Cargo.lock file, run `cargo update` to use the new version. This may also occur with an optional dependency that is not enabled. [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // unused patch should be in the lock file let lock = p.read_lockfile(); let toml: toml::Table = toml::from_str(&lock).unwrap(); assert_eq!(toml["patch"]["unused"].as_array().unwrap().len(), 1); assert_eq!(toml["patch"]["unused"][0]["name"].as_str(), Some("bar")); assert_eq!( toml["patch"]["unused"][0]["version"].as_str(), Some("0.2.0") ); } #[cargo_test] fn unused_with_mismatch_source_being_patched() { registry::alt_init(); Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" [patch.alternative] bar = { path = "bar" } [patch.crates-io] bar = { path = "baz" } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.2.0")) .file("bar/src/lib.rs", "not rust code") .file("baz/Cargo.toml", &basic_manifest("bar", "0.3.0")) .file("baz/src/lib.rs", "not rust code") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [WARNING] Patch `bar v0.2.0 ([ROOT]/foo/bar)` was not used in the crate graph. Perhaps you misspelled the source URL being patched. Possible URLs for `[patch.]`: crates-io [WARNING] Patch `bar v0.3.0 ([ROOT]/foo/baz)` was not used in the crate graph. Check that the patched package version and available features are compatible with the dependency requirements. If the patch has a different version from what is locked in the Cargo.lock file, run `cargo update` to use the new version. This may also occur with an optional dependency that is not enabled. [LOCKING] 1 package to latest compatible version [ADDING] bar v0.1.0 (available: v0.3.0) [DOWNLOADING] crates ... [DOWNLOADED] bar v0.1.0 (registry `dummy-registry`) [CHECKING] bar v0.1.0 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn prefer_patch_version() { Package::new("bar", "0.1.2").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" [patch.crates-io] bar = { path = "bar" } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1")) .file("bar/src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.1.1 ([ROOT]/foo/bar) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // there should be no patch.unused in the toml file let lock = p.read_lockfile(); let toml: toml::Table = toml::from_str(&lock).unwrap(); assert!(toml.get("patch").is_none()); } #[cargo_test] fn unused_from_config() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file( ".cargo/config.toml", r#" [patch.crates-io] bar = { path = "bar" } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.2.0")) .file("bar/src/lib.rs", "not rust code") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [WARNING] Patch `bar v0.2.0 ([ROOT]/foo/bar)` was not used in the crate graph. Check that the patched package version and available features are compatible with the dependency requirements. If the patch has a different version from what is locked in the Cargo.lock file, run `cargo update` to use the new version. This may also occur with an optional dependency that is not enabled. [LOCKING] 1 package to latest compatible version [ADDING] bar v0.1.0 (available: v0.2.0) [DOWNLOADING] crates ... [DOWNLOADED] bar v0.1.0 (registry `dummy-registry`) [CHECKING] bar v0.1.0 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [WARNING] Patch `bar v0.2.0 ([ROOT]/foo/bar)` was not used in the crate graph. Check that the patched package version and available features are compatible with the dependency requirements. If the patch has a different version from what is locked in the Cargo.lock file, run `cargo update` to use the new version. This may also occur with an optional dependency that is not enabled. [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // unused patch should be in the lock file let lock = p.read_lockfile(); let toml: toml::Table = toml::from_str(&lock).unwrap(); assert_eq!(toml["patch"]["unused"].as_array().unwrap().len(), 1); assert_eq!(toml["patch"]["unused"][0]["name"].as_str(), Some("bar")); assert_eq!( toml["patch"]["unused"][0]["version"].as_str(), Some("0.2.0") ); } #[cargo_test] fn unused_git() { Package::new("bar", "0.1.0").publish(); let foo = git::repo(&paths::root().join("override")) .file("Cargo.toml", &basic_manifest("bar", "0.2.0")) .file("src/lib.rs", "") .build(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1" [patch.crates-io] bar = {{ git = '{}' }} "#, foo.url() ), ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/override` [UPDATING] `dummy-registry` index [WARNING] Patch `bar v0.2.0 ([ROOTURL]/override#[..])` was not used in the crate graph. Check that the patched package version and available features are compatible with the dependency requirements. If the patch has a different version from what is locked in the Cargo.lock file, run `cargo update` to use the new version. This may also occur with an optional dependency that is not enabled. [LOCKING] 1 package to latest compatible version [ADDING] bar v0.1.0 (available: v0.2.0) [DOWNLOADING] crates ... [DOWNLOADED] bar v0.1.0 (registry `dummy-registry`) [CHECKING] bar v0.1.0 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [WARNING] Patch `bar v0.2.0 ([ROOTURL]/override#[..])` was not used in the crate graph. Check that the patched package version and available features are compatible with the dependency requirements. If the patch has a different version from what is locked in the Cargo.lock file, run `cargo update` to use the new version. This may also occur with an optional dependency that is not enabled. [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn add_patch() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", r#""#) .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.1.0 (registry `dummy-registry`) [CHECKING] bar v0.1.0 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" [patch.crates-io] bar = { path = 'bar' } "#, ); p.cargo("check") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [ADDING] bar v0.1.0 ([ROOT]/foo/bar) [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn add_patch_from_config() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", r#""#) .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.1.0 (registry `dummy-registry`) [CHECKING] bar v0.1.0 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.change_file( ".cargo/config.toml", r#" [patch.crates-io] bar = { path = 'bar' } "#, ); p.cargo("check") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [ADDING] bar v0.1.0 ([ROOT]/foo/bar) [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn add_ignored_patch() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1")) .file("bar/src/lib.rs", r#""#) .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.1.0 (registry `dummy-registry`) [CHECKING] bar v0.1.0 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" [patch.crates-io] bar = { path = 'bar' } "#, ); p.cargo("check") .with_stderr_data(str![[r#" [WARNING] Patch `bar v0.1.1 ([ROOT]/foo/bar)` was not used in the crate graph. Check that the patched package version and available features are compatible with the dependency requirements. If the patch has a different version from what is locked in the Cargo.lock file, run `cargo update` to use the new version. This may also occur with an optional dependency that is not enabled. [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [WARNING] Patch `bar v0.1.1 ([ROOT]/foo/bar)` was not used in the crate graph. Check that the patched package version and available features are compatible with the dependency requirements. If the patch has a different version from what is locked in the Cargo.lock file, run `cargo update` to use the new version. This may also occur with an optional dependency that is not enabled. [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("update").run(); p.cargo("check") .with_stderr_data(str![[r#" [CHECKING] bar v0.1.1 ([ROOT]/foo/bar) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn add_patch_with_features() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" [patch.crates-io] bar = { path = 'bar', features = ["some_feature"] } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", r#""#) .build(); p.cargo("check").with_stderr_data(str![[r#" [WARNING] patch for `bar` uses the features mechanism. default-features and features will not take effect because the patch dependency does not support this mechanism [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); p.cargo("check").with_stderr_data(str![[r#" [WARNING] patch for `bar` uses the features mechanism. default-features and features will not take effect because the patch dependency does not support this mechanism [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn add_patch_with_setting_default_features() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" [patch.crates-io] bar = { path = 'bar', default-features = false, features = ["none_default_feature"] } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", r#""#) .build(); p.cargo("check").with_stderr_data(str![[r#" [WARNING] patch for `bar` uses the features mechanism. default-features and features will not take effect because the patch dependency does not support this mechanism [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); p.cargo("check").with_stderr_data(str![[r#" [WARNING] patch for `bar` uses the features mechanism. default-features and features will not take effect because the patch dependency does not support this mechanism [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn no_warn_ws_patch() { Package::new("c", "0.1.0").publish(); // Don't issue an unused patch warning when the patch isn't used when // partially building a workspace. let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b", "c"] [patch.crates-io] c = { path = "c" } "#, ) .file("a/Cargo.toml", &basic_manifest("a", "0.1.0")) .file("a/src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" edition = "2015" [dependencies] c = "0.1.0" "#, ) .file("b/src/lib.rs", "") .file("c/Cargo.toml", &basic_manifest("c", "0.1.0")) .file("c/src/lib.rs", "") .build(); p.cargo("check -p a") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [CHECKING] a v0.1.0 ([ROOT]/foo/a) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn new_minor() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" [patch.crates-io] bar = { path = 'bar' } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1")) .file("bar/src/lib.rs", r#""#) .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.1.1 ([ROOT]/foo/bar) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn transitive_new_minor() { Package::new("baz", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = { path = 'bar' } [patch.crates-io] baz = { path = 'baz' } "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] [dependencies] baz = '0.1.0' "#, ) .file("bar/src/lib.rs", r#""#) .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.1")) .file("baz/src/lib.rs", r#""#) .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [CHECKING] baz v0.1.1 ([ROOT]/foo/baz) [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn new_major() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.2.0" [patch.crates-io] bar = { path = 'bar' } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.2.0")) .file("bar/src/lib.rs", r#""#) .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.2.0 ([ROOT]/foo/bar) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); Package::new("bar", "0.2.0").publish(); p.cargo("update").run(); p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.2.0" "#, ); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [ADDING] bar v0.2.0 [DOWNLOADING] crates ... [DOWNLOADED] bar v0.2.0 (registry `dummy-registry`) [CHECKING] bar v0.2.0 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn transitive_new_major() { Package::new("baz", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = { path = 'bar' } [patch.crates-io] baz = { path = 'baz' } "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] [dependencies] baz = '0.2.0' "#, ) .file("bar/src/lib.rs", r#""#) .file("baz/Cargo.toml", &basic_manifest("baz", "0.2.0")) .file("baz/src/lib.rs", r#""#) .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [CHECKING] baz v0.2.0 ([ROOT]/foo/baz) [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn shared_by_transitive() { Package::new("baz", "0.1.1").publish(); let baz = git::repo(&paths::root().join("override")) .file("Cargo.toml", &basic_manifest("baz", "0.1.2")) .file("src/lib.rs", "") .build(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = " 0.1.0" edition = "2015" [dependencies] bar = {{ path = "bar" }} baz = "0.1" [patch.crates-io] baz = {{ git = "{}", version = "0.1" }} "#, baz.url(), ), ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" [dependencies] baz = "0.1.1" "#, ) .file("bar/src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/override` [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [CHECKING] baz v0.1.2 ([ROOTURL]/override#[..]) [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn remove_patch() { Package::new("foo", "0.1.0").publish(); Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1" [patch.crates-io] foo = { path = 'foo' } bar = { path = 'bar' } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", r#""#) .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("foo/src/lib.rs", r#""#) .build(); // Generate a lock file where `foo` is unused p.cargo("check").run(); let lock_file1 = p.read_lockfile(); // Remove `foo` and generate a new lock file form the old one p.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1" [patch.crates-io] bar = { path = 'bar' } "#, ); p.cargo("check").run(); let lock_file2 = p.read_lockfile(); // Remove the lock file and build from scratch fs::remove_file(p.root().join("Cargo.lock")).unwrap(); p.cargo("check").run(); let lock_file3 = p.read_lockfile(); assert!(lock_file1.contains("foo")); assert_eq!(lock_file2, lock_file3); assert_ne!(lock_file1, lock_file2); } #[cargo_test] fn non_crates_io() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [patch.some-other-source] bar = { path = 'bar' } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", r#""#) .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: [patch] entry `some-other-source` should be a URL or registry name Caused by: invalid url `some-other-source`: relative URL without a base "#]]) .run(); } #[cargo_test] fn replace_with_crates_io() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [patch.crates-io] bar = "0.1" "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", r#""#) .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to resolve patches for `https://github.com/rust-lang/crates.io-index` Caused by: patch for `bar` in `https://github.com/rust-lang/crates.io-index` points to the same source, but patches must point to different sources "#]]) .run(); } #[cargo_test] fn patch_in_virtual() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo"] [patch.crates-io] bar = { path = "bar" } "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", r#""#) .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [dependencies] bar = "0.1" "#, ) .file("foo/src/lib.rs", r#""#) .build(); p.cargo("check -p foo") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [CHECKING] foo v0.1.0 ([ROOT]/foo/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check -p foo") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn patch_depends_on_another_patch() { Package::new("bar", "0.1.0") .file("src/lib.rs", "broken code") .publish(); Package::new("baz", "0.1.0") .dep("bar", "0.1") .file("src/lib.rs", "broken code") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.1.0" edition = "2015" [dependencies] bar = "0.1" baz = "0.1" [patch.crates-io] bar = { path = "bar" } baz = { path = "baz" } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1")) .file("bar/src/lib.rs", r#""#) .file( "baz/Cargo.toml", r#" [package] name = "baz" version = "0.1.1" edition = "2015" authors = [] [dependencies] bar = "0.1" "#, ) .file("baz/src/lib.rs", r#""#) .build(); p.cargo("check").run(); // Nothing should be rebuilt, no registry should be updated. p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn replace_prerelease() { Package::new("baz", "1.1.0-pre.1").publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar"] [patch.crates-io] baz = { path = "./baz" } "#, ) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = [] [dependencies] baz = "1.1.0-pre.1" "#, ) .file( "bar/src/main.rs", "extern crate baz; fn main() { baz::baz() }", ) .file( "baz/Cargo.toml", r#" [package] name = "baz" version = "1.1.0-pre.1" edition = "2015" authors = [] [workspace] "#, ) .file("baz/src/lib.rs", "pub fn baz() {}") .build(); p.cargo("check").run(); } #[cargo_test] fn patch_older() { Package::new("baz", "1.0.2").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { path = 'bar' } baz = "=1.0.1" [patch.crates-io] baz = { path = "./baz" } "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = [] [dependencies] baz = "1.0.0" "#, ) .file("bar/src/lib.rs", "") .file( "baz/Cargo.toml", r#" [package] name = "baz" version = "1.0.1" edition = "2015" authors = [] "#, ) .file("baz/src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [CHECKING] baz v1.0.1 ([ROOT]/foo/baz) [CHECKING] bar v0.5.0 ([ROOT]/foo/bar) [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn cycle() { Package::new("a", "1.0.0").publish(); Package::new("b", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] [patch.crates-io] a = {path="a"} b = {path="b"} "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "1.0.0" edition = "2015" [dependencies] b = "1.0" "#, ) .file("a/src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "1.0.0" edition = "2015" [dependencies] a = "1.0" "#, ) .file("b/src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] cyclic package dependency: package `a v1.0.0 ([ROOT]/foo/a)` depends on itself. Cycle: package `a v1.0.0 ([ROOT]/foo/a)` ... which satisfies dependency `a = "^1.0"` of package `b v1.0.0 ([ROOT]/foo/b)` ... which satisfies dependency `b = "^1.0"` of package `a v1.0.0 ([ROOT]/foo/a)` "#]]) .run(); } #[cargo_test] fn multipatch() { Package::new("a", "1.0.0").publish(); Package::new("a", "2.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] a1 = { version = "1", package = "a" } a2 = { version = "2", package = "a" } [patch.crates-io] b1 = { path = "a1", package = "a" } b2 = { path = "a2", package = "a" } "#, ) .file("src/lib.rs", "pub fn foo() { a1::f1(); a2::f2(); }") .file( "a1/Cargo.toml", r#" [package] name = "a" version = "1.0.0" edition = "2015" "#, ) .file("a1/src/lib.rs", "pub fn f1() {}") .file( "a2/Cargo.toml", r#" [package] name = "a" version = "2.0.0" edition = "2015" "#, ) .file("a2/src/lib.rs", "pub fn f2() {}") .build(); p.cargo("check").run(); } #[cargo_test] fn patch_same_version() { let bar = git::repo(&paths::root().join("override")) .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "") .build(); cargo_test_support::registry::init(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = "0.1" [patch.crates-io] bar = {{ path = "bar" }} bar2 = {{ git = '{}', package = 'bar' }} "#, bar.url(), ), ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" "#, ) .file("bar/src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/override` [ERROR] cannot have two `[patch]` entries which both resolve to `bar v0.1.0` "#]]) .run(); } #[cargo_test] fn two_semver_compatible() { let bar = git::repo(&paths::root().join("override")) .file("Cargo.toml", &basic_manifest("bar", "0.1.1")) .file("src/lib.rs", "") .build(); cargo_test_support::registry::init(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = "0.1" [patch.crates-io] bar = {{ path = "bar" }} bar2 = {{ git = '{}', package = 'bar' }} "#, bar.url(), ), ) .file("src/lib.rs", "pub fn foo() { bar::foo() }") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.2" edition = "2015" "#, ) .file("bar/src/lib.rs", "pub fn foo() {}") .build(); // assert the build succeeds and doesn't panic anywhere, and then afterwards // assert that the build succeeds again without updating anything or // building anything else. p.cargo("check").run(); p.cargo("check") .with_stderr_data(str![[r#" [WARNING] Patch `bar v0.1.1 ([ROOTURL]/override#[..])` was not used in the crate graph. Perhaps you misspelled the source URL being patched. Possible URLs for `[patch.]`: [ROOT]/foo/bar [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn multipatch_select_big() { let bar = git::repo(&paths::root().join("override")) .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "") .build(); cargo_test_support::registry::init(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = "*" [patch.crates-io] bar = {{ path = "bar" }} bar2 = {{ git = '{}', package = 'bar' }} "#, bar.url(), ), ) .file("src/lib.rs", "pub fn foo() { bar::foo() }") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.2.0" edition = "2015" "#, ) .file("bar/src/lib.rs", "pub fn foo() {}") .build(); // assert the build succeeds, which is only possible if 0.2.0 is selected // since 0.1.0 is missing the function we need. Afterwards assert that the // build succeeds again without updating anything or building anything else. p.cargo("check").run(); p.cargo("check") .with_stderr_data(str![[r#" [WARNING] Patch `bar v0.1.0 ([ROOTURL]/override#[..])` was not used in the crate graph. Perhaps you misspelled the source URL being patched. Possible URLs for `[patch.]`: [ROOT]/foo/bar [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn canonicalize_a_bunch() { let base = git::repo(&paths::root().join("base")) .file("Cargo.toml", &basic_manifest("base", "0.1.0")) .file("src/lib.rs", "") .build(); let intermediate = git::repo(&paths::root().join("intermediate")) .file( "Cargo.toml", &format!( r#" [package] name = "intermediate" version = "0.1.0" edition = "2015" [dependencies] # Note the lack of trailing slash base = {{ git = '{}' }} "#, base.url(), ), ) .file("src/lib.rs", "pub fn f() { base::f() }") .build(); let newbase = git::repo(&paths::root().join("newbase")) .file("Cargo.toml", &basic_manifest("base", "0.1.0")) .file("src/lib.rs", "pub fn f() {}") .build(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] # Note the trailing slashes base = {{ git = '{base}/' }} intermediate = {{ git = '{intermediate}/' }} [patch.'{base}'] # Note the lack of trailing slash base = {{ git = '{newbase}' }} "#, base = base.url(), intermediate = intermediate.url(), newbase = newbase.url(), ), ) .file("src/lib.rs", "pub fn a() { base::f(); intermediate::f() }") .build(); // Once to make sure it actually works p.cargo("check").run(); // Then a few more times for good measure to ensure no weird warnings about // `[patch]` are printed. p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn update_unused_new_version() { // If there is an unused patch entry, and then you update the patch, // make sure `cargo update` will be able to fix the lock file. Package::new("bar", "0.1.5").publish(); // Start with a lock file to 0.1.5, and an "unused" patch because the // version is too old. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = "0.1.5" [patch.crates-io] bar = { path = "../bar" } "#, ) .file("src/lib.rs", "") .build(); // Patch is too old. let bar = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.1.4")) .file("src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [WARNING] Patch `bar v0.1.4 ([ROOT]/bar)` was not used in the crate graph. ... "#]]) .run(); // unused patch should be in the lock file let lock = p.read_lockfile(); let toml: toml::Table = toml::from_str(&lock).unwrap(); assert_eq!(toml["patch"]["unused"].as_array().unwrap().len(), 1); assert_eq!(toml["patch"]["unused"][0]["name"].as_str(), Some("bar")); assert_eq!( toml["patch"]["unused"][0]["version"].as_str(), Some("0.1.4") ); // Oh, OK, let's update to the latest version. bar.change_file("Cargo.toml", &basic_manifest("bar", "0.1.6")); // Create a backup so we can test it with different options. fs::copy(p.root().join("Cargo.lock"), p.root().join("Cargo.lock.bak")).unwrap(); // Try to build again, this should automatically update Cargo.lock. p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [ADDING] bar v0.1.6 ([ROOT]/bar) [CHECKING] bar v0.1.6 ([ROOT]/bar) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // This should not update any registry. p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert!(!p.read_lockfile().contains("unused")); // Restore the lock file, and see if `update` will work, too. fs::copy(p.root().join("Cargo.lock.bak"), p.root().join("Cargo.lock")).unwrap(); // Try `update `. p.cargo("update bar") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [ADDING] bar v0.1.6 ([ROOT]/bar) [REMOVING] bar v0.1.5 "#]]) .run(); // Try with bare `cargo update`. fs::copy(p.root().join("Cargo.lock.bak"), p.root().join("Cargo.lock")).unwrap(); p.cargo("update") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [ADDING] bar v0.1.6 ([ROOT]/bar) [REMOVING] bar v0.1.5 "#]]) .run(); } #[cargo_test] fn too_many_matches() { // The patch locations has multiple versions that match. registry::alt_init(); Package::new("bar", "0.1.0").publish(); Package::new("bar", "0.1.0").alternative(true).publish(); Package::new("bar", "0.1.1").alternative(true).publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "0.1" [patch.crates-io] bar = { version = "0.1", registry = "alternative" } "#, ) .file("src/lib.rs", "") .build(); // Picks 0.1.1, the most recent version. p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [ERROR] failed to resolve patches for `https://github.com/rust-lang/crates.io-index` Caused by: patch for `bar` in `https://github.com/rust-lang/crates.io-index` failed to resolve Caused by: patch for `bar` in `registry `alternative`` resolved to more than one candidate Found versions: 0.1.0, 0.1.1 Update the patch definition to select only one package. For example, add an `=` version requirement to the patch definition, such as `version = "=0.1.1"`. "#]]) .run(); } #[cargo_test] fn no_matches() { // A patch to a location that does not contain the named package. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "0.1" [patch.crates-io] bar = { path = "bar" } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("abc", "0.1.0")) .file("bar/src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to resolve patches for `https://github.com/rust-lang/crates.io-index` Caused by: patch for `bar` in `https://github.com/rust-lang/crates.io-index` failed to resolve Caused by: The patch location `[ROOT]/foo/bar` does not appear to contain any packages matching the name `bar`. "#]]) .run(); } #[cargo_test] fn mismatched_version() { // A patch to a location that has an old version. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "0.1.1" [patch.crates-io] bar = { path = "bar", version = "0.1.1" } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to resolve patches for `https://github.com/rust-lang/crates.io-index` Caused by: patch for `bar` in `https://github.com/rust-lang/crates.io-index` failed to resolve Caused by: The patch location `[ROOT]/foo/bar` contains a `bar` package with version `0.1.0`, but the patch definition requires `^0.1.1`. Check that the version in the patch location is what you expect, and update the patch definition to match. "#]]) .run(); } #[cargo_test] fn patch_walks_backwards() { // Starting with a locked patch, change the patch so it points to an older version. Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "0.1" [patch.crates-io] bar = {path="bar"} "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1")) .file("bar/src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.1.1 ([ROOT]/foo/bar) [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Somehow the user changes the version backwards. p.change_file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNGRADING] bar v0.1.1 ([ROOT]/foo/bar) -> v0.1.0 [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn patch_walks_backwards_restricted() { // This is the same as `patch_walks_backwards`, but the patch contains a // `version` qualifier. This is unusual, just checking a strange edge case. Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "0.1" [patch.crates-io] bar = {path="bar", version="0.1.1"} "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1")) .file("bar/src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.1.1 ([ROOT]/foo/bar) [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Somehow the user changes the version backwards. p.change_file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to resolve patches for `https://github.com/rust-lang/crates.io-index` Caused by: patch for `bar` in `https://github.com/rust-lang/crates.io-index` failed to resolve Caused by: The patch location `[ROOT]/foo/bar` contains a `bar` package with version `0.1.0`, but the patch definition requires `^0.1.1`. Check that the version in the patch location is what you expect, and update the patch definition to match. "#]]) .run(); } #[cargo_test] fn patched_dep_new_version() { // What happens when a patch is locked, and then one of the patched // dependencies needs to be updated. In this case, the baz requirement // gets updated from 0.1.0 to 0.1.1. Package::new("bar", "0.1.0").dep("baz", "0.1.0").publish(); Package::new("baz", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "0.1" [patch.crates-io] bar = {path="bar"} "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" [dependencies] baz = "0.1" "#, ) .file("bar/src/lib.rs", "") .build(); // Lock everything. p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] baz v0.1.0 (registry `dummy-registry`) [CHECKING] baz v0.1.0 [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); Package::new("baz", "0.1.1").publish(); // Just the presence of the new version should not have changed anything. p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Modify the patch so it requires the new version. p.change_file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" [dependencies] baz = "0.1.1" "#, ); // Should unlock and update cleanly. p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [UPDATING] baz v0.1.0 -> v0.1.1 [DOWNLOADING] crates ... [DOWNLOADED] baz v0.1.1 (registry `dummy-registry`) [CHECKING] baz v0.1.1 [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn patch_update_doesnt_update_other_sources() { // Very extreme edge case, make sure a patch update doesn't update other // sources. registry::alt_init(); Package::new("bar", "0.1.0").publish(); Package::new("bar", "0.1.0").alternative(true).publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "0.1" bar_alt = { version = "0.1", registry = "alternative", package = "bar" } [patch.crates-io] bar = { path = "bar" } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data( str![[r#" [UPDATING] `dummy-registry` index [UPDATING] `alternative` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] bar v0.1.0 (registry `alternative`) [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [CHECKING] bar v0.1.0 (registry `alternative`) [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); // Publish new versions in both sources. Package::new("bar", "0.1.1").publish(); Package::new("bar", "0.1.1").alternative(true).publish(); // Since it is locked, nothing should change. p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Require new version on crates.io. p.change_file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1")); // This should not update bar_alt. p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [UPDATING] bar v0.1.0 ([ROOT]/foo/bar) -> v0.1.1 [CHECKING] bar v0.1.1 ([ROOT]/foo/bar) [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn can_update_with_alt_reg() { // A patch to an alt reg can update. registry::alt_init(); Package::new("bar", "0.1.0").publish(); Package::new("bar", "0.1.0").alternative(true).publish(); Package::new("bar", "0.1.1").alternative(true).publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "0.1" [patch.crates-io] bar = { version = "=0.1.1", registry = "alternative" } "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `alternative` index [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.1.1 (registry `alternative`) [CHECKING] bar v0.1.1 (registry `alternative`) [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); Package::new("bar", "0.1.2").alternative(true).publish(); // Should remain locked. p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // This does nothing, due to `=` requirement. p.cargo("update bar") .with_stderr_data(str![[r#" [UPDATING] `alternative` index [UPDATING] `dummy-registry` index [LOCKING] 0 packages to latest compatible versions [NOTE] pass `--verbose` to see 1 unchanged dependencies behind latest "#]]) .run(); // Bump to 0.1.2. p.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "0.1" [patch.crates-io] bar = { version = "=0.1.2", registry = "alternative" } "#, ); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `alternative` index [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [UPDATING] bar v0.1.1 (registry `alternative`) -> v0.1.2 [DOWNLOADING] crates ... [DOWNLOADED] bar v0.1.2 (registry `alternative`) [CHECKING] bar v0.1.2 (registry `alternative`) [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn gitoxide_clones_shallow_old_git_patch() { perform_old_git_patch(true) } fn perform_old_git_patch(shallow: bool) { // Example where an old lockfile with an explicit branch="master" in Cargo.toml. Package::new("bar", "1.0.0").publish(); let (bar, bar_repo) = git::new_repo("bar", |p| { p.file("Cargo.toml", &basic_manifest("bar", "1.0.0")) .file("src/lib.rs", "") }); let bar_oid = bar_repo.head().unwrap().target().unwrap(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" [patch.crates-io] bar = {{ git = "{}", branch = "master" }} "#, bar.url() ), ) .file( "Cargo.lock", &format!( r#" # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "bar" version = "1.0.0" source = "git+{}#{}" [[package]] name = "foo" version = "0.1.0" dependencies = [ "bar", ] "#, bar.url(), bar_oid ), ) .file("src/lib.rs", "") .build(); bar.change_file("Cargo.toml", &basic_manifest("bar", "2.0.0")); git::add(&bar_repo); git::commit(&bar_repo); // This *should* keep the old lock. let mut cargo = p.cargo("tree"); if shallow { cargo .arg("-Zgitoxide=fetch") .arg("-Zgit=shallow-deps") .masquerade_as_nightly_cargo(&[ "unstable features must be available for -Z gitoxide and -Z git", ]); } cargo // .env("CARGO_LOG", "trace") .with_stderr_data( "\ [UPDATING] git repository `[ROOTURL]/bar` [LOCKING] 1 package to latest compatible version [ADDING] bar v1.0.0 ([ROOTURL]/bar?branch=master#[..]) ", ) // .with_status(1) .with_stdout_data(format!( "\ foo v0.1.0 ([ROOT]/foo) └── bar v1.0.0 ([ROOTURL]/bar?branch=master#{}) ", &bar_oid.to_string()[..8] )) .run(); } #[cargo_test] fn old_git_patch() { perform_old_git_patch(false) } // From https://github.com/rust-lang/cargo/issues/7463 #[cargo_test] fn patch_eq_conflict_panic() { Package::new("bar", "0.1.0").publish(); Package::new("bar", "0.1.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "=0.1.0" [dev-dependencies] bar = "=0.1.1" [patch.crates-io] bar = {path="bar"} "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1")) .file("bar/src/lib.rs", "") .build(); p.cargo("generate-lockfile") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for `bar`. ... required by package `foo v0.1.0 ([ROOT]/foo)` versions that meet the requirements `=0.1.1` are: 0.1.1 all possible versions conflict with previously selected packages. previously selected package `bar v0.1.0` ... which satisfies dependency `bar = "=0.1.0"` of package `foo v0.1.0 ([ROOT]/foo)` failed to select a version for `bar` which could resolve this conflict "#]]) .run(); } // From https://github.com/rust-lang/cargo/issues/11336 #[cargo_test] fn mismatched_version2() { Package::new("qux", "0.1.0-beta.1").publish(); Package::new("qux", "0.1.0-beta.2").publish(); Package::new("bar", "0.1.0") .dep("qux", "=0.1.0-beta.1") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "0.1.0" qux = "0.1.0-beta.2" [patch.crates-io] qux = { path = "qux" } "#, ) .file("src/lib.rs", "") .file( "qux/Cargo.toml", r#" [package] name = "qux" version = "0.1.0-beta.1" edition = "2015" "#, ) .file("qux/src/lib.rs", "") .build(); p.cargo("generate-lockfile") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for `qux`. ... required by package `bar v0.1.0` ... which satisfies dependency `bar = "^0.1.0"` of package `foo v0.1.0 ([ROOT]/foo)` versions that meet the requirements `=0.1.0-beta.1` are: 0.1.0-beta.1 all possible versions conflict with previously selected packages. previously selected package `qux v0.1.0-beta.2` ... which satisfies dependency `qux = "^0.1.0-beta.2"` of package `foo v0.1.0 ([ROOT]/foo)` failed to select a version for `qux` which could resolve this conflict "#]]) .run(); } #[cargo_test] fn mismatched_version_with_prerelease() { Package::new("prerelease-deps", "0.0.1").publish(); // A patch to a location that has an prerelease version let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] prerelease-deps = "0.1.0" [patch.crates-io] prerelease-deps = { path = "./prerelease-deps" } "#, ) .file("src/lib.rs", "") .file( "prerelease-deps/Cargo.toml", &basic_manifest("prerelease-deps", "0.1.1-pre1"), ) .file("prerelease-deps/src/lib.rs", "") .build(); p.cargo("generate-lockfile") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for the requirement `prerelease-deps = "^0.1.0"` candidate versions found which didn't match: 0.1.1-pre1, 0.0.1 location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.1.0 ([ROOT]/foo)` if you are looking for the prerelease package it needs to be specified explicitly prerelease-deps = { version = "0.1.1-pre1" } perhaps a crate was updated and forgotten to be re-vendored? "#]]) .run(); } #[cargo_test] fn from_config_empty() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ) .file( ".cargo/config.toml", r#" [patch.''] bar = { path = 'bar' } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1")) .file("bar/src/lib.rs", r#""#) .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] [patch] entry `` should be a URL or registry name Caused by: invalid url ``: relative URL without a base "#]]) .run(); } #[cargo_test] fn from_manifest_empty() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" [patch.''] bar = { path = 'bar' } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1")) .file("bar/src/lib.rs", r#""#) .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: [patch] entry `` should be a URL or registry name Caused by: invalid url ``: relative URL without a base "#]]) .run(); } #[cargo_test] fn patched_reexport_stays_locked() { // Patch example where you emulate a semver-incompatible patch via a re-export. // Testing an issue where the lockfile does not stay locked after a new version is published. Package::new("bar", "1.0.0").publish(); Package::new("bar", "2.0.0").publish(); Package::new("bar", "3.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [workspace] [package] name = "foo" [dependencies] bar1 = {package="bar", version="1.0.0"} bar2 = {package="bar", version="2.0.0"} [patch.crates-io] bar1 = { package = "bar", path = "bar-1-as-3" } bar2 = { package = "bar", path = "bar-2-as-3" } "#, ) .file("src/lib.rs", "") .file( "bar-1-as-3/Cargo.toml", r#" [package] name = "bar" version = "1.0.999" [dependencies] bar = "3.0.0" "#, ) .file("bar-1-as-3/src/lib.rs", "") .file( "bar-2-as-3/Cargo.toml", r#" [package] name = "bar" version = "2.0.999" [dependencies] bar = "3.0.0" "#, ) .file("bar-2-as-3/src/lib.rs", "") .build(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.0.0 ([ROOT]/foo) ├── bar v1.0.999 ([ROOT]/foo/bar-1-as-3) │ └── bar v3.0.0 └── bar v2.0.999 ([ROOT]/foo/bar-2-as-3) └── bar v3.0.0 "#]]) .run(); std::fs::copy( p.root().join("Cargo.lock"), p.root().join("Cargo.lock.orig"), ) .unwrap(); Package::new("bar", "3.0.1").publish(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.0.0 ([ROOT]/foo) ├── bar v1.0.999 ([ROOT]/foo/bar-1-as-3) │ └── bar v3.0.0 └── bar v2.0.999 ([ROOT]/foo/bar-2-as-3) └── bar v3.0.0 "#]]) .run(); assert_eq!(p.read_file("Cargo.lock"), p.read_file("Cargo.lock.orig")); } #[cargo_test] fn patch_in_real_with_base() { let bar = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) .file("src/lib.rs", "pub fn hello() {}") .build(); Package::new("bar", "0.5.0").publish(); let p = project() .file( ".cargo/config.toml", &format!( r#" [path-bases] test = '{}' "#, bar.root().parent().unwrap().display() ), ) .file( "Cargo.toml", r#" cargo-features = ["path-bases"] [package] name = "foo" version = "0.5.0" authors = ["wycats@example.com"] edition = "2018" [dependencies] bar = "0.5.0" [patch.crates-io] bar = { base = 'test', path = 'bar' } "#, ) .file("src/lib.rs", "use bar::hello as _;") .build(); p.cargo("tree") .masquerade_as_nightly_cargo(&["path-bases"]) .with_stdout_data(str![[r#" foo v0.5.0 ([ROOT]/foo) └── bar v0.5.0 ([ROOT]/bar) "#]]) .run(); } #[cargo_test] fn patch_in_virtual_with_base() { let bar = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) .file("src/lib.rs", "pub fn hello() {}") .build(); Package::new("bar", "0.5.0").publish(); let p = project() .file( ".cargo/config.toml", &format!( r#" [path-bases] test = '{}' "#, bar.root().parent().unwrap().display() ), ) .file( "Cargo.toml", r#" cargo-features = ["path-bases"] [workspace] members = ["foo"] [patch.crates-io] bar = { base = 'test', path = 'bar' } "#, ) .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.5.0" authors = ["wycats@example.com"] edition = "2018" [dependencies] bar = "0.5.0" "#, ) .file("foo/src/lib.rs", "use bar::hello as _;") .build(); p.cargo("tree") .masquerade_as_nightly_cargo(&["path-bases"]) .with_stdout_data(str![[r#" foo v0.5.0 ([ROOT]/foo/foo) └── bar v0.5.0 ([ROOT]/bar) "#]]) .run(); } cargo-0.91.0/tests/testsuite/path.rs000064400000000000000000001342301046102023000155060ustar 00000000000000//! Tests for `path` dependencies. use std::fs; use crate::prelude::*; use cargo_test_support::paths; use cargo_test_support::registry::Package; use cargo_test_support::str; use cargo_test_support::{basic_lib_manifest, basic_manifest, main_file, project}; use cargo_test_support::{sleep_ms, t}; #[cargo_test] // I have no idea why this is failing spuriously on Windows; // for more info, see #3466. #[cfg(not(windows))] fn cargo_compile_with_nested_deps_shorthand() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.bar] version = "0.5.0" path = "bar" "#, ) .file("src/main.rs", &main_file(r#""{}", bar::gimme()"#, &["bar"])) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.baz] version = "0.5.0" path = "baz" [lib] name = "bar" "#, ) .file( "bar/src/bar.rs", r#" extern crate baz; pub fn gimme() -> String { baz::gimme() } "#, ) .file("bar/baz/Cargo.toml", &basic_lib_manifest("baz")) .file( "bar/baz/src/baz.rs", r#" pub fn gimme() -> String { "test passed".to_string() } "#, ) .build(); p.cargo("build") .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] baz v0.5.0 ([ROOT]/foo/bar/baz) [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert!(p.bin("foo").is_file()); p.process(&p.bin("foo")) .with_stdout_data(str![[r#" test passed "#]]) .run(); println!("cleaning"); p.cargo("clean -v") .with_stderr_data(str![[r#" [REMOVING] [ROOT]/foo/target [REMOVED] [FILE_NUM] files, [FILE_SIZE]B total "#]]) .run(); println!("building baz"); p.cargo("build -p baz") .with_stderr_data(str![[r#" [COMPILING] baz v0.5.0 ([ROOT]/foo/bar/baz) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); println!("building foo"); p.cargo("build -p foo") .with_stderr_data(str![[r#" [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn cargo_compile_with_root_dev_deps() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dev-dependencies.bar] version = "0.5.0" path = "../bar" [[bin]] name = "foo" "#, ) .file("src/main.rs", &main_file(r#""{}", bar::gimme()"#, &["bar"])) .build(); let _p2 = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) .file( "src/lib.rs", r#" pub fn gimme() -> &'static str { "zoidberg" } "#, ) .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [CHECKING] foo v0.5.0 ([ROOT]/foo) error[E0463]: can't find crate for `bar` ... "#]]) .run(); } #[cargo_test] fn cargo_compile_with_root_dev_deps_with_testing() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dev-dependencies.bar] version = "0.5.0" path = "../bar" [[bin]] name = "foo" "#, ) .file("src/main.rs", &main_file(r#""{}", bar::gimme()"#, &["bar"])) .build(); let _p2 = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) .file( "src/lib.rs", r#" pub fn gimme() -> &'static str { "zoidberg" } "#, ) .build(); p.cargo("test") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.5.0 ([ROOT]/bar) [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/main.rs (target/debug/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn cargo_compile_with_transitive_dev_deps() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.bar] version = "0.5.0" path = "bar" "#, ) .file("src/main.rs", &main_file(r#""{}", bar::gimme()"#, &["bar"])) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dev-dependencies.baz] git = "git://example.com/path/to/nowhere" [lib] name = "bar" "#, ) .file( "bar/src/bar.rs", r#" pub fn gimme() -> &'static str { "zoidberg" } "#, ) .build(); p.cargo("build") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert!(p.bin("foo").is_file()); p.process(&p.bin("foo")) .with_stdout_data(str![[r#" zoidberg "#]]) .run(); } #[cargo_test] fn no_rebuild_dependency() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.bar] path = "bar" "#, ) .file("src/main.rs", "extern crate bar; fn main() { bar::bar() }") .file("bar/Cargo.toml", &basic_lib_manifest("bar")) .file("bar/src/bar.rs", "pub fn bar() {}") .build(); // First time around we should compile both foo and bar p.cargo("check") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.5.0 ([ROOT]/foo/bar) [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); sleep_ms(1000); p.change_file( "src/main.rs", r#" extern crate bar; fn main() { bar::bar(); } "#, ); // Don't compile bar, but do recompile foo. p.cargo("check") .with_stderr_data(str![[r#" [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn deep_dependencies_trigger_rebuild() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.bar] path = "bar" "#, ) .file("src/main.rs", "extern crate bar; fn main() { bar::bar() }") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [lib] name = "bar" [dependencies.baz] path = "../baz" "#, ) .file( "bar/src/bar.rs", "extern crate baz; pub fn bar() { baz::baz() }", ) .file("baz/Cargo.toml", &basic_lib_manifest("baz")) .file("baz/src/baz.rs", "pub fn baz() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [CHECKING] baz v0.5.0 ([ROOT]/foo/baz) [CHECKING] bar v0.5.0 ([ROOT]/foo/bar) [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Make sure an update to baz triggers a rebuild of bar // // We base recompilation off mtime, so sleep for at least a second to ensure // that this write will change the mtime. sleep_ms(1000); p.change_file("baz/src/baz.rs", r#"pub fn baz() { println!("hello!"); }"#); sleep_ms(1000); p.cargo("check") .with_stderr_data(str![[r#" [CHECKING] baz v0.5.0 ([ROOT]/foo/baz) [CHECKING] bar v0.5.0 ([ROOT]/foo/bar) [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Make sure an update to bar doesn't trigger baz sleep_ms(1000); p.change_file( "bar/src/bar.rs", r#" extern crate baz; pub fn bar() { println!("hello!"); baz::baz(); } "#, ); sleep_ms(1000); p.cargo("check") .with_stderr_data(str![[r#" [CHECKING] bar v0.5.0 ([ROOT]/foo/bar) [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn no_rebuild_two_deps() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.bar] path = "bar" [dependencies.baz] path = "baz" "#, ) .file("src/main.rs", "extern crate bar; fn main() { bar::bar() }") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [lib] name = "bar" [dependencies.baz] path = "../baz" "#, ) .file("bar/src/bar.rs", "pub fn bar() {}") .file("baz/Cargo.toml", &basic_lib_manifest("baz")) .file("baz/src/baz.rs", "pub fn baz() {}") .build(); p.cargo("build") .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] baz v0.5.0 ([ROOT]/foo/baz) [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert!(p.bin("foo").is_file()); p.cargo("build") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert!(p.bin("foo").is_file()); } #[cargo_test] fn nested_deps_recompile() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.bar] version = "0.5.0" path = "src/bar" "#, ) .file("src/main.rs", &main_file(r#""{}", bar::gimme()"#, &["bar"])) .file("src/bar/Cargo.toml", &basic_lib_manifest("bar")) .file("src/bar/src/bar.rs", "pub fn gimme() -> i32 { 92 }") .build(); p.cargo("check") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.5.0 ([ROOT]/foo/src/bar) [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); sleep_ms(1000); p.change_file("src/main.rs", r#"fn main() {}"#); // This shouldn't recompile `bar` p.cargo("check") .with_stderr_data(str![[r#" [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn error_message_for_missing_manifest() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.bar] path = "src/bar" "#, ) .file("src/lib.rs", "") .file("src/bar/not-a-manifest", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to get `bar` as a dependency of package `foo v0.5.0 ([ROOT]/foo)` Caused by: failed to load source for dependency `bar` Caused by: Unable to update [ROOT]/foo/src/bar Caused by: failed to read `[ROOT]/foo/src/bar/Cargo.toml` Caused by: [NOT_FOUND] "#]]) .run(); } #[cargo_test] fn path_bases_not_stable() { let bar = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) .file("src/lib.rs", "") .build(); let p = project() .file( ".cargo/config.toml", &format!( r#" [path-bases] test = '{}' "#, bar.root().parent().unwrap().display() ), ) .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" authors = ["wycats@example.com"] [dependencies] bar = { base = 'test', path = 'bar' } "#, ) .file("src/lib.rs", "") .build(); p.cargo("build") .with_status(101) .with_stderr_data( "\ [ERROR] failed to parse manifest at `[..]/foo/Cargo.toml` Caused by: resolving path dependency bar Caused by: feature `path-bases` is required The package requires the Cargo feature called `path-bases`, but that feature is not stabilized in this version of Cargo ([..]). Consider trying a newer version of Cargo (this may require the nightly release). See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#path-bases for more information about the status of this feature. ", ) .run(); } #[cargo_test] fn path_with_base() { let bar = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) .file("src/lib.rs", "") .build(); let p = project() .file( ".cargo/config.toml", &format!( r#" [path-bases] test = '{}' "#, bar.root().parent().unwrap().display() ), ) .file( "Cargo.toml", r#" cargo-features = ["path-bases"] [package] name = "foo" version = "0.5.0" authors = ["wycats@example.com"] [dependencies] bar = { base = 'test', path = 'bar' } "#, ) .file("src/lib.rs", "") .build(); p.cargo("build -v") .masquerade_as_nightly_cargo(&["path-bases"]) .run(); } #[cargo_test] fn current_dir_with_base() { let bar = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) .file("src/lib.rs", "") .build(); let p = project() .file( ".cargo/config.toml", &format!( r#" [path-bases] test = '{}' "#, bar.root().display() ), ) .file( "Cargo.toml", r#" cargo-features = ["path-bases"] [package] name = "foo" version = "0.5.0" authors = ["wycats@example.com"] [dependencies] bar = { base = 'test', path = '.' } "#, ) .file("src/lib.rs", "") .build(); p.cargo("build -v") .masquerade_as_nightly_cargo(&["path-bases"]) .run(); } #[cargo_test] fn parent_dir_with_base() { let bar = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) .file("src/lib.rs", "") .build(); let p = project() .file( ".cargo/config.toml", &format!( r#" [path-bases] test = '{}/subdir' "#, bar.root().display() ), ) .file( "Cargo.toml", r#" cargo-features = ["path-bases"] [package] name = "foo" version = "0.5.0" authors = ["wycats@example.com"] [dependencies] bar = { base = 'test', path = '..' } "#, ) .file("src/lib.rs", "") .build(); p.cargo("build -v") .masquerade_as_nightly_cargo(&["path-bases"]) .run(); } #[cargo_test] fn inherit_dependency_using_base() { let bar = project() .at("dep_with_base") .file("Cargo.toml", &basic_manifest("dep_with_base", "0.5.0")) .file("src/lib.rs", "") .build(); let p = project() .file( ".cargo/config.toml", &format!( r#" [path-bases] test = '{}' "#, bar.root().parent().unwrap().display() ), ) .file( "Cargo.toml", r#" [package] name = "parent" version = "0.1.0" authors = [] [workspace] members = ["child"] [workspace.dependencies] dep_with_base = { base = 'test', path = 'dep_with_base' } "#, ) .file("src/main.rs", "fn main() {}") .file( "child/Cargo.toml", r#" cargo-features = ["path-bases"] [package] name = "child" version = "0.1.0" authors = [] [dependencies] dep_with_base = { workspace = true } "#, ) .file("child/src/main.rs", "fn main() {}") .build(); p.cargo("build -v") .masquerade_as_nightly_cargo(&["path-bases"]) .run(); } #[cargo_test] fn path_with_relative_base() { project() .at("shared_proj/bar") .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) .file("src/lib.rs", "") .build(); let p = project() .file( "../.cargo/config.toml", r#" [path-bases] test = 'shared_proj' "#, ) .file( "Cargo.toml", r#" cargo-features = ["path-bases"] [package] name = "foo" version = "0.5.0" authors = ["wycats@example.com"] [dependencies] bar = { base = 'test', path = 'bar' } "#, ) .file("src/lib.rs", "") .build(); p.cargo("build -v") .masquerade_as_nightly_cargo(&["path-bases"]) .run(); } #[cargo_test] fn workspace_builtin_base() { project() .at("dep_with_base") .file("Cargo.toml", &basic_manifest("dep_with_base", "0.5.0")) .file("src/lib.rs", "") .build(); let p = project() .file( "Cargo.toml", r#" [package] name = "parent" version = "0.1.0" authors = [] [workspace] members = ["child"] "#, ) .file("src/main.rs", "fn main() {}") .file( "child/Cargo.toml", r#" cargo-features = ["path-bases"] [package] name = "child" version = "0.1.0" authors = [] [dependencies] dep_with_base = { base = 'workspace', path = '../dep_with_base' } "#, ) .file("child/src/main.rs", "fn main() {}") .build(); p.cargo("build -v") .masquerade_as_nightly_cargo(&["path-bases"]) .run(); } #[cargo_test] fn workspace_builtin_base_not_a_workspace() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["path-bases"] [package] name = "foo" version = "0.1.0" authors = [] [dependencies] bar = { base = 'workspace', path = 'bar' } "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build") .masquerade_as_nightly_cargo(&["path-bases"]) .with_status(101) .with_stderr_data( "\ [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: resolving path dependency bar Caused by: failed to find a workspace root ", ) .run(); } #[cargo_test] fn shadow_workspace_builtin_base() { let bar = project() .at("dep_with_base") .file("Cargo.toml", &basic_manifest("dep_with_base", "0.5.0")) .file("src/lib.rs", "") .build(); let p = project() .file( ".cargo/config.toml", &format!( r#" [path-bases] workspace = '{}/subdir/anotherdir' "#, bar.root().parent().unwrap().display() ), ) .file( "Cargo.toml", r#" [package] name = "parent" version = "0.1.0" authors = [] [workspace] members = ["child"] "#, ) .file("src/main.rs", "fn main() {}") .file( "child/Cargo.toml", r#" cargo-features = ["path-bases"] [package] name = "child" version = "0.1.0" authors = [] [dependencies] dep_with_base = { base = 'workspace', path = '../../dep_with_base' } "#, ) .file("child/src/main.rs", "fn main() {}") .build(); p.cargo("build -v") .masquerade_as_nightly_cargo(&["path-bases"]) .run(); } #[cargo_test] fn unknown_base() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["path-bases"] [package] name = "foo" version = "0.5.0" authors = ["wycats@example.com"] [dependencies] bar = { base = 'test', path = 'bar' } "#, ) .file("src/lib.rs", "") .build(); p.cargo("build") .masquerade_as_nightly_cargo(&["path-bases"]) .with_status(101) .with_stderr_data( "\ [ERROR] failed to parse manifest at `[..]/foo/Cargo.toml` Caused by: resolving path dependency bar Caused by: path base `test` is undefined. You must add an entry for `test` in the Cargo configuration [path-bases] table. ", ) .run(); } #[cargo_test] fn base_without_path() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["path-bases"] [package] name = "foo" version = "0.5.0" authors = ["wycats@example.com"] [dependencies] bar = { version = '1.0.0', base = 'test' } "#, ) .file("src/lib.rs", "") .build(); p.cargo("build") .masquerade_as_nightly_cargo(&["path-bases"]) .with_status(101) .with_stderr_data( "\ [ERROR] failed to parse manifest at `[..]/foo/Cargo.toml` Caused by: resolving path dependency bar Caused by: `base` can only be used with path dependencies ", ) .run(); } #[cargo_test] fn invalid_base() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["path-bases"] [package] name = "foo" version = "0.5.0" authors = ["wycats@example.com"] [dependencies] bar = { base = '^^not-valid^^', path = 'bar' } "#, ) .file("src/lib.rs", "") .build(); p.cargo("build") .masquerade_as_nightly_cargo(&["path-bases"]) .with_status(101) .with_stderr_data( "\ [ERROR] invalid character `^` in path base name: `^^not-valid^^`, the first character must be a Unicode XID start character (most letters or `_`) --> Cargo.toml:10:23 | 10 | bar = { base = '^^not-valid^^', path = 'bar' } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ", ) .run(); } #[cargo_test] fn invalid_path_with_base() { let p = project() .file( ".cargo/config.toml", r#" [path-bases] test = 'shared_proj' "#, ) .file( "Cargo.toml", r#" cargo-features = ["path-bases"] [package] name = "foo" version = "0.5.0" authors = ["wycats@example.com"] edition = "2015" [dependencies] bar = { base = 'test', path = '"' } "#, ) .file("src/lib.rs", "") .build(); p.cargo("build") .masquerade_as_nightly_cargo(&["path-bases"]) .with_status(101) .with_stderr_data( "\ [ERROR] failed to get `bar` as a dependency of package `foo v0.5.0 ([ROOT]/foo)` Caused by: failed to load source for dependency `bar` Caused by: Unable to update [ROOT]/foo/shared_proj/\" Caused by: failed to read `[ROOT]/foo/shared_proj/\"/Cargo.toml` Caused by: [NOT_FOUND] ", ) .run(); } #[cargo_test] fn self_dependency_using_base() { let p = project() .file( ".cargo/config.toml", r#" [path-bases] test = '.' "#, ) .file( "Cargo.toml", r#" cargo-features = ["path-bases"] [package] name = "foo" version = "0.1.0" authors = [] edition = "2015" [dependencies] foo = { base = 'test', path = '.' } "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build") .masquerade_as_nightly_cargo(&["path-bases"]) .with_status(101) .with_stderr_data( "\ [ERROR] cyclic package dependency: package `foo v0.1.0 ([ROOT]/foo)` depends on itself. Cycle: package `foo v0.1.0 ([ROOT]/foo)` ... which satisfies path dependency `foo` of package `foo v0.1.0 ([ROOT]/foo)` ", ) .run(); } #[cargo_test] fn override_relative() { let bar = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) .file("src/lib.rs", "") .build(); fs::create_dir(&paths::root().join(".cargo")).unwrap(); fs::write( &paths::root().join(".cargo/config.toml"), r#"paths = ["bar"]"#, ) .unwrap(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.bar] path = '{}' "#, bar.root().display() ), ) .file("src/lib.rs", "") .build(); p.cargo("check -v").run(); } #[cargo_test] fn override_self() { let bar = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) .file("src/lib.rs", "") .build(); let p = project(); let root = p.root(); let p = p .file( ".cargo/config.toml", &format!("paths = ['{}']", root.display()), ) .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.bar] path = '{}' "#, bar.root().display() ), ) .file("src/lib.rs", "") .file("src/main.rs", "fn main() {}") .build(); p.cargo("check").run(); } #[cargo_test] fn override_path_dep() { let bar = project() .at("bar") .file( "p1/Cargo.toml", r#" [package] name = "p1" version = "0.5.0" edition = "2015" authors = [] [dependencies.p2] path = "../p2" "#, ) .file("p1/src/lib.rs", "") .file("p2/Cargo.toml", &basic_manifest("p2", "0.5.0")) .file("p2/src/lib.rs", "") .build(); let p = project() .file( ".cargo/config.toml", &format!( "paths = ['{}', '{}']", bar.root().join("p1").display(), bar.root().join("p2").display() ), ) .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.p2] path = '{}' "#, bar.root().join("p2").display() ), ) .file("src/lib.rs", "") .build(); p.cargo("check -v").run(); } #[cargo_test] fn path_dep_build_cmd() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] [dependencies.bar] version = "0.5.0" path = "bar" "#, ) .file("src/main.rs", &main_file(r#""{}", bar::gimme()"#, &["bar"])) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = ["wycats@example.com"] build = "build.rs" [lib] name = "bar" path = "src/bar.rs" "#, ) .file( "bar/build.rs", r#" use std::fs; fn main() { fs::copy("src/bar.rs.in", "src/bar.rs").unwrap(); } "#, ) .file("bar/src/bar.rs.in", "pub fn gimme() -> i32 { 0 }") .build(); p.root().join("bar").move_into_the_past(); p.cargo("build") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert!(p.bin("foo").is_file()); p.process(&p.bin("foo")) .with_stdout_data(str![[r#" 0 "#]]) .run(); // Touching bar.rs.in should cause the `build` command to run again. p.change_file("bar/src/bar.rs.in", "pub fn gimme() -> i32 { 1 }"); p.cargo("build") .with_stderr_data(str![[r#" [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.process(&p.bin("foo")) .with_stdout_data(str![[r#" 1 "#]]) .run(); } #[cargo_test] fn dev_deps_no_rebuild_lib() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dev-dependencies.bar] path = "bar" [lib] name = "foo" doctest = false "#, ) .file( "src/lib.rs", r#" #[cfg(test)] #[allow(unused_extern_crates)] extern crate bar; #[cfg(not(test))] pub fn foo() { env!("FOO"); } "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.5.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .build(); p.cargo("build") .env("FOO", "bar") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("test") .with_stderr_data(str![[r#" [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn custom_target_no_rebuild() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies] a = { path = "a" } [workspace] members = ["a", "b"] "#, ) .file("src/lib.rs", "") .file("a/Cargo.toml", &basic_manifest("a", "0.5.0")) .file("a/src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.5.0" edition = "2015" authors = [] [dependencies] a = { path = "../a" } "#, ) .file("b/src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [CHECKING] a v0.5.0 ([ROOT]/foo/a) [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); t!(fs::rename( p.root().join("target"), p.root().join("target_moved") )); p.cargo("check --manifest-path=b/Cargo.toml") .env("CARGO_TARGET_DIR", "target_moved") .with_stderr_data(str![[r#" [CHECKING] b v0.5.0 ([ROOT]/foo/b) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn override_and_depend() { let p = project() .no_manifest() .file( "a/a1/Cargo.toml", r#" [package] name = "a1" version = "0.5.0" edition = "2015" authors = [] [dependencies] a2 = { path = "../a2" } "#, ) .file("a/a1/src/lib.rs", "") .file("a/a2/Cargo.toml", &basic_manifest("a2", "0.5.0")) .file("a/a2/src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.5.0" edition = "2015" authors = [] [dependencies] a1 = { path = "../a/a1" } a2 = { path = "../a/a2" } "#, ) .file("b/src/lib.rs", "") .file("b/.cargo/config.toml", r#"paths = ["../a"]"#) .build(); p.cargo("check") .cwd("b") .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [CHECKING] a2 v0.5.0 ([ROOT]/foo/a) [CHECKING] a1 v0.5.0 ([ROOT]/foo/a) [CHECKING] b v0.5.0 ([ROOT]/foo/b) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn missing_path_dependency() { let p = project() .file("Cargo.toml", &basic_manifest("a", "0.5.0")) .file("src/lib.rs", "") .file( ".cargo/config.toml", r#"paths = ["../whoa-this-does-not-exist"]"#, ) .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to update path override `[ROOT]/whoa-this-does-not-exist` (defined in `[ROOT]/foo/.cargo/config.toml`) Caused by: failed to read directory `[ROOT]/whoa-this-does-not-exist` Caused by: [NOT_FOUND] "#]]) .run(); } #[cargo_test] fn invalid_path_dep_in_workspace_with_lockfile() { Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "top" version = "0.5.0" edition = "2015" authors = [] [workspace] [dependencies] foo = { path = "foo" } "#, ) .file("src/lib.rs", "") .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies] bar = "*" "#, ) .file("foo/src/lib.rs", "") .build(); // Generate a lock file p.cargo("check").run(); // Change the dependency on `bar` to an invalid path p.change_file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies] bar = { path = "" } "#, ); // Make sure we get a nice error. In the past this actually stack // overflowed! p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no matching package found searched package name: `bar` perhaps you meant: foo location searched: [ROOT]/foo/foo required by package `foo v0.5.0 ([ROOT]/foo/foo)` "#]]) .run(); } #[cargo_test] fn workspace_produces_rlib() { let p = project() .file( "Cargo.toml", r#" [package] name = "top" version = "0.5.0" edition = "2015" authors = [] [workspace] [dependencies] foo = { path = "foo" } "#, ) .file("src/lib.rs", "") .file("foo/Cargo.toml", &basic_manifest("foo", "0.5.0")) .file("foo/src/lib.rs", "") .build(); p.cargo("build").run(); assert!(p.root().join("target/debug/libtop.rlib").is_file()); assert!(!p.root().join("target/debug/libfoo.rlib").is_file()); } #[cargo_test] fn deep_path_error() { // Test for an error loading a path deep in the dependency graph. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] a = {path="a"} "#, ) .file("src/lib.rs", "") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" [dependencies] b = {path="../b"} "#, ) .file("a/src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" edition = "2015" [dependencies] c = {path="../c"} "#, ) .file("b/src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to get `c` as a dependency of package `b v0.1.0 ([ROOT]/foo/b)` ... which satisfies path dependency `b` of package `a v0.1.0 ([ROOT]/foo/a)` ... which satisfies path dependency `a` of package `foo v0.1.0 ([ROOT]/foo)` Caused by: failed to load source for dependency `c` Caused by: Unable to update [ROOT]/foo/c Caused by: failed to read `[ROOT]/foo/c/Cargo.toml` Caused by: [NOT_FOUND] "#]]) .run(); } #[cargo_test] fn catch_tricky_cycle() { let p = project() .file( "Cargo.toml", r#" [package] name = "message" version = "0.1.0" edition = "2015" [dev-dependencies] test = { path = "test" } "#, ) .file("src/lib.rs", "") .file( "tangle/Cargo.toml", r#" [package] name = "tangle" version = "0.1.0" edition = "2015" [dependencies] message = { path = ".." } snapshot = { path = "../snapshot" } "#, ) .file("tangle/src/lib.rs", "") .file( "snapshot/Cargo.toml", r#" [package] name = "snapshot" version = "0.1.0" edition = "2015" [dependencies] ledger = { path = "../ledger" } "#, ) .file("snapshot/src/lib.rs", "") .file( "ledger/Cargo.toml", r#" [package] name = "ledger" version = "0.1.0" edition = "2015" [dependencies] tangle = { path = "../tangle" } "#, ) .file("ledger/src/lib.rs", "") .file( "test/Cargo.toml", r#" [package] name = "test" version = "0.1.0" edition = "2015" [dependencies] snapshot = { path = "../snapshot" } "#, ) .file("test/src/lib.rs", "") .build(); p.cargo("test") .with_stderr_data(str![[r#" [ERROR] cyclic package dependency: package `ledger v0.1.0 ([ROOT]/foo/ledger)` depends on itself. Cycle: package `ledger v0.1.0 ([ROOT]/foo/ledger)` ... "#]]) .with_status(101) .run(); } #[cargo_test] fn same_name_version_changed() { // Illustrates having two path packages with the same name, but different versions. // Verifies it works correctly when one of the versions is changed. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" edition = "2021" [dependencies] foo2 = { path = "foo2", package = "foo" } "#, ) .file("src/lib.rs", "") .file( "foo2/Cargo.toml", r#" [package] name = "foo" version = "2.0.0" edition = "2021" "#, ) .file("foo2/src/lib.rs", "") .build(); p.cargo("tree") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version "#]]) .with_stdout_data(str![[r#" foo v1.0.0 ([ROOT]/foo) └── foo v2.0.0 ([ROOT]/foo/foo2) "#]]) .run(); p.change_file( "foo2/Cargo.toml", r#" [package] name = "foo" version = "2.0.1" edition = "2021" "#, ); p.cargo("tree") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [ADDING] foo v2.0.1 ([ROOT]/foo/foo2) "#]]) .with_stdout_data(str![[r#" foo v1.0.0 ([ROOT]/foo) └── foo v2.0.1 ([ROOT]/foo/foo2) "#]]) .run(); } cargo-0.91.0/tests/testsuite/paths.rs000064400000000000000000000157751046102023000157050ustar 00000000000000//! Tests for `paths` overrides. use crate::prelude::*; use cargo_test_support::registry::Package; use cargo_test_support::str; use cargo_test_support::{basic_manifest, project}; #[cargo_test] fn broken_path_override_warns() { Package::new("bar", "0.1.0").publish(); Package::new("bar", "0.2.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] a = { path = "a1" } "#, ) .file("src/lib.rs", "") .file( "a1/Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1" "#, ) .file("a1/src/lib.rs", "") .file( "a2/Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.2" "#, ) .file("a2/src/lib.rs", "") .file(".cargo/config.toml", r#"paths = ["a2"]"#) .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [WARNING] path override for crate `a` has altered the original list of dependencies; the dependency on `bar` was either added or modified to not match the previously resolved version This is currently allowed but is known to produce buggy behavior with spurious recompiles and changes to the crate graph. Path overrides unfortunately were never intended to support this feature, so for now this message is just a warning. In the future, however, this message will become a hard error. To change the dependency graph via an override it's recommended to use the `[patch]` feature of Cargo instead of the path override feature. This is documented online at the url below for more information. https://doc.rust-lang.org/cargo/reference/overriding-dependencies.html [DOWNLOADING] crates ... [DOWNLOADED] bar v0.2.0 (registry `dummy-registry`) [CHECKING] bar v0.2.0 [CHECKING] a v0.0.1 ([ROOT]/foo/a2) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn override_to_path_dep() { Package::new("bar", "0.1.0").dep("baz", "0.1").publish(); Package::new("baz", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" authors = [] [dependencies] baz = { path = "baz" } "#, ) .file("bar/src/lib.rs", "") .file("bar/baz/Cargo.toml", &basic_manifest("baz", "0.0.1")) .file("bar/baz/src/lib.rs", "") .file(".cargo/config.toml", r#"paths = ["bar"]"#) .build(); p.cargo("check").run(); } #[cargo_test] fn paths_ok_with_optional() { Package::new("baz", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = { path = "bar" } "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] [dependencies] baz = { version = "0.1", optional = true } "#, ) .file("bar/src/lib.rs", "") .file( "bar2/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] [dependencies] baz = { version = "0.1", optional = true } "#, ) .file("bar2/src/lib.rs", "") .file(".cargo/config.toml", r#"paths = ["bar2"]"#) .build(); p.cargo("check") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.1.0 ([ROOT]/foo/bar2) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn paths_add_optional_bad() { Package::new("baz", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2021" authors = [] [dependencies] bar = { path = "bar" } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "") .file( "bar2/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2021" authors = [] [dependencies] baz = { version = "0.1", optional = true } "#, ) .file("bar2/src/lib.rs", "") .file(".cargo/config.toml", r#"paths = ["bar2"]"#) .build(); p.cargo("check") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [WARNING] path override for crate `bar` has altered the original list of dependencies; the dependency on `baz` was either added or modified to not match the previously resolved version This is currently allowed but is known to produce buggy behavior with spurious recompiles and changes to the crate graph. Path overrides unfortunately were never intended to support this feature, so for now this message is just a warning. In the future, however, this message will become a hard error. To change the dependency graph via an override it's recommended to use the `[patch]` feature of Cargo instead of the path override feature. This is documented online at the url below for more information. https://doc.rust-lang.org/cargo/reference/overriding-dependencies.html [CHECKING] bar v0.1.0 ([ROOT]/foo/bar2) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } cargo-0.91.0/tests/testsuite/pgo.rs000064400000000000000000000062721046102023000153430ustar 00000000000000//! Test if PGO works. use std::path::PathBuf; use std::process::Command; use crate::prelude::*; use cargo_test_support::project; use cargo_test_support::str; fn llvm_profdata() -> Option { let output = Command::new("rustc") .arg("--print=target-libdir") .output() .expect("rustc to run"); assert!(output.status.success()); let mut libdir = PathBuf::from(String::from_utf8(output.stdout).unwrap()); assert!(libdir.pop()); let mut bin = libdir.join("bin").join("llvm-profdata"); bin.exists().then(|| bin.clone()).or_else(|| { bin.set_extension("exe"); bin.exists().then_some(bin) }) } // Rustc build may be without profiling support. // Mark it as nightly so it won't run on rust-lang/rust CI. #[cfg_attr( target_os = "linux", cargo_test(nightly, reason = "rust-lang/rust#133675") )] // macOS may emit different LLVM PGO warnings. // Windows LLVM has different requirements. #[cfg_attr(not(target_os = "linux"), cargo_test, ignore = "linux only")] fn pgo_works() { let Some(llvm_profdata) = llvm_profdata() else { return; }; let p = project() .file( "Cargo.toml", r#" [package] name = "foo" edition = "2021" "#, ) .file( "src/main.rs", r#" fn fibonacci(n: u64) -> u64 { match n { 0 => 0, 1 => 1, _ => fibonacci(n - 1) + fibonacci(n - 2), } } fn main() { for i in [15, 20, 25] { let _ = fibonacci(i); } } "#, ) .build(); let target_dir = p.build_dir(); let release_bin = target_dir.join("release").join("foo"); let pgo_data_dir = target_dir.join("pgo-data"); let profdata_path = target_dir.join("merged.profdata"); // Build the instrumented binary p.cargo("build --release") .env( "RUSTFLAGS", format!("-Cprofile-generate={}", pgo_data_dir.display()), ) .run(); // Run the instrumented binary cargo_test_support::execs() .with_process_builder(cargo_test_support::process(release_bin)) .run(); cargo_test_support::process(llvm_profdata) .arg("merge") .arg("-o") .arg(&profdata_path) .arg(pgo_data_dir) .status() .unwrap(); // Use merged profdata during optimization. // // -Cllvm-args=-pgo-warn-missing-function is essential. // If there are LLVM warnings, there might be something wrong. p.cargo("build --release -v") .env( "RUSTFLAGS", format!( "-Cprofile-use={} -Cllvm-args=-pgo-warn-missing-function", profdata_path.display() ), ) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.0 ([ROOT]/foo) [RUNNING] `rustc [..]-Cprofile-use=[ROOT]/foo/target/merged.profdata -Cllvm-args=-pgo-warn-missing-function` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]]) .run(); } cargo-0.91.0/tests/testsuite/pkgid.rs000064400000000000000000000255371046102023000156610ustar 00000000000000//! Tests for the `cargo pkgid` command. use std::path::PathBuf; use crate::prelude::*; use cargo_test_support::basic_bin_manifest; use cargo_test_support::basic_lib_manifest; use cargo_test_support::compare::assert_e2e; use cargo_test_support::git; use cargo_test_support::project; use cargo_test_support::registry::Package; use cargo_test_support::str; #[cargo_test] fn local() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar"] [package] name = "foo" version = "0.1.0" edition = "2018" "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2018" "#, ) .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("generate-lockfile").run(); p.cargo("pkgid foo") .with_stdout_data(str![[r#" path+[ROOTURL]/foo#0.1.0 "#]]) .run(); // Bad file URL. p.cargo("pkgid ./Cargo.toml") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid package ID specification: `./Cargo.toml` Caused by: package ID specification `./Cargo.toml` looks like a file path, maybe try [ROOTURL]/foo/Cargo.toml "#]]) .run(); // Bad file URL with similar name. p.cargo("pkgid './bar'") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid package ID specification: `./bar` [HELP] a package with a similar name exists: `bar` Caused by: package ID specification `./bar` looks like a file path, maybe try [ROOTURL]/foo/bar "#]]) .run(); } #[cargo_test] fn registry() { Package::new("crates-io", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [dependencies] crates-io = "0.1.0" "#, ) .file("src/main.rs", "fn main() {}") .file("cratesio", "") .build(); p.cargo("generate-lockfile").run(); p.cargo("pkgid crates-io") .with_stdout_data(str![[r#" registry+https://github.com/rust-lang/crates.io-index#crates-io@0.1.0 "#]]) .run(); // Bad URL. p.cargo("pkgid https://example.com/crates-io") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package ID specification `https://example.com/crates-io` did not match any packages [HELP] there are similar package ID specifications: crates-io@0.1.0 "#]]) .run(); // Bad name. p.cargo("pkgid crates_io") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package ID specification `crates_io` did not match any packages [HELP] a package with a similar name exists: `crates-io` "#]]) .run(); } #[cargo_test] fn multiple_versions() { Package::new("two-ver", "0.1.0").publish(); Package::new("two-ver", "0.2.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [dependencies] two-ver = "0.1.0" two-ver2 = { package = "two-ver", version = "0.2.0" } "#, ) .file("src/lib.rs", "") .file("cratesio", "") .build(); p.cargo("generate-lockfile").run(); p.cargo("pkgid two-ver:0.2.0") .with_stdout_data(str![[r#" registry+https://github.com/rust-lang/crates.io-index#two-ver@0.2.0 "#]]) .run(); // Incomplete version. p.cargo("pkgid two-ver@0") .with_status(101) .with_stderr_data(str![[r#" [ERROR] There are multiple `two-ver` packages in your project, and the specification `two-ver@0` is ambiguous. Please re-run this command with one of the following specifications: two-ver@0.1.0 two-ver@0.2.0 "#]]) .run(); // Incomplete version. p.cargo("pkgid two-ver@0.2") .with_stdout_data(str![[r#" registry+https://github.com/rust-lang/crates.io-index#two-ver@0.2.0 "#]]) .run(); // Ambiguous. p.cargo("pkgid two-ver") .with_status(101) .with_stderr_data(str![[r#" [ERROR] There are multiple `two-ver` packages in your project, and the specification `two-ver` is ambiguous. Please re-run this command with one of the following specifications: two-ver@0.1.0 two-ver@0.2.0 "#]]) .run(); // Bad version. p.cargo("pkgid two-ver:0.3.0") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package ID specification `two-ver@0.3.0` did not match any packages [HELP] there are similar package ID specifications: two-ver@0.1.0 two-ver@0.2.0 "#]]) .run(); } // Not for `cargo pkgid` but the `PackageIdSpec` format #[cargo_test] fn multiple_git_same_version() { // Test what happens if different packages refer to the same git repo with // different refs, and the package version is the same. let (xyz_project, xyz_repo) = git::new_repo("xyz", |project| { project .file("Cargo.toml", &basic_lib_manifest("xyz")) .file("src/lib.rs", "fn example() {}") }); let rev1 = xyz_repo.revparse_single("HEAD").unwrap().id(); xyz_project.change_file("src/lib.rs", "pub fn example() {}"); git::add(&xyz_repo); let rev2 = git::commit(&xyz_repo); // Both rev1 and rev2 point to version 0.1.0. let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [dependencies] bar = {{ path = "bar" }} xyz = {{ git = "{}", rev = "{}" }} "#, xyz_project.url(), rev1 ), ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", &format!( r#" [package] name = "bar" version = "0.1.0" [dependencies] xyz = {{ git = "{}", rev = "{}" }} "#, xyz_project.url(), rev2 ), ) .file("bar/src/lib.rs", "") .build(); p.cargo("check").run(); p.cargo("tree") .with_stdout_data(&format!( "\ foo v0.1.0 ([ROOT]/foo) ├── bar v0.1.0 ([ROOT]/foo/bar) │ └── xyz v0.5.0 ([ROOTURL]/xyz?rev={}#{}) └── xyz v0.5.0 ([ROOTURL]/xyz?rev={}#{}) ", rev2, &rev2.to_string()[..8], rev1, &rev1.to_string()[..8] )) .run(); // FIXME: This fails since xyz is ambiguous, but the // possible pkgids are also ambiguous. p.cargo("pkgid xyz") .with_status(101) .with_stderr_data(str![[r#" [ERROR] There are multiple `xyz` packages in your project, and the specification `xyz` is ambiguous. Please re-run this command with one of the following specifications: git+[ROOTURL]/xyz?rev=[..]#0.5.0 git+[ROOTURL]/xyz?rev=[..]#0.5.0 "#]]) .run(); // TODO, what should the `-p` value be here? //p.cargo("update -p") } // Keep Package ID format in sync among // // * Package ID specifications // * machine-readable message via `--message-format=json` // * `cargo metadata` output // * SBOMs #[cargo_test] fn pkgid_json_message_metadata_consistency() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", "fn main() {}") .file("build.rs", "fn main() {}") .build(); p.cargo("generate-lockfile").run(); let output = p.cargo("pkgid").arg("foo").run(); let pkgid = String::from_utf8(output.stdout).unwrap(); let pkgid = pkgid.trim(); assert_e2e().eq(pkgid, str!["path+[ROOTURL]/foo#0.5.0"]); p.cargo("check --message-format=json") .with_stdout_data( str![[r#" [ { "manifest_path": "[ROOT]/foo/Cargo.toml", "package_id": "path+[ROOTURL]/foo#0.5.0", "reason": "compiler-artifact", "...": "{...}" }, { "package_id": "path+[ROOTURL]/foo#0.5.0", "reason": "build-script-executed", "...": "{...}" }, { "manifest_path": "[ROOT]/foo/Cargo.toml", "package_id": "path+[ROOTURL]/foo#0.5.0", "reason": "compiler-artifact", "...": "{...}" }, { "reason": "build-finished", "success": true } ] "#]] .is_json() .against_jsonlines(), ) .run(); p.cargo("metadata") .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [ "wycats@example.com" ], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo#0.5.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/Cargo.toml", "metadata": null, "name": "foo", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": "{...}", "version": "0.5.0" } ], "resolve": { "nodes": [ { "dependencies": [], "deps": [], "features": [], "id": "path+[ROOTURL]/foo#0.5.0" } ], "root": "path+[ROOTURL]/foo#0.5.0" }, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo#0.5.0" ], "workspace_members": [ "path+[ROOTURL]/foo#0.5.0" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); p.cargo("build -Zsbom") .env("CARGO_BUILD_SBOM", "true") .masquerade_as_nightly_cargo(&["sbom"]) .run(); let path = { let mut path = p.bin("foo").into_os_string(); path.push(".cargo-sbom.json"); PathBuf::from(path) }; assert!(path.is_file()); let output = std::fs::read_to_string(&path).unwrap(); assert_e2e().eq( output, snapbox::str![[r#" { "crates": [ { "dependencies": [ { "index": 1, "kind": "build" } ], "features": [], "id": "path+[ROOTURL]/foo#0.5.0", "kind": [ "bin" ] }, { "dependencies": [], "features": [], "id": "path+[ROOTURL]/foo#0.5.0", "kind": [ "custom-build" ] } ], "root": 0, "rustc": "{...}", "target": "[HOST_TARGET]", "version": 1 } "#]] .is_json(), ); } cargo-0.91.0/tests/testsuite/precise_pre_release.rs000064400000000000000000000145521046102023000205560ustar 00000000000000//! Tests for selecting pre-release versions with `update --precise`. use crate::prelude::*; use cargo_test_support::{project, str}; #[cargo_test] fn requires_nightly_cargo() { cargo_test_support::registry::init(); for version in ["0.1.1", "0.1.2-pre.0"] { cargo_test_support::registry::Package::new("my-dependency", version).publish(); } let p = project() .file( "Cargo.toml", r#" [package] name = "package" [dependencies] my-dependency = "0.1.1" "#, ) .file("src/lib.rs", "") .build(); p.cargo("update my-dependency --precise 0.1.2-pre.0") .with_status(101) // This error is suffering from #12579 but still demonstrates that updating to // a pre-release does not work on stable .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for the requirement `my-dependency = "^0.1.1"` candidate versions found which didn't match: 0.1.2-pre.0 location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `package v0.0.0 ([ROOT]/foo)` if you are looking for the prerelease package it needs to be specified explicitly my-dependency = { version = "0.1.2-pre.0" } perhaps a crate was updated and forgotten to be re-vendored? "#]]) .run(); } #[cargo_test] fn update_pre_release() { cargo_test_support::registry::init(); for version in ["0.1.1", "0.1.2-pre.0"] { cargo_test_support::registry::Package::new("my-dependency", version).publish(); } let p = project() .file( "Cargo.toml", r#" [package] name = "package" [dependencies] my-dependency = "0.1.1" "#, ) .file("src/lib.rs", "") .build(); p.cargo("update my-dependency --precise 0.1.2-pre.0 -Zunstable-options") .masquerade_as_nightly_cargo(&["precise-pre-release"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [UPDATING] my-dependency v0.1.1 -> v0.1.2-pre.0 "#]]) .run(); let lockfile = p.read_lockfile(); assert!(lockfile.contains("\nname = \"my-dependency\"\nversion = \"0.1.2-pre.0\"")); } #[cargo_test] fn pre_release_should_unmatched() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("my-dependency", "0.1.2").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "package" [dependencies] my-dependency = "0.1.2" "#, ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); let lockfile = p.read_lockfile(); assert!(lockfile.contains("\nname = \"my-dependency\"\nversion = \"0.1.2\"")); // 0.1.2-pre.0 < 0.1.2 so it doesn't match cargo_test_support::registry::Package::new("my-dependency", "0.1.2-pre.0").publish(); p.cargo("update -p my-dependency --precise 0.1.2-pre.0 -Zunstable-options") .masquerade_as_nightly_cargo(&["precise-pre-release"]) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for the requirement `my-dependency = "^0.1.2"` candidate versions found which didn't match: 0.1.2-pre.0 location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `package v0.0.0 ([ROOT]/foo)` if you are looking for the prerelease package it needs to be specified explicitly my-dependency = { version = "0.1.2-pre.0" } perhaps a crate was updated and forgotten to be re-vendored? "#]]) .run(); cargo_test_support::registry::Package::new("my-dependency", "0.2.0-0").publish(); // 0.2.0-0 is the upper bound we exclude, so it doesn't match p.cargo("update -p my-dependency --precise 0.2.0-0 -Zunstable-options") .masquerade_as_nightly_cargo(&["precise-pre-release"]) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for the requirement `my-dependency = "^0.1.2"` candidate versions found which didn't match: 0.2.0-0 location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `package v0.0.0 ([ROOT]/foo)` if you are looking for the prerelease package it needs to be specified explicitly my-dependency = { version = "0.2.0-0" } perhaps a crate was updated and forgotten to be re-vendored? "#]]) .run(); let lockfile = p.read_lockfile(); assert!(lockfile.contains("\nname = \"my-dependency\"\nversion = \"0.1.2\"")); } #[cargo_test] fn pre_release_should_matched() { cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("my-dependency", "0.1.2").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "package" [dependencies] my-dependency = "0.1.2" "#, ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); let lockfile = p.read_lockfile(); assert!(lockfile.contains("\nname = \"my-dependency\"\nversion = \"0.1.2\"")); // Test upgrade // 0.1.3 is in the range, so it match cargo_test_support::registry::Package::new("my-dependency", "0.1.3").publish(); p.cargo("update -p my-dependency --precise 0.1.3 -Zunstable-options") .masquerade_as_nightly_cargo(&["precise-pre-release"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [UPDATING] my-dependency v0.1.2 -> v0.1.3 "#]]) .run(); let lockfile = p.read_lockfile(); assert!(lockfile.contains("\nname = \"my-dependency\"\nversion = \"0.1.3\"")); // Test downgrade // v0.1.3-pre.1 is in the range, so it match cargo_test_support::registry::Package::new("my-dependency", "0.1.3-pre.1").publish(); p.cargo("update -p my-dependency --precise 0.1.3-pre.1 -Zunstable-options") .masquerade_as_nightly_cargo(&["precise-pre-release"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [DOWNGRADING] my-dependency v0.1.3 -> v0.1.3-pre.1 "#]]) .run(); let lockfile = p.read_lockfile(); assert!(lockfile.contains("\nname = \"my-dependency\"\nversion = \"0.1.3-pre.1\"")); } cargo-0.91.0/tests/testsuite/proc_macro.rs000064400000000000000000000327621046102023000167050ustar 00000000000000//! Tests for proc-macros. use crate::prelude::*; use cargo_test_support::project; use cargo_test_support::str; #[cargo_test] fn probe_cfg_before_crate_type_discovery() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [target.'cfg(not(stage300))'.dependencies.noop] path = "../noop" "#, ) .file( "src/main.rs", r#" #[macro_use] extern crate noop; #[derive(Noop)] struct X; fn main() {} "#, ) .build(); let _noop = project() .at("noop") .file( "Cargo.toml", r#" [package] name = "noop" version = "0.0.1" edition = "2015" authors = [] [lib] proc-macro = true "#, ) .file( "src/lib.rs", r#" extern crate proc_macro; use proc_macro::TokenStream; #[proc_macro_derive(Noop)] pub fn noop(_input: TokenStream) -> TokenStream { "".parse().unwrap() } "#, ) .build(); p.cargo("check").run(); } #[cargo_test] fn noop() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.noop] path = "../noop" "#, ) .file( "src/main.rs", r#" #[macro_use] extern crate noop; #[derive(Noop)] struct X; fn main() {} "#, ) .build(); let _noop = project() .at("noop") .file( "Cargo.toml", r#" [package] name = "noop" version = "0.0.1" edition = "2015" authors = [] [lib] proc-macro = true "#, ) .file( "src/lib.rs", r#" extern crate proc_macro; use proc_macro::TokenStream; #[proc_macro_derive(Noop)] pub fn noop(_input: TokenStream) -> TokenStream { "".parse().unwrap() } "#, ) .build(); p.cargo("check").run(); p.cargo("check").run(); } #[cargo_test] fn impl_and_derive() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.transmogrify] path = "../transmogrify" "#, ) .file( "src/main.rs", r#" #[macro_use] extern crate transmogrify; trait ImplByTransmogrify { fn impl_by_transmogrify(&self) -> bool; } #[derive(Transmogrify, Debug)] struct X { success: bool } fn main() { let x = X::new(); assert!(x.impl_by_transmogrify()); println!("{:?}", x); } "#, ) .build(); let _transmogrify = project() .at("transmogrify") .file( "Cargo.toml", r#" [package] name = "transmogrify" version = "0.0.1" edition = "2015" authors = [] [lib] proc-macro = true "#, ) .file( "src/lib.rs", r#" extern crate proc_macro; use proc_macro::TokenStream; #[proc_macro_derive(Transmogrify)] #[doc(hidden)] pub fn transmogrify(input: TokenStream) -> TokenStream { " impl X { fn new() -> Self { X { success: true } } } impl ImplByTransmogrify for X { fn impl_by_transmogrify(&self) -> bool { true } } ".parse().unwrap() } "#, ) .build(); p.cargo("build").run(); p.cargo("run") .with_stdout_data(str![[r#" X { success: true } "#]]) .run(); } #[cargo_test] fn proc_macro_doctest() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [lib] proc-macro = true "#, ) .file( "src/lib.rs", r#" #![crate_type = "proc-macro"] extern crate proc_macro; use proc_macro::TokenStream; /// ``` /// assert!(true); /// ``` #[proc_macro_derive(Bar)] pub fn derive(_input: TokenStream) -> TokenStream { "".parse().unwrap() } #[test] fn a() { assert!(true); } "#, ) .build(); foo.cargo("test") .with_stdout_data(str![[r#" running 1 test test a ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test src/lib.rs - derive (line 8) ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn proc_macro_crate_type() { // Verify that `crate-type = ["proc-macro"]` is the same as `proc-macro = true` // and that everything, including rustdoc, works correctly. let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] pm = { path = "pm" } "#, ) .file( "src/lib.rs", r#" //! ``` //! use foo::THING; //! assert_eq!(THING, 123); //! ``` #[macro_use] extern crate pm; #[derive(MkItem)] pub struct S; #[cfg(test)] mod tests { use super::THING; #[test] fn it_works() { assert_eq!(THING, 123); } } "#, ) .file( "pm/Cargo.toml", r#" [package] name = "pm" version = "0.1.0" edition = "2015" [lib] crate-type = ["proc-macro"] "#, ) .file( "pm/src/lib.rs", r#" extern crate proc_macro; use proc_macro::TokenStream; #[proc_macro_derive(MkItem)] pub fn mk_item(_input: TokenStream) -> TokenStream { "pub const THING: i32 = 123;".parse().unwrap() } "#, ) .build(); foo.cargo("test") .with_stdout_data(str![[r#" running 1 test test tests::it_works ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test src/lib.rs - (line 2) ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn proc_macro_crate_type_warning() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [lib] crate-type = ["proc-macro"] "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check") .with_stderr_data(str![[r#" [WARNING] library `foo` should only specify `proc-macro = true` instead of setting `crate-type` [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn lib_plugin_unused_key_warning() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [lib] plugin = true "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check") .with_stderr_data(str![[r#" [WARNING] unused manifest key: lib.plugin [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn proc_macro_crate_type_warning_plugin() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [lib] crate-type = ["proc-macro"] "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check") .with_stderr_data(str![[r#" [WARNING] library `foo` should only specify `proc-macro = true` instead of setting `crate-type` [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn proc_macro_crate_type_multiple() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [lib] crate-type = ["proc-macro", "rlib"] "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check") .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: cannot mix `proc-macro` crate type with others "#]]) .with_status(101) .run(); } #[cargo_test] fn proc_macro_extern_prelude() { // Check that proc_macro is in the extern prelude. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [lib] proc-macro = true "#, ) .file( "src/lib.rs", r#" use proc_macro::TokenStream; #[proc_macro] pub fn foo(input: TokenStream) -> TokenStream { "".parse().unwrap() } "#, ) .build(); p.cargo("test").run(); p.cargo("doc").run(); } #[cargo_test] fn proc_macro_built_once() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ['a', 'b'] resolver = "2" "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" [build-dependencies] the-macro = { path = '../the-macro' } "#, ) .file("a/build.rs", "fn main() {}") .file("a/src/main.rs", "fn main() {}") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" edition = "2015" [dependencies] the-macro = { path = '../the-macro', features = ['a'] } "#, ) .file("b/src/main.rs", "fn main() {}") .file( "the-macro/Cargo.toml", r#" [package] name = "the-macro" version = "0.1.0" edition = "2015" [lib] proc-macro = true [features] a = [] "#, ) .file("the-macro/src/lib.rs", "") .build(); p.cargo("build --verbose") .with_stderr_data( str![[r#" [COMPILING] the-macro v0.1.0 ([ROOT]/foo/the-macro) [RUNNING] `rustc --crate-name the_macro [..]` [COMPILING] b v0.1.0 ([ROOT]/foo/b) [RUNNING] `rustc --crate-name b [..]` [COMPILING] a v0.1.0 ([ROOT]/foo/a) [RUNNING] `rustc --crate-name build_script_build [..]` [RUNNING] `[ROOT]/foo/target/debug/build/a-[HASH]/build-script-build` [RUNNING] `rustc --crate-name a [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } cargo-0.91.0/tests/testsuite/profile_config.rs000064400000000000000000000355561046102023000175520ustar 00000000000000//! Tests for profiles defined in config files. use crate::prelude::*; use cargo_test_support::registry::Package; use cargo_test_support::{basic_lib_manifest, paths, project, str}; use cargo_util_schemas::manifest::TomlDebugInfo; // TODO: this should be remove once -Zprofile-rustflags is stabilized #[cargo_test] fn rustflags_works_with_zflag() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" "#, ) .file("src/main.rs", "fn main() {}") .file( ".cargo/config.toml", r#" [profile.dev] rustflags = ["-C", "link-dead-code=yes"] "#, ) .build(); p.cargo("check -v") .masquerade_as_nightly_cargo(&["profile-rustflags"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] config profile `dev` is not valid (defined in `[ROOT]/foo/.cargo/config.toml`) Caused by: feature `profile-rustflags` is required ... "#]]) .run(); p.cargo("check -v -Zprofile-rustflags") .masquerade_as_nightly_cargo(&["profile-rustflags"]) .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] -C link-dead-code=yes [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.change_file( ".cargo/config.toml", r#" [unstable] profile-rustflags = true [profile.dev] rustflags = ["-C", "link-dead-code=yes"] "#, ); p.cargo("check -v") .masquerade_as_nightly_cargo(&["profile-rustflags"]) .with_stderr_data(str![[r#" [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn profile_config_validate_warnings() { let p = project() .file("Cargo.toml", &basic_lib_manifest("foo")) .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [profile.test] opt-level = 3 [profile.asdf] opt-level = 3 [profile.dev] bad-key = true [profile.dev.build-override] bad-key-bo = true [profile.dev.package.bar] bad-key-bar = true "#, ) .build(); p.cargo("build").with_stderr_data(str![[r#" [WARNING] unused config key `profile.dev.bad-key` in `[ROOT]/foo/.cargo/config.toml` [WARNING] unused config key `profile.dev.build-override.bad-key-bo` in `[ROOT]/foo/.cargo/config.toml` [WARNING] unused config key `profile.dev.package.bar.bad-key-bar` in `[ROOT]/foo/.cargo/config.toml` [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]].unordered()).run(); } #[cargo_test] fn profile_config_error_paths() { // Errors in config show where the error is located. let p = project() .file("Cargo.toml", &basic_lib_manifest("foo")) .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [profile.dev] opt-level = 3 "#, ) .file( paths::home().join(".cargo/config.toml"), r#" [profile.dev] rpath = "foo" "#, ) .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] error in [ROOT]/foo/.cargo/config.toml: could not load config key `profile.dev` Caused by: error in [ROOT]/home/.cargo/config.toml: `profile.dev.rpath` expected true/false, but found a string "#]]) .run(); } #[cargo_test] fn profile_config_validate_errors() { let p = project() .file("Cargo.toml", &basic_lib_manifest("foo")) .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [profile.dev.package.foo] panic = "abort" "#, ) .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] config profile `dev` is not valid (defined in `[ROOT]/foo/.cargo/config.toml`) Caused by: `panic` may not be specified in a `package` profile "#]]) .run(); } #[cargo_test] fn profile_config_syntax_errors() { let p = project() .file("Cargo.toml", &basic_lib_manifest("foo")) .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [profile.dev] codegen-units = "foo" "#, ) .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] error in [ROOT]/foo/.cargo/config.toml: could not load config key `profile.dev` Caused by: error in [ROOT]/foo/.cargo/config.toml: `profile.dev.codegen-units` expected an integer, but found a string "#]]) .run(); } #[cargo_test] fn profile_config_override_spec_multiple() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = { path = "bar" } "#, ) .file( ".cargo/config.toml", r#" [profile.dev.package.bar] opt-level = 3 [profile.dev.package."bar:0.5.0"] opt-level = 3 "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_lib_manifest("bar")) .file("bar/src/lib.rs", "") .build(); // Unfortunately this doesn't tell you which file, hopefully it's not too // much of a problem. p.cargo("build -v") .with_status(101) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [ERROR] multiple package overrides in profile `dev` match package `bar v0.5.0 ([ROOT]/foo/bar)` found package specs: bar, bar@0.5.0 "#]]) .run(); } #[cargo_test] fn profile_config_all_options() { // Ensure all profile options are supported. let p = project() .file("src/main.rs", "fn main() {}") .file( ".cargo/config.toml", r#" [profile.release] opt-level = 1 debug = true debug-assertions = true overflow-checks = false rpath = true lto = true codegen-units = 2 panic = "abort" incremental = true "#, ) .build(); p.cargo("build --release -v") .env_remove("CARGO_INCREMENTAL") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] -C opt-level=1 -C panic=abort -C lto[..]-C codegen-units=2 -C debuginfo=2 [..]-C debug-assertions=on -C overflow-checks=off [..]-C rpath --out-dir [ROOT]/foo/target/release/deps -C incremental=[ROOT]/foo/target/release/incremental[..]` [FINISHED] `release` profile [optimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn profile_config_override_precedence() { // Config values take precedence over manifest values. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = {path = "bar"} [profile.dev] codegen-units = 2 [profile.dev.package.bar] opt-level = 3 "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_lib_manifest("bar")) .file("bar/src/lib.rs", "") .file( ".cargo/config.toml", r#" [profile.dev.package.bar] opt-level = 2 "#, ) .build(); p.cargo("build -v") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [RUNNING] `rustc --crate-name bar [..] -C opt-level=2[..]-C codegen-units=2 [..]` [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]-C codegen-units=2 [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn profile_config_no_warn_unknown_override() { let p = project() .file("Cargo.toml", &basic_lib_manifest("foo")) .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [profile.dev.package.bar] codegen-units = 4 "#, ) .build(); p.cargo("build") .with_stderr_does_not_contain("[..]warning[..]") .run(); } #[cargo_test] fn profile_config_mixed_types() { let p = project() .file("Cargo.toml", &basic_lib_manifest("foo")) .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [profile.dev] opt-level = 3 "#, ) .file( paths::home().join(".cargo/config.toml"), r#" [profile.dev] opt-level = 's' "#, ) .build(); p.cargo("build -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc [..]-C opt-level=3 [..]` [FINISHED] `dev` profile [optimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn named_config_profile() { // Exercises config named profiles. // foo -> middle -> bar -> dev // middle exists in Cargo.toml, the others in .cargo/config.toml use super::config::GlobalContextBuilder; use cargo::core::compiler::CompileKind; use cargo::core::profiles::{Profiles, UnitFor}; use cargo::core::{PackageId, Workspace}; use std::fs; paths::root().join(".cargo").mkdir_p(); fs::write( paths::root().join(".cargo/config.toml"), r#" [profile.foo] inherits = "middle" codegen-units = 2 [profile.foo.build-override] codegen-units = 6 [profile.foo.package.dep] codegen-units = 7 [profile.middle] inherits = "bar" codegen-units = 3 [profile.bar] inherits = "dev" codegen-units = 4 debug = 1 "#, ) .unwrap(); fs::write( paths::root().join("Cargo.toml"), r#" [workspace] [profile.middle] inherits = "bar" codegen-units = 1 opt-level = 1 [profile.middle.package.dep] overflow-checks = false [profile.foo.build-override] codegen-units = 5 debug-assertions = false [profile.foo.package.dep] codegen-units = 8 "#, ) .unwrap(); let gctx = GlobalContextBuilder::new().build(); let profile_name = "foo".into(); let ws = Workspace::new(&paths::root().join("Cargo.toml"), &gctx).unwrap(); let profiles = Profiles::new(&ws, profile_name).unwrap(); let crates_io = cargo::core::SourceId::crates_io(&gctx).unwrap(); let a_pkg = PackageId::try_new("a", "0.1.0", crates_io).unwrap(); let dep_pkg = PackageId::try_new("dep", "0.1.0", crates_io).unwrap(); // normal package let kind = CompileKind::Host; let p = profiles.get_profile(a_pkg, true, true, UnitFor::new_normal(kind), kind); assert_eq!(p.name, "foo"); assert_eq!(p.codegen_units, Some(2)); // "foo" from config assert_eq!(p.opt_level, "1"); // "middle" from manifest assert_eq!(p.debuginfo.into_inner(), TomlDebugInfo::Limited); // "bar" from config assert_eq!(p.debug_assertions, true); // "dev" built-in (ignore build-override) assert_eq!(p.overflow_checks, true); // "dev" built-in (ignore package override) // build-override let bo = profiles.get_profile(a_pkg, true, true, UnitFor::new_host(false, kind), kind); assert_eq!(bo.name, "foo"); assert_eq!(bo.codegen_units, Some(6)); // "foo" build override from config assert_eq!(bo.opt_level, "0"); // default to zero assert_eq!(bo.debuginfo.into_inner(), TomlDebugInfo::Limited); // SAME as normal assert_eq!(bo.debug_assertions, false); // "foo" build override from manifest assert_eq!(bo.overflow_checks, true); // SAME as normal // package overrides let po = profiles.get_profile(dep_pkg, false, true, UnitFor::new_normal(kind), kind); assert_eq!(po.name, "foo"); assert_eq!(po.codegen_units, Some(7)); // "foo" package override from config assert_eq!(po.opt_level, "1"); // SAME as normal assert_eq!(po.debuginfo.into_inner(), TomlDebugInfo::Limited); // SAME as normal assert_eq!(po.debug_assertions, true); // SAME as normal assert_eq!(po.overflow_checks, false); // "middle" package override from manifest } #[cargo_test] fn named_env_profile() { // Environment variables used to define a named profile. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" "#, ) .file("src/lib.rs", "") .build(); p.cargo("build -v --profile=other") .env("CARGO_PROFILE_OTHER_CODEGEN_UNITS", "1") .env("CARGO_PROFILE_OTHER_INHERITS", "dev") .with_stderr_data(str![[r#" [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc [..]-C codegen-units=1 [..]` [FINISHED] `other` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn test_with_dev_profile() { // The `test` profile inherits from `dev` for both local crates and // dependencies. Package::new("somedep", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] somedep = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("test --lib --no-run -v") .env("CARGO_PROFILE_DEV_DEBUG", "0") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] somedep v1.0.0 (registry `dummy-registry`) [COMPILING] somedep v1.0.0 [RUNNING] `rustc --crate-name somedep [..]` [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `test` profile [unoptimized] target(s) in [ELAPSED]s [EXECUTABLE] `[ROOT]/foo/target/debug/deps/foo-[HASH][EXE]` "#]]) .with_stdout_does_not_contain("[..] -C debuginfo=0[..]") .run(); } cargo-0.91.0/tests/testsuite/profile_custom.rs000064400000000000000000000422171046102023000176070ustar 00000000000000//! Tests for named profiles. use crate::prelude::*; use cargo_test_support::{basic_lib_manifest, project, str}; #[cargo_test] fn inherits_on_release() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [profile.release] inherits = "dev" "#, ) .file("src/lib.rs", "") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] `inherits` must not be specified in root profile `release` "#]]) .run(); } #[cargo_test] fn missing_inherits() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [profile.release-lto] codegen-units = 7 "#, ) .file("src/lib.rs", "") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] profile `release-lto` is missing an `inherits` directive (`inherits` is required for all profiles except `dev` or `release`) "#]]) .run(); } #[cargo_test] fn invalid_profile_name() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [profile.'.release-lto'] inherits = "release" codegen-units = 7 "#, ) .file("src/lib.rs", "") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid character `.` in profile name: `.release-lto`, allowed characters are letters, numbers, underscore, and hyphen --> Cargo.toml:8:26 | 8 | [profile.'.release-lto'] | ^^^^^^^^^^^^^^ | "#]]) .run(); } #[cargo_test] // We are currently uncertain if dir-name will ever be exposed to the user. // The code for it still roughly exists, but only for the internal profiles. // This test was kept in case we ever want to enable support for it again. #[ignore = "dir-name is disabled"] fn invalid_dir_name() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [profile.'release-lto'] inherits = "release" dir-name = ".subdir" codegen-units = 7 "#, ) .file("src/lib.rs", "") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: Invalid character `.` in dir-name: `.subdir`", "#]]) .run(); } #[cargo_test] fn dir_name_disabled() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [profile.release-lto] inherits = "release" dir-name = "lto" lto = true "#, ) .file("src/lib.rs", "") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: dir-name="lto" in profile `release-lto` is not currently allowed, directory names are tied to the profile name for custom profiles "#]]) .run(); } #[cargo_test] fn invalid_inherits() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [profile.'release-lto'] inherits = ".release" codegen-units = 7 "#, ) .file("src/lib.rs", "") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] profile `release-lto` inherits from `.release`, but that profile is not defined "#]]) .run(); } #[cargo_test] fn non_existent_inherits() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [profile.release-lto] codegen-units = 7 inherits = "non-existent" "#, ) .file("src/lib.rs", "") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] profile `release-lto` inherits from `non-existent`, but that profile is not defined "#]]) .run(); } #[cargo_test] fn self_inherits() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [profile.release-lto] codegen-units = 7 inherits = "release-lto" "#, ) .file("src/lib.rs", "") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] profile inheritance loop detected with profile `release-lto` inheriting `release-lto` "#]]) .run(); } #[cargo_test] fn inherits_loop() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [profile.release-lto] codegen-units = 7 inherits = "release-lto2" [profile.release-lto2] codegen-units = 7 inherits = "release-lto" "#, ) .file("src/lib.rs", "") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] profile inheritance loop detected with profile `release-lto2` inheriting `release-lto` "#]]) .run(); } #[cargo_test] fn overrides_with_custom() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] xxx = {path = "xxx"} yyy = {path = "yyy"} [profile.dev] codegen-units = 7 [profile.dev.package.xxx] codegen-units = 5 [profile.dev.package.yyy] codegen-units = 3 [profile.other] inherits = "dev" codegen-units = 2 [profile.other.package.yyy] codegen-units = 6 "#, ) .file("src/lib.rs", "") .file("xxx/Cargo.toml", &basic_lib_manifest("xxx")) .file("xxx/src/lib.rs", "") .file("yyy/Cargo.toml", &basic_lib_manifest("yyy")) .file("yyy/src/lib.rs", "") .build(); // profile overrides are inherited between profiles using inherits and have a // higher priority than profile options provided by custom profiles p.cargo("build -v") .with_stderr_data( str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] xxx v0.5.0 ([ROOT]/foo/xxx) [COMPILING] yyy v0.5.0 ([ROOT]/foo/yyy) [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name xxx [..] -C codegen-units=5 [..]` [RUNNING] `rustc --crate-name yyy [..] -C codegen-units=3 [..]` [RUNNING] `rustc --crate-name foo [..] -C codegen-units=7 [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); // This also verifies that the custom profile names appears in the finished line. p.cargo("build --profile=other -v") .with_stderr_data( str![[r#" [COMPILING] xxx v0.5.0 ([ROOT]/foo/xxx) [COMPILING] yyy v0.5.0 ([ROOT]/foo/yyy) [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name xxx [..] -C codegen-units=5 [..]` [RUNNING] `rustc --crate-name yyy [..] -C codegen-units=6 [..]` [RUNNING] `rustc --crate-name foo [..] -C codegen-units=2 [..]` [FINISHED] `other` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn conflicting_usage() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build --profile=dev --release") .with_status(1) .with_stderr_data(str![[r#" [ERROR] the argument '--profile ' cannot be used with '--release' Usage: cargo[EXE] build --profile For more information, try '--help'. "#]]) .run(); p.cargo("install --profile=release --debug") .with_status(1) .with_stderr_data(str![[r#" [ERROR] the argument '--profile ' cannot be used with '--debug' Usage: cargo[EXE] install --profile [CRATE[@]]... For more information, try '--help'. "#]]) .run(); p.cargo("check --profile=dev --release") .with_status(1) .with_stderr_data(str![[r#" [ERROR] the argument '--profile ' cannot be used with '--release' Usage: cargo[EXE] check --profile For more information, try '--help'. "#]]) .run(); } #[cargo_test] fn clean_custom_dirname() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [profile.other] inherits = "release" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build --release") .with_stdout_data("") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("clean -p foo").run(); p.cargo("build --release") .with_stdout_data("") .with_stderr_data(str![[r#" [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("clean -p foo --release").run(); p.cargo("build --release") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build") .with_stdout_data("") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build --profile=other") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `other` profile [optimized] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("clean").arg("--release").run(); // Make sure that 'other' was not cleaned assert!(p.build_dir().is_dir()); assert!(p.build_dir().join("debug").is_dir()); assert!(p.build_dir().join("other").is_dir()); assert!(!p.build_dir().join("release").is_dir()); // This should clean 'other' p.cargo("clean --profile=other") .with_stderr_data(str![[r#" [REMOVED] [FILE_NUM] files, [FILE_SIZE]B total "#]]) .run(); assert!(p.build_dir().join("debug").is_dir()); assert!(!p.build_dir().join("other").is_dir()); } #[cargo_test] fn unknown_profile() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" "#, ) .file("src/lib.rs", "") .build(); p.cargo("build --profile alpha") .with_stderr_data(str![[r#" [ERROR] profile `alpha` is not defined "#]]) .with_status(101) .run(); // Clean has a separate code path, need to check it too. p.cargo("clean --profile alpha") .with_stderr_data(str![[r#" [ERROR] profile `alpha` is not defined "#]]) .with_status(101) .run(); } #[cargo_test] fn reserved_profile_names() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [profile.doc] opt-level = 1 "#, ) .file("src/lib.rs", "") .build(); p.cargo("build --profile=doc") .with_status(101) .with_stderr_data(str![[r#" [ERROR] profile `doc` is reserved and not allowed to be explicitly specified "#]]) .run(); // Not an exhaustive list, just a sample. for name in ["build", "cargo", "check", "rustc", "CaRgO_startswith"] { p.cargo(&format!("build --profile={}", name)) .with_status(101) .with_stderr_data(&format!( "\ [ERROR] profile name `{}` is reserved Please choose a different name. See https://doc.rust-lang.org/cargo/reference/profiles.html for more on configuring profiles. ", name )) .run(); } for name in ["build", "check", "cargo", "rustc", "CaRgO_startswith"] { p.change_file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2015" [profile.{}] opt-level = 1 "#, name ), ); let highlight = "^".repeat(name.len()); p.cargo("build") .with_status(101) .with_stderr_data(&format!( "\ [ERROR] profile name `{name}` is reserved Please choose a different name. See https://doc.rust-lang.org/cargo/reference/profiles.html for more on configuring profiles. --> Cargo.toml:7:30 | 7 | [profile.{name}] | {highlight} | " )) .run(); } p.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [profile.debug] debug = 1 inherits = "dev" "#, ); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] profile name `debug` is reserved To configure the default development profile, use the name `dev` as in [profile.dev] See https://doc.rust-lang.org/cargo/reference/profiles.html for more on configuring profiles. --> Cargo.toml:8:25 | 8 | [profile.debug] | ^^^^^ | "#]]) .run(); } #[cargo_test] fn legacy_commands_support_custom() { // These commands have had `--profile` before custom named profiles. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [profile.super-dev] codegen-units = 3 inherits = "dev" "#, ) .file("src/lib.rs", "") .build(); for command in ["rustc", "fix", "check"] { let mut pb = p.cargo(command); if command == "fix" { pb.arg("--allow-no-vcs"); } pb.arg("--profile=super-dev") .arg("-v") .with_stderr_data(str![ r#" ... [RUNNING] [..]codegen-units=3[..] ... "# ]) .run(); p.build_dir().rm_rf(); } } #[cargo_test] fn legacy_rustc() { // `cargo rustc` historically has supported dev/test/bench/check // other profiles are covered in check::rustc_check let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [profile.dev] codegen-units = 3 "#, ) .file("src/lib.rs", "") .build(); p.cargo("rustc --profile dev -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]-C codegen-units=3[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } cargo-0.91.0/tests/testsuite/profile_overrides.rs000064400000000000000000000404361046102023000203000ustar 00000000000000//! Tests for profile overrides (build-override and per-package overrides). use crate::prelude::*; use cargo_test_support::registry::Package; use cargo_test_support::{basic_lib_manifest, basic_manifest, project, str}; #[cargo_test] fn profile_override_basic() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = {path = "bar"} [profile.dev] opt-level = 1 [profile.dev.package.bar] opt-level = 3 "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_lib_manifest("bar")) .file("bar/src/lib.rs", "") .build(); p.cargo("check -v") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.5.0 ([ROOT]/foo/bar) [RUNNING] `rustc --crate-name bar [..] -C opt-level=3 [..]` [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] -C opt-level=1 [..]` [FINISHED] `dev` profile [optimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn profile_override_warnings() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = {path = "bar"} [profile.dev.package.bart] opt-level = 3 [profile.dev.package.no-suggestion] opt-level = 3 [profile.dev.package."bar:1.2.3"] opt-level = 3 "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_lib_manifest("bar")) .file("bar/src/lib.rs", "") .build(); p.cargo("build").with_stderr_data(str![[r#" ... [WARNING] profile package spec `bar@1.2.3` in profile `dev` has a version or URL that does not match any of the packages: bar v0.5.0 ([ROOT]/foo/bar) [WARNING] profile package spec `bart` in profile `dev` did not match any packages [HELP] a package with a similar name exists: `bar` [WARNING] profile package spec `no-suggestion` in profile `dev` did not match any packages [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn profile_override_bad_settings() { let bad_values = [ ( "panic = \"abort\"", "`panic` may not be specified in a `package` profile", ), ( "lto = true", "`lto` may not be specified in a `package` profile", ), ( "rpath = true", "`rpath` may not be specified in a `package` profile", ), ("package = {}", "package-specific profiles cannot be nested"), ]; for &(snippet, expected) in bad_values.iter() { let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = {{path = "bar"}} [profile.dev.package.bar] {} "#, snippet ), ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_lib_manifest("bar")) .file("bar/src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(format!( "\ ... Caused by:\n {} ", expected )) .run(); } } #[cargo_test] fn profile_override_hierarchy() { // Test that the precedence rules are correct for different types. let p = project() .file( "Cargo.toml", r#" [workspace] members = ["m1", "m2", "m3"] [profile.dev] codegen-units = 1 [profile.dev.package.m2] codegen-units = 2 [profile.dev.package."*"] codegen-units = 3 [profile.dev.build-override] codegen-units = 4 "#, ) // m1 .file( "m1/Cargo.toml", r#" [package] name = "m1" version = "0.0.1" edition = "2015" [dependencies] m2 = { path = "../m2" } dep = { path = "../../dep" } "#, ) .file("m1/src/lib.rs", "extern crate m2; extern crate dep;") .file("m1/build.rs", "fn main() {}") // m2 .file( "m2/Cargo.toml", r#" [package] name = "m2" version = "0.0.1" edition = "2015" [dependencies] m3 = { path = "../m3" } [build-dependencies] m3 = { path = "../m3" } dep = { path = "../../dep" } "#, ) .file("m2/src/lib.rs", "extern crate m3;") .file( "m2/build.rs", "extern crate m3; extern crate dep; fn main() {}", ) // m3 .file("m3/Cargo.toml", &basic_lib_manifest("m3")) .file("m3/src/lib.rs", "") .build(); // dep (outside of workspace) let _dep = project() .at("dep") .file("Cargo.toml", &basic_lib_manifest("dep")) .file("src/lib.rs", "") .build(); // Profiles should be: // m3: 4 (as build.rs dependency) // m3: 1 (as [profile.dev] as workspace member) // dep: 3 (as [profile.dev.package."*"] as non-workspace member) // m1 build.rs: 4 (as [profile.dev.build-override]) // m2 build.rs: 2 (as [profile.dev.package.m2]) // m2: 2 (as [profile.dev.package.m2]) // m1: 1 (as [profile.dev]) p.cargo("build -v") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] m3 v0.5.0 ([ROOT]/foo/m3) [COMPILING] dep v0.5.0 ([ROOT]/dep) [RUNNING] `rustc --crate-name m3 --edition=2015 m3/src/lib.rs [..] --crate-type lib --emit=[..]link[..]-C codegen-units=4 [..]` [RUNNING] `rustc --crate-name dep [..][ROOT]/dep/src/lib.rs [..] --crate-type lib --emit=[..]link[..]-C codegen-units=3 [..]` [RUNNING] `rustc --crate-name m3 --edition=2015 m3/src/lib.rs [..] --crate-type lib --emit=[..]link[..]-C codegen-units=1 [..]` [RUNNING] `rustc --crate-name build_script_build --edition=2015 m1/build.rs [..] --crate-type bin --emit=[..]link[..]-C codegen-units=4 [..]` [COMPILING] m2 v0.0.1 ([ROOT]/foo/m2) [RUNNING] `rustc --crate-name build_script_build --edition=2015 m2/build.rs [..] --crate-type bin --emit=[..]link[..]-C codegen-units=2 [..]` [RUNNING] `[ROOT]/foo/target/debug/build/m1-[HASH]/build-script-build` [RUNNING] `[ROOT]/foo/target/debug/build/m2-[HASH]/build-script-build` [RUNNING] `rustc --crate-name m2 --edition=2015 m2/src/lib.rs [..] --crate-type lib --emit=[..]link[..]-C codegen-units=2 [..]` [COMPILING] m1 v0.0.1 ([ROOT]/foo/m1) [RUNNING] `rustc --crate-name m1 --edition=2015 m1/src/lib.rs [..] --crate-type lib --emit=[..]link[..]-C codegen-units=1 [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]].unordered()) .run(); } #[cargo_test] fn profile_override_spec_multiple() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = { path = "bar" } [profile.dev.package.bar] opt-level = 3 [profile.dev.package."bar:0.5.0"] opt-level = 3 "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_lib_manifest("bar")) .file("bar/src/lib.rs", "") .build(); p.cargo("check -v") .with_status(101) .with_stderr_data(str![[r#" ... [ERROR] multiple package overrides in profile `dev` match package `bar v0.5.0 ([ROOT]/foo/bar)` found package specs: bar, bar@0.5.0 "#]]) .run(); } #[cargo_test] fn profile_override_spec_with_version() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = { path = "bar" } [profile.dev.package."bar:0.5.0"] codegen-units = 2 "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_lib_manifest("bar")) .file("bar/src/lib.rs", "") .build(); p.cargo("check -v") .with_stderr_data(str![[r#" ... [CHECKING] bar v0.5.0 ([ROOT]/foo/bar) [RUNNING] `rustc [..]bar/src/lib.rs [..] -C codegen-units=2 [..]` ... "#]]) .run(); } #[cargo_test] fn profile_override_spec_with_partial_version() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = { path = "bar" } [profile.dev.package."bar:0.5"] codegen-units = 2 "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_lib_manifest("bar")) .file("bar/src/lib.rs", "") .build(); p.cargo("check -v") .with_stderr_data(str![[r#" ... [CHECKING] bar v0.5.0 ([ROOT]/foo/bar) [RUNNING] `rustc [..]bar/src/lib.rs [..] -C codegen-units=2 [..]` ... "#]]) .run(); } #[cargo_test] fn profile_override_spec() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["m1", "m2"] [profile.dev.package."dep:1.0.0"] codegen-units = 1 [profile.dev.package."dep:2.0.0"] codegen-units = 2 "#, ) // m1 .file( "m1/Cargo.toml", r#" [package] name = "m1" version = "0.0.1" edition = "2015" [dependencies] dep = { path = "../../dep1" } "#, ) .file("m1/src/lib.rs", "extern crate dep;") // m2 .file( "m2/Cargo.toml", r#" [package] name = "m2" version = "0.0.1" edition = "2015" [dependencies] dep = {path = "../../dep2" } "#, ) .file("m2/src/lib.rs", "extern crate dep;") .build(); project() .at("dep1") .file("Cargo.toml", &basic_manifest("dep", "1.0.0")) .file("src/lib.rs", "") .build(); project() .at("dep2") .file("Cargo.toml", &basic_manifest("dep", "2.0.0")) .file("src/lib.rs", "") .build(); p.cargo("check -v") .with_stderr_data( str![[r#" ... [RUNNING] `rustc [..][ROOT]/dep1/src/lib.rs [..] -C codegen-units=1 [..]` [RUNNING] `rustc [..][ROOT]/dep2/src/lib.rs [..] -C codegen-units=2 [..]` ... "#]] .unordered(), ) .run(); } #[cargo_test] fn override_proc_macro() { Package::new("shared", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [dependencies] shared = "1.0" pm = {path = "pm"} [profile.dev.build-override] codegen-units = 4 "#, ) .file("src/lib.rs", r#"pm::eat!{}"#) .file( "pm/Cargo.toml", r#" [package] name = "pm" version = "0.1.0" edition = "2015" [lib] proc-macro = true [dependencies] shared = "1.0" "#, ) .file( "pm/src/lib.rs", r#" extern crate proc_macro; use proc_macro::TokenStream; #[proc_macro] pub fn eat(_item: TokenStream) -> TokenStream { "".parse().unwrap() } "#, ) .build(); p.cargo("check -v") // Shared built for the proc-macro. .with_stderr_data(str![[r#" ... [RUNNING] `rustc [..]--crate-name shared [..] -C codegen-units=4[..]` ... [RUNNING] `rustc [..]--crate-name pm [..] -C codegen-units=4[..]` ... "#]]) // Shared built for the library. .with_stderr_line_without( &["[RUNNING] `rustc --crate-name shared --edition=2015"], &["-C codegen-units"], ) .with_stderr_line_without( &["[RUNNING] `rustc [..]--crate-name foo"], &["-C codegen-units"], ) .run(); } #[cargo_test] fn no_warning_ws() { // https://github.com/rust-lang/cargo/issues/7378, avoid warnings in a workspace. let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] [profile.dev.package.a] codegen-units = 3 "#, ) .file("a/Cargo.toml", &basic_manifest("a", "0.1.0")) .file("a/src/lib.rs", "") .file("b/Cargo.toml", &basic_manifest("b", "0.1.0")) .file("b/src/lib.rs", "") .build(); p.cargo("check -p b") .with_stderr_data(str![[r#" [CHECKING] b v0.1.0 ([ROOT]/foo/b) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn build_override_shared() { // A dependency with a build script that is shared with a build // dependency, using different profile settings. That is: // // foo DEBUG=2 // ├── common DEBUG=2 // │ └── common Run build.rs DEBUG=2 // │ └── common build.rs DEBUG=0 (build_override) // └── foo Run build.rs DEBUG=2 // └── foo build.rs DEBUG=0 (build_override) // └── common DEBUG=0 (build_override) // └── common Run build.rs DEBUG=0 (build_override) // └── common build.rs DEBUG=0 (build_override) // // The key part here is that `common` RunCustomBuild is run twice, once // with DEBUG=2 (as a dependency of foo) and once with DEBUG=0 (as a // build-dependency of foo's build script). Package::new("common", "1.0.0") .file( "build.rs", r#" fn main() { if std::env::var("DEBUG").unwrap() != "false" { println!("cargo::rustc-cfg=foo_debug"); } else { println!("cargo::rustc-cfg=foo_release"); } } "#, ) .file( "src/lib.rs", r#" pub fn foo() -> u32 { if cfg!(foo_debug) { assert!(cfg!(debug_assertions)); 1 } else if cfg!(foo_release) { assert!(!cfg!(debug_assertions)); 2 } else { panic!("not set"); } } "#, ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [build-dependencies] common = "1.0" [dependencies] common = "1.0" [profile.dev.build-override] debug = 0 debug-assertions = false "#, ) .file( "build.rs", r#" fn main() { assert_eq!(common::foo(), 2); } "#, ) .file( "src/main.rs", r#" fn main() { assert_eq!(common::foo(), 1); } "#, ) .build(); p.cargo("run").run(); } cargo-0.91.0/tests/testsuite/profile_targets.rs000064400000000000000000001141151046102023000177430ustar 00000000000000//! Tests for checking exactly how profiles correspond with each unit. For //! example, the `test` profile applying to test targets, but not other //! targets, etc. use crate::prelude::*; use cargo_test_support::{Project, basic_manifest, project, str}; fn all_target_project() -> Project { // This abuses the `codegen-units` setting so that we can verify exactly // which profile is used for each compiler invocation. project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = { path = "bar" } [build-dependencies] bdep = { path = "bdep" } [profile.dev] codegen-units = 1 panic = "abort" [profile.release] codegen-units = 2 panic = "abort" [profile.test] codegen-units = 3 [profile.bench] codegen-units = 4 [profile.dev.build-override] codegen-units = 5 [profile.release.build-override] codegen-units = 6 "#, ) .file("src/lib.rs", "extern crate bar;") .file("src/main.rs", "extern crate foo; fn main() {}") .file("examples/ex1.rs", "extern crate foo; fn main() {}") .file("tests/test1.rs", "extern crate foo;") .file("benches/bench1.rs", "extern crate foo;") .file( "build.rs", r#" extern crate bdep; fn main() { eprintln!("foo custom build PROFILE={} DEBUG={} OPT_LEVEL={}", std::env::var("PROFILE").unwrap(), std::env::var("DEBUG").unwrap(), std::env::var("OPT_LEVEL").unwrap(), ); } "#, ) // `bar` package. .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "") // `bdep` package. .file( "bdep/Cargo.toml", r#" [package] name = "bdep" version = "0.0.1" edition = "2015" [dependencies] bar = { path = "../bar" } "#, ) .file("bdep/src/lib.rs", "extern crate bar;") .build() } #[cargo_test] fn profile_selection_build() { let p = all_target_project(); // `build` // NOTES: // - bdep `panic` is not set because it thinks `build.rs` is a plugin. // - build_script_build is built without panic because it thinks `build.rs` is a plugin. // - We make sure that the build dependencies bar, bdep, and build.rs // are built with debuginfo=0. p.cargo("build -vv") .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..] [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..] [COMPILING] bdep v0.0.1 ([ROOT]/foo/bdep) [RUNNING] `[..] rustc --crate-name bdep --edition=2015 bdep/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..] [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[..] rustc --crate-name build_script_build --edition=2015 build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=5 [..] [RUNNING] `[..][ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [foo 0.0.1] foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0 [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]link -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..] [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/main.rs [..]--crate-type bin --emit=[..]link -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]].unordered()) .with_stderr_does_not_contain("[..] -C debuginfo=0[..]") .run(); p.cargo("build -vv") .with_stderr_data( str![[r#" [FRESH] bdep v0.0.1 ([ROOT]/foo/bdep) [FRESH] bar v0.0.1 ([ROOT]/foo/bar) [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn profile_selection_build_release() { let p = all_target_project(); // `build --release` p.cargo("build --release -vv") .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C opt-level=3 -C panic=abort[..]-C codegen-units=2 [..] [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=6 [..] [COMPILING] bdep v0.0.1 ([ROOT]/foo/bdep) [RUNNING] `[..] rustc --crate-name bdep --edition=2015 bdep/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=6 [..] [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[..] rustc --crate-name build_script_build --edition=2015 build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=6 [..] [RUNNING] `[..][ROOT]/foo/target/release/build/foo-[HASH]/build-script-build` [foo 0.0.1] foo custom build PROFILE=release DEBUG=false OPT_LEVEL=3 [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]link -C opt-level=3 -C panic=abort[..]-C codegen-units=2 [..] [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/main.rs [..]--crate-type bin --emit=[..]link -C opt-level=3 -C panic=abort[..]-C codegen-units=2 [..] [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]].unordered()) .run(); p.cargo("build --release -vv") .with_stderr_data( str![[r#" [FRESH] bar v0.0.1 ([ROOT]/foo/bar) [FRESH] bdep v0.0.1 ([ROOT]/foo/bdep) [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn profile_selection_build_all_targets() { let p = all_target_project(); // `build` // NOTES: // - bdep `panic` is not set because it thinks `build.rs` is a plugin. // - build_script_build is built without panic because it thinks // `build.rs` is a plugin. // - Benchmark dependencies are compiled in `dev` mode, which may be // surprising. See issue rust-lang/cargo#4929. // - We make sure that the build dependencies bar, bdep, and build.rs are built with // debuginfo=0; but since we don't pass `-C debuginfo` when it's set to 0, we have to test // explicitly that there's no `-C debuginfo` flag. // // - Dependency profiles: // Pkg Target Profile Reason // --- ------ ------- ------ // bar lib dev For foo-bin // bar lib dev-panic For tests/benches and bdep // bdep lib dev-panic For foo build.rs // foo custom dev-panic // // - `foo` target list is: // Target Profile Mode // ------ ------- ---- // lib dev+panic build (a normal lib target) // lib dev-panic build (used by tests/benches) // lib dev dev // test dev dev // bench dev dev // bin dev dev // bin dev build // example dev build p.cargo("build --all-targets -vv") .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C embed-bitcode=[..]-C codegen-units=1 -C debuginfo=2 [..]` [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C panic=abort -C embed-bitcode=[..]-C codegen-units=1 -C debuginfo=2 [..]` [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C embed-bitcode=[..]-C codegen-units=5 [..]` [COMPILING] bdep v0.0.1 ([ROOT]/foo/bdep) [RUNNING] `[..] rustc --crate-name bdep --edition=2015 bdep/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..]` [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[..] rustc --crate-name build_script_build --edition=2015 build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=5 [..]` [RUNNING] `[..][ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [foo 0.0.1] foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0 [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]link -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..]` [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/lib.rs [..]--emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 [..]--test [..]` [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 [..]` [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/main.rs [..]--emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 [..]--test [..]` [RUNNING] `[..] rustc --crate-name test1 --edition=2015 tests/test1.rs [..]--emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 [..]--test [..]` [RUNNING] `[..] rustc --crate-name bench1 --edition=2015 benches/bench1.rs [..]--emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 [..]--test [..]` [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/main.rs [..]--crate-type bin --emit=[..]link -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..]` [RUNNING] `[..] rustc --crate-name ex1 --edition=2015 examples/ex1.rs [..]--crate-type bin --emit=[..]link -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]].unordered()) .with_stderr_does_not_contain("[..] -C debuginfo=0[..]") .run(); p.cargo("build -vv") .with_stderr_data( str![[r#" [FRESH] bar v0.0.1 ([ROOT]/foo/bar) [FRESH] bdep v0.0.1 ([ROOT]/foo/bdep) [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn profile_selection_build_all_targets_release() { let p = all_target_project(); // `build --all-targets --release` // NOTES: // - bdep `panic` is not set because it thinks `build.rs` is a plugin. // - bar compiled twice. It tries with and without panic, but the "is a // plugin" logic is forcing it to be cleared. // - build_script_build is built without panic because it thinks // `build.rs` is a plugin. // - build_script_build is being run two times. Once for the `dev` and // `test` targets, once for the `bench` targets. // TODO: "PROFILE" says debug both times, though! // // - Dependency profiles: // Pkg Target Profile Reason // --- ------ ------- ------ // bar lib release For foo-bin // bar lib release-panic For tests/benches and bdep // bdep lib release-panic For foo build.rs // foo custom release-panic // // - `foo` target list is: // Target Profile Mode // ------ ------- ---- // lib release+panic build (a normal lib target) // lib release-panic build (used by tests/benches) // lib release test (bench/test de-duped) // test release test // bench release test // bin release test (bench/test de-duped) // bin release build // example release build p.cargo("build --all-targets --release -vv") .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C opt-level=3 -C embed-bitcode=[..]-C codegen-units=2 [..]` [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C opt-level=3 -C panic=abort -C embed-bitcode=[..]-C codegen-units=2 [..]` [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C embed-bitcode=[..]-C codegen-units=6 [..]` [COMPILING] bdep v0.0.1 ([ROOT]/foo/bdep) [RUNNING] `[..] rustc --crate-name bdep --edition=2015 bdep/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=6 [..]` [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[..] rustc --crate-name build_script_build --edition=2015 build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=6 [..]` [RUNNING] `[..][ROOT]/foo/target/release/build/foo-[HASH]/build-script-build` [foo 0.0.1] foo custom build PROFILE=release DEBUG=false OPT_LEVEL=3 [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]link -C opt-level=3 -C panic=abort[..]-C codegen-units=2 [..]` [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/lib.rs [..]--emit=[..]link -C opt-level=3[..]-C codegen-units=2 --test [..]` [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]link -C opt-level=3[..]-C codegen-units=2 [..]` [RUNNING] `[..] rustc --crate-name test1 --edition=2015 tests/test1.rs [..]--emit=[..]link -C opt-level=3[..]-C codegen-units=2 --test [..]` [RUNNING] `[..] rustc --crate-name bench1 --edition=2015 benches/bench1.rs [..]--emit=[..]link -C opt-level=3[..]-C codegen-units=2 --test [..]` [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/main.rs [..]--emit=[..]link -C opt-level=3[..]-C codegen-units=2 --test [..]` [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/main.rs [..]--crate-type bin --emit=[..]link -C opt-level=3 -C panic=abort[..]-C codegen-units=2 [..]` [RUNNING] `[..] rustc --crate-name ex1 --edition=2015 examples/ex1.rs [..]--crate-type bin --emit=[..]link -C opt-level=3 -C panic=abort[..]-C codegen-units=2 [..]` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]].unordered()) .run(); p.cargo("build --all-targets --release -vv") .with_stderr_data( str![[r#" [FRESH] bar v0.0.1 ([ROOT]/foo/bar) [FRESH] bdep v0.0.1 ([ROOT]/foo/bdep) [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn profile_selection_test() { let p = all_target_project(); // `test` // NOTES: // - Dependency profiles: // Pkg Target Profile Reason // --- ------ ------- ------ // bar lib test For foo-bin // bar lib test-panic For tests/benches and bdep // bdep lib test-panic For foo build.rs // foo custom test-panic // // - `foo` target list is: // Target Profile Mode // ------ ------- ---- // lib test-panic build (for tests) // lib test build (for bins) // lib test test // test test test // example test-panic build // bin test test // bin test build // p.cargo("test -vv") .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C embed-bitcode=[..]-C codegen-units=3 -C debuginfo=2 [..]` [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C embed-bitcode=[..]-C codegen-units=5 [..]` [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C panic=abort[..]-C embed-bitcode=[..]-C codegen-units=3 -C debuginfo=2 [..]` [COMPILING] bdep v0.0.1 ([ROOT]/foo/bdep) [RUNNING] `[..] rustc --crate-name bdep --edition=2015 bdep/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..]` [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[..] rustc --crate-name build_script_build --edition=2015 build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=5 [..]` [RUNNING] `[..][ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [foo 0.0.1] foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0 [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]link -C panic=abort[..]-C codegen-units=3 -C debuginfo=2 [..]` [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=3 -C debuginfo=2 [..]` [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/lib.rs [..]--emit=[..]link[..]-C codegen-units=3 -C debuginfo=2 [..]--test [..]` [RUNNING] `[..] rustc --crate-name test1 --edition=2015 tests/test1.rs [..]--emit=[..]link[..]-C codegen-units=3 -C debuginfo=2 [..]--test [..]` [RUNNING] `[..] rustc --crate-name ex1 --edition=2015 examples/ex1.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=3 -C debuginfo=2 [..]` [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/main.rs [..]--emit=[..]link[..]-C codegen-units=3 -C debuginfo=2 [..]--test [..]` [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/main.rs [..]--crate-type bin --emit=[..]link -C panic=abort[..]-C codegen-units=3 -C debuginfo=2 [..]` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[..][ROOT]/foo/target/debug/deps/foo-[HASH][EXE]` [RUNNING] `[..][ROOT]/foo/target/debug/deps/foo-[HASH][EXE]` [RUNNING] `[..][ROOT]/foo/target/debug/deps/test1-[HASH][EXE]` [DOCTEST] foo [RUNNING] `[..] rustdoc [..]--test [..] "#]].unordered()) .run(); p.cargo("test -vv") .with_stderr_data( str![[r#" [FRESH] bdep v0.0.1 ([ROOT]/foo/bdep) [FRESH] bar v0.0.1 ([ROOT]/foo/bar) [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[..][ROOT]/foo/target/debug/deps/foo-[HASH][EXE]` [RUNNING] `[..][ROOT]/foo/target/debug/deps/foo-[HASH][EXE]` [RUNNING] `[..][ROOT]/foo/target/debug/deps/test1-[HASH][EXE]` [DOCTEST] foo [RUNNING] `[..] rustdoc [..]--test [..] "#]] .unordered(), ) .run(); } #[cargo_test] fn profile_selection_test_release() { let p = all_target_project(); // `test --release` // NOTES: // - Dependency profiles: // Pkg Target Profile Reason // --- ------ ------- ------ // bar lib release For foo-bin // bar lib release-panic For tests/benches and bdep // bdep lib release-panic For foo build.rs // foo custom release-panic // // - `foo` target list is: // Target Profile Mode // ------ ------- ---- // lib release-panic build (for tests) // lib release build (for bins) // lib release test // test release test // example release-panic build // bin release test // bin release build // p.cargo("test --release -vv") .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=6 [..]` [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C opt-level=3 -C panic=abort[..]-C codegen-units=2 [..]` [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C opt-level=3[..]-C codegen-units=2[..]` [COMPILING] bdep v0.0.1 ([ROOT]/foo/bdep) [RUNNING] `[..] rustc --crate-name bdep --edition=2015 bdep/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=6 [..]` [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[..] rustc --crate-name build_script_build --edition=2015 build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=6 [..]` [RUNNING] `[..][ROOT]/foo/target/release/build/foo-[HASH]/build-script-build` [foo 0.0.1] foo custom build PROFILE=release DEBUG=false OPT_LEVEL=3 [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]link -C opt-level=3 -C panic=abort[..]-C codegen-units=2 [..]` [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]link -C opt-level=3[..]-C codegen-units=2 [..]` [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/lib.rs [..]--emit=[..]link -C opt-level=3[..]-C codegen-units=2 --test [..]` [RUNNING] `[..] rustc --crate-name test1 --edition=2015 tests/test1.rs [..]--emit=[..]link -C opt-level=3[..]-C codegen-units=2 --test [..]` [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/main.rs [..]--emit=[..]link -C opt-level=3[..]-C codegen-units=2 --test [..]` [RUNNING] `[..] rustc --crate-name ex1 --edition=2015 examples/ex1.rs [..]--crate-type bin --emit=[..]link -C opt-level=3[..]-C codegen-units=2 [..]` [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/main.rs [..]--crate-type bin --emit=[..]link -C opt-level=3 -C panic=abort[..]-C codegen-units=2 [..]` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [RUNNING] `[..][ROOT]/foo/target/release/deps/foo-[HASH][EXE]` [RUNNING] `[..][ROOT]/foo/target/release/deps/foo-[HASH][EXE]` [RUNNING] `[..][ROOT]/foo/target/release/deps/test1-[HASH][EXE]` [DOCTEST] foo [RUNNING] `[..] rustdoc [..]--test [..]` "#]].unordered()) .run(); p.cargo("test --release -vv") .with_stderr_data( str![[r#" [FRESH] bdep v0.0.1 ([ROOT]/foo/bdep) [FRESH] bar v0.0.1 ([ROOT]/foo/bar) [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [RUNNING] `[..][ROOT]/foo/target/release/deps/foo-[HASH][EXE]` [RUNNING] `[..][ROOT]/foo/target/release/deps/foo-[HASH][EXE]` [RUNNING] `[..][ROOT]/foo/target/release/deps/test1-[HASH][EXE]` [DOCTEST] foo [RUNNING] `[..] rustdoc [..]--test [..] "#]] .unordered(), ) .run(); } #[cargo_test] fn profile_selection_bench() { let p = all_target_project(); // `bench` // NOTES: // - Dependency profiles: // Pkg Target Profile Reason // --- ------ ------- ------ // bar lib bench For foo-bin // bar lib bench-panic For tests/benches and bdep // bdep lib bench-panic For foo build.rs // foo custom bench-panic // // - `foo` target list is: // Target Profile Mode // ------ ------- ---- // lib bench-panic build (for benches) // lib bench build (for bins) // lib bench test(bench) // bench bench test(bench) // bin bench test(bench) // bin bench build // p.cargo("bench -vv") .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C opt-level=3 -C embed-bitcode=[..]-C codegen-units=4 [..]` [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C opt-level=3 -C panic=abort -C embed-bitcode=[..]-C codegen-units=4 [..]` [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C embed-bitcode=[..]-C codegen-units=6 [..]` [COMPILING] bdep v0.0.1 ([ROOT]/foo/bdep) [RUNNING] `[..] rustc --crate-name bdep --edition=2015 bdep/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=6 [..]` [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[..] rustc --crate-name build_script_build --edition=2015 build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=6 [..]` [RUNNING] `[..][ROOT]/foo/target/release/build/foo-[HASH]/build-script-build` [foo 0.0.1] foo custom build PROFILE=release DEBUG=false OPT_LEVEL=3 [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]link -C opt-level=3 -C panic=abort[..]-C codegen-units=4 [..]` [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]link -C opt-level=3[..]-C codegen-units=4 [..]` [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/lib.rs [..]--emit=[..]link -C opt-level=3[..]-C codegen-units=4 --test [..]` [RUNNING] `[..] rustc --crate-name bench1 --edition=2015 benches/bench1.rs [..]--emit=[..]link -C opt-level=3[..]-C codegen-units=4 --test [..]` [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/main.rs [..]--emit=[..]link -C opt-level=3[..]-C codegen-units=4 --test [..]` [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/main.rs [..]--crate-type bin --emit=[..]link -C opt-level=3 -C panic=abort[..]-C codegen-units=4 [..]` [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] `[..][ROOT]/foo/target/release/deps/foo-[HASH][EXE] --bench` [RUNNING] `[..][ROOT]/foo/target/release/deps/foo-[HASH][EXE] --bench` [RUNNING] `[..][ROOT]/foo/target/release/deps/bench1-[HASH][EXE] --bench` "#]].unordered()) .run(); p.cargo("bench -vv") .with_stderr_data( str![[r#" [FRESH] bdep v0.0.1 ([ROOT]/foo/bdep) [FRESH] bar v0.0.1 ([ROOT]/foo/bar) [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] `[..][ROOT]/foo/target/release/deps/foo-[HASH][EXE] --bench` [RUNNING] `[..][ROOT]/foo/target/release/deps/foo-[HASH][EXE] --bench` [RUNNING] `[..][ROOT]/foo/target/release/deps/bench1-[HASH][EXE] --bench` "#]] .unordered(), ) .run(); } #[cargo_test] fn profile_selection_check_all_targets() { let p = all_target_project(); // `check` // NOTES: // - Dependency profiles: // Pkg Target Profile Action Reason // --- ------ ------- ------ ------ // bar lib dev* link For bdep // bar lib dev-panic metadata For tests/benches // bar lib dev metadata For lib/bins // bdep lib dev* link For foo build.rs // foo custom dev* link For build.rs // // `*` = wants panic, but it is cleared when args are built. // // - foo target list is: // Target Profile Mode // ------ ------- ---- // lib dev check // lib dev-panic check (for tests/benches) // lib dev-panic check-test (checking lib as a unittest) // example dev check // test dev-panic check-test // bench dev-panic check-test // bin dev check // bin dev-panic check-test (checking bin as a unittest) // p.cargo("check --all-targets -vv") .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C embed-bitcode=[..]-C codegen-units=5 [..]` [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]metadata -C embed-bitcode=[..]-C codegen-units=1 -C debuginfo=2 [..]` [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]metadata -C panic=abort -C embed-bitcode=[..]-C codegen-units=1 -C debuginfo=2 [..]` [COMPILING] bdep v0.0.1 ([ROOT]/foo/bdep) [RUNNING] `[..] rustc --crate-name bdep --edition=2015 bdep/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..]` [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[..] rustc --crate-name build_script_build --edition=2015 build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=5 [..]` [RUNNING] `[..][ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [foo 0.0.1] foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0 [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]metadata -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..]` [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]metadata[..]-C codegen-units=1 -C debuginfo=2 [..]` [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/lib.rs [..]--emit=[..]metadata[..]-C codegen-units=1 -C debuginfo=2 [..]--test [..]` [RUNNING] `[..] rustc --crate-name test1 --edition=2015 tests/test1.rs [..]--emit=[..]metadata[..]-C codegen-units=1 -C debuginfo=2 [..]--test [..]` [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/main.rs [..]--emit=[..]metadata[..]-C codegen-units=1 -C debuginfo=2 [..]--test [..]` [RUNNING] `[..] rustc --crate-name bench1 --edition=2015 benches/bench1.rs [..]--emit=[..]metadata[..]-C codegen-units=1 -C debuginfo=2 [..]--test [..]` [RUNNING] `[..] rustc --crate-name ex1 --edition=2015 examples/ex1.rs [..]--crate-type bin --emit=[..]metadata -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..]` [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/main.rs [..]--crate-type bin --emit=[..]metadata -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]].unordered()) .run(); // Starting with Rust 1.27, rustc emits `rmeta` files for bins, so // everything should be completely fresh. Previously, bins were being // rechecked. // See PR rust-lang/rust#49289 and issue rust-lang/cargo#3624. p.cargo("check --all-targets -vv") .with_stderr_data( str![[r#" [FRESH] bdep v0.0.1 ([ROOT]/foo/bdep) [FRESH] bar v0.0.1 ([ROOT]/foo/bar) [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn profile_selection_check_all_targets_release() { let p = all_target_project(); // `check --release` // See issue rust-lang/cargo#5218. // This is a pretty straightforward variant of // `profile_selection_check_all_targets` that uses `release` instead of // `dev` for all targets. p.cargo("check --all-targets --release -vv") .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=6 [..] [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]metadata -C opt-level=3 -C panic=abort[..]-C codegen-units=2 [..] [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]metadata -C opt-level=3[..]-C codegen-units=2 [..] [COMPILING] bdep v0.0.1 ([ROOT]/foo/bdep) [RUNNING] `[..] rustc --crate-name bdep --edition=2015 bdep/src/lib.rs [..]--crate-type lib --emit=[..]link [..]-C codegen-units=6 [..] [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[..] rustc --crate-name build_script_build --edition=2015 build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=6 [..] [RUNNING] `[..][ROOT]/foo/target/release/build/foo-[HASH]/build-script-build` [foo 0.0.1] foo custom build PROFILE=release DEBUG=false OPT_LEVEL=3 [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]metadata -C opt-level=3 -C panic=abort[..]-C codegen-units=2 [..] [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]metadata -C opt-level=3[..]-C codegen-units=2 [..] [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/lib.rs [..]--emit=[..]metadata -C opt-level=3[..]-C codegen-units=2 --test [..] [RUNNING] `[..] rustc --crate-name test1 --edition=2015 tests/test1.rs [..]--emit=[..]metadata -C opt-level=3[..]-C codegen-units=2 --test [..] [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/main.rs [..]--emit=[..]metadata -C opt-level=3[..]-C codegen-units=2 --test [..] [RUNNING] `[..] rustc --crate-name bench1 --edition=2015 benches/bench1.rs [..]--emit=[..]metadata -C opt-level=3[..]-C codegen-units=2 --test [..] [RUNNING] `[..] rustc --crate-name ex1 --edition=2015 examples/ex1.rs [..]--crate-type bin --emit=[..]metadata -C opt-level=3 -C panic=abort[..]-C codegen-units=2 [..] [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/main.rs [..]--crate-type bin --emit=[..]metadata -C opt-level=3 -C panic=abort[..]-C codegen-units=2 [..] [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]].unordered()) .run(); p.cargo("check --all-targets --release -vv") .with_stderr_data( str![[r#" [FRESH] bar v0.0.1 ([ROOT]/foo/bar) [FRESH] bdep v0.0.1 ([ROOT]/foo/bdep) [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn profile_selection_check_all_targets_test() { let p = all_target_project(); // `check --profile=test` // - Dependency profiles: // Pkg Target Profile Action Reason // --- ------ ------- ------ ------ // bar lib test* link For bdep // bar lib test-panic metadata For tests/benches // bdep lib test* link For foo build.rs // foo custom test* link For build.rs // // `*` = wants panic, but it is cleared when args are built. // // - foo target list is: // Target Profile Mode // ------ ------- ---- // lib test-panic check-test (for tests/benches) // lib test-panic check-test (checking lib as a unittest) // example test-panic check-test // test test-panic check-test // bench test-panic check-test // bin test-panic check-test // p.cargo("check --all-targets --profile=test -vv") .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..]` [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 [..]` [COMPILING] bdep v0.0.1 ([ROOT]/foo/bdep) [RUNNING] `[..] rustc --crate-name bdep --edition=2015 bdep/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..]` [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[..] rustc --crate-name build_script_build --edition=2015 build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=5 [..]` [RUNNING] `[..][ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [foo 0.0.1] foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0 [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 [..]` [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/lib.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 [..]--test [..]` [RUNNING] `[..] rustc --crate-name test1 --edition=2015 tests/test1.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 [..]--test [..]` [RUNNING] `[..] rustc --crate-name foo --edition=2015 src/main.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 [..]--test [..]` [RUNNING] `[..] rustc --crate-name bench1 --edition=2015 benches/bench1.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 [..]--test [..]` [RUNNING] `[..] rustc --crate-name ex1 --edition=2015 examples/ex1.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 [..]--test [..]` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]].unordered()) .run(); p.cargo("check --all-targets --profile=test -vv") .with_stderr_data( str![[r#" [FRESH] bdep v0.0.1 ([ROOT]/foo/bdep) [FRESH] bar v0.0.1 ([ROOT]/foo/bar) [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn profile_selection_doc() { let p = all_target_project(); // `doc` // NOTES: // - Dependency profiles: // Pkg Target Profile Action Reason // --- ------ ------- ------ ------ // bar lib dev* link For bdep // bar lib dev metadata For rustdoc // bdep lib dev* link For foo build.rs // foo custom dev* link For build.rs // // `*` = wants panic, but it is cleared when args are built. p.cargo("doc -vv") .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [DOCUMENTING] bar v0.0.1 ([ROOT]/foo/bar) [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..]` [RUNNING] `[..] rustdoc [..]--crate-name bar bar/src/lib.rs [..] [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]metadata -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..]` [COMPILING] bdep v0.0.1 ([ROOT]/foo/bdep) [RUNNING] `[..] rustc --crate-name bdep --edition=2015 bdep/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..]` [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[..] rustc --crate-name build_script_build --edition=2015 build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=5 [..]` [RUNNING] `[..][ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [foo 0.0.1] foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0 [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[..] rustdoc [..]--crate-name foo src/lib.rs [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]].unordered()) .run(); } cargo-0.91.0/tests/testsuite/profile_trim_paths.rs000064400000000000000000000723241046102023000204510ustar 00000000000000//! Tests for `-Ztrim-paths`. use crate::prelude::*; use cargo_test_support::basic_manifest; use cargo_test_support::git; use cargo_test_support::paths; use cargo_test_support::project; use cargo_test_support::registry::Package; use cargo_test_support::str; #[cargo_test] fn gated_manifest() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [profile.dev] trim-paths = "macro" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .masquerade_as_nightly_cargo(&["-Ztrim-paths"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: feature `trim-paths` is required ... "#]]) .run(); } #[cargo_test] fn gated_config_toml() { let p = project() .file( ".cargo/config.toml", r#" [profile.dev] trim-paths = "macro" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .masquerade_as_nightly_cargo(&["-Ztrim-paths"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] config profile `dev` is not valid (defined in `[ROOT]/foo/.cargo/config.toml`) Caused by: feature `trim-paths` is required ... "#]]) .run(); } #[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")] fn release_profile_default_to_object() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" "#, ) .file("src/lib.rs", "") .build(); p.cargo("build --release --verbose -Ztrim-paths") .masquerade_as_nightly_cargo(&["-Ztrim-paths"]) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..]-Zremap-path-scope=object --remap-path-prefix=[ROOT]/foo=. --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")] fn one_option() { let build = |option| { let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" [profile.dev] trim-paths = "{option}" "# ), ) .file("src/lib.rs", "") .build(); p.cargo("build -v -Ztrim-paths") }; for option in ["macro", "diagnostics", "object", "all"] { build(option) .masquerade_as_nightly_cargo(&["-Ztrim-paths"]) .with_stderr_data(&format!( "\ [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..]\ -Zremap-path-scope={option} \ --remap-path-prefix=[ROOT]/foo=. \ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s ", )) .run(); } build("none") .masquerade_as_nightly_cargo(&["-Ztrim-paths"]) .with_stderr_does_not_contain("[..]-Zremap-path-scope=[..]") .with_stderr_does_not_contain("[..]--remap-path-prefix=[..]") .run(); } #[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")] fn multiple_options() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [profile.dev] trim-paths = ["diagnostics", "macro", "object"] "#, ) .file("src/lib.rs", "") .build(); p.cargo("build --verbose -Ztrim-paths") .masquerade_as_nightly_cargo(&["-Ztrim-paths"]) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..]-Zremap-path-scope=diagnostics,macro,object --remap-path-prefix=[ROOT]/foo=. --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")] fn profile_merge_works() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [profile.dev] trim-paths = ["macro"] [profile.custom] inherits = "dev" trim-paths = ["diagnostics"] "#, ) .file("src/lib.rs", "") .build(); p.cargo("build -v -Ztrim-paths --profile custom") .masquerade_as_nightly_cargo(&["-Ztrim-paths"]) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..]-Zremap-path-scope=diagnostics --remap-path-prefix=[ROOT]/foo=. --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]` [FINISHED] `custom` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")] fn registry_dependency() { Package::new("bar", "0.0.1") .file("Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("src/lib.rs", r#"pub fn f() { println!("{}", file!()); }"#) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = "0.0.1" [profile.dev] trim-paths = "object" "#, ) .file("src/main.rs", "fn main() { bar::f(); }") .build(); p.cargo("run --verbose -Ztrim-paths") .masquerade_as_nightly_cargo(&["-Ztrim-paths"]) .with_stdout_data(str![[r#" -[..]/bar-0.0.1/src/lib.rs "#]]) // Omit the hash of Source URL .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) [COMPILING] bar v0.0.1 [RUNNING] `rustc [..]-Zremap-path-scope=object --remap-path-prefix=[ROOT]/home/.cargo/registry/src= --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]` [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..]-Zremap-path-scope=object --remap-path-prefix=[ROOT]/foo=. --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")] fn registry_dependency_with_build_script_codegen() { Package::new("bar", "0.0.1") .file("Cargo.toml", &basic_manifest("bar", "0.0.1")) .file( "build.rs", r#" fn main() { let out_dir = std::env::var("OUT_DIR").unwrap(); let dest = std::path::PathBuf::from(out_dir); std::fs::write( dest.join("bindings.rs"), "pub fn my_file() -> &'static str { file!() }", ) .unwrap(); } "#, ) .file( "src/lib.rs", r#" include!(concat!(env!("OUT_DIR"), "/bindings.rs")); "#, ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = "0.0.1" [profile.dev] trim-paths = "object" "#, ) .file( "src/main.rs", r#"fn main() { println!("{}", bar::my_file()); }"#, ) .build(); p.cargo("run --verbose -Ztrim-paths") .masquerade_as_nightly_cargo(&["-Ztrim-paths"]) // Macros should be sanitized .with_stdout_data(str![[r#" /cargo/build-dir/debug/build/bar-[HASH]/out/bindings.rs "#]]) // Omit the hash of Source URL .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) [COMPILING] bar v0.0.1 [RUNNING] `rustc --crate-name build_script_build [..]-Zremap-path-scope=object --remap-path-prefix=[ROOT]/home/.cargo/registry/src= --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]` [RUNNING] `[ROOT]/foo/target/debug/build/bar-[HASH]/build-script-build` [RUNNING] `rustc --crate-name bar [..]-Zremap-path-scope=object --remap-path-prefix=[ROOT]/home/.cargo/registry/src= --remap-path-prefix=[ROOT]/foo/target=/cargo/build-dir --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]-Zremap-path-scope=object --remap-path-prefix=[ROOT]/foo=. --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")] fn git_dependency() { let git_project = git::new("bar", |project| { project .file("Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("src/lib.rs", r#"pub fn f() { println!("{}", file!()); }"#) }); let url = git_project.url(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = {{ git = "{url}" }} [profile.dev] trim-paths = "object" "# ), ) .file("src/main.rs", "fn main() { bar::f(); }") .build(); p.cargo("run --verbose -Ztrim-paths") .masquerade_as_nightly_cargo(&["-Ztrim-paths"]) .with_stdout_data(str![[r#" bar-[..]/[..]/src/lib.rs "#]]) // Omit the hash of Source URL and commit .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/bar` [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.0.1 ([ROOTURL]/bar#[..]) [RUNNING] `rustc [..]-Zremap-path-scope=object --remap-path-prefix=[ROOT]/home/.cargo/git/checkouts= --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]` [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..]-Zremap-path-scope=object --remap-path-prefix=[ROOT]/foo=. --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")] fn path_dependency() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = { path = "cocktail-bar" } [profile.dev] trim-paths = "object" "#, ) .file("src/main.rs", "fn main() { bar::f(); }") .file("cocktail-bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file( "cocktail-bar/src/lib.rs", r#"pub fn f() { println!("{}", file!()); }"#, ) .build(); p.cargo("run --verbose -Ztrim-paths") .masquerade_as_nightly_cargo(&["-Ztrim-paths"]) .with_stdout_data(str![[r#" cocktail-bar/src/lib.rs "#]]) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.0.1 ([ROOT]/foo/cocktail-bar) [RUNNING] `rustc [..]-Zremap-path-scope=object --remap-path-prefix=[ROOT]/foo=. --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]` [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..]-Zremap-path-scope=object --remap-path-prefix=[ROOT]/foo=. --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")] fn path_dependency_outside_workspace() { let _bar = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("src/lib.rs", r#"pub fn f() { println!("{}", file!()); }"#) .build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = { path = "../bar" } [profile.dev] trim-paths = "object" "#, ) .file("src/main.rs", "fn main() { bar::f(); }") .build(); p.cargo("run --verbose -Ztrim-paths") .masquerade_as_nightly_cargo(&["-Ztrim-paths"]) .with_stdout_data(str![[r#" bar-0.0.1/src/lib.rs "#]]) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.0.1 ([ROOT]/bar) [RUNNING] `rustc [..]-Zremap-path-scope=object --remap-path-prefix=[ROOT]/bar=bar-0.0.1 --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]` [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..]-Zremap-path-scope=object --remap-path-prefix=[ROOT]/foo=. --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")] fn diagnostics_works() { Package::new("bar", "0.0.1") .file("Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("src/lib.rs", r#"pub fn f() { let unused = 0; }"#) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = "0.0.1" [profile.dev] trim-paths = "diagnostics" "#, ) .file("src/lib.rs", "") .build(); let registry_src = paths::home().join(".cargo/registry/src"); let registry_src = registry_src.display(); p.cargo("build -vv -Ztrim-paths") .masquerade_as_nightly_cargo(&["-Ztrim-paths"]) .with_stderr_line_without( &["[..]bar-0.0.1/src/lib.rs:1[..]"], &[&format!("{registry_src}")], ) .with_stderr_data(str![[r#" ... [RUNNING] `[..] rustc [..]-Zremap-path-scope=diagnostics --remap-path-prefix=[ROOT]/home/.cargo/registry/src= --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]` [WARNING] unused variable: `unused` ... [RUNNING] `[..] rustc [..]-Zremap-path-scope=diagnostics --remap-path-prefix=[ROOT]/foo=. --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]` ... "#]]) .run(); } #[cfg(target_os = "macos")] mod object_works { use super::*; fn inspect_debuginfo(path: &std::path::Path) -> Vec { std::process::Command::new("nm") .arg("-pa") .arg(path) .output() .expect("nm works") .stdout } #[cargo_test(requires = "nm", nightly, reason = "-Zremap-path-scope is unstable")] fn with_split_debuginfo_off() { object_works_helper("off", inspect_debuginfo); } #[cargo_test(requires = "nm", nightly, reason = "-Zremap-path-scope is unstable")] fn with_split_debuginfo_packed() { object_works_helper("packed", inspect_debuginfo); } #[cargo_test(requires = "nm", nightly, reason = "-Zremap-path-scope is unstable")] fn with_split_debuginfo_unpacked() { object_works_helper("unpacked", inspect_debuginfo); } } #[cfg(target_os = "linux")] mod object_works { use super::*; fn inspect_debuginfo(path: &std::path::Path) -> Vec { std::process::Command::new("readelf") .arg("--debug-dump=info") .arg("--debug-dump=no-follow-links") // older version can't recognized but just a warning .arg(path) .output() .expect("readelf works") .stdout } #[cargo_test( requires = "readelf", nightly, reason = "-Zremap-path-scope is unstable" )] fn with_split_debuginfo_off() { object_works_helper("off", inspect_debuginfo); } #[cargo_test( requires = "readelf", nightly, reason = "-Zremap-path-scope is unstable" )] fn with_split_debuginfo_packed() { object_works_helper("packed", inspect_debuginfo); } #[cargo_test( requires = "readelf", nightly, reason = "-Zremap-path-scope is unstable" )] fn with_split_debuginfo_unpacked() { object_works_helper("unpacked", inspect_debuginfo); } } #[cfg(target_env = "msvc")] mod object_works { use super::*; fn inspect_debuginfo(path: &std::path::Path) -> Vec { std::process::Command::new("strings") .arg(path) .output() .expect("strings works") .stdout } // windows-msvc supports split-debuginfo=packed only #[cargo_test( requires = "strings", nightly, reason = "-Zremap-path-scope is unstable" )] fn with_split_debuginfo_packed() { object_works_helper("packed", inspect_debuginfo); } } fn object_works_helper(split_debuginfo: &str, run: impl Fn(&std::path::Path) -> Vec) { let registry_src = paths::home().join(".cargo/registry/src"); let registry_src_bytes = registry_src.as_os_str().as_encoded_bytes(); let rust_src = "/lib/rustc/src/rust".as_bytes(); Package::new("bar", "0.0.1") .file("Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("src/lib.rs", r#"pub fn f() { println!("{}", file!()); }"#) .publish(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = "0.0.1" [profile.dev] split-debuginfo = "{split_debuginfo}" "# ), ) .file("src/main.rs", "fn main() { bar::f(); }") .build(); let pkg_root = p.root(); let pkg_root = pkg_root.as_os_str().as_encoded_bytes(); p.cargo("build").run(); let bin_path = p.bin("foo"); assert!(bin_path.is_file()); let stdout = run(&bin_path); // On windows-msvc every debuginfo is in pdb file, so can't find anything here. if cfg!(target_env = "msvc") { // TODO: re-enable this check when rustc bootstrap disables remapping // // assert!(memchr::memmem::find(&stdout, rust_src).is_some()); assert!(memchr::memmem::find(&stdout, registry_src_bytes).is_none()); assert!(memchr::memmem::find(&stdout, pkg_root).is_none()); } else { // TODO: re-enable this check when rustc bootstrap disables remapping // // assert!(memchr::memmem::find(&stdout, rust_src).is_some()); assert!(memchr::memmem::find(&stdout, registry_src_bytes).is_some()); assert!(memchr::memmem::find(&stdout, pkg_root).is_some()); } p.cargo("clean").run(); p.cargo("build --verbose -Ztrim-paths") .arg("--config") .arg(r#"profile.dev.trim-paths="object""#) .masquerade_as_nightly_cargo(&["-Ztrim-paths"]) .with_stderr_data(&format!( "\ [COMPILING] bar v0.0.1 [RUNNING] `rustc [..]-C split-debuginfo={split_debuginfo} [..]\ -Zremap-path-scope=object \ --remap-path-prefix=[ROOT]/home/.cargo/registry/src= \ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..]-C split-debuginfo={split_debuginfo} [..]\ -Zremap-path-scope=object \ --remap-path-prefix=[ROOT]/foo=. \ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s ", )) .run(); let bin_path = p.bin("foo"); assert!(bin_path.is_file()); let stdout = run(&bin_path); assert!(memchr::memmem::find(&stdout, rust_src).is_none()); for line in stdout.split(|c| c == &b'\n') { let registry = memchr::memmem::find(line, registry_src_bytes).is_none(); let local = memchr::memmem::find(line, pkg_root).is_none(); if registry && local { continue; } #[cfg(target_os = "macos")] { // `OSO` symbols can't be trimmed at this moment. // See if memchr::memmem::find(line, b" OSO ").is_some() { continue; } } panic!( "unexpected untrimmed symbol: {}", String::from_utf8(line.into()).unwrap() ); } } // TODO: might want to move to test/testsuite/build_script.rs once stabilized. #[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")] fn custom_build_env_var_trim_paths() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" "#, ) .file("src/lib.rs", "") .file("build.rs", "") .build(); let test_cases = [ ("[]", "none"), ("\"all\"", "all"), ("\"diagnostics\"", "diagnostics"), ("\"macro\"", "macro"), ("\"none\"", "none"), ("\"object\"", "object"), ("false", "none"), ("true", "all"), ( r#"["diagnostics", "macro", "object"]"#, "diagnostics,macro,object", ), ]; for (opts, expected) in test_cases { p.change_file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" [profile.dev] trim-paths = {opts} "# ), ); p.change_file( "build.rs", &format!( r#" fn main() {{ assert_eq!( std::env::var("CARGO_TRIM_PATHS").unwrap().as_str(), "{expected}", ); }} "# ), ); p.cargo("build -Ztrim-paths") .masquerade_as_nightly_cargo(&["-Ztrim-paths"]) .run(); } } // This test is disabled, as it currently doesn't work due to issues with lldb. #[cfg(any())] #[cfg(unix)] #[cargo_test(requires = "lldb", nightly, reason = "-Zremap-path-scope is unstable")] fn lldb_works_after_trimmed() { use cargo_test_support::compare::assert_e2e; use cargo_util::is_ci; if !is_ci() { // On macOS lldb requires elevated privileges to run developer tools. // See rust-lang/cargo#13413 return; } let run_lldb = |path| { std::process::Command::new("lldb") .args(["-o", "breakpoint set --file src/main.rs --line 4"]) .args(["-o", "run"]) .args(["-o", "continue"]) .args(["-o", "exit"]) .arg("--no-use-colors") .arg(path) .output() .expect("lldb works") }; let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [profile.dev] trim-paths = "object" "#, ) .file( "src/main.rs", r#" fn main() { let msg = "Hello, Ferris!"; println!("{msg}"); } "#, ) .build(); p.cargo("build --verbose -Ztrim-paths") .masquerade_as_nightly_cargo(&["-Ztrim-paths"]) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..]-Zremap-path-scope=object --remap-path-prefix=[ROOT]/foo=. --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let bin_path = p.bin("foo"); assert!(bin_path.is_file()); let stdout = String::from_utf8(run_lldb(bin_path).stdout).unwrap(); assert_e2e().eq( &stdout, str![[r#" ... [..]stopped[..] [..]stop reason = breakpoint 1.1[..] ... (lldb) continue Hello, Ferris! ... "#]], ); } // This test is disabled, as it currently doesn't work. #[cfg(any())] #[cfg(target_env = "msvc")] #[cargo_test(requires = "cdb", nightly, reason = "-Zremap-path-scope is unstable")] fn cdb_works_after_trimmed() { use cargo_test_support::compare::assert_e2e; let run_debugger = |path| { std::process::Command::new("cdb") .args(["-c", "bp `main.rs:4`;g;g;q"]) .arg(path) .output() .expect("debugger works") }; let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [profile.dev] trim-paths = "object" "#, ) .file( "src/main.rs", r#" fn main() { let msg = "Hello, Ferris!"; println!("{msg}"); } "#, ) .build(); p.cargo("build --verbose -Ztrim-paths") .masquerade_as_nightly_cargo(&["-Ztrim-paths"]) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..]-Zremap-path-scope=object --remap-path-prefix=[ROOT]/foo=. --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let bin_path = p.bin("foo"); assert!(bin_path.is_file()); let stdout = String::from_utf8(run_debugger(bin_path).stdout).unwrap(); assert_e2e().eq( &stdout, str![[r#" ... Breakpoint 0 hit Hello, Ferris! ... "#]], ); } #[cargo_test(nightly, reason = "rustdoc --remap-path-prefix is unstable")] fn rustdoc_without_diagnostics_scope() { Package::new("bar", "0.0.1") .file("Cargo.toml", &basic_manifest("bar", "0.0.1")) .file( "src/lib.rs", r#" /// pub struct Bar; "#, ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = "0.0.1" [profile.dev] trim-paths = "object" "#, ) .file("src/lib.rs", "") .build(); p.cargo("doc -vv -Ztrim-paths") .masquerade_as_nightly_cargo(&["-Ztrim-paths"]) .with_stderr_data(str![[r#" ... [WARNING] unopened HTML tag `script` --> [ROOT]/home/.cargo/registry/src/-[HASH]/bar-0.0.1/src/lib.rs:2:17 ... "#]]) .run(); } #[cargo_test(nightly, reason = "rustdoc --remap-path-prefix is unstable")] fn rustdoc_diagnostics_works() { // This is expected to work after rust-lang/rust#128736 Package::new("bar", "0.0.1") .file("Cargo.toml", &basic_manifest("bar", "0.0.1")) .file( "src/lib.rs", r#" /// pub struct Bar; "#, ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = "0.0.1" [profile.dev] trim-paths = "diagnostics" "#, ) .file("src/lib.rs", "") .build(); p.cargo("doc -vv -Ztrim-paths") .masquerade_as_nightly_cargo(&["-Ztrim-paths"]) .with_stderr_data(str![[r#" ... [RUNNING] `[..]rustc [..]-Zremap-path-scope=diagnostics --remap-path-prefix=[ROOT]/home/.cargo/registry/src= --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]` ... [WARNING] unopened HTML tag `script` --> -[..]/bar-0.0.1/src/lib.rs:2:17 ... "#]]) .run(); } cargo-0.91.0/tests/testsuite/profiles.rs000064400000000000000000000617271046102023000164070ustar 00000000000000//! Tests for profiles. use std::env; use crate::prelude::*; use cargo_test_support::registry::Package; use cargo_test_support::{project, rustc_host, str}; #[cargo_test] fn profile_overrides() { let p = project() .file( "Cargo.toml", r#" [package] name = "test" version = "0.0.0" edition = "2015" authors = [] [profile.dev] opt-level = 1 debug = false rpath = true "#, ) .file("src/lib.rs", "") .build(); p.cargo("build -v").with_stderr_data(str![[r#" [COMPILING] test v0.0.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name test --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]link[..] -C opt-level=1[..] -C debug-assertions=on[..] -C metadata=[..] -C rpath --out-dir [ROOT]/foo/target/debug/deps [..] -L dependency=[ROOT]/foo/target/debug/deps` [FINISHED] `dev` profile [optimized] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn opt_level_override_0() { let p = project() .file( "Cargo.toml", r#" [package] name = "test" version = "0.0.0" edition = "2015" authors = [] [profile.dev] opt-level = 0 "#, ) .file("src/lib.rs", "") .build(); p.cargo("build -v").with_stderr_data(str![[r#" [COMPILING] test v0.0.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name test --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C debuginfo=2 [..] -C metadata=[..] --out-dir [ROOT]/foo/target/debug/deps -L dependency=[ROOT]/foo/target/debug/deps` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn debug_override_1() { let p = project() .file( "Cargo.toml", r#" [package] name = "test" version = "0.0.0" edition = "2015" authors = [] [profile.dev] debug = 1 "#, ) .file("src/lib.rs", "") .build(); p.cargo("build -v").with_stderr_data(str![[r#" [COMPILING] test v0.0.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name test --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C debuginfo=1 [..]-C metadata=[..] --out-dir [ROOT]/foo/target/debug/deps -L dependency=[ROOT]/foo/target/debug/deps` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } fn check_opt_level_override(profile_level: &str, rustc_level: &str) { let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "test" version = "0.0.0" edition = "2015" authors = [] [profile.dev] opt-level = {level} "#, level = profile_level ), ) .file("src/lib.rs", "") .build(); p.cargo("build -v") .with_stderr_data(&format!( "\ [COMPILING] test v0.0.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name test --edition=2015 src/lib.rs [..]--crate-type lib \ --emit=[..]link \ -C opt-level={level}[..]\ -C debuginfo=2 [..]\ -C debug-assertions=on[..] \ -C metadata=[..] \ --out-dir [..] \ -L dependency=[ROOT]/foo/target/debug/deps` [FINISHED] `dev` profile [..]+ debuginfo] target(s) in [ELAPSED]s ", level = rustc_level )) .run(); } #[cargo_test] fn opt_level_overrides() { for &(profile_level, rustc_level) in &[ ("1", "1"), ("2", "2"), ("3", "3"), ("\"s\"", "s"), ("\"z\"", "z"), ] { check_opt_level_override(profile_level, rustc_level) } } #[cargo_test] fn top_level_overrides_deps() { let p = project() .file( "Cargo.toml", r#" [package] name = "test" version = "0.0.0" edition = "2015" authors = [] [profile.release] opt-level = 1 debug = true [dependencies.foo] path = "foo" "#, ) .file("src/lib.rs", "") .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" authors = [] [profile.release] opt-level = 0 debug = false [lib] name = "foo" crate-type = ["dylib", "rlib"] "#, ) .file("foo/src/lib.rs", "") .build(); p.cargo("build -v --release") .with_stderr_data(&format!( "\ [LOCKING] 1 package to latest compatible version [COMPILING] foo v0.0.0 ([ROOT]/foo/foo) [RUNNING] `rustc --crate-name foo --edition=2015 foo/src/lib.rs [..]\ --crate-type dylib --crate-type rlib \ --emit=[..]link \ -C prefer-dynamic \ -C opt-level=1[..]\ -C debuginfo=2 [..]\ -C metadata=[..] \ --out-dir [ROOT]/foo/target/release/deps \ -L dependency=[ROOT]/foo/target/release/deps` [COMPILING] test v0.0.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name test --edition=2015 src/lib.rs [..]--crate-type lib \ --emit=[..]link \ -C opt-level=1[..]\ -C debuginfo=2 [..]\ -C metadata=[..] \ --out-dir [..] \ -L dependency=[ROOT]/foo/target/release/deps \ --extern foo=[ROOT]/foo/target/release/deps/\ {prefix}foo[..]{suffix} \ --extern foo=[ROOT]/foo/target/release/deps/libfoo.rlib` [FINISHED] `release` profile [optimized + debuginfo] target(s) in [ELAPSED]s ", prefix = env::consts::DLL_PREFIX, suffix = env::consts::DLL_SUFFIX )) .run(); } #[cargo_test] fn profile_in_non_root_manifest_triggers_a_warning() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [workspace] members = ["bar"] [profile.dev] debug = false "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] workspace = ".." [profile.dev] opt-level = 1 "#, ) .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("build -v") .cwd("bar") .with_stderr_data(str![[r#" [WARNING] profiles for the non root package will be ignored, specify profiles at the workspace root: package: [ROOT]/foo/bar/Cargo.toml workspace: [ROOT]/foo/Cargo.toml [COMPILING] bar v0.1.0 ([ROOT]/foo/bar) [RUNNING] `rustc [..]` [FINISHED] `dev` profile [unoptimized] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn profile_in_virtual_manifest_works() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar"] [profile.dev] opt-level = 1 debug = false "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] workspace = ".." "#, ) .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("build -v") .cwd("bar") .with_stderr_data(str![[r#" [COMPILING] bar v0.1.0 ([ROOT]/foo/bar) [RUNNING] `rustc [..]` [FINISHED] `dev` profile [optimized] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn profile_lto_string_bool_dev() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [profile.dev] lto = "true" "#, ) .file("src/lib.rs", "") .build(); p.cargo("build") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: `lto` setting of string `"true"` for `dev` profile is not a valid setting, must be a boolean (`true`/`false`) or a string (`"thin"`/`"fat"`/`"off"`) or omitted. "#]]) .run(); } #[cargo_test] fn profile_panic_test_bench() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [profile.test] panic = "abort" [profile.bench] panic = "abort" "#, ) .file("src/lib.rs", "") .build(); p.cargo("build") .with_stderr_data(str![[r#" [WARNING] `panic` setting is ignored for `bench` profile [WARNING] `panic` setting is ignored for `test` profile [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn profile_doc_deprecated() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [profile.doc] opt-level = 0 "#, ) .file("src/lib.rs", "") .build(); p.cargo("build") .with_stderr_data(str![[r#" [WARNING] profile `doc` is deprecated and has no effect ... "#]]) .run(); } #[cargo_test] fn panic_unwind_does_not_build_twice() { // Check for a bug where `lib` was built twice, once with panic set and // once without. Since "unwind" is the default, they are the same and // should only be built once. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [profile.dev] panic = "unwind" "#, ) .file("src/lib.rs", "") .file("src/main.rs", "fn main() {}") .file("tests/t1.rs", "") .build(); p.cargo("test -v --tests --no-run") .with_stderr_data( str![[r#" [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib [..]` [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..] --test [..]` [RUNNING] `rustc --crate-name foo --edition=2015 src/main.rs [..]--crate-type bin [..]` [RUNNING] `rustc --crate-name foo --edition=2015 src/main.rs [..] --test [..]` [RUNNING] `rustc --crate-name t1 --edition=2015 tests/t1.rs [..]` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [EXECUTABLE] `[ROOT]/foo/target/debug/deps/foo-[HASH][EXE]` [EXECUTABLE] `[ROOT]/foo/target/debug/deps/foo-[HASH][EXE]` [EXECUTABLE] `[ROOT]/foo/target/debug/deps/t1-[HASH][EXE]` "#]] .unordered(), ) .run(); } #[cargo_test] fn debug_0_report() { // The finished line handles 0 correctly. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [profile.dev] debug = 0 "#, ) .file("src/lib.rs", "") .build(); p.cargo("build -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]` [FINISHED] `dev` profile [unoptimized] target(s) in [ELAPSED]s "#]]) .with_stderr_does_not_contain("-C debuginfo") .run(); } #[cargo_test] fn thin_lto_works() { let p = project() .file( "Cargo.toml", r#" [package] name = "top" version = "0.5.0" edition = "2015" authors = [] [profile.release] lto = 'thin' "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build --release -v") .with_stderr_data(str![[r#" [COMPILING] top v0.5.0 ([ROOT]/foo) [RUNNING] `rustc [..] -C lto=thin [..]` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn strip_works() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [profile.release] strip = 'symbols' "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build --release -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc [..] -C strip=symbols [..]` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn strip_passes_unknown_option_to_rustc() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [profile.release] strip = 'unknown' "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build --release -v") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc [..] -C strip=unknown [..]` [ERROR] incorrect value `unknown` for [..] `strip` [..] was expected ... "#]]) .run(); } #[cargo_test] fn strip_accepts_true_to_strip_symbols() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [profile.release] strip = true "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build --release -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc [..] -C strip=symbols [..]` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn strip_accepts_false_to_disable_strip() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [profile.release] strip = false "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build --release -v") .with_stderr_does_not_contain("[RUNNING] `rustc [..] -C strip[..]`") .run(); } #[cargo_test] fn strip_debuginfo_in_release() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build --release -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc [..] -C strip=debuginfo[..]` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build --release -v --target") .arg(rustc_host()) .with_stderr_data(str![[r#" [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc [..] -C strip=debuginfo[..]` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn strip_debuginfo_without_debug() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [profile.dev] debug = 0 "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] -C strip=debuginfo[..]` [FINISHED] `dev` profile [unoptimized] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn do_not_strip_debuginfo_with_requested_debug() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { path = "bar" } [profile.release.package.bar] debug = 1 "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" "#, ) .file("bar/src/lib.rs", "") .build(); p.cargo("build --release -v") .with_stderr_does_not_contain("[RUNNING] `rustc [..] -C strip=debuginfo[..]`") .run(); } #[cargo_test] fn rustflags_works() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["profile-rustflags"] [profile.dev] rustflags = ["-C", "link-dead-code=yes"] [package] name = "foo" version = "0.0.1" edition = "2015" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build -v") .masquerade_as_nightly_cargo(&["profile-rustflags"]) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] -C link-dead-code=yes [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn rustflags_works_with_env() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["profile-rustflags"] [package] name = "foo" version = "0.0.1" edition = "2015" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build -v") .env("CARGO_PROFILE_DEV_RUSTFLAGS", "-C link-dead-code=yes") .masquerade_as_nightly_cargo(&["profile-rustflags"]) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] -C link-dead-code=yes [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn rustflags_requires_cargo_feature() { let p = project() .file( "Cargo.toml", r#" [profile.dev] rustflags = ["-C", "link-dead-code=yes"] [package] name = "foo" version = "0.0.1" edition = "2015" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build -v") .masquerade_as_nightly_cargo(&["profile-rustflags"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: feature `profile-rustflags` is required The package requires the Cargo feature called `profile-rustflags`, but that feature is not stabilized in this version of Cargo (1.[..]). Consider adding `cargo-features = ["profile-rustflags"]` to the top of Cargo.toml (above the [package] table) to tell Cargo you are opting in to use this unstable feature. See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#profile-rustflags-option for more information about the status of this feature. "#]]) .run(); Package::new("bar", "1.0.0").publish(); p.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = "1.0" [profile.dev.package.bar] rustflags = ["-C", "link-dead-code=yes"] "#, ); p.cargo("check") .masquerade_as_nightly_cargo(&["profile-rustflags"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: feature `profile-rustflags` is required The package requires the Cargo feature called `profile-rustflags`, but that feature is not stabilized in this version of Cargo (1.[..]). Consider adding `cargo-features = ["profile-rustflags"]` to the top of Cargo.toml (above the [package] table) to tell Cargo you are opting in to use this unstable feature. See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#profile-rustflags-option for more information about the status of this feature. "#]]) .run(); } #[cargo_test] fn debug_options_valid() { let build = |option| { let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" authors = [] version = "0.0.0" edition = "2015" [profile.dev] debug = "{option}" "# ), ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build -v") }; for (option, cli) in [ ("line-directives-only", "line-directives-only"), ("line-tables-only", "line-tables-only"), ("limited", "1"), ("full", "2"), ] { build(option) .with_stderr_data(&format!( "\ ... [RUNNING] `rustc [..]-C debuginfo={cli} [..]` ... " )) .run(); } build("none") .with_stderr_does_not_contain("[..]-C debuginfo[..]") .run(); } #[cargo_test] fn profile_hint_mostly_unused_warn_without_gate() { Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = "1.0" [profile.dev.package.bar] hint-mostly-unused = true "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check -v") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [WARNING] bar@1.0.0: ignoring 'hint-mostly-unused' profile option, pass `-Zprofile-hint-mostly-unused` to enable it [CHECKING] bar v1.0.0 [RUNNING] `rustc --crate-name bar [..]` [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .with_stderr_does_not_contain("-Zhint-mostly-unused") .run(); } #[cargo_test(nightly, reason = "-Zhint-mostly-unused is unstable")] fn profile_hint_mostly_unused_nightly() { Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = "1.0" [profile.dev.package.bar] hint-mostly-unused = true "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check -Zprofile-hint-mostly-unused -v") .masquerade_as_nightly_cargo(&["profile-hint-mostly-unused"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [CHECKING] bar v1.0.0 [RUNNING] `rustc --crate-name bar [..] -Zhint-mostly-unused [..]` [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .with_stderr_does_not_contain( "[RUNNING] `rustc --crate-name foo [..] -Zhint-mostly-unused [..]", ) .run(); } cargo-0.91.0/tests/testsuite/progress.rs000064400000000000000000000067571046102023000164320ustar 00000000000000//! Tests for progress bar. use crate::prelude::*; use cargo_test_support::project; use cargo_test_support::registry::Package; use cargo_test_support::str; #[cargo_test] fn bad_progress_config_unknown_when() { let p = project() .file( ".cargo/config.toml", r#" [term] progress = { when = 'unknown' } "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] error in [ROOT]/foo/.cargo/config.toml: could not load config key `term.progress.when` Caused by: unknown variant `unknown`, expected one of `auto`, `never`, `always` "#]]) .run(); } #[cargo_test] fn bad_progress_config_missing_width() { let p = project() .file( ".cargo/config.toml", r#" [term] progress = { when = 'always' } "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] "always" progress requires a `width` key "#]]) .run(); } #[cargo_test] fn default_progress_is_auto() { let p = project() .file( ".cargo/config.toml", r#" [term] progress = { width = 1000 } "#, ) .file("src/lib.rs", "") .build(); p.cargo("check").run(); } #[cargo_test] fn always_shows_progress() { const N: usize = 3; let mut deps = String::new(); for i in 1..=N { Package::new(&format!("dep{}", i), "1.0.0").publish(); deps.push_str(&format!("dep{} = \"1.0\"\n", i)); } let p = project() .file( ".cargo/config.toml", r#" [term] progress = { when = 'always', width = 100 } "#, ) .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [dependencies] {} "#, deps ), ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data( str![[r#" [DOWNLOADING] [..] crate [..] [DOWNLOADED] 3 crates ([..]) in [..]s [BUILDING] [..] [..]/4: [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s ... "#]] .unordered(), ) .run(); } #[cargo_test] fn never_progress() { const N: usize = 3; let mut deps = String::new(); for i in 1..=N { Package::new(&format!("dep{}", i), "1.0.0").publish(); deps.push_str(&format!("dep{} = \"1.0\"\n", i)); } let p = project() .file( ".cargo/config.toml", r#" [term] progress = { when = 'never' } "#, ) .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [dependencies] {} "#, deps ), ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_stderr_does_not_contain("[DOWNLOADING] [..] crates [..]") .with_stderr_does_not_contain("[..][DOWNLOADED] 3 crates ([..]) in [..]") .with_stderr_does_not_contain("[BUILDING] [..] [..]/4: [..]") .run(); } cargo-0.91.0/tests/testsuite/pub_priv.rs000064400000000000000000000460041046102023000164010ustar 00000000000000//! Tests for public/private dependencies. use crate::prelude::*; use cargo_test_support::project; use cargo_test_support::registry::{Dependency, Package}; use cargo_test_support::str; #[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")] fn exported_priv_warning() { Package::new("priv_dep", "0.1.0") .file("src/lib.rs", "pub struct FromPriv;") .publish(); let p = project() .file( "Cargo.toml", r#" cargo-features = ["public-dependency"] [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] priv_dep = "0.1.0" "#, ) .file( "src/lib.rs", " extern crate priv_dep; pub fn use_priv(_: priv_dep::FromPriv) {} ", ) .build(); p.cargo("check --message-format=short") .masquerade_as_nightly_cargo(&["public-dependency"]) .with_stderr_data(str![[r#" ... src/lib.rs:3:13: [WARNING] type `FromPriv` from private dependency 'priv_dep' in public interface ... "#]]) .run(); } #[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")] fn exported_pub_dep() { Package::new("pub_dep", "0.1.0") .file("src/lib.rs", "pub struct FromPub;") .publish(); let p = project() .file( "Cargo.toml", r#" cargo-features = ["public-dependency"] [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] pub_dep = {version = "0.1.0", public = true} "#, ) .file( "src/lib.rs", " extern crate pub_dep; pub fn use_pub(_: pub_dep::FromPub) {} ", ) .build(); p.cargo("check --message-format=short") .masquerade_as_nightly_cargo(&["public-dependency"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] pub_dep v0.1.0 (registry `dummy-registry`) [CHECKING] pub_dep v0.1.0 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] pub fn requires_nightly_cargo() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["public-dependency"] "#, ) .file("src/lib.rs", "") .build(); p.cargo("check --message-format=short") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: the cargo feature `public-dependency` requires a nightly version of Cargo, but this is the `stable` channel See https://doc.rust-lang.org/book/appendix-07-nightly-rust.html for more information about Rust release channels. See https://doc.rust-lang.org/[..]cargo/reference/unstable.html#public-dependency for more information about using this feature. "#]]) .run(); } #[cargo_test] fn requires_feature() { Package::new("pub_dep", "0.1.0") .file("src/lib.rs", "") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] pub_dep = { version = "0.1.0", public = true } "#, ) .file("src/lib.rs", "") .build(); p.cargo("check --message-format=short") .masquerade_as_nightly_cargo(&["public-dependency"]) .with_stderr_data(str![[r#" [WARNING] ignoring `public` on dependency pub_dep, pass `-Zpublic-dependency` to enable support for it [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] pub_dep v0.1.0 (registry `dummy-registry`) [CHECKING] pub_dep v0.1.0 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn pub_dev_dependency() { Package::new("pub_dep", "0.1.0") .file("src/lib.rs", "pub struct FromPub;") .publish(); let p = project() .file( "Cargo.toml", r#" cargo-features = ["public-dependency"] [package] name = "foo" version = "0.0.1" edition = "2015" [dev-dependencies] pub_dep = {version = "0.1.0", public = true} "#, ) .file( "src/lib.rs", " extern crate pub_dep; pub fn use_pub(_: pub_dep::FromPub) {} ", ) .build(); p.cargo("check --message-format=short") .masquerade_as_nightly_cargo(&["public-dependency"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: 'public' specifier can only be used on regular dependencies, not dev-dependencies "#]]) .run(); } #[cargo_test] fn pub_dev_dependency_without_feature() { Package::new("pub_dep", "0.1.0") .file("src/lib.rs", "pub struct FromPub;") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dev-dependencies] pub_dep = {version = "0.1.0", public = true} "#, ) .file( "tests/mod.rs", " extern crate pub_dep; pub fn use_pub(_: pub_dep::FromPub) {} ", ) .build(); p.cargo("check --message-format=short") .with_stderr_data(str![[r#" [WARNING] 'public' specifier can only be used on regular dependencies, not dev-dependencies [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")] fn workspace_pub_disallowed() { Package::new("foo1", "0.1.0") .file("src/lib.rs", "pub struct FromFoo;") .publish(); Package::new("foo2", "0.1.0") .file("src/lib.rs", "pub struct FromFoo;") .publish(); Package::new("foo3", "0.1.0") .file("src/lib.rs", "pub struct FromFoo;") .publish(); let p = project() .file( "Cargo.toml", r#" cargo-features = ["public-dependency"] [package] name = "foo" version = "0.0.1" edition = "2015" [workspace.dependencies] foo1 = "0.1.0" foo2 = { version = "0.1.0", public = true } foo3 = { version = "0.1.0", public = false } [dependencies] foo1 = { workspace = true, public = true } foo2 = { workspace = true } foo3 = { workspace = true, public = true } "#, ) .file( "src/lib.rs", " #![deny(exported_private_dependencies)] pub fn use_priv1(_: foo1::FromFoo) {} pub fn use_priv2(_: foo2::FromFoo) {} pub fn use_priv3(_: foo3::FromFoo) {} ", ) .build(); p.cargo("check") .masquerade_as_nightly_cargo(&["public-dependency"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: foo2 is public, but workspace dependencies cannot be public "#]]) .run(); } #[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")] fn allow_priv_in_tests() { Package::new("priv_dep", "0.1.0") .file("src/lib.rs", "pub struct FromPriv;") .publish(); let p = project() .file( "Cargo.toml", r#" cargo-features = ["public-dependency"] [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] priv_dep = {version = "0.1.0", public = false} "#, ) .file( "tests/mod.rs", " extern crate priv_dep; pub fn use_priv(_: priv_dep::FromPriv) {} ", ) .build(); p.cargo("check --tests --message-format=short") .masquerade_as_nightly_cargo(&["public-dependency"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] priv_dep v0.1.0 (registry `dummy-registry`) [CHECKING] priv_dep v0.1.0 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")] fn allow_priv_in_benchs() { Package::new("priv_dep", "0.1.0") .file("src/lib.rs", "pub struct FromPriv;") .publish(); let p = project() .file( "Cargo.toml", r#" cargo-features = ["public-dependency"] [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] priv_dep = {version = "0.1.0", public = false} "#, ) .file( "benches/mod.rs", " extern crate priv_dep; pub fn use_priv(_: priv_dep::FromPriv) {} ", ) .build(); p.cargo("check --benches --message-format=short") .masquerade_as_nightly_cargo(&["public-dependency"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] priv_dep v0.1.0 (registry `dummy-registry`) [CHECKING] priv_dep v0.1.0 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")] fn allow_priv_in_bins() { Package::new("priv_dep", "0.1.0") .file("src/lib.rs", "pub struct FromPriv;") .publish(); let p = project() .file( "Cargo.toml", r#" cargo-features = ["public-dependency"] [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] priv_dep = {version = "0.1.0", public = false} "#, ) .file( "src/main.rs", " extern crate priv_dep; pub fn use_priv(_: priv_dep::FromPriv) {} fn main() {} ", ) .build(); p.cargo("check --bins --message-format=short") .masquerade_as_nightly_cargo(&["public-dependency"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] priv_dep v0.1.0 (registry `dummy-registry`) [CHECKING] priv_dep v0.1.0 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")] fn allow_priv_in_examples() { Package::new("priv_dep", "0.1.0") .file("src/lib.rs", "pub struct FromPriv;") .publish(); let p = project() .file( "Cargo.toml", r#" cargo-features = ["public-dependency"] [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] priv_dep = {version = "0.1.0", public = false} "#, ) .file( "examples/lib.rs", " extern crate priv_dep; pub fn use_priv(_: priv_dep::FromPriv) {} fn main() {} ", ) .build(); p.cargo("check --examples --message-format=short") .masquerade_as_nightly_cargo(&["public-dependency"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] priv_dep v0.1.0 (registry `dummy-registry`) [CHECKING] priv_dep v0.1.0 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")] fn allow_priv_in_custom_build() { Package::new("priv_dep", "0.1.0") .file("src/lib.rs", "pub struct FromPriv;") .publish(); let p = project() .file( "Cargo.toml", r#" cargo-features = ["public-dependency"] [package] name = "foo" version = "0.0.1" edition = "2015" [build-dependencies] priv_dep = "0.1.0" "#, ) .file("src/main.rs", "fn main() {}") .file( "build.rs", " extern crate priv_dep; pub fn use_priv(_: priv_dep::FromPriv) {} fn main() {} ", ) .build(); p.cargo("check --all-targets --message-format=short") .masquerade_as_nightly_cargo(&["public-dependency"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] priv_dep v0.1.0 (registry `dummy-registry`) [COMPILING] priv_dep v0.1.0 [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")] fn publish_package_with_public_dependency() { Package::new("pub_bar", "0.1.0") .file("src/lib.rs", "pub struct FromPub;") .publish(); Package::new("bar", "0.1.0") .cargo_feature("public-dependency") .add_dep(Dependency::new("pub_bar", "0.1.0").public(true)) .file( "src/lib.rs", " extern crate pub_bar; pub use pub_bar::FromPub as BarFromPub; ", ) .publish(); let p = project() .file( "Cargo.toml", r#" cargo-features = ["public-dependency"] [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = {version = "0.1.0", public = true} "#, ) .file( "src/lib.rs", " extern crate bar; pub fn use_pub(_: bar::BarFromPub) {} ", ) .build(); p.cargo("check --message-format=short") .masquerade_as_nightly_cargo(&["public-dependency"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] pub_bar v0.1.0 (registry `dummy-registry`) [DOWNLOADED] bar v0.1.0 (registry `dummy-registry`) [CHECKING] pub_bar v0.1.0 [CHECKING] bar v0.1.0 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")] fn verify_mix_cargo_feature_z() { Package::new("dep", "0.1.0") .file("src/lib.rs", "pub struct FromDep;") .publish(); Package::new("priv_dep", "0.1.0") .file("src/lib.rs", "pub struct FromPriv;") .publish(); Package::new("pub_dep", "0.1.0") .file("src/lib.rs", "pub struct FromPub;") .publish(); let p = project() .file( "Cargo.toml", r#" cargo-features = ["public-dependency"] [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] dep = "0.1.0" priv_dep = {version = "0.1.0", public = false} pub_dep = {version = "0.1.0", public = true} "#, ) .file( "src/lib.rs", " extern crate dep; extern crate priv_dep; extern crate pub_dep; pub fn use_dep(_: dep::FromDep) {} pub fn use_priv(_: priv_dep::FromPriv) {} pub fn use_pub(_: pub_dep::FromPub) {} ", ) .build(); p.cargo("check -Zpublic-dependency --message-format=short") .masquerade_as_nightly_cargo(&["public-dependency"]) .with_stderr_data(str![[r#" ... src/lib.rs:5:13: [WARNING] type `FromDep` from private dependency 'dep' in public interface src/lib.rs:6:13: [WARNING] type `FromPriv` from private dependency 'priv_dep' in public interface ... "#]]) .run(); } #[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")] fn verify_z_public_dependency() { Package::new("dep", "0.1.0") .file("src/lib.rs", "pub struct FromDep;") .publish(); Package::new("priv_dep", "0.1.0") .file("src/lib.rs", "pub struct FromPriv;") .publish(); Package::new("pub_dep", "0.1.0") .file("src/lib.rs", "pub struct FromPub;") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] dep = "0.1.0" priv_dep = {version = "0.1.0", public = false} pub_dep = {version = "0.1.0", public = true} "#, ) .file( "src/lib.rs", " extern crate dep; extern crate priv_dep; extern crate pub_dep; pub fn use_dep(_: dep::FromDep) {} pub fn use_priv(_: priv_dep::FromPriv) {} pub fn use_pub(_: pub_dep::FromPub) {} ", ) .build(); p.cargo("check -Zpublic-dependency --message-format=short") .masquerade_as_nightly_cargo(&["public-dependency"]) .with_stderr_data( str![[r#" ... src/lib.rs:5:13: [WARNING] type `FromDep` from private dependency 'dep' in public interface src/lib.rs:6:13: [WARNING] type `FromPriv` from private dependency 'priv_dep' in public interface ... "#]] .unordered(), ) .run(); } cargo-0.91.0/tests/testsuite/publish.rs000064400000000000000000004010511046102023000162160ustar 00000000000000//! Tests for the `cargo publish` command. use std::fs; use std::sync::{Arc, Mutex}; use crate::prelude::*; use cargo_test_support::git::{self, repo}; use cargo_test_support::registry::{self, Package, RegistryBuilder, Response}; use cargo_test_support::{Project, paths}; use cargo_test_support::{basic_manifest, project, publish, str}; const CLEAN_FOO_JSON: &str = r#" { "authors": [], "badges": {}, "categories": [], "deps": [], "description": "foo", "documentation": "foo", "features": {}, "homepage": "foo", "keywords": [], "license": "MIT", "license_file": null, "links": null, "name": "foo", "readme": null, "readme_file": null, "repository": "foo", "rust_version": null, "vers": "0.0.1" } "#; fn validate_upload_foo() { publish::validate_upload( r#" { "authors": [], "badges": {}, "categories": [], "deps": [], "description": "foo", "documentation": null, "features": {}, "homepage": null, "keywords": [], "license": "MIT", "license_file": null, "links": null, "name": "foo", "readme": null, "readme_file": null, "repository": null, "rust_version": null, "vers": "0.0.1" } "#, "foo-0.0.1.crate", &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], ); } fn validate_upload_li() { publish::validate_upload( r#" { "authors": [], "badges": {}, "categories": [], "deps": [], "description": "li", "documentation": null, "features": {}, "homepage": null, "keywords": [], "license": "MIT", "license_file": null, "links": null, "name": "li", "readme": null, "readme_file": null, "repository": null, "rust_version": "1.69", "vers": "0.0.1" } "#, "li-0.0.1.crate", &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], ); } #[cargo_test] fn simple() { let registry = RegistryBuilder::new().http_api().http_index().build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("publish --no-verify") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] foo v0.0.1 ([ROOT]/foo) [UPLOADED] foo v0.0.1 to registry `crates-io` [NOTE] waiting for foo v0.0.1 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.0.1 at registry `crates-io` "#]]) .run(); validate_upload_foo(); } #[cargo_test] fn duplicate_version() { let registry_dupl = RegistryBuilder::new().http_api().http_index().build(); Package::new("foo", "0.0.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("publish --dry-run") .replace_crates_io(registry_dupl.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] crate foo@0.0.1 already exists on crates.io index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] foo v0.0.1 ([ROOT]/foo) [WARNING] aborting upload due to dry run "#]]) .run(); p.cargo("publish") .replace_crates_io(registry_dupl.index_url()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] crates.io index [ERROR] crate foo@0.0.1 already exists on crates.io index "#]]) .run(); } // Check that the `token` key works at the root instead of under a // `[registry]` table. #[cargo_test] fn simple_publish_with_http() { let _reg = registry::RegistryBuilder::new() .http_api() .token(registry::Token::Plaintext("sekrit".to_string())) .build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("publish --no-verify --token sekrit --registry dummy-registry") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] foo v0.0.1 ([ROOT]/foo) [UPLOADED] foo v0.0.1 to registry `dummy-registry` [NOTE] waiting for foo v0.0.1 to be available at registry `dummy-registry`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.0.1 at registry `dummy-registry` "#]]) .run(); } #[cargo_test] fn simple_publish_with_asymmetric() { let _reg = registry::RegistryBuilder::new() .http_api() .http_index() .alternative_named("dummy-registry") .token(registry::Token::rfc_key()) .build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("publish --no-verify -Zasymmetric-token --registry dummy-registry") .masquerade_as_nightly_cargo(&["asymmetric-token"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] foo v0.0.1 ([ROOT]/foo) [UPLOADED] foo v0.0.1 to registry `dummy-registry` [NOTE] waiting for foo v0.0.1 to be available at registry `dummy-registry`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.0.1 at registry `dummy-registry` "#]]) .run(); } #[cargo_test] fn old_token_location() { // `publish` generally requires a remote registry let registry = registry::RegistryBuilder::new().http_api().build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build(); let credentials = paths::home().join(".cargo/credentials.toml"); fs::remove_file(&credentials).unwrap(); // Verify can't publish without a token. p.cargo("publish --no-verify") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] crates.io index [ERROR] no token found, please run `cargo login` or use environment variable CARGO_REGISTRY_TOKEN "#]]) .run(); fs::write(&credentials, format!(r#"token = "{}""#, registry.token())).unwrap(); p.cargo("publish --no-verify") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] foo v0.0.1 ([ROOT]/foo) [UPLOADED] foo v0.0.1 to registry `crates-io` [NOTE] waiting for foo v0.0.1 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.0.1 at registry `crates-io` "#]]) .run(); // Skip `validate_upload_foo` as we just cared we got far enough for verify the token behavior. // Other tests will verify the endpoint gets the right payload. } #[cargo_test] fn simple_with_index() { // `publish` generally requires a remote registry let registry = registry::RegistryBuilder::new().http_api().build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("publish --no-verify") .arg("--token") .arg(registry.token()) .arg("--index") .arg(registry.index_url().as_str()) .with_stderr_data(str![[r#" [UPDATING] `[ROOT]/registry` index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] foo v0.0.1 ([ROOT]/foo) [UPLOADED] foo v0.0.1 to registry `[ROOT]/registry` [NOTE] waiting for foo v0.0.1 to be available at registry `[ROOT]/registry`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.0.1 at registry `[ROOT]/registry` "#]]) .run(); // Skip `validate_upload_foo` as we just cared we got far enough for verify the VCS behavior. // Other tests will verify the endpoint gets the right payload. } #[cargo_test] fn git_deps() { // Use local registry for faster test times since no publish will occur let registry = registry::init(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" [dependencies.foo] git = "git://path/to/nowhere" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("publish --no-verify") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] crates.io index [ERROR] failed to verify manifest at `[ROOT]/foo/Cargo.toml` Caused by: all dependencies must have a version requirement specified when publishing. dependency `foo` does not specify a version Note: The published dependency will use the version from crates.io, the `git` specification will be removed from the dependency declaration. "#]]) .run(); } #[cargo_test] fn path_dependency_no_version() { // Use local registry for faster test times since no publish will occur let registry = registry::init(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" [dependencies.bar] path = "bar" "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "") .build(); p.cargo("publish") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] crates.io index [ERROR] failed to verify manifest at `[ROOT]/foo/Cargo.toml` Caused by: all dependencies must have a version requirement specified when publishing. dependency `bar` does not specify a version Note: The published dependency will use the version from crates.io, the `path` specification will be removed from the dependency declaration. "#]]) .run(); } #[cargo_test] fn unpublishable_crate() { // Use local registry for faster test times since no publish will occur let registry = registry::init(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" publish = false "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("publish --index") .arg(registry.index_url().as_str()) .with_status(101) .with_stderr_data(str![[r#" [ERROR] `foo` cannot be published. `package.publish` must be set to `true` or a non-empty list in Cargo.toml to publish. "#]]) .run(); } #[cargo_test] fn dont_publish_dirty() { // Use local registry for faster test times since no publish will occur let registry = registry::init(); let p = project().file("bar", "").build(); let _ = git::repo(&paths::root().join("foo")) .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" documentation = "foo" homepage = "foo" repository = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("publish") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] crates.io index [ERROR] 1 files in the working directory contain changes that were not yet committed into git: bar to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag "#]]) .run(); } #[cargo_test] fn publish_clean() { // `publish` generally requires a remote registry let registry = registry::RegistryBuilder::new().http_api().build(); let p = project().build(); let _ = repo(&paths::root().join("foo")) .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" documentation = "foo" homepage = "foo" repository = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("publish") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 5 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] foo v0.0.1 ([ROOT]/foo) [UPLOADED] foo v0.0.1 to registry `crates-io` [NOTE] waiting for foo v0.0.1 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.0.1 at registry `crates-io` "#]]) .run(); // Skip `validate_upload_foo_clean` as we just cared we got far enough for verify the VCS behavior. // Other tests will verify the endpoint gets the right payload. } #[cargo_test] fn publish_in_sub_repo() { // `publish` generally requires a remote registry let registry = registry::RegistryBuilder::new().http_api().build(); let p = project().no_manifest().file("baz", "").build(); let _ = repo(&paths::root().join("foo")) .file( "bar/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" documentation = "foo" homepage = "foo" repository = "foo" "#, ) .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("publish") .replace_crates_io(registry.index_url()) .cwd("bar") .with_stderr_data(str![[r#" [UPDATING] crates.io index [PACKAGING] foo v0.0.1 ([ROOT]/foo/bar) [PACKAGED] 5 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo/bar) [COMPILING] foo v0.0.1 ([ROOT]/foo/bar/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] foo v0.0.1 ([ROOT]/foo/bar) [UPLOADED] foo v0.0.1 to registry `crates-io` [NOTE] waiting for foo v0.0.1 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.0.1 at registry `crates-io` "#]]) .run(); // Skip `validate_upload_foo_clean` as we just cared we got far enough for verify the VCS behavior. // Other tests will verify the endpoint gets the right payload. } #[cargo_test] fn publish_when_ignored() { // `publish` generally requires a remote registry let registry = registry::RegistryBuilder::new().http_api().build(); let p = project().file("baz", "").build(); let _ = repo(&paths::root().join("foo")) .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" documentation = "foo" homepage = "foo" repository = "foo" "#, ) .file("src/main.rs", "fn main() {}") .file(".gitignore", "baz") .build(); p.cargo("publish") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 6 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] foo v0.0.1 ([ROOT]/foo) [UPLOADED] foo v0.0.1 to registry `crates-io` [NOTE] waiting for foo v0.0.1 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.0.1 at registry `crates-io` "#]]) .run(); // Skip `validate_upload` as we just cared we got far enough for verify the VCS behavior. // Other tests will verify the endpoint gets the right payload. } #[cargo_test] fn ignore_when_crate_ignored() { // `publish` generally requires a remote registry let registry = registry::RegistryBuilder::new().http_api().build(); let p = project().no_manifest().file("bar/baz", "").build(); let _ = repo(&paths::root().join("foo")) .file(".gitignore", "bar") .nocommit_file( "bar/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" documentation = "foo" homepage = "foo" repository = "foo" "#, ) .nocommit_file("bar/src/main.rs", "fn main() {}"); p.cargo("publish") .replace_crates_io(registry.index_url()) .cwd("bar") .with_stderr_data(str![[r#" [UPDATING] crates.io index [PACKAGING] foo v0.0.1 ([ROOT]/foo/bar) [PACKAGED] 5 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo/bar) [COMPILING] foo v0.0.1 ([ROOT]/foo/bar/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] foo v0.0.1 ([ROOT]/foo/bar) [UPLOADED] foo v0.0.1 to registry `crates-io` [NOTE] waiting for foo v0.0.1 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.0.1 at registry `crates-io` "#]]) .run(); // Skip `validate_upload` as we just cared we got far enough for verify the VCS behavior. // Other tests will verify the endpoint gets the right payload. } #[cargo_test] fn new_crate_rejected() { // Use local registry for faster test times since no publish will occur let registry = registry::init(); let p = project().file("baz", "").build(); let _ = repo(&paths::root().join("foo")) .nocommit_file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" documentation = "foo" homepage = "foo" repository = "foo" "#, ) .nocommit_file("src/main.rs", "fn main() {}"); p.cargo("publish") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] crates.io index [ERROR] 3 files in the working directory contain changes that were not yet committed into git: ... "#]]) .run(); } #[cargo_test] fn dry_run() { // Use local registry for faster test times since no publish will occur let registry = registry::init(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("publish --dry-run --index") .arg(registry.index_url().as_str()) .with_stderr_data(str![[r#" [UPDATING] `[ROOT]/registry` index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] foo v0.0.1 ([ROOT]/foo) [WARNING] aborting upload due to dry run "#]]) .run(); // Ensure the API request wasn't actually made assert!(registry::api_path().join("api/v1/crates").exists()); assert!(!registry::api_path().join("api/v1/crates/new").exists()); } #[cargo_test] fn registry_not_in_publish_list() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" publish = [ "test" ] "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("publish") .arg("--registry") .arg("alternative") .with_status(101) .with_stderr_data(str![[r#" [ERROR] `foo` cannot be published. The registry `alternative` is not listed in the `package.publish` value in Cargo.toml. "#]]) .run(); } #[cargo_test] fn publish_empty_list() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" publish = [] "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("publish --registry alternative") .with_status(101) .with_stderr_data(str![[r#" [ERROR] `foo` cannot be published. `package.publish` must be set to `true` or a non-empty list in Cargo.toml to publish. "#]]) .run(); } #[cargo_test] fn publish_allowed_registry() { let _registry = RegistryBuilder::new() .http_api() .http_index() .alternative() .build(); let p = project().build(); let _ = repo(&paths::root().join("foo")) .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" documentation = "foo" homepage = "foo" repository = "foo" publish = ["alternative"] "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("publish --registry alternative") .with_stderr_data(str![[r#" [UPDATING] `alternative` index [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 5 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] foo v0.0.1 ([ROOT]/foo) [UPLOADED] foo v0.0.1 to registry `alternative` [NOTE] waiting for foo v0.0.1 to be available at registry `alternative`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.0.1 at registry `alternative` "#]]) .run(); publish::validate_alt_upload( CLEAN_FOO_JSON, "foo-0.0.1.crate", &[ "Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs", ".cargo_vcs_info.json", ], ); } #[cargo_test] fn publish_implicitly_to_only_allowed_registry() { let _registry = RegistryBuilder::new() .http_api() .http_index() .alternative() .build(); let p = project().build(); let _ = repo(&paths::root().join("foo")) .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" documentation = "foo" homepage = "foo" repository = "foo" publish = ["alternative"] "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("publish") .with_stderr_data(str![[r#" [NOTE] found `alternative` as only allowed registry. Publishing to it automatically. [UPDATING] `alternative` index [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 5 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] foo v0.0.1 ([ROOT]/foo) [UPLOADED] foo v0.0.1 to registry `alternative` [NOTE] waiting for foo v0.0.1 to be available at registry `alternative`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.0.1 at registry `alternative` "#]]) .run(); publish::validate_alt_upload( CLEAN_FOO_JSON, "foo-0.0.1.crate", &[ "Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs", ".cargo_vcs_info.json", ], ); } #[cargo_test] fn publish_failed_with_index_and_only_allowed_registry() { let registry = RegistryBuilder::new() .http_api() .http_index() .alternative() .build(); let p = project().build(); let _ = repo(&paths::root().join("foo")) .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" documentation = "foo" homepage = "foo" repository = "foo" publish = ["alternative"] "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("publish") .arg("--index") .arg(registry.index_url().as_str()) .with_status(101) .with_stderr_data(str![[r#" [ERROR] command-line argument --index requires --token to be specified "#]]) .run(); } #[cargo_test] fn publish_fail_with_no_registry_specified() { let p = project().build(); let _ = repo(&paths::root().join("foo")) .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" documentation = "foo" homepage = "foo" repository = "foo" publish = ["alternative", "test"] "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("publish") .with_status(101) .with_stderr_data(str![[r#" [ERROR] --registry is required to disambiguate between "alternative" or "test" registries "#]]) .run(); } #[cargo_test] fn block_publish_no_registry() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" publish = [] "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("publish --registry alternative") .with_status(101) .with_stderr_data(str![[r#" [ERROR] `foo` cannot be published. `package.publish` must be set to `true` or a non-empty list in Cargo.toml to publish. "#]]) .run(); } // Explicitly setting `crates-io` in the publish list. #[cargo_test] fn publish_with_crates_io_explicit() { // `publish` generally requires a remote registry let registry = registry::RegistryBuilder::new().http_api().build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" publish = ["crates-io"] "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("publish --registry alternative") .with_status(101) .with_stderr_data(str![[r#" [ERROR] `foo` cannot be published. The registry `alternative` is not listed in the `package.publish` value in Cargo.toml. "#]]) .run(); p.cargo("publish") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] foo v0.0.1 ([ROOT]/foo) [UPLOADED] foo v0.0.1 to registry `crates-io` [NOTE] waiting for foo v0.0.1 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.0.1 at registry `crates-io` "#]]) .run(); } #[cargo_test] fn publish_with_select_features() { // `publish` generally requires a remote registry let registry = registry::RegistryBuilder::new().http_api().build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" [features] required = [] optional = [] "#, ) .file( "src/main.rs", "#[cfg(not(feature = \"required\"))] compile_error!(\"This crate requires `required` feature!\"); fn main() {}", ) .build(); p.cargo("publish --features required") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] foo v0.0.1 ([ROOT]/foo) [UPLOADED] foo v0.0.1 to registry `crates-io` [NOTE] waiting for foo v0.0.1 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.0.1 at registry `crates-io` "#]]) .run(); } #[cargo_test] fn publish_with_all_features() { // `publish` generally requires a remote registry let registry = registry::RegistryBuilder::new().http_api().build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" [features] required = [] optional = [] "#, ) .file( "src/main.rs", "#[cfg(not(feature = \"required\"))] compile_error!(\"This crate requires `required` feature!\"); fn main() {}", ) .build(); p.cargo("publish --all-features") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] foo v0.0.1 ([ROOT]/foo) [UPLOADED] foo v0.0.1 to registry `crates-io` [NOTE] waiting for foo v0.0.1 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.0.1 at registry `crates-io` "#]]) .run(); } #[cargo_test] fn publish_with_no_default_features() { // Use local registry for faster test times since no publish will occur let registry = registry::init(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" [features] default = ["required"] required = [] "#, ) .file( "src/main.rs", "#[cfg(not(feature = \"required\"))] compile_error!(\"This crate requires `required` feature!\"); fn main() {}", ) .build(); p.cargo("publish --no-default-features") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" ... [ERROR] This crate requires `required` feature! ... "#]]) .run(); } #[cargo_test] fn publish_with_patch() { let registry = RegistryBuilder::new().http_api().http_index().build(); Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" [dependencies] bar = "1.0" [patch.crates-io] bar = { path = "bar" } "#, ) .file( "src/main.rs", "extern crate bar; fn main() { bar::newfunc(); }", ) .file("bar/Cargo.toml", &basic_manifest("bar", "1.0.0")) .file("bar/src/lib.rs", "pub fn newfunc() {}") .build(); // Check that it works with the patched crate. p.cargo("build").run(); // Check that verify fails with patched crate which has new functionality. p.cargo("publish") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" ... error[E0425]: cannot find function `newfunc` in crate `bar` ... "#]]) .run(); // Remove the usage of new functionality and try again. p.change_file("src/main.rs", "extern crate bar; pub fn main() {}"); p.cargo("publish") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [UPDATING] crates.io index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] bar v1.0.0 [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] foo v0.0.1 ([ROOT]/foo) [UPLOADED] foo v0.0.1 to registry `crates-io` [NOTE] waiting for foo v0.0.1 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.0.1 at registry `crates-io` "#]]) .run(); publish::validate_upload( r#" { "authors": [], "badges": {}, "categories": [], "deps": [ { "default_features": true, "features": [], "kind": "normal", "name": "bar", "optional": false, "target": null, "version_req": "^1.0" } ], "description": "foo", "documentation": null, "features": {}, "homepage": null, "keywords": [], "license": "MIT", "license_file": null, "links": null, "name": "foo", "readme": null, "readme_file": null, "repository": null, "rust_version": null, "vers": "0.0.1" } "#, "foo-0.0.1.crate", &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], ); } #[cargo_test] fn publish_checks_for_token_before_verify() { let registry = registry::RegistryBuilder::new() .no_configure_token() .build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build(); // Assert upload token error before the package is verified p.cargo("publish") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] crates.io index [ERROR] no token found, please run `cargo login` or use environment variable CARGO_REGISTRY_TOKEN "#]]) .with_stderr_does_not_contain("[VERIFYING] foo v0.0.1 ([CWD])") .run(); // Assert package verified successfully on dry run p.cargo("publish --dry-run") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] foo v0.0.1 ([ROOT]/foo) [WARNING] aborting upload due to dry run "#]]) .run(); } #[cargo_test] fn publish_with_bad_source() { let p = project() .file( ".cargo/config.toml", r#" [source.crates-io] replace-with = 'local-registry' [source.local-registry] local-registry = 'registry' "#, ) .file("src/lib.rs", "") .build(); p.cargo("publish") .with_status(101) .with_stderr_data(str![[r#" [ERROR] crates-io is replaced with non-remote-registry source registry `[ROOT]/foo/registry`; include `--registry crates-io` to use crates.io "#]]) .run(); p.change_file( ".cargo/config.toml", r#" [source.crates-io] replace-with = "vendored-sources" [source.vendored-sources] directory = "vendor" "#, ); p.cargo("publish") .with_status(101) .with_stderr_data(str![[r#" [ERROR] crates-io is replaced with non-remote-registry source dir [ROOT]/foo/vendor; include `--registry crates-io` to use crates.io "#]]) .run(); } // A dependency with both `git` and `version`. #[cargo_test] fn publish_git_with_version() { let registry = RegistryBuilder::new().http_api().http_index().build(); Package::new("dep1", "1.0.1") .file("src/lib.rs", "pub fn f() -> i32 {1}") .publish(); let git_project = git::new("dep1", |project| { project .file("Cargo.toml", &basic_manifest("dep1", "1.0.0")) .file("src/lib.rs", "pub fn f() -> i32 {2}") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" authors = [] edition = "2018" license = "MIT" description = "foo" [dependencies] dep1 = {{version = "1.0", git="{}"}} "#, git_project.url() ), ) .file( "src/main.rs", r#" pub fn main() { println!("{}", dep1::f()); } "#, ) .build(); p.cargo("run") .with_stdout_data(str![[r#" 2 "#]]) .run(); p.cargo("publish --no-verify") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.1.0 ([ROOT]/foo) [UPDATING] crates.io index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] foo v0.1.0 ([ROOT]/foo) [UPLOADED] foo v0.1.0 to registry `crates-io` [NOTE] waiting for foo v0.1.0 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.1.0 at registry `crates-io` "#]]) .run(); publish::validate_upload_with_contents( r#" { "authors": [], "badges": {}, "categories": [], "deps": [ { "default_features": true, "features": [], "kind": "normal", "name": "dep1", "optional": false, "target": null, "version_req": "^1.0" } ], "description": "foo", "documentation": null, "features": {}, "homepage": null, "keywords": [], "license": "MIT", "license_file": null, "links": null, "name": "foo", "readme": null, "readme_file": null, "repository": null, "rust_version": null, "vers": "0.1.0" } "#, "foo-0.1.0.crate", &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], [ ( "Cargo.toml", // Check that only `version` is included in Cargo.toml. str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "foo" version = "0.1.0" authors = [] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" readme = false license = "MIT" [[bin]] name = "foo" path = "src/main.rs" [dependencies.dep1] version = "1.0" "##]], ), ( "Cargo.lock", // The important check here is that it is 1.0.1 in the registry. str![[r##" # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "dep1" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "[..]" [[package]] name = "foo" version = "0.1.0" dependencies = [ "dep1", ] "##]], ), ], ); } #[cargo_test] fn publish_dev_dep_stripping() { let registry = RegistryBuilder::new().http_api().http_index().build(); Package::new("normal-only", "1.0.0") .feature("cat", &[]) .publish(); Package::new("optional-dep-feature", "1.0.0") .feature("cat", &[]) .publish(); Package::new("optional-namespaced", "1.0.0") .feature("cat", &[]) .publish(); Package::new("optional-renamed-dep-feature", "1.0.0") .feature("cat", &[]) .publish(); Package::new("optional-renamed-namespaced", "1.0.0") .feature("cat", &[]) .publish(); Package::new("build-only", "1.0.0") .feature("cat", &[]) .publish(); Package::new("normal-and-dev", "1.0.0") .feature("cat", &[]) .publish(); Package::new("target-normal-only", "1.0.0") .feature("cat", &[]) .publish(); Package::new("target-build-only", "1.0.0") .feature("cat", &[]) .publish(); Package::new("target-normal-and-dev", "1.0.0") .feature("cat", &[]) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] license = "MIT" description = "foo" documentation = "foo" homepage = "foo" repository = "foo" [features] foo_feature = [ "normal-only/cat", "build-only/cat", "dev-only/cat", "renamed-dev-only01/cat", "normal-and-dev/cat", "target-normal-only/cat", "target-build-only/cat", "target-dev-only/cat", "target-normal-and-dev/cat", "optional-dep-feature/cat", "dep:optional-namespaced", "optional-renamed-dep-feature10/cat", "dep:optional-renamed-namespaced10", ] [dependencies] normal-only = { version = "1.0", features = ["cat"] } normal-and-dev = { version = "1.0", features = ["cat"] } optional-dep-feature = { version = "1.0", features = ["cat"], optional = true } optional-namespaced = { version = "1.0", features = ["cat"], optional = true } optional-renamed-dep-feature10 = { version = "1.0", features = ["cat"], optional = true, package = "optional-renamed-dep-feature" } optional-renamed-namespaced10 = { version = "1.0", features = ["cat"], optional = true, package = "optional-renamed-namespaced" } [build-dependencies] build-only = { version = "1.0", features = ["cat"] } [dev-dependencies] dev-only = { path = "../dev-only", features = ["cat"] } renamed-dev-only01 = { path = "../renamed-dev-only", features = ["cat"], package = "renamed-dev-only" } normal-and-dev = { version = "1.0", features = ["cat"] } [target.'cfg(unix)'.dependencies] target-normal-only = { version = "1.0", features = ["cat"] } target-normal-and-dev = { version = "1.0", features = ["cat"] } [target.'cfg(unix)'.build-dependencies] target-build-only = { version = "1.0", features = ["cat"] } [target.'cfg(unix)'.dev-dependencies] target-dev-only = { path = "../dev-only", features = ["cat"] } target-normal-and-dev = { version = "1.0", features = ["cat"] } "#, ) .file("src/main.rs", "") .file( "dev-only/Cargo.toml", r#" [package] name = "dev-only" version = "0.1.0" edition = "2015" authors = [] [features] cat = [] "#, ) .file( "dev-only/src/lib.rs", r#" #[cfg(feature = "cat")] pub fn cat() {} "#, ) .file( "renamed-dev-only/Cargo.toml", r#" [package] name = "renamed-dev-only" version = "0.1.0" edition = "2015" authors = [] [features] cat = [] "#, ) .file( "renamed-dev-only/src/lib.rs", r#" #[cfg(feature = "cat")] pub fn cat() {} "#, ) .build(); p.cargo("publish --no-verify") .env("RUSTFLAGS", "--cfg unix") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [PACKAGING] foo v0.1.0 ([ROOT]/foo) [UPDATING] crates.io index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] foo v0.1.0 ([ROOT]/foo) [UPLOADED] foo v0.1.0 to registry `crates-io` [NOTE] waiting for foo v0.1.0 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.1.0 at registry `crates-io` "#]]) .run(); publish::validate_upload_with_contents( r#" { "authors": [], "badges": {}, "categories": [], "deps": [ { "default_features": true, "features": [ "cat" ], "kind": "normal", "name": "normal-and-dev", "optional": false, "target": null, "version_req": "^1.0" }, { "default_features": true, "features": [ "cat" ], "kind": "normal", "name": "normal-only", "optional": false, "target": null, "version_req": "^1.0" }, { "default_features": true, "features": [ "cat" ], "kind": "normal", "name": "optional-dep-feature", "optional": true, "target": null, "version_req": "^1.0" }, { "default_features": true, "features": [ "cat" ], "kind": "normal", "name": "optional-namespaced", "optional": true, "target": null, "version_req": "^1.0" }, { "default_features": true, "explicit_name_in_toml": "optional-renamed-dep-feature10", "features": [ "cat" ], "kind": "normal", "name": "optional-renamed-dep-feature", "optional": true, "target": null, "version_req": "^1.0" }, { "default_features": true, "explicit_name_in_toml": "optional-renamed-namespaced10", "features": [ "cat" ], "kind": "normal", "name": "optional-renamed-namespaced", "optional": true, "target": null, "version_req": "^1.0" }, { "default_features": true, "features": [ "cat" ], "kind": "dev", "name": "normal-and-dev", "optional": false, "target": null, "version_req": "^1.0" }, { "default_features": true, "features": [ "cat" ], "kind": "build", "name": "build-only", "optional": false, "target": null, "version_req": "^1.0" }, { "default_features": true, "features": [ "cat" ], "kind": "normal", "name": "target-normal-and-dev", "optional": false, "target": "cfg(unix)", "version_req": "^1.0" }, { "default_features": true, "features": [ "cat" ], "kind": "normal", "name": "target-normal-only", "optional": false, "target": "cfg(unix)", "version_req": "^1.0" }, { "default_features": true, "features": [ "cat" ], "kind": "build", "name": "target-build-only", "optional": false, "target": "cfg(unix)", "version_req": "^1.0" }, { "default_features": true, "features": [ "cat" ], "kind": "dev", "name": "target-normal-and-dev", "optional": false, "target": "cfg(unix)", "version_req": "^1.0" } ], "description": "foo", "documentation": "foo", "features": { "foo_feature": [ "normal-only/cat", "build-only/cat", "normal-and-dev/cat", "target-normal-only/cat", "target-build-only/cat", "target-normal-and-dev/cat", "optional-dep-feature/cat", "dep:optional-namespaced", "optional-renamed-dep-feature10/cat", "dep:optional-renamed-namespaced10" ] }, "homepage": "foo", "keywords": [], "license": "MIT", "license_file": null, "links": null, "name": "foo", "readme": null, "readme_file": null, "repository": "foo", "rust_version": null, "vers": "0.1.0" } "#, "foo-0.1.0.crate", &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], [( "Cargo.toml", str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "foo" version = "0.1.0" authors = [] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" homepage = "foo" documentation = "foo" readme = false license = "MIT" repository = "foo" [features] foo_feature = [ "normal-only/cat", "build-only/cat", "normal-and-dev/cat", "target-normal-only/cat", "target-build-only/cat", "target-normal-and-dev/cat", "optional-dep-feature/cat", "dep:optional-namespaced", "optional-renamed-dep-feature10/cat", "dep:optional-renamed-namespaced10", ] [[bin]] name = "foo" path = "src/main.rs" [dependencies.normal-and-dev] version = "1.0" features = ["cat"] [dependencies.normal-only] version = "1.0" features = ["cat"] [dependencies.optional-dep-feature] version = "1.0" features = ["cat"] optional = true [dependencies.optional-namespaced] version = "1.0" features = ["cat"] optional = true [dependencies.optional-renamed-dep-feature10] version = "1.0" features = ["cat"] optional = true package = "optional-renamed-dep-feature" [dependencies.optional-renamed-namespaced10] version = "1.0" features = ["cat"] optional = true package = "optional-renamed-namespaced" [dev-dependencies.normal-and-dev] version = "1.0" features = ["cat"] [build-dependencies.build-only] version = "1.0" features = ["cat"] [target."cfg(unix)".dependencies.target-normal-and-dev] version = "1.0" features = ["cat"] [target."cfg(unix)".dependencies.target-normal-only] version = "1.0" features = ["cat"] [target."cfg(unix)".build-dependencies.target-build-only] version = "1.0" features = ["cat"] [target."cfg(unix)".dev-dependencies.target-normal-and-dev] version = "1.0" features = ["cat"] "##]], )], ); } #[cargo_test] fn credentials_ambiguous_filename() { // `publish` generally requires a remote registry let registry = registry::RegistryBuilder::new().http_api().build(); // Make token in `credentials.toml` incorrect to ensure it is not read. let credentials_toml = paths::home().join(".cargo/credentials.toml"); fs::write(credentials_toml, r#"token = "wrong-token""#).unwrap(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("publish --no-verify") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" ... Unauthorized message from server. "#]]) .run(); // Favor `credentials` if exists. let credentials = paths::home().join(".cargo/credentials"); fs::write(credentials, r#"token = "sekrit""#).unwrap(); p.cargo("publish --no-verify") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] both `[ROOT]/home/.cargo/credentials` and `[ROOT]/home/.cargo/credentials.toml` exist. Using `[ROOT]/home/.cargo/credentials` [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] foo v0.0.1 ([ROOT]/foo) [UPLOADED] foo v0.0.1 to registry `crates-io` [NOTE] waiting for foo v0.0.1 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.0.1 at registry `crates-io` "#]]) .run(); } // --index will not load registry.token to avoid possibly leaking // crates.io token to another server. #[cargo_test] fn index_requires_token() { // Use local registry for faster test times since no publish will occur let registry = registry::init(); let credentials = paths::home().join(".cargo/credentials.toml"); fs::remove_file(&credentials).unwrap(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" "#, ) .file("src/lib.rs", "") .build(); p.cargo("publish --no-verify --index") .arg(registry.index_url().as_str()) .with_status(101) .with_stderr_data(str![[r#" [ERROR] command-line argument --index requires --token to be specified "#]]) .run(); } // publish with source replacement without --registry #[cargo_test] fn cratesio_source_replacement() { registry::init(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" "#, ) .file("src/lib.rs", "") .build(); p.cargo("publish --no-verify") .with_status(101) .with_stderr_data(str![[r#" [ERROR] crates-io is replaced with remote registry dummy-registry; include `--registry dummy-registry` or `--registry crates-io` "#]]) .run(); } // Registry returns an API error. #[cargo_test] fn api_error_json() { let _registry = registry::RegistryBuilder::new() .alternative() .http_api() .add_responder("/api/v1/crates/new", |_, _| Response { body: br#"{"errors": [{"detail": "you must be logged in"}]}"#.to_vec(), code: 403, headers: vec![], }) .build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" documentation = "foo" homepage = "foo" repository = "foo" "#, ) .file("src/lib.rs", "") .build(); p.cargo("publish --no-verify --registry alternative") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] foo v0.0.1 ([ROOT]/foo) [ERROR] failed to publish to registry at http://127.0.0.1:[..]/ Caused by: the remote server responded with an error (status 403 Forbidden): you must be logged in "#]]) .run(); } // Registry returns an API error with a 200 status code. #[cargo_test] fn api_error_200() { let _registry = registry::RegistryBuilder::new() .alternative() .http_api() .add_responder("/api/v1/crates/new", |_, _| Response { body: br#"{"errors": [{"detail": "max upload size is 123"}]}"#.to_vec(), code: 200, headers: vec![], }) .build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" documentation = "foo" homepage = "foo" repository = "foo" "#, ) .file("src/lib.rs", "") .build(); p.cargo("publish --no-verify --registry alternative") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] foo v0.0.1 ([ROOT]/foo) [ERROR] failed to publish to registry at http://127.0.0.1:[..]/ Caused by: the remote server responded with an [ERROR] max upload size is 123 "#]]) .run(); } // Registry returns an error code without a JSON message. #[cargo_test] fn api_error_code() { let _registry = registry::RegistryBuilder::new() .alternative() .http_api() .add_responder("/api/v1/crates/new", |_, _| Response { body: br#"go away"#.to_vec(), code: 400, headers: vec![], }) .build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" documentation = "foo" homepage = "foo" repository = "foo" "#, ) .file("src/lib.rs", "") .build(); p.cargo("publish --no-verify --registry alternative") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] foo v0.0.1 ([ROOT]/foo) [ERROR] failed to publish to registry at http://127.0.0.1:[..]/ Caused by: failed to get a 200 OK response, got 400 headers: HTTP/1.1 400 Content-Length: 7 Connection: close body: go away "#]]) .run(); } // Registry has a network error. #[cargo_test] fn api_curl_error() { let _registry = registry::RegistryBuilder::new() .alternative() .http_api() .add_responder("/api/v1/crates/new", |_, _| { panic!("broke"); }) .build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" documentation = "foo" homepage = "foo" repository = "foo" "#, ) .file("src/lib.rs", "") .build(); // This doesn't check for the exact text of the error in the remote // possibility that cargo is linked with a weird version of libcurl, or // curl changes the text of the message. Currently the message 52 // (CURLE_GOT_NOTHING) is: // Server returned nothing (no headers, no data) (Empty reply from server) p.cargo("publish --no-verify --registry alternative") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] foo v0.0.1 ([ROOT]/foo) [ERROR] failed to publish to registry at http://127.0.0.1:[..]/ Caused by: [52] Server returned nothing (no headers, no data) (Empty reply from server) "#]]) .run(); } // Registry returns an invalid response. #[cargo_test] fn api_other_error() { let _registry = registry::RegistryBuilder::new() .alternative() .http_api() .add_responder("/api/v1/crates/new", |_, _| Response { body: b"\xff".to_vec(), code: 200, headers: vec![], }) .build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" documentation = "foo" homepage = "foo" repository = "foo" "#, ) .file("src/lib.rs", "") .build(); p.cargo("publish --no-verify --registry alternative") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] foo v0.0.1 ([ROOT]/foo) [ERROR] failed to publish to registry at http://127.0.0.1:[..]/ Caused by: invalid response body from server Caused by: invalid utf-8 sequence of 1 bytes from index 0 "#]]) .run(); } #[cargo_test] fn in_package_workspace() { let registry = RegistryBuilder::new().http_api().http_index().build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2021" [workspace] members = ["li"] "#, ) .file("src/main.rs", "fn main() {}") .file( "li/Cargo.toml", r#" [package] name = "li" version = "0.0.1" edition = "2015" rust-version = "1.69" description = "li" license = "MIT" "#, ) .file("li/src/main.rs", "fn main() {}") .build(); p.cargo("publish -p li --no-verify") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] li v0.0.1 ([ROOT]/foo/li) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] li v0.0.1 ([ROOT]/foo/li) [UPLOADED] li v0.0.1 to registry `crates-io` [NOTE] waiting for li v0.0.1 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] li v0.0.1 at registry `crates-io` "#]]) .run(); validate_upload_li(); } #[cargo_test] fn with_duplicate_spec_in_members() { let registry = RegistryBuilder::new().http_api().http_index().build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [workspace] resolver = "2" members = ["li","bar"] default-members = ["li","bar"] "#, ) .file("src/main.rs", "fn main() {}") .file( "li/Cargo.toml", r#" [package] name = "li" version = "0.0.1" edition = "2015" description = "li" license = "MIT" "#, ) .file("li/src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" description = "bar" license = "MIT" "#, ) .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("publish --no-verify") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] bar v0.0.1 ([ROOT]/foo/bar) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] li v0.0.1 ([ROOT]/foo/li) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] bar v0.0.1 ([ROOT]/foo/bar) [UPLOADED] bar v0.0.1 to registry `crates-io` [UPLOADING] li v0.0.1 ([ROOT]/foo/li) [UPLOADED] li v0.0.1 to registry `crates-io` [NOTE] waiting for bar v0.0.1 or li v0.0.1 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crates should be available shortly. [PUBLISHED] bar v0.0.1 and li v0.0.1 at registry `crates-io` "#]]) .run(); } #[cargo_test] fn in_package_workspace_with_members_with_features_old() { let registry = RegistryBuilder::new().http_api().http_index().build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [workspace] members = ["li"] "#, ) .file("src/main.rs", "fn main() {}") .file( "li/Cargo.toml", r#" [package] name = "li" version = "0.0.1" edition = "2015" rust-version = "1.69" description = "li" license = "MIT" "#, ) .file("li/src/main.rs", "fn main() {}") .build(); p.cargo("publish -p li --no-verify") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] li v0.0.1 ([ROOT]/foo/li) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] li v0.0.1 ([ROOT]/foo/li) [UPLOADED] li v0.0.1 to registry `crates-io` [NOTE] waiting for li v0.0.1 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] li v0.0.1 at registry `crates-io` "#]]) .run(); validate_upload_li(); } #[cargo_test] fn in_virtual_workspace() { let registry = RegistryBuilder::new().http_api().http_index().build(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo"] "#, ) .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" "#, ) .file("foo/src/main.rs", "fn main() {}") .build(); p.cargo("publish --no-verify") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] foo v0.0.1 ([ROOT]/foo/foo) [UPLOADED] foo v0.0.1 to registry `crates-io` [NOTE] waiting for foo v0.0.1 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.0.1 at registry `crates-io` "#]]) .run(); } #[cargo_test] fn in_virtual_workspace_with_p() { let registry = RegistryBuilder::new().http_api().http_index().build(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo","li"] "#, ) .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" "#, ) .file("foo/src/main.rs", "fn main() {}") .file( "li/Cargo.toml", r#" [package] name = "li" version = "0.0.1" edition = "2015" rust-version = "1.69" description = "li" license = "MIT" "#, ) .file("li/src/main.rs", "fn main() {}") .build(); p.cargo("publish -p li --no-verify") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] li v0.0.1 ([ROOT]/foo/li) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] li v0.0.1 ([ROOT]/foo/li) [UPLOADED] li v0.0.1 to registry `crates-io` [NOTE] waiting for li v0.0.1 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] li v0.0.1 at registry `crates-io` "#]]) .run(); } #[cargo_test] fn in_package_workspace_not_found() { // Use local registry for faster test times since no publish will occur let registry = registry::init(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2021" [workspace] "#, ) .file("src/main.rs", "fn main() {}") .file( "li/Cargo.toml", r#" [package] name = "li" version = "0.0.1" edition = "2021" authors = [] license = "MIT" description = "li" "#, ) .file("li/src/main.rs", "fn main() {}") .build(); p.cargo("publish -p li --no-verify") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" [ERROR] package ID specification `li` did not match any packages [HELP] a package with a similar name exists: `foo` "#]]) .run(); } #[cargo_test] fn in_package_workspace_found_multiple() { let registry = RegistryBuilder::new().http_api().http_index().build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2021" [workspace] members = ["li","lii"] "#, ) .file("src/main.rs", "fn main() {}") .file( "li/Cargo.toml", r#" [package] name = "li" version = "0.0.1" edition = "2021" authors = [] license = "MIT" description = "li" "#, ) .file("li/src/main.rs", "fn main() {}") .file( "lii/Cargo.toml", r#" [package] name = "lii" version = "0.0.1" edition = "2021" authors = [] license = "MIT" description = "lii" "#, ) .file("lii/src/main.rs", "fn main() {}") .build(); p.cargo("publish -p li* --no-verify") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] li v0.0.1 ([ROOT]/foo/li) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] lii v0.0.1 ([ROOT]/foo/lii) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] li v0.0.1 ([ROOT]/foo/li) [UPLOADED] li v0.0.1 to registry `crates-io` [UPLOADING] lii v0.0.1 ([ROOT]/foo/lii) [UPLOADED] lii v0.0.1 to registry `crates-io` [NOTE] waiting for li v0.0.1 or lii v0.0.1 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crates should be available shortly. [PUBLISHED] li v0.0.1 and lii v0.0.1 at registry `crates-io` "#]]) .run(); } #[cargo_test] fn publish_path_dependency_without_workspace() { let registry = RegistryBuilder::new().http_api().http_index().build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2021" [dependencies.bar] path = "bar" "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2021" authors = [] license = "MIT" description = "bar" "#, ) .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("publish -p bar --no-verify") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" [ERROR] package ID specification `bar` did not match any packages [HELP] a package with a similar name exists: `foo` "#]]) .run(); } #[cargo_test] fn http_api_not_noop() { let registry = registry::RegistryBuilder::new().http_api().build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("publish") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] foo v0.0.1 ([ROOT]/foo) [UPLOADED] foo v0.0.1 to registry `crates-io` [NOTE] waiting for foo v0.0.1 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.0.1 at registry `crates-io` "#]]) .run(); let p = project() .file( "Cargo.toml", r#" [project] name = "bar" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" [dependencies] foo = "0.0.1" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build").run(); } #[cargo_test] fn wait_for_first_publish() { // Counter for number of tries before the package is "published" let arc: Arc> = Arc::new(Mutex::new(0)); let arc2 = arc.clone(); // Registry returns an invalid response. let registry = registry::RegistryBuilder::new() .http_index() .http_api() .add_responder("/index/de/la/delay", move |req, server| { let mut lock = arc.lock().unwrap(); *lock += 1; if *lock <= 1 { server.not_found(req) } else { server.index(req) } }) .build(); let p = project() .file( "Cargo.toml", r#" [package] name = "delay" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" "#, ) .file("src/lib.rs", "") .build(); p.cargo("publish --no-verify") .replace_crates_io(registry.index_url()) .with_status(0) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] delay v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] delay v0.0.1 ([ROOT]/foo) [UPLOADED] delay v0.0.1 to registry `crates-io` [NOTE] waiting for delay v0.0.1 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] delay v0.0.1 at registry `crates-io` "#]]) .run(); // Verify the responder has been pinged let lock = arc2.lock().unwrap(); assert_eq!(*lock, 2); drop(lock); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] delay = "0.0.1" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build").with_status(0).run(); } /// A separate test is needed for package names with - or _ as they hit /// the responder twice per cargo invocation. If that ever gets changed /// this test will need to be changed accordingly. #[cargo_test] fn wait_for_first_publish_underscore() { // Counter for number of tries before the package is "published" let arc: Arc> = Arc::new(Mutex::new(0)); let arc2 = arc.clone(); let misses = Arc::new(Mutex::new(Vec::new())); let misses2 = misses.clone(); // Registry returns an invalid response. let registry = registry::RegistryBuilder::new() .http_index() .http_api() .add_responder("/index/de/la/delay_with_underscore", move |req, server| { let mut lock = arc.lock().unwrap(); *lock += 1; if *lock <= 1 { server.not_found(req) } else { server.index(req) } }) .not_found_handler(move |req, _| { misses.lock().unwrap().push(req.url.to_string()); Response { body: b"not found".to_vec(), code: 404, headers: vec![], } }) .build(); let p = project() .file( "Cargo.toml", r#" [package] name = "delay_with_underscore" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" "#, ) .file("src/lib.rs", "") .build(); p.cargo("publish --no-verify") .replace_crates_io(registry.index_url()) .with_status(0) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] delay_with_underscore v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] delay_with_underscore v0.0.1 ([ROOT]/foo) [UPLOADED] delay_with_underscore v0.0.1 to registry `crates-io` [NOTE] waiting for delay_with_underscore v0.0.1 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] delay_with_underscore v0.0.1 at registry `crates-io` "#]]) .run(); // Verify the repsponder has been pinged let lock = arc2.lock().unwrap(); assert_eq!(*lock, 2); drop(lock); { let misses = misses2.lock().unwrap(); assert!( misses.len() == 1, "should only have 1 not found URL; instead found {misses:?}" ); } let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] delay_with_underscore = "0.0.1" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build").with_status(0).run(); } #[cargo_test] fn wait_for_subsequent_publish() { // Counter for number of tries before the package is "published" let arc: Arc> = Arc::new(Mutex::new(0)); let arc2 = arc.clone(); let publish_req = Arc::new(Mutex::new(None)); let publish_req2 = publish_req.clone(); let registry = registry::RegistryBuilder::new() .http_index() .http_api() .add_responder("/api/v1/crates/new", move |req, server| { // Capture the publish request, but defer publishing *publish_req.lock().unwrap() = Some(req.clone()); server.ok(req) }) .add_responder("/index/de/la/delay", move |req, server| { let mut lock = arc.lock().unwrap(); *lock += 1; if *lock == 3 { // Run the publish on the 3rd attempt let rep = server .check_authorized_publish(&publish_req2.lock().unwrap().as_ref().unwrap()); assert_eq!(rep.code, 200); } server.index(req) }) .build(); // Publish an earlier version Package::new("delay", "0.0.1") .file("src/lib.rs", "") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "delay" version = "0.0.2" edition = "2015" authors = [] license = "MIT" description = "foo" "#, ) .file("src/lib.rs", "") .build(); p.cargo("publish --no-verify") .replace_crates_io(registry.index_url()) .with_status(0) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] delay v0.0.2 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] delay v0.0.2 ([ROOT]/foo) [UPLOADED] delay v0.0.2 to registry `crates-io` [NOTE] waiting for delay v0.0.2 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] delay v0.0.2 at registry `crates-io` "#]]) .run(); // Verify the responder has been pinged let lock = arc2.lock().unwrap(); assert_eq!(*lock, 3); drop(lock); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] delay = "0.0.2" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check").with_status(0).run(); } #[cargo_test] fn skip_wait_for_publish() { // Intentionally using local registry so the crate never makes it to the index let registry = registry::init(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" "#, ) .file("src/main.rs", "fn main() {}") .file( ".cargo/config.toml", " [publish] timeout = 0 ", ) .build(); p.cargo("publish --no-verify -Zpublish-timeout") .replace_crates_io(registry.index_url()) .masquerade_as_nightly_cargo(&["publish-timeout"]) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] foo v0.0.1 ([ROOT]/foo) [UPLOADED] foo v0.0.1 to registry `crates-io` "#]]) .run(); } #[cargo_test] fn timeout_waiting_for_publish() { // Publish doesn't happen within the timeout window. let registry = registry::RegistryBuilder::new() .http_api() .delayed_index_update(20) .build(); let p = project() .file( "Cargo.toml", r#" [package] name = "delay" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" "#, ) .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [publish] timeout = 2 "#, ) .build(); p.cargo("publish --no-verify -Zpublish-timeout") .replace_crates_io(registry.index_url()) .masquerade_as_nightly_cargo(&["publish-timeout"]) .with_status(0) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] delay v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] delay v0.0.1 ([ROOT]/foo) [UPLOADED] delay v0.0.1 to registry `crates-io` [NOTE] waiting for delay v0.0.1 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [WARNING] timed out waiting for delay v0.0.1 to be available in registry `crates-io` [NOTE] the registry may have a backlog that is delaying making the crate available. The crate should be available soon. "#]]) .run(); } #[cargo_test] fn timeout_waiting_for_dependency_publish() { // Publish doesn't happen within the timeout window. let registry = registry::RegistryBuilder::new() .http_api() .delayed_index_update(20) .build(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["main", "other", "dep"] "#, ) .file( "main/Cargo.toml", r#" [package] name = "main" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" [dependencies] dep = { version = "0.0.1", path = "../dep" } "#, ) .file("main/src/main.rs", "fn main() {}") .file( "other/Cargo.toml", r#" [package] name = "other" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" [dependencies] dep = { version = "0.0.1", path = "../dep" } "#, ) .file("other/src/main.rs", "fn main() {}") .file( "dep/Cargo.toml", r#" [package] name = "dep" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" "#, ) .file("dep/src/lib.rs", "") .file( ".cargo/config.toml", r#" [publish] timeout = 2 "#, ) .build(); p.cargo("publish --no-verify -Zpublish-timeout") .replace_crates_io(registry.index_url()) .masquerade_as_nightly_cargo(&["publish-timeout"]) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] dep v0.0.1 ([ROOT]/foo/dep) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] main v0.0.1 ([ROOT]/foo/main) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] other v0.0.1 ([ROOT]/foo/other) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] dep v0.0.1 ([ROOT]/foo/dep) [UPLOADED] dep v0.0.1 to registry `crates-io` [NOTE] waiting for dep v0.0.1 to be available at registry `crates-io`. 2 remaining crates to be published [WARNING] timed out waiting for dep v0.0.1 to be available in registry `crates-io` [NOTE] the registry may have a backlog that is delaying making the crate available. The crate should be available soon. [ERROR] unable to publish main v0.0.1 and other v0.0.1 due to a timeout while waiting for published dependencies to be available. "#]]) .run(); } #[cargo_test] fn package_selection() { let registry = registry::RegistryBuilder::new().http_api().build(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] "#, ) .file("a/Cargo.toml", &basic_manifest("a", "0.1.0")) .file("a/src/lib.rs", "#[test] fn a() {}") .file("b/Cargo.toml", &basic_manifest("b", "0.1.0")) .file("b/src/lib.rs", "#[test] fn b() {}") .build(); p.cargo("publish --no-verify --dry-run --workspace") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no description, license, license-file, documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] a v0.1.0 ([ROOT]/foo/a) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [WARNING] manifest has no description, license, license-file, documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] b v0.1.0 ([ROOT]/foo/b) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] a v0.1.0 ([ROOT]/foo/a) [WARNING] aborting upload due to dry run [UPLOADING] b v0.1.0 ([ROOT]/foo/b) [WARNING] aborting upload due to dry run "#]]) .with_stdout_data(str![[r#""#]]) .run(); p.cargo("publish --no-verify --dry-run --package a --package b") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no description, license, license-file, documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] a v0.1.0 ([ROOT]/foo/a) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [WARNING] manifest has no description, license, license-file, documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] b v0.1.0 ([ROOT]/foo/b) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] a v0.1.0 ([ROOT]/foo/a) [WARNING] aborting upload due to dry run [UPLOADING] b v0.1.0 ([ROOT]/foo/b) [WARNING] aborting upload due to dry run "#]]) .with_stdout_data(str![[r#""#]]) .run(); p.cargo("publish --no-verify --dry-run --workspace --exclude b") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no description, license, license-file, documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] a v0.1.0 ([ROOT]/foo/a) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] a v0.1.0 ([ROOT]/foo/a) [WARNING] aborting upload due to dry run "#]]) .with_stdout_data(str![[r#""#]]) .run(); } #[cargo_test] fn wait_for_git_publish() { // Slow publish to an index with a git index. let registry = registry::RegistryBuilder::new() .http_api() .delayed_index_update(5) .build(); // Publish an earlier version Package::new("delay", "0.0.1") .file("src/lib.rs", "") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "delay" version = "0.0.2" edition = "2015" authors = [] license = "MIT" description = "foo" "#, ) .file("src/lib.rs", "") .build(); p.cargo("publish --no-verify") .replace_crates_io(registry.index_url()) .with_status(0) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] delay v0.0.2 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] delay v0.0.2 ([ROOT]/foo) [UPLOADED] delay v0.0.2 to registry `crates-io` [NOTE] waiting for delay v0.0.2 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] delay v0.0.2 at registry `crates-io` "#]]) .run(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] delay = "0.0.2" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check").with_status(0).run(); } #[cargo_test] fn invalid_token() { // Checks publish behavior with an invalid token. let registry = RegistryBuilder::new().http_api().http_index().build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" documentation = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("publish --no-verify") .replace_crates_io(registry.index_url()) .env("CARGO_REGISTRY_TOKEN", "\x16") .with_stderr_data(str![[r#" [UPDATING] crates.io index [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] foo v0.0.1 ([ROOT]/foo) [ERROR] failed to publish to registry at http://127.0.0.1:[..]/ Caused by: token contains invalid characters. Only printable ISO-8859-1 characters are allowed as it is sent in a HTTPS header. "#]]) .with_status(101) .run(); } #[cargo_test] fn versionless_package() { // Use local registry for faster test times since no publish will occur let registry = registry::init(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" description = "foo" "#, ) .file("src/main.rs", r#"fn main() { println!("hello"); }"#) .build(); p.cargo("publish") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" [ERROR] `foo` cannot be published. `package.publish` must be set to `true` or a non-empty list in Cargo.toml to publish. "#]]) .run(); } // A workspace with three projects that depend on one another (level1 -> level2 -> level3). // level1 is a binary package, to test lockfile generation. fn workspace_with_local_deps_project() -> Project { project() .file( "Cargo.toml", r#" [workspace] members = ["level1", "level2", "level3"] [workspace.dependencies] level2 = { path = "level2", version = "0.0.1" } "# ) .file( "level1/Cargo.toml", r#" [package] name = "level1" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "level1" repository = "bar" [dependencies] # Let one dependency also specify features, for the added test coverage when generating package files. level2 = { workspace = true, features = ["foo"] } "#, ) .file("level1/src/main.rs", "fn main() {}") .file( "level2/Cargo.toml", r#" [package] name = "level2" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "level2" repository = "bar" [features] foo = [] [dependencies] level3 = { path = "../level3", version = "0.0.1" } "# ) .file("level2/src/lib.rs", "") .file( "level3/Cargo.toml", r#" [package] name = "level3" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "level3" repository = "bar" "#, ) .file("level3/src/lib.rs", "") .build() } #[cargo_test] fn workspace_with_local_deps() { let registry = RegistryBuilder::new().http_api().http_index().build(); let p = workspace_with_local_deps_project(); p.cargo("publish") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [PACKAGING] level3 v0.0.1 ([ROOT]/foo/level3) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [PACKAGING] level2 v0.0.1 ([ROOT]/foo/level2) [UPDATING] crates.io index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [PACKAGING] level1 v0.0.1 ([ROOT]/foo/level1) [UPDATING] crates.io index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] level3 v0.0.1 ([ROOT]/foo/level3) [COMPILING] level3 v0.0.1 ([ROOT]/foo/target/package/level3-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [VERIFYING] level2 v0.0.1 ([ROOT]/foo/level2) [UPDATING] crates.io index [UNPACKING] level3 v0.0.1 (registry `[ROOT]/foo/target/package/tmp-registry`) [COMPILING] level3 v0.0.1 [COMPILING] level2 v0.0.1 ([ROOT]/foo/target/package/level2-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [VERIFYING] level1 v0.0.1 ([ROOT]/foo/level1) [UPDATING] crates.io index [UNPACKING] level2 v0.0.1 (registry `[ROOT]/foo/target/package/tmp-registry`) [COMPILING] level3 v0.0.1 [COMPILING] level2 v0.0.1 [COMPILING] level1 v0.0.1 ([ROOT]/foo/target/package/level1-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] level3 v0.0.1 ([ROOT]/foo/level3) [UPLOADED] level3 v0.0.1 to registry `crates-io` [NOTE] waiting for level3 v0.0.1 to be available at registry `crates-io`. 2 remaining crates to be published [PUBLISHED] level3 v0.0.1 at registry `crates-io` [UPLOADING] level2 v0.0.1 ([ROOT]/foo/level2) [UPLOADED] level2 v0.0.1 to registry `crates-io` [NOTE] waiting for level2 v0.0.1 to be available at registry `crates-io`. 1 remaining crate to be published [PUBLISHED] level2 v0.0.1 at registry `crates-io` [UPLOADING] level1 v0.0.1 ([ROOT]/foo/level1) [UPLOADED] level1 v0.0.1 to registry `crates-io` [NOTE] waiting for level1 v0.0.1 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] level1 v0.0.1 at registry `crates-io` "#]]) .run(); } #[cargo_test] fn workspace_parallel() { let registry = RegistryBuilder::new().http_api().http_index().build(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b", "c"] "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "a" repository = "bar" "#, ) .file("a/src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "b" repository = "bar" "#, ) .file("b/src/lib.rs", "") .file( "c/Cargo.toml", r#" [package] name = "c" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "c" repository = "bar" [dependencies] a = { path = "../a", version = "0.0.1" } b = { path = "../b", version = "0.0.1" } "#, ) .file("c/src/lib.rs", "") .build(); p.cargo("publish") .replace_crates_io(registry.index_url()) .with_stderr_data( str![[r#" [UPDATING] crates.io index [PACKAGING] a v0.0.1 ([ROOT]/foo/a) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [PACKAGING] b v0.0.1 ([ROOT]/foo/b) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [PACKAGING] c v0.0.1 ([ROOT]/foo/c) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] a v0.0.1 ([ROOT]/foo/a) [COMPILING] a v0.0.1 ([ROOT]/foo/target/package/a-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [VERIFYING] b v0.0.1 ([ROOT]/foo/b) [COMPILING] b v0.0.1 ([ROOT]/foo/target/package/b-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [VERIFYING] c v0.0.1 ([ROOT]/foo/c) [UPDATING] crates.io index [UNPACKING] a v0.0.1 (registry `[ROOT]/foo/target/package/tmp-registry`) [UNPACKING] b v0.0.1 (registry `[ROOT]/foo/target/package/tmp-registry`) [COMPILING] a v0.0.1 [COMPILING] b v0.0.1 [COMPILING] c v0.0.1 ([ROOT]/foo/target/package/c-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADED] b v0.0.1 to registry `crates-io` [UPLOADED] a v0.0.1 to registry `crates-io` [NOTE] waiting for a v0.0.1 or b v0.0.1 to be available at registry `crates-io`. 1 remaining crate to be published [PUBLISHED] a v0.0.1 and b v0.0.1 at registry `crates-io` [UPLOADING] c v0.0.1 ([ROOT]/foo/c) [UPLOADED] c v0.0.1 to registry `crates-io` [NOTE] waiting for c v0.0.1 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] c v0.0.1 at registry `crates-io` [UPLOADING] a v0.0.1 ([ROOT]/foo/a) [UPLOADING] b v0.0.1 ([ROOT]/foo/b) [UPDATING] crates.io index "#]] .unordered(), ) .run(); } #[cargo_test] fn workspace_missing_dependency() { let registry = RegistryBuilder::new().http_api().http_index().build(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "a" repository = "bar" "#, ) .file("a/src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "b" repository = "bar" [dependencies] a = { path = "../a", version = "0.0.1" } "#, ) .file("b/src/lib.rs", "") .build(); p.cargo("publish -p b") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] crates.io index [PACKAGING] b v0.0.1 ([ROOT]/foo/b) [UPDATING] crates.io index [ERROR] failed to prepare local package for uploading Caused by: no matching package named `a` found location searched: crates.io index required by package `b v0.0.1 ([ROOT]/foo/b)` "#]]) .run(); p.cargo("publish -p a") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [PACKAGING] a v0.0.1 ([ROOT]/foo/a) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] a v0.0.1 ([ROOT]/foo/a) [COMPILING] a v0.0.1 ([ROOT]/foo/target/package/a-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] a v0.0.1 ([ROOT]/foo/a) [UPLOADED] a v0.0.1 to registry `crates-io` [NOTE] waiting for a v0.0.1 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] a v0.0.1 at registry `crates-io` "#]]) .run(); // Publishing the whole workspace now will fail, as `a` is already published. p.cargo("publish") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] crates.io index [ERROR] crate a@0.0.1 already exists on crates.io index "#]]) .run(); } #[cargo_test] fn one_unpublishable_package() { let registry = RegistryBuilder::new().http_api().http_index().build(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["dep", "main"] "#, ) .file( "main/Cargo.toml", r#" [package] name = "main" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "main" repository = "bar" publish = false [dependencies] dep = { path = "../dep", version = "0.1.0" } "#, ) .file("main/src/main.rs", "fn main() {}") .file( "dep/Cargo.toml", r#" [package] name = "dep" version = "0.1.0" edition = "2015" authors = [] license = "MIT" description = "dep" repository = "bar" "#, ) .file("dep/src/lib.rs", "") .build(); p.cargo("publish") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [PACKAGING] dep v0.1.0 ([ROOT]/foo/dep) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] dep v0.1.0 ([ROOT]/foo/dep) [COMPILING] dep v0.1.0 ([ROOT]/foo/target/package/dep-0.1.0) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] dep v0.1.0 ([ROOT]/foo/dep) [UPLOADED] dep v0.1.0 to registry `crates-io` [NOTE] waiting for dep v0.1.0 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] dep v0.1.0 at registry `crates-io` "#]]) .run(); } #[cargo_test] fn virtual_ws_with_multiple_unpublishable_package() { let registry = RegistryBuilder::new().http_api().http_index().build(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["dep", "main", "publishable"] "#, ) .file( "main/Cargo.toml", r#" [package] name = "main" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "main" repository = "bar" publish = false [dependencies] dep = { path = "../dep", version = "0.1.0" } "#, ) .file("main/src/main.rs", "fn main() {}") .file( "dep/Cargo.toml", r#" [package] name = "dep" version = "0.1.0" edition = "2015" authors = [] license = "MIT" description = "dep" repository = "bar" publish = false "#, ) .file("dep/src/lib.rs", "") .file( "publishable/Cargo.toml", r#" [package] name = "publishable" version = "0.1.0" edition = "2015" license = "MIT" description = "foo" repository = "foo" "#, ) .file("publishable/src/lib.rs", "") .build(); p.cargo("publish") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [PACKAGING] publishable v0.1.0 ([ROOT]/foo/publishable) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] publishable v0.1.0 ([ROOT]/foo/publishable) [COMPILING] publishable v0.1.0 ([ROOT]/foo/target/package/publishable-0.1.0) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] publishable v0.1.0 ([ROOT]/foo/publishable) [UPLOADED] publishable v0.1.0 to registry `crates-io` [NOTE] waiting for publishable v0.1.0 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] publishable v0.1.0 at registry `crates-io` "#]]) .run(); } #[cargo_test] fn workspace_flag_with_unpublishable_packages() { let registry = RegistryBuilder::new().http_api().http_index().build(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["publishable", "non-publishable"] [package] name = "cwd" version = "0.0.0" edition = "2015" license = "MIT" description = "foo" repository = "foo" publish = false "#, ) .file("src/lib.rs", "") .file( "publishable/Cargo.toml", r#" [package] name = "publishable" version = "0.0.0" edition = "2015" license = "MIT" description = "foo" repository = "foo" publish = true "#, ) .file("publishable/src/lib.rs", "") .file( "non-publishable/Cargo.toml", r#" [package] name = "non-publishable" version = "0.0.0" edition = "2015" license = "MIT" description = "foo" repository = "foo" publish = false "#, ) .file("non-publishable/src/lib.rs", "") .build(); p.cargo("publish --workspace") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [PACKAGING] publishable v0.0.0 ([ROOT]/foo/publishable) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] publishable v0.0.0 ([ROOT]/foo/publishable) [COMPILING] publishable v0.0.0 ([ROOT]/foo/target/package/publishable-0.0.0) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] publishable v0.0.0 ([ROOT]/foo/publishable) [UPLOADED] publishable v0.0.0 to registry `crates-io` [NOTE] waiting for publishable v0.0.0 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] publishable v0.0.0 at registry `crates-io` "#]]) .run(); } #[cargo_test] fn unpublishable_package_as_versioned_dev_dep() { let registry = RegistryBuilder::new().http_api().http_index().build(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["dep", "main"] "#, ) .file( "main/Cargo.toml", r#" [package] name = "main" version = "0.0.1" edition = "2015" license = "MIT" description = "main" repository = "bar" [dev-dependencies] dep = { path = "../dep", version = "0.1.0" } "#, ) .file("main/src/main.rs", "fn main() {}") .file( "dep/Cargo.toml", r#" [package] name = "dep" version = "0.1.0" edition = "2015" license = "MIT" description = "dep" repository = "bar" publish = false "#, ) .file("dep/src/lib.rs", "") .build(); // It is expected to find the versioned dev dep not being published, // regardless with `--dry-run` or `--no-verify`. p.cargo("publish") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] crates.io index [PACKAGING] main v0.0.1 ([ROOT]/foo/main) [UPDATING] crates.io index [ERROR] failed to prepare local package for uploading Caused by: no matching package named `dep` found location searched: crates.io index required by package `main v0.0.1 ([ROOT]/foo/main)` "#]]) .run(); p.cargo("publish --dry-run") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] crates.io index [PACKAGING] main v0.0.1 ([ROOT]/foo/main) [UPDATING] crates.io index [ERROR] failed to prepare local package for uploading Caused by: no matching package named `dep` found location searched: crates.io index required by package `main v0.0.1 ([ROOT]/foo/main)` "#]]) .run(); p.cargo("publish --no-verify") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] crates.io index [PACKAGING] main v0.0.1 ([ROOT]/foo/main) [UPDATING] crates.io index [ERROR] failed to prepare local package for uploading Caused by: no matching package named `dep` found location searched: crates.io index required by package `main v0.0.1 ([ROOT]/foo/main)` "#]]) .run(); } #[cargo_test] fn all_unpublishable_packages() { let registry = RegistryBuilder::new().http_api().http_index().build(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["non-publishable1", "non-publishable2"] "#, ) .file( "non-publishable1/Cargo.toml", r#" [package] name = "non-publishable1" version = "0.0.0" edition = "2015" license = "MIT" description = "foo" repository = "foo" publish = false "#, ) .file("non-publishable1/src/lib.rs", "") .file( "non-publishable2/Cargo.toml", r#" [package] name = "non-publishable2" version = "0.0.0" edition = "2015" license = "MIT" description = "foo" repository = "foo" publish = false "#, ) .file("non-publishable2/src/lib.rs", "") .build(); p.cargo("publish --workspace") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [WARNING] nothing to publish, but found 2 unpublishable packages [NOTE] to publish packages, set `package.publish` to `true` or a non-empty list "#]]) .run(); } #[cargo_test] fn checksum_changed() { let registry = RegistryBuilder::new().http_api().http_index().build(); Package::new("dep", "1.0.0").publish(); Package::new("transitive", "1.0.0") .dep("dep", "1.0.0") .publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["dep"] [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" documentation = "foo" [dependencies] dep = { path = "./dep", version = "1.0.0" } transitive = "1.0.0" "#, ) .file("src/lib.rs", "") .file( "dep/Cargo.toml", r#" [package] name = "dep" version = "1.0.0" edition = "2015" "#, ) .file("dep/src/lib.rs", "") .build(); p.cargo("check").run(); p.cargo("publish --dry-run --workspace") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] crate dep@1.0.0 already exists on crates.io index [WARNING] manifest has no description, license, license-file, documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] dep v1.0.0 ([ROOT]/foo/dep) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [PACKAGING] foo v0.0.1 ([ROOT]/foo) [UPDATING] crates.io index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] dep v1.0.0 ([ROOT]/foo/dep) [COMPILING] dep v1.0.0 ([ROOT]/foo/target/package/dep-1.0.0) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [VERIFYING] foo v0.0.1 ([ROOT]/foo) [UNPACKING] dep v1.0.0 (registry `[ROOT]/foo/target/package/tmp-registry`) [COMPILING] dep v1.0.0 [COMPILING] transitive v1.0.0 [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] dep v1.0.0 ([ROOT]/foo/dep) [WARNING] aborting upload due to dry run [UPLOADING] foo v0.0.1 ([ROOT]/foo) [WARNING] aborting upload due to dry run "#]]) .run(); } cargo-0.91.0/tests/testsuite/publish_lockfile.rs000064400000000000000000000416021046102023000200700ustar 00000000000000//! Tests for including `Cargo.lock` when publishing/packaging. use std::fs::File; use crate::prelude::*; use crate::utils::cargo_process; use cargo_test_support::registry::Package; use cargo_test_support::{ basic_manifest, git, paths, project, publish::validate_crate_contents, str, }; fn pl_manifest(name: &str, version: &str, extra: &str) -> String { format!( r#" [package] name = "{}" version = "{}" edition = "2015" authors = [] license = "MIT" description = "foo" documentation = "foo" homepage = "foo" repository = "foo" {} "#, name, version, extra ) } #[cargo_test] fn removed() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["publish-lockfile"] [package] name = "foo" version = "0.1.0" edition = "2015" publish-lockfile = true license = "MIT" description = "foo" documentation = "foo" homepage = "foo" repository = "foo" "#, ) .file("src/lib.rs", "") .build(); p.cargo("package") .masquerade_as_nightly_cargo(&["publish-lockfile"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: the cargo feature `publish-lockfile` has been removed in the 1.37 release Remove the feature from Cargo.toml to remove this error. See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#publish-lockfile for more information about using this feature. "#]]) .run(); } #[cargo_test] fn package_lockfile() { let p = project() .file("Cargo.toml", &pl_manifest("foo", "0.0.1", "")) .file("src/main.rs", "fn main() {}") .build(); p.cargo("package") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert!(p.root().join("target/package/foo-0.0.1.crate").is_file()); p.cargo("package -l") .with_stdout_data(str![[r#" Cargo.lock Cargo.toml Cargo.toml.orig src/main.rs "#]]) .run(); p.cargo("package") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", &["Cargo.toml", "Cargo.toml.orig", "Cargo.lock", "src/main.rs"], (), ); } #[cargo_test] fn package_lockfile_git_repo() { // Create a Git repository containing a minimal Rust project. let g = git::repo(&paths::root().join("foo")) .file("Cargo.toml", &pl_manifest("foo", "0.0.1", "")) .file("src/main.rs", "fn main() {}") .build(); cargo_process("package -l") .cwd(g.root()) .with_stdout_data(str![[r#" .cargo_vcs_info.json Cargo.lock Cargo.toml Cargo.toml.orig src/main.rs "#]]) .run(); cargo_process("package -v") .cwd(g.root()) .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [ARCHIVING] .cargo_vcs_info.json [ARCHIVING] Cargo.lock [ARCHIVING] Cargo.toml [ARCHIVING] Cargo.toml.orig [ARCHIVING] src/main.rs [PACKAGED] 5 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [RUNNING] `rustc --crate-name foo --edition=2015 src/main.rs [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn lock_file_with_library() { let p = project() .file("Cargo.toml", &pl_manifest("foo", "0.0.1", "")) .file("src/lib.rs", "") .build(); p.cargo("package").run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs", "Cargo.lock"], (), ); } #[cargo_test] fn lock_file_and_workspace() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo"] "#, ) .file("foo/Cargo.toml", &pl_manifest("foo", "0.0.1", "")) .file("foo/src/main.rs", "fn main() {}") .build(); p.cargo("package").cwd("foo").run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", &["Cargo.toml", "Cargo.toml.orig", "src/main.rs", "Cargo.lock"], (), ); } #[cargo_test] fn note_resolve_changes() { // `multi` has multiple sources (path and registry). Package::new("multi", "0.1.0").publish(); // `updated` is always from registry, but should not change. Package::new("updated", "1.0.0").publish(); // `patched` is [patch]ed. Package::new("patched", "1.0.0").publish(); let p = project() .file( "Cargo.toml", &pl_manifest( "foo", "0.0.1", r#" [dependencies] multi = { path = "multi", version = "0.1" } updated = "1.0" patched = "1.0" [patch.crates-io] patched = { path = "patched" } "#, ), ) .file("src/main.rs", "fn main() {}") .file("multi/Cargo.toml", &basic_manifest("multi", "0.1.0")) .file("multi/src/lib.rs", "") .file("patched/Cargo.toml", &basic_manifest("patched", "1.0.0")) .file("patched/src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); // Make sure this does not change or warn. Package::new("updated", "1.0.1").publish(); p.cargo("package --no-verify -v --allow-dirty") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [ARCHIVING] Cargo.lock [UPDATING] `dummy-registry` index [NOTE] package `multi v0.1.0` added to the packaged Cargo.lock file, was originally sourced from `[ROOT]/foo/multi` [NOTE] package `patched v1.0.0` added to the packaged Cargo.lock file, was originally sourced from `[ROOT]/foo/patched` [ARCHIVING] Cargo.toml [ARCHIVING] Cargo.toml.orig [ARCHIVING] src/main.rs [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [WARNING] found (git) Cargo.toml ignored at `[..]/foo/Cargo.toml` in workdir `[..]` "#]].unordered()) .run(); } #[cargo_test] fn outdated_lock_version_change_does_not_warn() { // If the version of the package being packaged changes, but Cargo.lock is // not updated, don't bother warning about it. let p = project() .file("Cargo.toml", &pl_manifest("foo", "0.1.0", "")) .file("src/main.rs", "fn main() {}") .build(); p.cargo("generate-lockfile").run(); p.change_file("Cargo.toml", &pl_manifest("foo", "0.2.0", "")); p.cargo("package --no-verify") .with_stderr_data(str![[r#" [PACKAGING] foo v0.2.0 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) "#]]) .run(); } #[cargo_test] fn no_warn_workspace_extras() { // Other entries in workspace lock file should be ignored. Package::new("dep1", "1.0.0").publish(); Package::new("dep2", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] "#, ) .file( "a/Cargo.toml", &pl_manifest( "a", "0.1.0", r#" [dependencies] dep1 = "1.0" "#, ), ) .file("a/src/main.rs", "fn main() {}") .file( "b/Cargo.toml", &pl_manifest( "b", "0.1.0", r#" [dependencies] dep2 = "1.0" "#, ), ) .file("b/src/main.rs", "fn main() {}") .build(); p.cargo("generate-lockfile").run(); p.cargo("package --no-verify") .cwd("a") .with_stderr_data(str![[r#" [PACKAGING] a v0.1.0 ([ROOT]/foo/a) [UPDATING] `dummy-registry` index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) "#]]) .run(); } #[cargo_test] fn warn_package_with_yanked() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", &pl_manifest( "foo", "0.0.1", r#" [dependencies] bar = "0.1" "#, ), ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("generate-lockfile").run(); Package::new("bar", "0.1.0").yanked(true).publish(); // Make sure it sticks with the locked (yanked) version. Package::new("bar", "0.1.1").publish(); p.cargo("package --no-verify") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [UPDATING] `dummy-registry` index [WARNING] package `bar v0.1.0` in Cargo.lock is yanked in registry `crates-io`, consider updating to a version that is not yanked [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) "#]]) .run(); } #[cargo_test] fn warn_install_with_yanked() { Package::new("bar", "0.1.0").yanked(true).publish(); Package::new("bar", "0.1.1").publish(); Package::new("foo", "0.1.0") .dep("bar", "0.1") .file("src/main.rs", "fn main() {}") .file( "Cargo.lock", r#" [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "foo" version = "0.1.0" dependencies = [ "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] "#, ) .publish(); cargo_process("install --locked foo") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] foo v0.1.0 (registry `dummy-registry`) [INSTALLING] foo v0.1.0 [WARNING] package `bar v0.1.0` in Cargo.lock is yanked in registry `crates-io`, consider running without --locked [DOWNLOADING] crates ... [DOWNLOADED] bar v0.1.0 (registry `dummy-registry`) [COMPILING] bar v0.1.0 [COMPILING] foo v0.1.0 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE] [INSTALLED] package `foo v0.1.0` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); // Try again without --locked, make sure it uses 0.1.1 and does not warn. cargo_process("install --force foo") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [INSTALLING] foo v0.1.0 [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.1.1 (registry `dummy-registry`) [COMPILING] bar v0.1.1 [COMPILING] foo v0.1.0 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [REPLACING] [ROOT]/home/.cargo/bin/foo[EXE] [REPLACED] package `foo v0.1.0` with `foo v0.1.0` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); } #[cargo_test] fn ignore_lockfile() { // With an explicit `include` list, but Cargo.lock in .gitignore, don't // complain about `Cargo.lock` being ignored. Note that it is still // included in the packaged regardless. let p = git::new("foo", |p| { p.file( "Cargo.toml", &pl_manifest( "foo", "0.0.1", r#" include = [ "src/main.rs" ] "#, ), ) .file("src/main.rs", "fn main() {}") .file(".gitignore", "Cargo.lock") }); p.cargo("package -l") .with_stdout_data(str![[r#" .cargo_vcs_info.json Cargo.lock Cargo.toml Cargo.toml.orig src/main.rs "#]]) .run(); p.cargo("generate-lockfile").run(); p.cargo("package -v") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [ARCHIVING] .cargo_vcs_info.json [ARCHIVING] Cargo.lock [ARCHIVING] Cargo.toml [ARCHIVING] Cargo.toml.orig [ARCHIVING] src/main.rs [PACKAGED] 5 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [RUNNING] `rustc --crate-name foo --edition=2015 src/main.rs [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn ignore_lockfile_inner() { // Ignore `Cargo.lock` if in .gitignore in a git subdirectory. let p = git::new("foo", |p| { p.no_manifest() .file("bar/Cargo.toml", &pl_manifest("bar", "0.0.1", "")) .file("bar/src/main.rs", "fn main() {}") .file("bar/.gitignore", "Cargo.lock") }); p.cargo("generate-lockfile").cwd("bar").run(); p.cargo("package -v --no-verify") .cwd("bar") .with_stderr_data(str![[r#" [PACKAGING] bar v0.0.1 ([ROOT]/foo/bar) [ARCHIVING] .cargo_vcs_info.json [ARCHIVING] .gitignore [ARCHIVING] Cargo.lock [ARCHIVING] Cargo.toml [ARCHIVING] Cargo.toml.orig [ARCHIVING] src/main.rs [PACKAGED] 6 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) "#]]) .run(); } #[cargo_test] fn use_workspace_root_lockfile() { // Issue #11148 // Workspace members should use `Cargo.lock` at workspace root Package::new("serde", "0.2.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" [dependencies] serde = "0.2" [workspace] members = ["bar"] "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "bar" workspace = ".." [dependencies] serde = "0.2" "#, ) .file("bar/src/main.rs", "fn main() {}") .build(); // Create `Cargo.lock` in the workspace root. p.cargo("generate-lockfile").run(); // Now, add a newer version of `serde`. Package::new("serde", "0.2.1").publish(); // Expect: package `bar` uses `serde v0.2.0` as required by workspace `Cargo.lock`. p.cargo("package --workspace") .with_stderr_data(str![[r#" [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] bar v0.0.1 ([ROOT]/foo/bar) [UPDATING] `dummy-registry` index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] bar v0.0.1 ([ROOT]/foo/bar) [DOWNLOADING] crates ... [DOWNLOADED] serde v0.2.0 (registry `dummy-registry`) [COMPILING] serde v0.2.0 [COMPILING] bar v0.0.1 ([ROOT]/foo/target/package/bar-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] serde v0.2.0 [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let package_path = p.root().join("target/package/foo-0.0.1.crate"); assert!(package_path.is_file()); let f = File::open(&package_path).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], (), ); let package_path = p.root().join("target/package/bar-0.0.1.crate"); assert!(package_path.is_file()); let f = File::open(&package_path).unwrap(); validate_crate_contents( f, "bar-0.0.1.crate", &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], (), ); } cargo-0.91.0/tests/testsuite/read_manifest.rs000064400000000000000000000121651046102023000173550ustar 00000000000000//! Tests for the `cargo read-manifest` command. use crate::prelude::*; use cargo_test_support::{basic_bin_manifest, main_file, project, str}; pub fn basic_bin_manifest_with_readme(name: &str, readme_filename: &str) -> String { format!( r#" [package] name = "{}" version = "0.5.0" authors = ["wycats@example.com"] readme = {} [[bin]] name = "{}" "#, name, readme_filename, name ) } #[cargo_test] fn cargo_read_manifest_path_to_cargo_toml_relative() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("read-manifest --manifest-path foo/Cargo.toml") .cwd(p.root().parent().unwrap()) .with_stdout_data( str![[r#" { "readme": null, "...": "{...}" } "#]] .is_json(), ) .run(); } #[cargo_test] fn cargo_read_manifest_path_to_cargo_toml_absolute() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("read-manifest --manifest-path") .arg(p.root().join("Cargo.toml")) .cwd(p.root().parent().unwrap()) .with_stdout_data( str![[r#" { "readme": null, "...": "{...}" } "#]] .is_json(), ) .run(); } #[cargo_test] fn cargo_read_manifest_path_to_cargo_toml_parent_relative() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("read-manifest --manifest-path foo") .cwd(p.root().parent().unwrap()) .with_status(101) .with_stderr_data(str![[r#" [ERROR] the manifest-path must be a path to a Cargo.toml file "#]]) .run(); } #[cargo_test] fn cargo_read_manifest_path_to_cargo_toml_parent_absolute() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("read-manifest --manifest-path") .arg(p.root()) .cwd(p.root().parent().unwrap()) .with_status(101) .with_stderr_data(str![[r#" [ERROR] the manifest-path must be a path to a Cargo.toml file "#]]) .run(); } #[cargo_test] fn cargo_read_manifest_cwd() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("read-manifest") .with_stdout_data( str![[r#" { "readme": null, "...": "{...}" } "#]] .is_json(), ) .run(); } #[cargo_test] fn cargo_read_manifest_with_specified_readme() { let p = project() .file( "Cargo.toml", &basic_bin_manifest_with_readme("foo", r#""SomeReadme.txt""#), ) .file("SomeReadme.txt", "Sample Project") .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("read-manifest") .with_stdout_data( str![[r#" { "readme": "SomeReadme.txt", "...": "{...}" } "#]] .is_json(), ) .run(); } #[cargo_test] fn cargo_read_manifest_default_readme() { let assert_output = |readme, expected| { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file(readme, "Sample project") .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("read-manifest").with_stdout_data(expected).run(); }; assert_output( "README.md", str![[r#" { "readme": "README.md", "...": "{...}" } "#]] .is_json(), ); assert_output( "README.txt", str![[r#" { "readme": "README.txt", "...": "{...}" } "#]] .is_json(), ); assert_output( "README", str![[r#" { "readme": "README", "...": "{...}" } "#]] .is_json(), ); } #[cargo_test] fn cargo_read_manifest_suppress_default_readme() { let p = project() .file( "Cargo.toml", &basic_bin_manifest_with_readme("foo", "false"), ) .file("README.txt", "Sample project") .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("read-manifest") .with_stdout_data( str![[r#" { "readme": null, "...": "{...}" } "#]] .is_json(), ) .run(); } // If a file named README.md exists, and `readme = true`, the value `README.md` should be defaulted in. #[cargo_test] fn cargo_read_manifest_defaults_readme_if_true() { let p = project() .file("Cargo.toml", &basic_bin_manifest_with_readme("foo", "true")) .file("README.md", "Sample project") .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("read-manifest") .with_stdout_data( str![[r#" { "readme": "README.md", "...": "{...}" } "#]] .is_json(), ) .run(); } cargo-0.91.0/tests/testsuite/registry.rs000064400000000000000000003653661046102023000164420ustar 00000000000000//! Tests for normal registry dependencies. use std::fmt::Write; use std::fs::{self, File}; use std::path::{Path, PathBuf}; use std::sync::Arc; use std::sync::Mutex; use crate::prelude::*; use crate::utils::cargo_process; use cargo::core::SourceId; use cargo_test_support::paths; use cargo_test_support::registry::{ self, Dependency, Package, RegistryBuilder, Response, TestRegistry, registry_path, }; use cargo_test_support::{basic_manifest, project, str}; use cargo_test_support::{git, t}; use cargo_util::paths::remove_dir_all; fn setup_http() -> TestRegistry { RegistryBuilder::new().http_index().build() } #[cargo_test] fn test_server_stops() { let server = setup_http(); server.join(); // ensure the server fully shuts down } #[cargo_test] fn simple_http() { let _server = setup_http(); simple( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) [CHECKING] bar v0.0.1 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], str![[r#" [CHECKING] bar v0.0.1 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], ); } #[cargo_test] fn simple_git() { simple( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) [CHECKING] bar v0.0.1 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], str![[r#" [CHECKING] bar v0.0.1 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], ); } fn simple(pre_clean_expected: impl IntoData, post_clean_expected: impl IntoData) { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = ">= 0.0.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("bar", "0.0.1").publish(); p.cargo("check").with_stderr_data(pre_clean_expected).run(); p.cargo("clean").run(); assert!(paths::home().join(".cargo/registry/CACHEDIR.TAG").is_file()); // Don't download a second time p.cargo("check").with_stderr_data(post_clean_expected).run(); } #[cargo_test] fn deps_http() { let _server = setup_http(); deps(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] ba[..] v0.0.1 (registry `dummy-registry`) [DOWNLOADED] ba[..] v0.0.1 (registry `dummy-registry`) [CHECKING] baz v0.0.1 [CHECKING] bar v0.0.1 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); } #[cargo_test] fn deps_git() { deps(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] ba[..] v0.0.1 (registry `dummy-registry`) [DOWNLOADED] ba[..] v0.0.1 (registry `dummy-registry`) [CHECKING] baz v0.0.1 [CHECKING] bar v0.0.1 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); } fn deps(expected: impl IntoData) { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = ">= 0.0.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("baz", "0.0.1").publish(); Package::new("bar", "0.0.1").dep("baz", "*").publish(); p.cargo("check").with_stderr_data(expected).run(); assert!(paths::home().join(".cargo/registry/CACHEDIR.TAG").is_file()); } #[cargo_test] fn nonexistent_http() { let _server = setup_http(); nonexistent(str![[r#" [UPDATING] `dummy-registry` index [ERROR] no matching package named `nonexistent` found location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.0.1 ([ROOT]/foo)` "#]]); } #[cargo_test] fn nonexistent_git() { nonexistent(str![[r#" [UPDATING] `dummy-registry` index [ERROR] no matching package named `nonexistent` found location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.0.1 ([ROOT]/foo)` "#]]); } fn nonexistent(expected: impl IntoData) { Package::new("init", "0.0.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] nonexistent = ">= 0.0.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_status(101) .with_stderr_data(expected) .run(); } #[cargo_test] fn wrong_case_http() { let _server = setup_http(); wrong_case(str![[r#" [UPDATING] `dummy-registry` index [ERROR] no matching package found searched package name: `Init` perhaps you meant: init location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.0.1 ([ROOT]/foo)` "#]]); } #[cargo_test] fn wrong_case_git() { wrong_case(str![[r#" [UPDATING] `dummy-registry` index [ERROR] no matching package found searched package name: `Init` perhaps you meant: init location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.0.1 ([ROOT]/foo)` "#]]); } fn wrong_case(expected: impl IntoData) { Package::new("init", "0.0.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] Init = ">= 0.0.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); // #5678 to make this work p.cargo("check") .with_status(101) .with_stderr_data(expected) .run(); } #[cargo_test] fn mis_hyphenated_http() { let _server = setup_http(); mis_hyphenated(str![[r#" [UPDATING] `dummy-registry` index [ERROR] no matching package found searched package name: `mis_hyphenated` perhaps you meant: mis-hyphenated location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.0.1 ([ROOT]/foo)` "#]]); } #[cargo_test] fn mis_hyphenated_git() { mis_hyphenated(str![[r#" [UPDATING] `dummy-registry` index [ERROR] no matching package found searched package name: `mis_hyphenated` perhaps you meant: mis-hyphenated location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.0.1 ([ROOT]/foo)` "#]]); } fn mis_hyphenated(expected: impl IntoData) { Package::new("mis-hyphenated", "0.0.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] mis_hyphenated = ">= 0.0.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); // #2775 to make this work p.cargo("check") .with_status(101) .with_stderr_data(expected) .run(); } #[cargo_test] fn wrong_version_http() { let _server = setup_http(); wrong_version( str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for the requirement `foo = ">=1.0.0"` candidate versions found which didn't match: 0.0.2, 0.0.1 location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.0.1 ([ROOT]/foo)` perhaps a crate was updated and forgotten to be re-vendored? "#]], str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for the requirement `foo = ">=1.0.0"` candidate versions found which didn't match: 0.0.4, 0.0.3, 0.0.2, ... location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.0.1 ([ROOT]/foo)` perhaps a crate was updated and forgotten to be re-vendored? "#]], ); } #[cargo_test] fn wrong_version_git() { wrong_version( str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for the requirement `foo = ">=1.0.0"` candidate versions found which didn't match: 0.0.2, 0.0.1 location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.0.1 ([ROOT]/foo)` perhaps a crate was updated and forgotten to be re-vendored? "#]], str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for the requirement `foo = ">=1.0.0"` candidate versions found which didn't match: 0.0.4, 0.0.3, 0.0.2, ... location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.0.1 ([ROOT]/foo)` perhaps a crate was updated and forgotten to be re-vendored? "#]], ); } fn wrong_version(pre_publish_expected: impl IntoData, post_publish_expected: impl IntoData) { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] foo = ">= 1.0.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("foo", "0.0.1").publish(); Package::new("foo", "0.0.2").publish(); p.cargo("check") .with_status(101) .with_stderr_data(pre_publish_expected) .run(); Package::new("foo", "0.0.3").publish(); Package::new("foo", "0.0.4").publish(); p.cargo("check") .with_status(101) .with_stderr_data(post_publish_expected) .run(); } #[cargo_test] fn bad_cksum_http() { let _server = setup_http(); bad_cksum(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bad-cksum v0.0.1 (registry `dummy-registry`) [ERROR] failed to download replaced source registry `crates-io` Caused by: failed to verify the checksum of `bad-cksum v0.0.1 (registry `dummy-registry`)` "#]]); } #[cargo_test] fn bad_cksum_git() { bad_cksum(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bad-cksum v0.0.1 (registry `dummy-registry`) [ERROR] failed to download replaced source registry `crates-io` Caused by: failed to verify the checksum of `bad-cksum v0.0.1 (registry `dummy-registry`)` "#]]); } fn bad_cksum(expected: impl IntoData) { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bad-cksum = ">= 0.0.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); let pkg = Package::new("bad-cksum", "0.0.1"); pkg.publish(); t!(File::create(&pkg.archive_dst())); p.cargo("check -v") .with_status(101) .with_stderr_data(expected) .run(); } #[cargo_test] fn update_registry_http() { let _server = setup_http(); update_registry( str![[r#" [UPDATING] `dummy-registry` index [ERROR] no matching package named `notyet` found location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.0.1 ([ROOT]/foo)` "#]], str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] notyet v0.0.1 (registry `dummy-registry`) [CHECKING] notyet v0.0.1 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], ); } #[cargo_test] fn update_registry_git() { update_registry( str![[r#" [UPDATING] `dummy-registry` index [ERROR] no matching package named `notyet` found location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.0.1 ([ROOT]/foo)` "#]], str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] notyet v0.0.1 (registry `dummy-registry`) [CHECKING] notyet v0.0.1 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], ); } fn update_registry(pre_publish_expected: impl IntoData, post_publish_expected: impl IntoData) { Package::new("init", "0.0.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] notyet = ">= 0.0.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_status(101) .with_stderr_data(pre_publish_expected) .run(); Package::new("notyet", "0.0.1").publish(); p.cargo("check") .with_stderr_data(post_publish_expected) .run(); } #[cargo_test] fn package_with_path_deps_http() { let _server = setup_http(); package_with_path_deps( str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [UPDATING] `dummy-registry` index [ERROR] failed to prepare local package for uploading Caused by: no matching package named `notyet` found location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.0.1 ([ROOT]/foo)` "#]], str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [UPDATING] `dummy-registry` index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [DOWNLOADING] crates ... [DOWNLOADED] notyet v0.0.1 (registry `dummy-registry`) [COMPILING] notyet v0.0.1 [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], ); } #[cargo_test] fn package_with_path_deps_git() { package_with_path_deps( str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [UPDATING] `dummy-registry` index [ERROR] failed to prepare local package for uploading Caused by: no matching package named `notyet` found location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.0.1 ([ROOT]/foo)` "#]], str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [UPDATING] `dummy-registry` index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [DOWNLOADING] crates ... [DOWNLOADED] notyet v0.0.1 (registry `dummy-registry`) [COMPILING] notyet v0.0.1 [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], ); } fn package_with_path_deps( pre_publish_expected: impl IntoData, post_publish_expected: impl IntoData, ) { Package::new("init", "0.0.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" repository = "bar" [dependencies.notyet] version = "0.0.1" path = "notyet" "#, ) .file("src/main.rs", "fn main() {}") .file("notyet/Cargo.toml", &basic_manifest("notyet", "0.0.1")) .file("notyet/src/lib.rs", "") .build(); p.cargo("package") .with_status(101) .with_stderr_data(pre_publish_expected) .run(); Package::new("notyet", "0.0.1").publish(); p.cargo("package") .with_stderr_data(post_publish_expected) .run(); } #[cargo_test] fn lockfile_locks_http() { let _server = setup_http(); lockfile_locks( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) [CHECKING] bar v0.0.1 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], ); } #[cargo_test] fn lockfile_locks_git() { lockfile_locks( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) [CHECKING] bar v0.0.1 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], ); } fn lockfile_locks(pre_publish_expected: impl IntoData, post_publish_expected: impl IntoData) { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "*" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("bar", "0.0.1").publish(); p.cargo("check") .with_stderr_data(pre_publish_expected) .run(); p.root().move_into_the_past(); Package::new("bar", "0.0.2").publish(); p.cargo("check") .with_stderr_data(post_publish_expected) .run(); } #[cargo_test] fn lockfile_locks_transitively_http() { let _server = setup_http(); lockfile_locks_transitively( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] ba[..] v0.0.1 (registry `dummy-registry`) [DOWNLOADED] ba[..] v0.0.1 (registry `dummy-registry`) [CHECKING] baz v0.0.1 [CHECKING] bar v0.0.1 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], ); } #[cargo_test] fn lockfile_locks_transitively_git() { lockfile_locks_transitively( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] ba[..] v0.0.1 (registry `dummy-registry`) [DOWNLOADED] ba[..] v0.0.1 (registry `dummy-registry`) [CHECKING] baz v0.0.1 [CHECKING] bar v0.0.1 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], ); } fn lockfile_locks_transitively( pre_publish_expected: impl IntoData, post_publish_expected: impl IntoData, ) { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "*" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("baz", "0.0.1").publish(); Package::new("bar", "0.0.1").dep("baz", "*").publish(); p.cargo("check") .with_stderr_data(pre_publish_expected) .run(); p.root().move_into_the_past(); Package::new("baz", "0.0.2").publish(); Package::new("bar", "0.0.2").dep("baz", "*").publish(); p.cargo("check") .with_stderr_data(post_publish_expected) .run(); } #[cargo_test] fn yanks_are_not_used_http() { let _server = setup_http(); yanks_are_not_used(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] ba[..] v0.0.1 (registry `dummy-registry`) [DOWNLOADED] ba[..] v0.0.1 (registry `dummy-registry`) [CHECKING] baz v0.0.1 [CHECKING] bar v0.0.1 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); } #[cargo_test] fn yanks_are_not_used_git() { yanks_are_not_used(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] ba[..] v0.0.1 (registry `dummy-registry`) [DOWNLOADED] ba[..] v0.0.1 (registry `dummy-registry`) [CHECKING] baz v0.0.1 [CHECKING] bar v0.0.1 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); } fn yanks_are_not_used(expected: impl IntoData) { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "*" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("baz", "0.0.1").publish(); Package::new("baz", "0.0.2").yanked(true).publish(); Package::new("bar", "0.0.1").dep("baz", "*").publish(); Package::new("bar", "0.0.2") .dep("baz", "*") .yanked(true) .publish(); p.cargo("check").with_stderr_data(expected).run(); } #[cargo_test] fn relying_on_a_yank_is_bad_http() { let _server = setup_http(); relying_on_a_yank_is_bad(str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for the requirement `baz = "=0.0.2"` version 0.0.2 is yanked location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `bar v0.0.1` ... which satisfies dependency `bar = "*"` of package `foo v0.0.1 ([ROOT]/foo)` "#]]); } #[cargo_test] fn relying_on_a_yank_is_bad_git() { relying_on_a_yank_is_bad(str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for the requirement `baz = "=0.0.2"` version 0.0.2 is yanked location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `bar v0.0.1` ... which satisfies dependency `bar = "*"` of package `foo v0.0.1 ([ROOT]/foo)` "#]]); } fn relying_on_a_yank_is_bad(expected: impl IntoData) { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "*" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("baz", "0.0.1").publish(); Package::new("baz", "0.0.2").yanked(true).publish(); Package::new("bar", "0.0.1").dep("baz", "=0.0.2").publish(); p.cargo("check") .with_status(101) .with_stderr_data(expected) .run(); } #[cargo_test] fn yanks_in_lockfiles_are_ok_http() { let _server = setup_http(); yanks_in_lockfiles_are_ok( str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for the requirement `bar = "*"` version 0.0.1 is yanked location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.0.1 ([ROOT]/foo)` "#]], ); } #[cargo_test] fn yanks_in_lockfiles_are_ok_git() { yanks_in_lockfiles_are_ok( str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for the requirement `bar = "*"` version 0.0.1 is yanked location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.0.1 ([ROOT]/foo)` "#]], ); } fn yanks_in_lockfiles_are_ok(expected_check: impl IntoData, expected_update: impl IntoData) { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "*" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("bar", "0.0.1").publish(); p.cargo("check").run(); registry_path().join("3").rm_rf(); Package::new("bar", "0.0.1").yanked(true).publish(); p.cargo("check").with_stderr_data(expected_check).run(); p.cargo("update") .with_status(101) .with_stderr_data(expected_update) .run(); } #[cargo_test] fn yanks_in_lockfiles_are_ok_for_other_update_http() { let _server = setup_http(); yanks_in_lockfiles_are_ok_for_other_update( str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for the requirement `bar = "*"` version 0.0.1 is yanked location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.0.1 ([ROOT]/foo)` "#]], str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [UPDATING] baz v0.0.1 -> v0.0.2 "#]], ); } #[cargo_test] fn yanks_in_lockfiles_are_ok_for_other_update_git() { yanks_in_lockfiles_are_ok_for_other_update( str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for the requirement `bar = "*"` version 0.0.1 is yanked location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.0.1 ([ROOT]/foo)` "#]], str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [UPDATING] baz v0.0.1 -> v0.0.2 "#]], ); } fn yanks_in_lockfiles_are_ok_for_other_update( expected_check: impl IntoData, expected_update: impl IntoData, expected_other_update: impl IntoData, ) { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "*" baz = "*" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("bar", "0.0.1").publish(); Package::new("baz", "0.0.1").publish(); p.cargo("check").run(); registry_path().join("3").rm_rf(); Package::new("bar", "0.0.1").yanked(true).publish(); Package::new("baz", "0.0.1").publish(); p.cargo("check").with_stderr_data(expected_check).run(); Package::new("baz", "0.0.2").publish(); p.cargo("update") .with_status(101) .with_stderr_data(expected_update) .run(); p.cargo("update baz") .with_stderr_data(expected_other_update) .run(); } #[cargo_test] fn yanks_in_lockfiles_are_ok_with_new_dep_http() { let _server = setup_http(); yanks_in_lockfiles_are_ok_with_new_dep(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [ADDING] baz v0.0.1 [DOWNLOADING] crates ... [DOWNLOADED] baz v0.0.1 (registry `dummy-registry`) [CHECKING] baz v0.0.1 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); } #[cargo_test] fn yanks_in_lockfiles_are_ok_with_new_dep_git() { yanks_in_lockfiles_are_ok_with_new_dep(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [ADDING] baz v0.0.1 [DOWNLOADING] crates ... [DOWNLOADED] baz v0.0.1 (registry `dummy-registry`) [CHECKING] baz v0.0.1 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); } fn yanks_in_lockfiles_are_ok_with_new_dep(expected: impl IntoData) { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "*" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("bar", "0.0.1").publish(); p.cargo("check").run(); registry_path().join("3").rm_rf(); Package::new("bar", "0.0.1").yanked(true).publish(); Package::new("baz", "0.0.1").publish(); p.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "*" baz = "*" "#, ); p.cargo("check").with_stderr_data(expected).run(); } #[cargo_test] fn update_with_lockfile_if_packages_missing_http() { let _server = setup_http(); update_with_lockfile_if_packages_missing(str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); } #[cargo_test] fn update_with_lockfile_if_packages_missing_git() { update_with_lockfile_if_packages_missing(str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); } fn update_with_lockfile_if_packages_missing(expected: impl IntoData) { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "*" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("bar", "0.0.1").publish(); p.cargo("check").run(); p.root().move_into_the_past(); paths::home().join(".cargo/registry").rm_rf(); p.cargo("check").with_stderr_data(expected).run(); } #[cargo_test] fn update_lockfile_http() { let _server = setup_http(); update_lockfile( str![[r#" [UPDATING] `dummy-registry` index [UPDATING] bar v0.0.1 -> v0.0.2 "#]], str![[r#" [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.2 (registry `dummy-registry`) [CHECKING] bar v0.0.2 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [UPDATING] bar v0.0.2 -> v0.0.3 "#]], str![[r#" [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.3 (registry `dummy-registry`) [CHECKING] bar v0.0.3 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [UPDATING] bar v0.0.3 -> v0.0.4 [ADDING] spam v0.2.5 "#]], str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [UPDATING] bar v0.0.4 -> v0.0.5 [REMOVING] spam v0.2.5 "#]], ); } #[cargo_test] fn update_lockfile_git() { update_lockfile( str![[r#" [UPDATING] `dummy-registry` index [UPDATING] bar v0.0.1 -> v0.0.2 "#]], str![[r#" [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.2 (registry `dummy-registry`) [CHECKING] bar v0.0.2 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [UPDATING] bar v0.0.2 -> v0.0.3 "#]], str![[r#" [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.3 (registry `dummy-registry`) [CHECKING] bar v0.0.3 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [UPDATING] bar v0.0.3 -> v0.0.4 [ADDING] spam v0.2.5 "#]], str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [UPDATING] bar v0.0.4 -> v0.0.5 [REMOVING] spam v0.2.5 "#]], ); } fn update_lockfile( expected_update: impl IntoData, expected_check: impl IntoData, expected_other_update: impl IntoData, expected_other_check: impl IntoData, expected_new_update: impl IntoData, expected_new_check: impl IntoData, ) { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "*" "#, ) .file("src/main.rs", "fn main() {}") .build(); println!("0.0.1"); Package::new("bar", "0.0.1").publish(); p.cargo("check").run(); Package::new("bar", "0.0.2").publish(); Package::new("bar", "0.0.3").publish(); paths::home().join(".cargo/registry").rm_rf(); println!("0.0.2 update"); p.cargo("update bar --precise 0.0.2") .with_stderr_data(expected_update) .run(); println!("0.0.2 build"); p.cargo("check").with_stderr_data(expected_check).run(); println!("0.0.3 update"); p.cargo("update bar") .with_stderr_data(expected_other_update) .run(); println!("0.0.3 build"); p.cargo("check") .with_stderr_data(expected_other_check) .run(); println!("new dependencies update"); Package::new("bar", "0.0.4").dep("spam", "0.2.5").publish(); Package::new("spam", "0.2.5").publish(); p.cargo("update bar") .with_stderr_data(expected_new_update) .run(); println!("new dependencies update"); Package::new("bar", "0.0.5").publish(); p.cargo("update bar") .with_stderr_data(expected_new_check) .run(); } #[cargo_test] fn dev_dependency_not_used_http() { let _server = setup_http(); dev_dependency_not_used(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) [CHECKING] bar v0.0.1 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); } #[cargo_test] fn dev_dependency_not_used_git() { dev_dependency_not_used(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) [CHECKING] bar v0.0.1 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); } fn dev_dependency_not_used(expected: impl IntoData) { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "*" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("baz", "0.0.1").publish(); Package::new("bar", "0.0.1").dev_dep("baz", "*").publish(); p.cargo("check").with_stderr_data(expected).run(); } #[cargo_test] fn bad_license_file_http() { let registry = setup_http(); bad_license_file( ®istry, str![[r#" ... [ERROR] license-file `foo` does not appear to exist (relative to `[ROOT]/foo`). ... "#]], ); } #[cargo_test] fn bad_license_file_git() { let registry = registry::init(); bad_license_file( ®istry, str![[r#" ... [ERROR] license-file `foo` does not appear to exist (relative to `[ROOT]/foo`). ... "#]], ); } fn bad_license_file(registry: &TestRegistry, expected: impl IntoData) { Package::new("foo", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license-file = "foo" description = "bar" repository = "baz" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("publish -v") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(expected) .run(); } #[cargo_test] fn updating_a_dep_http() { let _server = setup_http(); updating_a_dep( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) [CHECKING] bar v0.0.1 [CHECKING] a v0.0.1 ([ROOT]/foo/a) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [UPDATING] bar v0.0.1 -> v0.1.0 [DOWNLOADING] crates ... [DOWNLOADED] bar v0.1.0 (registry `dummy-registry`) [CHECKING] bar v0.1.0 [CHECKING] a v0.0.1 ([ROOT]/foo/a) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], ); } #[cargo_test] fn updating_a_dep_git() { updating_a_dep( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) [CHECKING] bar v0.0.1 [CHECKING] a v0.0.1 ([ROOT]/foo/a) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [UPDATING] bar v0.0.1 -> v0.1.0 [DOWNLOADING] crates ... [DOWNLOADED] bar v0.1.0 (registry `dummy-registry`) [CHECKING] bar v0.1.0 [CHECKING] a v0.0.1 ([ROOT]/foo/a) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], ); } fn updating_a_dep(pre_update_expected: impl IntoData, post_update_expected: impl IntoData) { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.a] path = "a" "#, ) .file("src/main.rs", "fn main() {}") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "*" "#, ) .file("a/src/lib.rs", "") .build(); Package::new("bar", "0.0.1").publish(); p.cargo("check").with_stderr_data(pre_update_expected).run(); assert!(paths::home().join(".cargo/registry/CACHEDIR.TAG").is_file()); // Now delete the CACHEDIR.TAG file: this is the situation we'll be in after // upgrading from a version of Cargo that doesn't mark this directory, to one that // does. It should be recreated. fs::remove_file(paths::home().join(".cargo/registry/CACHEDIR.TAG")) .expect("remove CACHEDIR.TAG"); p.change_file( "a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" "#, ); Package::new("bar", "0.1.0").publish(); println!("second"); p.cargo("check") .with_stderr_data(post_update_expected) .run(); assert!( paths::home().join(".cargo/registry/CACHEDIR.TAG").is_file(), "CACHEDIR.TAG recreated in existing registry" ); } #[cargo_test] fn git_and_registry_dep_http() { let _server = setup_http(); git_and_registry_dep( str![[r#" [UPDATING] `dummy-registry` index [UPDATING] git repository `[ROOTURL]/b` [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] a v0.0.1 (registry `dummy-registry`) [CHECKING] a v0.0.1 [CHECKING] b v0.0.1 ([ROOTURL]/b#[..]) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], ); } #[cargo_test] fn git_and_registry_dep_git() { git_and_registry_dep( str![[r#" [UPDATING] `dummy-registry` index [UPDATING] git repository `[ROOTURL]/b` [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] a v0.0.1 (registry `dummy-registry`) [CHECKING] a v0.0.1 [CHECKING] b v0.0.1 ([ROOTURL]/b#[..]) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], ); } fn git_and_registry_dep(pre_move_expected: impl IntoData, post_move_expected: impl IntoData) { let b = git::repo(&paths::root().join("b")) .file( "Cargo.toml", r#" [package] name = "b" version = "0.0.1" edition = "2015" authors = [] [dependencies] a = "0.0.1" "#, ) .file("src/lib.rs", "") .build(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] a = "0.0.1" [dependencies.b] git = '{}' "#, b.url() ), ) .file("src/main.rs", "fn main() {}") .build(); Package::new("a", "0.0.1").publish(); p.root().move_into_the_past(); p.cargo("check").with_stderr_data(pre_move_expected).run(); p.root().move_into_the_past(); println!("second"); p.cargo("check").with_stderr_data(post_move_expected).run(); } #[cargo_test] fn update_publish_then_update_http() { let _server = setup_http(); update_publish_then_update(str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] a v0.1.1 (registry `dummy-registry`) [COMPILING] a v0.1.1 [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); } #[cargo_test] fn update_publish_then_update_git() { update_publish_then_update(str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] a v0.1.1 (registry `dummy-registry`) [COMPILING] a v0.1.1 [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); } fn update_publish_then_update(expected: impl IntoData) { // First generate a Cargo.lock and a clone of the registry index at the // "head" of the current registry. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies] a = "0.1.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("a", "0.1.0").publish(); p.cargo("build").run(); // Next, publish a new package and back up the copy of the registry we just // created. Package::new("a", "0.1.1").publish(); let registry = paths::home().join(".cargo/registry"); let backup = paths::root().join("registry-backup"); t!(fs::rename(®istry, &backup)); // Generate a Cargo.lock with the newer version, and then move the old copy // of the registry back into place. let p2 = project() .at("foo2") .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies] a = "0.1.1" "#, ) .file("src/main.rs", "fn main() {}") .build(); p2.cargo("build").run(); registry.rm_rf(); t!(fs::rename(&backup, ®istry)); t!(fs::rename( p2.root().join("Cargo.lock"), p.root().join("Cargo.lock") )); // Finally, build the first project again (with our newer Cargo.lock) which // should force an update of the old registry, download the new crate, and // then build everything again. p.cargo("build").with_stderr_data(expected).run(); } #[cargo_test] fn fetch_downloads_http() { let _server = setup_http(); fetch_downloads(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] a v0.1.0 (registry `dummy-registry`) "#]]); } #[cargo_test] fn fetch_downloads_git() { fetch_downloads(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] a v0.1.0 (registry `dummy-registry`) "#]]); } fn fetch_downloads(expected: impl IntoData) { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies] a = "0.1.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("a", "0.1.0").publish(); p.cargo("fetch").with_stderr_data(expected).run(); } #[cargo_test] fn update_transitive_dependency_http() { let _server = setup_http(); update_transitive_dependency( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [UPDATING] b v0.1.0 -> v0.1.1 "#]], str![[r#" [DOWNLOADING] crates ... [DOWNLOADED] b v0.1.1 (registry `dummy-registry`) [CHECKING] b v0.1.1 [CHECKING] a v0.1.0 [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], ); } #[cargo_test] fn update_transitive_dependency_git() { update_transitive_dependency( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [UPDATING] b v0.1.0 -> v0.1.1 "#]], str![[r#" [DOWNLOADING] crates ... [DOWNLOADED] b v0.1.1 (registry `dummy-registry`) [CHECKING] b v0.1.1 [CHECKING] a v0.1.0 [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], ); } fn update_transitive_dependency(expected_update: impl IntoData, expected_check: impl IntoData) { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies] a = "0.1.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("a", "0.1.0").dep("b", "*").publish(); Package::new("b", "0.1.0").publish(); p.cargo("fetch").run(); Package::new("b", "0.1.1").publish(); p.cargo("update b").with_stderr_data(expected_update).run(); p.cargo("check").with_stderr_data(expected_check).run(); } #[cargo_test] fn update_backtracking_ok_http() { let _server = setup_http(); update_backtracking_ok(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [UPDATING] hyper v0.6.5 -> v0.6.6 [UPDATING] openssl v0.1.0 -> v0.1.1 "#]]); } #[cargo_test] fn update_backtracking_ok_git() { update_backtracking_ok(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [UPDATING] hyper v0.6.5 -> v0.6.6 [UPDATING] openssl v0.1.0 -> v0.1.1 "#]]); } fn update_backtracking_ok(expected: impl IntoData) { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies] webdriver = "0.1" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("webdriver", "0.1.0") .dep("hyper", "0.6") .publish(); Package::new("hyper", "0.6.5") .dep("openssl", "0.1") .dep("cookie", "0.1") .publish(); Package::new("cookie", "0.1.0") .dep("openssl", "0.1") .publish(); Package::new("openssl", "0.1.0").publish(); p.cargo("generate-lockfile").run(); Package::new("openssl", "0.1.1").publish(); Package::new("hyper", "0.6.6") .dep("openssl", "0.1.1") .dep("cookie", "0.1.0") .publish(); p.cargo("update hyper").with_stderr_data(expected).run(); } #[cargo_test] fn update_multiple_packages_http() { let _server = setup_http(); update_multiple_packages( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [UPDATING] a v0.1.0 -> v0.1.1 [UPDATING] b v0.1.0 -> v0.1.1 [NOTE] pass `--verbose` to see 1 unchanged dependencies behind latest "#]], str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [UPDATING] c v0.1.0 -> v0.1.1 "#]], str![[r#" [DOWNLOADING] crates ... [DOWNLOADED] a v0.1.1 (registry `dummy-registry`) [DOWNLOADED] b v0.1.1 (registry `dummy-registry`) [DOWNLOADED] c v0.1.1 (registry `dummy-registry`) [CHECKING] a v0.1.1 [CHECKING] c v0.1.1 [CHECKING] b v0.1.1 [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], ); } #[cargo_test] fn update_multiple_packages_git() { update_multiple_packages( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [UPDATING] a v0.1.0 -> v0.1.1 [UPDATING] b v0.1.0 -> v0.1.1 [NOTE] pass `--verbose` to see 1 unchanged dependencies behind latest "#]], str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [UPDATING] c v0.1.0 -> v0.1.1 "#]], str![[r#" [DOWNLOADING] crates ... [DOWNLOADED] c v0.1.1 (registry `dummy-registry`) [DOWNLOADED] b v0.1.1 (registry `dummy-registry`) [DOWNLOADED] a v0.1.1 (registry `dummy-registry`) [CHECKING] b v0.1.1 [CHECKING] a v0.1.1 [CHECKING] c v0.1.1 [CHECKING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]], ); } fn update_multiple_packages( expected_update: impl IntoData, expected_other_update: impl IntoData, expected_check: impl IntoData, ) { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies] a = "*" b = "*" c = "*" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("a", "0.1.0").publish(); Package::new("b", "0.1.0").publish(); Package::new("c", "0.1.0").publish(); p.cargo("fetch").run(); Package::new("a", "0.1.1").publish(); Package::new("b", "0.1.1").publish(); Package::new("c", "0.1.1").publish(); p.cargo("update a b") .with_stderr_data(expected_update) .run(); p.cargo("update b c") .with_stderr_data(expected_other_update) .run(); p.cargo("check") .with_stderr_data(IntoData::unordered(expected_check)) .run(); } #[cargo_test] fn bundled_crate_in_registry_http() { let _server = setup_http(); bundled_crate_in_registry(); } #[cargo_test] fn bundled_crate_in_registry_git() { bundled_crate_in_registry(); } fn bundled_crate_in_registry() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies] bar = "0.1" baz = "0.1" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("bar", "0.1.0").publish(); Package::new("baz", "0.1.0") .dep("bar", "0.1.0") .file( "Cargo.toml", r#" [package] name = "baz" version = "0.1.0" edition = "2015" authors = [] [dependencies] bar = { path = "bar", version = "0.1.0" } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "") .publish(); p.cargo("run").run(); } #[cargo_test] fn update_same_prefix_oh_my_how_was_this_a_bug_http() { let _server = setup_http(); update_same_prefix_oh_my_how_was_this_a_bug(); } #[cargo_test] fn update_same_prefix_oh_my_how_was_this_a_bug_git() { update_same_prefix_oh_my_how_was_this_a_bug(); } fn update_same_prefix_oh_my_how_was_this_a_bug() { let p = project() .file( "Cargo.toml", r#" [package] name = "ugh" version = "0.5.0" edition = "2015" authors = [] [dependencies] foo = "0.1" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("foobar", "0.2.0").publish(); Package::new("foo", "0.1.0") .dep("foobar", "0.2.0") .publish(); p.cargo("generate-lockfile").run(); p.cargo("update foobar --precise=0.2.0").run(); } #[cargo_test] fn use_semver_http() { let _server = setup_http(); use_semver(); } #[cargo_test] fn use_semver_git() { use_semver(); } fn use_semver() { let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = [] [dependencies] foo = "1.2.3-alpha.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("foo", "1.2.3-alpha.0").publish(); p.cargo("check").run(); } #[cargo_test] fn use_semver_package_incorrectly_http() { let _server = setup_http(); use_semver_package_incorrectly(str![[r#" [ERROR] failed to select a version for the requirement `a = "^0.1"` candidate versions found which didn't match: 0.1.1-alpha.0 location searched: [ROOT]/foo/a required by package `b v0.1.0 ([ROOT]/foo/b)` if you are looking for the prerelease package it needs to be specified explicitly a = { version = "0.1.1-alpha.0" } "#]]); } #[cargo_test] fn use_semver_package_incorrectly_git() { use_semver_package_incorrectly(str![[r#" [ERROR] failed to select a version for the requirement `a = "^0.1"` candidate versions found which didn't match: 0.1.1-alpha.0 location searched: [ROOT]/foo/a required by package `b v0.1.0 ([ROOT]/foo/b)` if you are looking for the prerelease package it needs to be specified explicitly a = { version = "0.1.1-alpha.0" } "#]]); } fn use_semver_package_incorrectly(expected: impl IntoData) { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.1-alpha.0" edition = "2015" authors = [] "#, ) .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" edition = "2015" authors = [] [dependencies] a = { version = "^0.1", path = "../a" } "#, ) .file("a/src/main.rs", "fn main() {}") .file("b/src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_status(101) .with_stderr_data(expected) .run(); } #[cargo_test] fn only_download_relevant_http() { let _server = setup_http(); only_download_relevant(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 3 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] baz v0.1.0 (registry `dummy-registry`) [CHECKING] baz v0.1.0 [CHECKING] bar v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); } #[cargo_test] fn only_download_relevant_git() { only_download_relevant(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 3 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] baz v0.1.0 (registry `dummy-registry`) [CHECKING] baz v0.1.0 [CHECKING] bar v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); } fn only_download_relevant(expected: impl IntoData) { let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = [] [target.foo.dependencies] foo = "*" [dev-dependencies] bar = "*" [dependencies] baz = "*" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("foo", "0.1.0").publish(); Package::new("bar", "0.1.0").publish(); Package::new("baz", "0.1.0").publish(); p.cargo("check").with_stderr_data(expected).run(); } #[cargo_test] fn resolve_and_backtracking_http() { let _server = setup_http(); resolve_and_backtracking(); } #[cargo_test] fn resolve_and_backtracking_git() { resolve_and_backtracking(); } fn resolve_and_backtracking() { let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = [] [dependencies] foo = "*" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("foo", "0.1.1") .feature_dep("bar", "0.1", &["a", "b"]) .publish(); Package::new("foo", "0.1.0").publish(); p.cargo("check").run(); } #[cargo_test] fn upstream_warnings_on_extra_verbose_http() { let _server = setup_http(); upstream_warnings_on_extra_verbose(str![[r#" ... [WARNING] function `unused` is never used ... "#]]); } #[cargo_test] fn upstream_warnings_on_extra_verbose_git() { upstream_warnings_on_extra_verbose(str![[r#" ... [WARNING] function `unused` is never used ... "#]]); } fn upstream_warnings_on_extra_verbose(expected: impl IntoData) { let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = [] [dependencies] foo = "*" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("foo", "0.1.0") .file("src/lib.rs", "fn unused() {}") .publish(); p.cargo("check -vv").with_stderr_data(expected).run(); } #[cargo_test] fn disallow_network_http() { let _server = setup_http(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = [] [dependencies] foo = "*" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check --frozen") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no matching package named `foo` found location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `bar v0.5.0 ([ROOT]/foo)` As a reminder, you're using offline mode (--frozen) which can sometimes cause surprising resolution failures, if this error is too confusing you may wish to retry without `--frozen`. "#]]) .run(); } #[cargo_test] fn disallow_network_git() { let _server = RegistryBuilder::new().build(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = [] [dependencies] foo = "*" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check --frozen") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no matching package named `foo` found location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `bar v0.5.0 ([ROOT]/foo)` As a reminder, you're using offline mode (--frozen) which can sometimes cause surprising resolution failures, if this error is too confusing you may wish to retry without `--frozen`. "#]]) .run(); } #[cargo_test] fn add_dep_dont_update_registry_http() { let _server = setup_http(); add_dep_dont_update_registry(str![[r#" [CHECKING] bar v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); } #[cargo_test] fn add_dep_dont_update_registry_git() { add_dep_dont_update_registry(str![[r#" [CHECKING] bar v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); } fn add_dep_dont_update_registry(expected: impl IntoData) { let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = [] [dependencies] baz = { path = "baz" } "#, ) .file("src/main.rs", "fn main() {}") .file( "baz/Cargo.toml", r#" [package] name = "baz" version = "0.5.0" edition = "2015" authors = [] [dependencies] remote = "0.3" "#, ) .file("baz/src/lib.rs", "") .build(); Package::new("remote", "0.3.4").publish(); p.cargo("check").run(); p.change_file( "Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = [] [dependencies] baz = { path = "baz" } remote = "0.3" "#, ); p.cargo("check").with_stderr_data(expected).run(); } #[cargo_test] fn bump_version_dont_update_registry_http() { let _server = setup_http(); bump_version_dont_update_registry(str![[r#" [CHECKING] bar v0.6.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); } #[cargo_test] fn bump_version_dont_update_registry_git() { bump_version_dont_update_registry(str![[r#" [CHECKING] bar v0.6.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]); } fn bump_version_dont_update_registry(expected: impl IntoData) { let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = [] [dependencies] baz = { path = "baz" } "#, ) .file("src/main.rs", "fn main() {}") .file( "baz/Cargo.toml", r#" [package] name = "baz" version = "0.5.0" edition = "2015" authors = [] [dependencies] remote = "0.3" "#, ) .file("baz/src/lib.rs", "") .build(); Package::new("remote", "0.3.4").publish(); p.cargo("check").run(); p.change_file( "Cargo.toml", r#" [package] name = "bar" version = "0.6.0" edition = "2015" authors = [] [dependencies] baz = { path = "baz" } "#, ); p.cargo("check").with_stderr_data(expected).run(); } #[cargo_test] fn toml_lies_but_index_is_truth_http() { let _server = setup_http(); toml_lies_but_index_is_truth(); } #[cargo_test] fn toml_lies_but_index_is_truth_git() { toml_lies_but_index_is_truth(); } fn toml_lies_but_index_is_truth() { Package::new("foo", "0.2.0").publish(); Package::new("bar", "0.3.0") .dep("foo", "0.2.0") .file( "Cargo.toml", r#" [package] name = "bar" version = "0.3.0" edition = "2015" authors = [] [dependencies] foo = "0.1.0" "#, ) .file("src/lib.rs", "extern crate foo;") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = [] [dependencies] bar = "0.3" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check -v").run(); } #[cargo_test] fn vv_prints_warnings_http() { let _server = setup_http(); vv_prints_warnings(); } #[cargo_test] fn vv_prints_warnings_git() { vv_prints_warnings(); } fn vv_prints_warnings() { Package::new("foo", "0.2.0") .file( "src/lib.rs", "#![deny(warnings)] fn foo() {} // unused function", ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "fo" version = "0.5.0" edition = "2015" authors = [] [dependencies] foo = "0.2" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check -vv").run(); } #[cargo_test] fn bad_and_or_malicious_packages_rejected_http() { let _server = setup_http(); bad_and_or_malicious_packages_rejected(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] foo v0.2.0 (registry `dummy-registry`) [ERROR] failed to download replaced source registry `crates-io` Caused by: failed to unpack package `foo v0.2.0 (registry `dummy-registry`)` Caused by: invalid tarball downloaded, contains a file at "foo-0.1.0/src/lib.rs" which isn't under "foo-0.2.0" "#]]); } #[cargo_test] fn bad_and_or_malicious_packages_rejected_git() { bad_and_or_malicious_packages_rejected(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] foo v0.2.0 (registry `dummy-registry`) [ERROR] failed to download replaced source registry `crates-io` Caused by: failed to unpack package `foo v0.2.0 (registry `dummy-registry`)` Caused by: invalid tarball downloaded, contains a file at "foo-0.1.0/src/lib.rs" which isn't under "foo-0.2.0" "#]]); } fn bad_and_or_malicious_packages_rejected(expected: impl IntoData) { Package::new("foo", "0.2.0") .extra_file("foo-0.1.0/src/lib.rs", "") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "fo" version = "0.5.0" edition = "2015" authors = [] [dependencies] foo = "0.2" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check -vv") .with_status(101) .with_stderr_data(expected) .run(); } #[cargo_test] fn git_init_templatedir_missing_http() { let _server = setup_http(); git_init_templatedir_missing(); } #[cargo_test] fn git_init_templatedir_missing_git() { git_init_templatedir_missing(); } fn git_init_templatedir_missing() { Package::new("foo", "0.2.0").dep("bar", "*").publish(); Package::new("bar", "0.2.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "fo" version = "0.5.0" edition = "2015" authors = [] [dependencies] foo = "0.2" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check").run(); remove_dir_all(paths::home().join(".cargo/registry")).unwrap(); fs::write( paths::home().join(".gitconfig"), r#" [init] templatedir = nowhere "#, ) .unwrap(); p.cargo("check").run(); p.cargo("check").run(); } #[cargo_test] fn rename_deps_and_features_http() { let _server = setup_http(); rename_deps_and_features(); } #[cargo_test] fn rename_deps_and_features_git() { rename_deps_and_features(); } fn rename_deps_and_features() { Package::new("foo", "0.1.0") .file("src/lib.rs", "pub fn f1() {}") .publish(); Package::new("foo", "0.2.0") .file("src/lib.rs", "pub fn f2() {}") .publish(); Package::new("bar", "0.2.0") .add_dep( Dependency::new("foo01", "0.1.0") .package("foo") .optional(true), ) .add_dep(Dependency::new("foo02", "0.2.0").package("foo")) .feature("another", &["foo01"]) .file( "src/lib.rs", r#" extern crate foo02; #[cfg(feature = "foo01")] extern crate foo01; pub fn foo() { foo02::f2(); #[cfg(feature = "foo01")] foo01::f1(); } "#, ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.5.0" edition = "2015" authors = [] [dependencies] bar = "0.2" "#, ) .file( "src/main.rs", " extern crate bar; fn main() { bar::foo(); } ", ) .build(); p.cargo("check").run(); p.cargo("check --features bar/foo01").run(); p.cargo("check --features bar/another").run(); } #[cargo_test] fn ignore_invalid_json_lines_http() { let _server = setup_http(); ignore_invalid_json_lines(); } #[cargo_test] fn ignore_invalid_json_lines_git() { ignore_invalid_json_lines(); } fn ignore_invalid_json_lines() { Package::new("foo", "0.1.0").publish(); Package::new("foo", "0.1.1") .invalid_index_line(true) .publish(); Package::new("foo", "0.2.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.5.0" edition = "2015" authors = [] [dependencies] foo = '0.1.0' foo02 = { version = '0.2.0', package = 'foo' } "#, ) .file("src/lib.rs", "") .build(); p.cargo("check").run(); } #[cargo_test] fn invalid_json_lines_error() { Package::new("foo", "0.1.0") .rust_version("1.0") .schema_version(2) .publish(); Package::new("foo", "0.1.1") // Bad name field, too corrupt to use .invalid_index_line(true) .publish(); Package::new("foo", "0.1.2") // Bad version field, too corrupt to use .index_line( r#"{"cksum":"7ca5fc2301ad96ade45356faf53225aea36437d99930bbfa951155c01faecf79","deps":[],"features":{},"links":null,"name":"foo","vers":"bad","yanked":false,"rust_version":"1.2345","v":1000000000}"#, ) .publish(); Package::new("foo", "0.1.3") // Bad field, report rust version .index_line( r#"{"cksum":"7ca5fc2301ad96ade45356faf53225aea36437d99930bbfa951155c01faecf79","deps":[],"features":"bad","links":null,"name":"foo","vers":"0.1.3","yanked":false,"rust_version":"1.2345","v":1000000000}"#, ) .publish(); Package::new("foo", "0.1.4") // Bad field, report schema .index_line( r#"{"cksum":"7ca5fc2301ad96ade45356faf53225aea36437d99930bbfa951155c01faecf79","deps":[],"features":"bad","links":null,"name":"foo","vers":"0.1.4","yanked":false,"v":1000000000}"#, ) .publish(); Package::new("foo", "0.1.5") // Bad field, report error .index_line( r#"{"cksum":"7ca5fc2301ad96ade45356faf53225aea36437d99930bbfa951155c01faecf79","deps":[],"features":"bad","links":null,"name":"foo","vers":"0.1.5","yanked":false}"#, ) .publish(); Package::new("foo", "0.1.6") // Bad field with bad rust version, report schema .index_line( r#"{"cksum":"7ca5fc2301ad96ade45356faf53225aea36437d99930bbfa951155c01faecf79","deps":[],"features":"bad","links":null,"name":"foo","vers":"0.1.6","yanked":false,"rust_version":"bad","v":1000000000}"#, ) .publish(); Package::new("foo", "0.1.7") // Bad field with bad rust version and schema, report error .index_line( r#"{"cksum":"7ca5fc2301ad96ade45356faf53225aea36437d99930bbfa951155c01faecf79","deps":[],"features":"bad","links":null,"name":"foo","vers":"0.1.7","yanked":false,"rust_version":"bad","v":"bad"}"#, ) .publish(); Package::new("foo", "0.2.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.5.0" edition = "2015" authors = [] [dependencies] foo = "0.1.1" "#, ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for the requirement `foo = "^0.1.1"` version 0.1.3 requires cargo 1.2345 version 0.1.4 requires a Cargo version that supports index version 1000000000 version 0.1.5's index entry is invalid version 0.1.6 requires a Cargo version that supports index version 1000000000 version 0.1.7's index entry is invalid location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `a v0.5.0 ([ROOT]/foo)` "#]]) .run(); p.cargo("generate-lockfile") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for the requirement `foo = "^0.1.1"` version 0.1.3 requires cargo 1.2345 version 0.1.4 requires a Cargo version that supports index version 1000000000 version 0.1.5's index entry is invalid version 0.1.6 requires a Cargo version that supports index version 1000000000 version 0.1.7's index entry is invalid location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `a v0.5.0 ([ROOT]/foo)` "#]]) .run(); } #[cargo_test] fn readonly_registry_still_works_http() { let _server = setup_http(); readonly_registry_still_works(); } #[cargo_test] fn readonly_registry_still_works_git() { readonly_registry_still_works(); } fn readonly_registry_still_works() { Package::new("foo", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.5.0" edition = "2015" authors = [] [dependencies] foo = '0.1.0' "#, ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); p.cargo("fetch --locked").run(); chmod_readonly(&paths::home(), true); p.cargo("check").run(); // make sure we un-readonly the files afterwards so "cargo clean" can remove them (#6934) chmod_readonly(&paths::home(), false); fn chmod_readonly(path: &Path, readonly: bool) { for entry in t!(path.read_dir()) { let entry = t!(entry); let path = entry.path(); if t!(entry.file_type()).is_dir() { chmod_readonly(&path, readonly); } else { set_readonly(&path, readonly); } } set_readonly(path, readonly); } fn set_readonly(path: &Path, readonly: bool) { let mut perms = t!(path.metadata()).permissions(); perms.set_readonly(readonly); t!(fs::set_permissions(path, perms)); } } #[cargo_test(ignore_windows = "On Windows setting file attributes is a bit complicated")] fn unaccessible_registry_cache_still_works() { Package::new("foo", "0.1.0").publish(); Package::new("fo2", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.5.0" edition = "2015" authors = [] [dependencies] foo = '0.1.0' fo2 = '0.1.0' "#, ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); p.cargo("fetch --locked").run(); let cache_path = inner_dir(&paths::cargo_home().join("registry/index")).join(".cache"); let f_cache_path = cache_path.join("3/f"); // Remove the permissions from the cache path that contains the "foo" crate set_permissions(&f_cache_path, 0o000); // Now run a build and make sure we properly build and warn the user p.cargo("build") .with_stderr_data(str![[r#" [WARNING] failed to write cache, path: [ROOT]/home/.cargo/registry/index/-[HASH]/.cache/3/f/fo[..], [ERROR] Permission denied (os error 13) [COMPILING] fo[..] v0.1.0 [COMPILING] fo[..] v0.1.0 [COMPILING] a v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // make sure we add the permissions to the files afterwards so "cargo clean" can remove them (#6934) set_permissions(&f_cache_path, 0o777); #[cfg_attr(windows, allow(unused_variables))] fn set_permissions(path: &Path, permissions: u32) { #[cfg(not(windows))] { use std::os::unix::fs::PermissionsExt; let mut perms = t!(path.metadata()).permissions(); perms.set_mode(permissions); t!(fs::set_permissions(path, perms)); } #[cfg(windows)] panic!("This test is not supported on windows. See the reason in the #[cargo_test] macro"); } fn inner_dir(path: &Path) -> PathBuf { for entry in t!(path.read_dir()) { let path = t!(entry).path(); if path.is_dir() { return path; } } panic!("could not find inner directory of {path:?}"); } } #[cargo_test] fn registry_index_rejected_http() { let _server = setup_http(); registry_index_rejected( str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: the `registry.index` config value is no longer supported Use `[source]` replacement to alter the default index for crates.io. "#]], str![[r#" [ERROR] the `registry.index` config value is no longer supported Use `[source]` replacement to alter the default index for crates.io. "#]], ); } #[cargo_test] fn registry_index_rejected_git() { registry_index_rejected( str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: the `registry.index` config value is no longer supported Use `[source]` replacement to alter the default index for crates.io. "#]], str![[r#" [ERROR] the `registry.index` config value is no longer supported Use `[source]` replacement to alter the default index for crates.io. "#]], ); } fn registry_index_rejected(expected_check: impl IntoData, expected_login: impl IntoData) { Package::new("dep", "0.1.0").publish(); let p = project() .file( ".cargo/config.toml", r#" [registry] index = "https://example.com/" "#, ) .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] dep = "0.1" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(expected_check) .run(); p.cargo("login") .with_status(101) .with_stderr_data(expected_login) .run(); } #[cargo_test] fn package_lock_inside_package_is_overwritten() { let registry = registry::init(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = ">= 0.0.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("bar", "0.0.1") .file("src/lib.rs", "") .file(".cargo-ok", "") .publish(); p.cargo("check").run(); let id = SourceId::for_registry(registry.index_url()).unwrap(); let hash = cargo::util::hex::short_hash(&id); let ok = paths::cargo_home() .join("registry") .join("src") .join(format!("-{}", hash)) .join("bar-0.0.1") .join(".cargo-ok"); assert_eq!(ok.metadata().unwrap().len(), 7); } #[cargo_test] fn package_lock_as_a_symlink_inside_package_is_overwritten() { let registry = registry::init(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = ">= 0.0.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("bar", "0.0.1") .file("src/lib.rs", "pub fn f() {}") .symlink(".cargo-ok", "src/lib.rs") .publish(); p.cargo("check").run(); let id = SourceId::for_registry(registry.index_url()).unwrap(); let hash = cargo::util::hex::short_hash(&id); let pkg_root = paths::cargo_home() .join("registry") .join("src") .join(format!("-{}", hash)) .join("bar-0.0.1"); let ok = pkg_root.join(".cargo-ok"); let librs = pkg_root.join("src/lib.rs"); // Is correctly overwritten and doesn't affect the file linked to assert_eq!(ok.metadata().unwrap().len(), 7); assert_eq!(fs::read_to_string(librs).unwrap(), "pub fn f() {}"); } #[cargo_test] fn ignores_unknown_index_version_http() { let _server = setup_http(); ignores_unknown_index_version(str![[r#" foo v0.1.0 ([ROOT]/foo) └── bar v1.0.0 "#]]); } #[cargo_test] fn ignores_unknown_index_version_git() { ignores_unknown_index_version(str![[r#" foo v0.1.0 ([ROOT]/foo) └── bar v1.0.0 "#]]); } fn ignores_unknown_index_version(expected: impl IntoData) { // If the version field is not understood, it is ignored. Package::new("bar", "1.0.0").publish(); Package::new("bar", "1.0.1") .schema_version(u32::MAX) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree").with_stdout_data(expected).run(); } #[cargo_test] fn unknown_index_version_error() { Package::new("bar", "0.0.1").publish(); // If the version field is not understood, it is ignored. Package::new("bar", "1.0.1") .schema_version(u32::MAX) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for the requirement `bar = "^1.0"` version 1.0.1 requires a Cargo version that supports index version 4294967295 location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.1.0 ([ROOT]/foo)` "#]]) .run(); p.cargo("generate-lockfile") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for the requirement `bar = "^1.0"` version 1.0.1 requires a Cargo version that supports index version 4294967295 location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.1.0 ([ROOT]/foo)` "#]]) .run(); } #[cargo_test] fn unknown_index_version_with_msrv_error() { Package::new("bar", "0.0.1").publish(); // If the version field is not understood, it is ignored. Package::new("bar", "1.0.1") .schema_version(u32::MAX) .rust_version("1.2345") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for the requirement `bar = "^1.0"` version 1.0.1 requires cargo 1.2345 location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.1.0 ([ROOT]/foo)` "#]]) .run(); } #[cargo_test] fn protocol() { cargo_process("install bar") .with_status(101) .env("CARGO_REGISTRIES_CRATES_IO_PROTOCOL", "invalid") .with_stderr_data(str![[r#" [ERROR] unsupported registry protocol `invalid` (defined in environment variable `CARGO_REGISTRIES_CRATES_IO_PROTOCOL`) "#]]) .run(); } #[cargo_test] fn http_requires_trailing_slash() { cargo_process("install bar --index sparse+https://invalid.crates.io/test") .with_status(101) .with_stderr_data(str![[r#" [ERROR] sparse registry url must end in a slash `/`: sparse+https://invalid.crates.io/test "#]]) .run(); } // Limit the test to debug builds so that `__CARGO_TEST_MAX_UNPACK_SIZE` will take affect. #[cfg(debug_assertions)] #[cargo_test] fn reach_max_unpack_size() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = ">= 0.0.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); // Size of bar.crate is around 180 bytes. Package::new("bar", "0.0.1").publish(); p.cargo("check") .env("__CARGO_TEST_MAX_UNPACK_SIZE", "8") // hit 8 bytes limit and boom! .env("__CARGO_TEST_MAX_UNPACK_RATIO", "0") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) [ERROR] failed to download replaced source registry `crates-io` Caused by: failed to unpack package `bar v0.0.1 (registry `dummy-registry`)` Caused by: failed to iterate over archive Caused by: maximum limit reached when reading "#]]) .run(); // Restore to the default ratio and it should compile. p.cargo("check") .env("__CARGO_TEST_MAX_UNPACK_SIZE", "8") .with_stderr_data(str![[r#" [CHECKING] bar v0.0.1 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn sparse_blocking_count() { let fail_count = Mutex::new(0); let _registry = RegistryBuilder::new() .http_index() .add_responder("/index/3/b/bar", move |req, server| { let mut fail_count = fail_count.lock().unwrap(); if *fail_count < 1 { *fail_count += 1; server.internal_server_error(req) } else { server.index(req) } }) .build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = ">= 0.0.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("bar", "0.0.1").publish(); // Ensure we have the expected number of `block_until_ready` calls. // The 1st (0 transfers pending), is the deliberate extra call in `ensure_loaded` for a source. // The 2nd (1 transfers pending), is the registry `config.json`. // the 3rd (1 transfers pending), is the package metadata for `bar`. p.cargo("check") .env("CARGO_LOG", "network::HttpRegistry::block_until_ready=trace") .with_stderr_data(str![[r#" [..] TRACE network::HttpRegistry::block_until_ready: 0 transfers pending [UPDATING] `dummy-registry` index [..] TRACE network::HttpRegistry::block_until_ready: 1 transfers pending [..] TRACE network::HttpRegistry::block_until_ready: 1 transfers pending [WARNING] spurious network error (3 tries remaining): failed to get successful HTTP response from `[..]/index/3/b/bar` ([..]), got 500 body: internal server error [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) [CHECKING] bar v0.0.1 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn sparse_retry_single() { let fail_count = Mutex::new(0); let _registry = RegistryBuilder::new() .http_index() .add_responder("/index/3/b/bar", move |req, server| { let mut fail_count = fail_count.lock().unwrap(); if *fail_count < 2 { *fail_count += 1; server.internal_server_error(req) } else { server.index(req) } }) .build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = ">= 0.0.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("bar", "0.0.1").publish(); p.cargo("check").with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [WARNING] spurious network error (3 tries remaining): failed to get successful HTTP response from `http://127.0.0.1:[..]/index/3/b/bar` (127.0.0.1), got 500 body: internal server error [WARNING] spurious network error (2 tries remaining): failed to get successful HTTP response from `http://127.0.0.1:[..]/index/3/b/bar` (127.0.0.1), got 500 body: internal server error [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) [CHECKING] bar v0.0.1 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn sparse_retry_multiple() { // Tests retry behavior of downloading lots of packages with various // failure rates accessing the sparse index. // The index is the number of retries, the value is the number of packages // that retry that number of times. Thus 50 packages succeed on first try, // 25 on second, etc. const RETRIES: &[u32] = &[50, 25, 12, 6]; let pkgs: Vec<_> = RETRIES .iter() .enumerate() .flat_map(|(retries, num)| { (0..*num) .into_iter() .map(move |n| (retries as u32, format!("{}-{n}-{retries}", rand_prefix()))) }) .collect(); let mut builder = RegistryBuilder::new().http_index(); let fail_counts: Arc>> = Arc::new(Mutex::new(vec![0; pkgs.len()])); let mut cargo_toml = r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] "# .to_string(); // The expected stderr output. let mut expected = "\ [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... " .to_string(); for (n, (retries, name)) in pkgs.iter().enumerate() { let count_clone = fail_counts.clone(); let retries = *retries; let ab = &name[..2]; let cd = &name[2..4]; builder = builder.add_responder(format!("/index/{ab}/{cd}/{name}"), move |req, server| { let mut fail_counts = count_clone.lock().unwrap(); if fail_counts[n] < retries { fail_counts[n] += 1; server.internal_server_error(req) } else { server.index(req) } }); write!(&mut cargo_toml, "{name} = \"1.0.0\"\n").unwrap(); for retry in 0..retries { let remain = 3 - retry; write!( &mut expected, "[WARNING] spurious network error ({remain} {} remaining): \ failed to get successful HTTP response from \ `http://127.0.0.1:[..]/{ab}/{cd}/{name}` (127.0.0.1), got 500\n\ body:\n\ internal server error\n", if remain != 1 { "tries" } else { "try" } ) .unwrap(); } write!( &mut expected, "\ [DOWNLOADED] {name} v1.0.0 (registry `dummy-registry`) " ) .unwrap(); } write!( &mut expected, "\ [LOCKING] 93 packages to latest compatible versions " ) .unwrap(); let _server = builder.build(); for (_, name) in &pkgs { Package::new(name, "1.0.0").publish(); } let p = project() .file("Cargo.toml", &cargo_toml) .file("src/lib.rs", "") .build(); p.cargo("fetch") .with_stderr_data(IntoData::unordered(expected)) .run(); } #[cargo_test] fn dl_retry_single() { // Tests retry behavior of downloading a package. // This tests a single package which exercises the code path that causes // it to block. let fail_count = Mutex::new(0); let _server = RegistryBuilder::new() .http_index() .add_responder("/dl/bar/1.0.0/download", move |req, server| { let mut fail_count = fail_count.lock().unwrap(); if *fail_count < 2 { *fail_count += 1; server.internal_server_error(req) } else { server.dl(req) } }) .build(); Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("fetch").with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [WARNING] spurious network error (3 tries remaining): failed to get successful HTTP response from `http://127.0.0.1:[..]/dl/bar/1.0.0/download` (127.0.0.1), got 500 body: internal server error [WARNING] spurious network error (2 tries remaining): failed to get successful HTTP response from `http://127.0.0.1:[..]/dl/bar/1.0.0/download` (127.0.0.1), got 500 body: internal server error [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) "#]]).run(); } /// Creates a random prefix to randomly spread out the package names /// to somewhat evenly distribute the different failures at different /// points. fn rand_prefix() -> String { use rand::Rng; const CHARS: &[u8] = b"abcdefghijklmnopqrstuvwxyz"; let mut rng = rand::rng(); (0..5) .map(|_| CHARS[rng.random_range(0..CHARS.len())] as char) .collect() } #[cargo_test] fn dl_retry_multiple() { // Tests retry behavior of downloading lots of packages with various // failure rates. // The index is the number of retries, the value is the number of packages // that retry that number of times. Thus 50 packages succeed on first try, // 25 on second, etc. const RETRIES: &[u32] = &[50, 25, 12, 6]; let pkgs: Vec<_> = RETRIES .iter() .enumerate() .flat_map(|(retries, num)| { (0..*num) .into_iter() .map(move |n| (retries as u32, format!("{}-{n}-{retries}", rand_prefix()))) }) .collect(); let mut builder = RegistryBuilder::new().http_index(); let fail_counts: Arc>> = Arc::new(Mutex::new(vec![0; pkgs.len()])); let mut cargo_toml = r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] "# .to_string(); // The expected stderr output. let mut expected = "\ [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... " .to_string(); for (n, (retries, name)) in pkgs.iter().enumerate() { let count_clone = fail_counts.clone(); let retries = *retries; builder = builder.add_responder(format!("/dl/{name}/1.0.0/download"), move |req, server| { let mut fail_counts = count_clone.lock().unwrap(); if fail_counts[n] < retries { fail_counts[n] += 1; server.internal_server_error(req) } else { server.dl(req) } }); write!(&mut cargo_toml, "{name} = \"1.0.0\"\n").unwrap(); for retry in 0..retries { let remain = 3 - retry; write!( &mut expected, "[WARNING] spurious network error ({remain} {} remaining): \ failed to get successful HTTP response from \ `http://127.0.0.1:[..]/dl/{name}/1.0.0/download` (127.0.0.1), got 500\n\ body:\n\ internal server error\n", if remain != 1 { "tries" } else { "try" } ) .unwrap(); } write!( &mut expected, "[DOWNLOADED] {name} v1.0.0 (registry `dummy-registry`)\n" ) .unwrap(); } write!( &mut expected, "[LOCKING] 93 packages to latest compatible versions\n" ) .unwrap(); let _server = builder.build(); for (_, name) in &pkgs { Package::new(name, "1.0.0").publish(); } let p = project() .file("Cargo.toml", &cargo_toml) .file("src/lib.rs", "") .build(); p.cargo("fetch") .with_stderr_data(IntoData::unordered(expected)) .run(); } #[cargo_test] fn retry_too_many_requests() { let fail_count = Mutex::new(0); let _registry = RegistryBuilder::new() .http_index() .add_responder("/index/3/b/bar", move |req, server| { let mut fail_count = fail_count.lock().unwrap(); if *fail_count < 1 { *fail_count += 1; server.too_many_requests(req, std::time::Duration::from_secs(1)) } else { server.index(req) } }) .build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = ">= 0.0.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("bar", "0.0.1").publish(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [WARNING] spurious network error (3 tries remaining): failed to get successful HTTP response from `[..]/index/3/b/bar` ([..]), got 429 body: too many requests, try again in 1 seconds [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) [CHECKING] bar v0.0.1 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn deleted_entry() { // Checks the behavior when a package is removed from the index. // This is done occasionally on crates.io to handle things like // copyright takedowns. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "0.1" "#, ) .file("src/lib.rs", "") .build(); // First, test removing a single version, but leaving an older version. Package::new("bar", "0.1.0").publish(); let bar_path = Path::new("3/b/bar"); let bar_reg_path = registry_path().join(&bar_path); let old_index = fs::read_to_string(&bar_reg_path).unwrap(); Package::new("bar", "0.1.1").publish(); p.cargo("tree") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.1.1 (registry `dummy-registry`) "#]]) .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── bar v0.1.1 "#]]) .run(); // Remove 0.1.1 fs::remove_file(paths::root().join("dl/bar/0.1.1/download")).unwrap(); let repo = git2::Repository::open(registry_path()).unwrap(); let mut index = repo.index().unwrap(); fs::write(&bar_reg_path, &old_index).unwrap(); index.add_path(&bar_path).unwrap(); index.write().unwrap(); git::commit(&repo); // With `Cargo.lock` unchanged, it shouldn't have an impact. p.cargo("tree") .with_stderr_data("") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── bar v0.1.1 "#]]) .run(); // Regenerating Cargo.lock should switch to old version. fs::remove_file(p.root().join("Cargo.lock")).unwrap(); p.cargo("tree") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.1.0 (registry `dummy-registry`) "#]]) .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── bar v0.1.0 "#]]) .run(); // Remove the package entirely. fs::remove_file(paths::root().join("dl/bar/0.1.0/download")).unwrap(); let mut index = repo.index().unwrap(); index.remove(&bar_path, 0).unwrap(); index.write().unwrap(); git::commit(&repo); fs::remove_file(&bar_reg_path).unwrap(); // With `Cargo.lock` unchanged, it shouldn't have an impact. p.cargo("tree") .with_stderr_data("") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── bar v0.1.0 "#]]) .run(); // Regenerating Cargo.lock should fail. fs::remove_file(p.root().join("Cargo.lock")).unwrap(); p.cargo("tree") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] no matching package named `bar` found location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.1.0 ([ROOT]/foo)` "#]]) .with_status(101) .run(); } #[cargo_test] fn corrupted_ok_overwritten() { // Checks what happens if .cargo-ok gets truncated, such as if the file is // created, but the flush/close is interrupted. Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1" "#, ) .file("src/lib.rs", "") .build(); p.cargo("fetch") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) "#]]) .run(); let ok = glob::glob( paths::home() .join(".cargo/registry/src/*/bar-1.0.0/.cargo-ok") .to_str() .unwrap(), ) .unwrap() .next() .unwrap() .unwrap(); // Simulate cargo being interrupted, or filesystem corruption. fs::write(&ok, "").unwrap(); assert_eq!(fs::read_to_string(&ok).unwrap(), ""); p.cargo("fetch").with_stderr_data("").run(); assert_eq!(fs::read_to_string(&ok).unwrap(), r#"{"v":1}"#); } #[cargo_test] fn not_found_permutations() { // Test for querying permutations for a missing dependency. let misses = Arc::new(Mutex::new(Vec::new())); let misses2 = misses.clone(); let _registry = RegistryBuilder::new() .http_index() .not_found_handler(move |req, _server| { let mut misses = misses2.lock().unwrap(); misses.push(req.url.path().to_string()); Response { code: 404, headers: vec![], body: b"not found".to_vec(), } }) .build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] a-b_c = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] no matching package named `a-b_c` found location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.0.1 ([ROOT]/foo)` "#]]) .run(); let mut misses = misses.lock().unwrap(); misses.sort(); assert_eq!( &*misses, &[ "/index/a-/b-/a-b-c", "/index/a-/b_/a-b_c", "/index/a_/b_/a_b_c" ] ); } #[cargo_test] fn default_auth_error() { // Check for the error message for an authentication error when default is set. let crates_io = RegistryBuilder::new().http_api().build(); let _alternative = RegistryBuilder::new().http_api().alternative().build(); paths::home().join(".cargo/credentials.toml").rm_rf(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" license = "MIT" description = "foo" "#, ) .file("src/lib.rs", "") .build(); // Test output before setting the default. p.cargo("publish --no-verify") .replace_crates_io(crates_io.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [ERROR] no token found, please run `cargo login` or use environment variable CARGO_REGISTRY_TOKEN "#]]) .with_status(101) .run(); p.cargo("publish --no-verify --registry alternative") .replace_crates_io(crates_io.index_url()) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [ERROR] no token found for `alternative`, please run `cargo login --registry alternative` or use environment variable CARGO_REGISTRIES_ALTERNATIVE_TOKEN "#]]) .with_status(101) .run(); // Test the output with the default. cargo_util::paths::append( &paths::cargo_home().join("config.toml"), br#" [registry] default = "alternative" "#, ) .unwrap(); p.cargo("publish --no-verify") .replace_crates_io(crates_io.index_url()) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [ERROR] no token found for `alternative`, please run `cargo login --registry alternative` or use environment variable CARGO_REGISTRIES_ALTERNATIVE_TOKEN "#]]) .with_status(101) .run(); p.cargo("publish --no-verify --registry crates-io") .replace_crates_io(crates_io.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [ERROR] no token found, please run `cargo login --registry crates-io` or use environment variable CARGO_REGISTRY_TOKEN "#]]) .with_status(101) .run(); } const SAMPLE_HEADERS: &[&str] = &[ "x-amz-cf-pop: SFO53-P2", "x-amz-cf-id: vEc3osJrCAXVaciNnF4Vev-hZFgnYwmNZtxMKRJ5bF6h9FTOtbTMnA==", "x-cache: Hit from cloudfront", "server: AmazonS3", "x-amz-version-id: pvsJYY_JGsWiSETZvLJKb7DeEW5wWq1W", "x-amz-server-side-encryption: AES256", "content-type: text/plain", "via: 1.1 bcbc5b46216015493e082cfbcf77ef10.cloudfront.net (CloudFront)", ]; #[cargo_test] fn debug_header_message_index() { // The error message should include some headers for debugging purposes. let _server = RegistryBuilder::new() .http_index() .add_responder("/index/3/b/bar", |_, _| Response { code: 503, headers: SAMPLE_HEADERS.iter().map(|s| s.to_string()).collect(), body: b"Please slow down".to_vec(), }) .build(); Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("fetch") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [WARNING] spurious network error (3 tries remaining): failed to get successful HTTP response from `http://127.0.0.1:[..]/index/3/b/bar` (127.0.0.1), got 503 body: Please slow down [WARNING] spurious network error (2 tries remaining): failed to get successful HTTP response from `http://127.0.0.1:[..]/index/3/b/bar` (127.0.0.1), got 503 body: Please slow down [WARNING] spurious network error (1 try remaining): failed to get successful HTTP response from `http://127.0.0.1:[..]/index/3/b/bar` (127.0.0.1), got 503 body: Please slow down [ERROR] failed to get `bar` as a dependency of package `foo v0.1.0 ([ROOT]/foo)` Caused by: failed to query replaced source registry `crates-io` Caused by: download of 3/b/bar failed Caused by: failed to get successful HTTP response from `http://127.0.0.1:[..]/index/3/b/bar` (127.0.0.1), got 503 debug headers: x-amz-cf-pop: SFO53-P2 x-amz-cf-id: vEc3osJrCAXVaciNnF4Vev-hZFgnYwmNZtxMKRJ5bF6h9FTOtbTMnA== x-cache: Hit from cloudfront body: Please slow down "#]]) .run(); } #[cargo_test] fn debug_header_message_dl() { // Same as debug_header_message_index, but for the dl endpoint which goes // through a completely different code path. let _server = RegistryBuilder::new() .http_index() .add_responder("/dl/bar/1.0.0/download", |_, _| Response { code: 503, headers: SAMPLE_HEADERS.iter().map(|s| s.to_string()).collect(), body: b"Please slow down".to_vec(), }) .build(); Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("fetch") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [WARNING] spurious network error (3 tries remaining): failed to get successful HTTP response from `http://127.0.0.1:[..]/dl/bar/1.0.0/download` (127.0.0.1), got 503 body: Please slow down [WARNING] spurious network error (2 tries remaining): failed to get successful HTTP response from `http://127.0.0.1:[..]/dl/bar/1.0.0/download` (127.0.0.1), got 503 body: Please slow down [WARNING] spurious network error (1 try remaining): failed to get successful HTTP response from `http://127.0.0.1:[..]/dl/bar/1.0.0/download` (127.0.0.1), got 503 body: Please slow down [ERROR] failed to download from `http://127.0.0.1:[..]/dl/bar/1.0.0/download` Caused by: failed to get successful HTTP response from `http://127.0.0.1:[..]/dl/bar/1.0.0/download` (127.0.0.1), got 503 debug headers: x-amz-cf-pop: SFO53-P2 x-amz-cf-id: vEc3osJrCAXVaciNnF4Vev-hZFgnYwmNZtxMKRJ5bF6h9FTOtbTMnA== x-cache: Hit from cloudfront body: Please slow down "#]]) .run(); } #[cfg(unix)] #[cargo_test] fn set_mask_during_unpacking() { use std::os::unix::fs::MetadataExt; Package::new("bar", "1.0.0") .file_with_mode("example.sh", 0o777, "#!/bin/sh") .file_with_mode("src/lib.rs", 0o666, "") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("fetch") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) "#]]) .run(); let src_file_path = |path: &str| { glob::glob( paths::home() .join(".cargo/registry/src/*/bar-1.0.0/") .join(path) .to_str() .unwrap(), ) .unwrap() .next() .unwrap() .unwrap() }; let umask = cargo::util::get_umask(); let metadata = fs::metadata(src_file_path("src/lib.rs")).unwrap(); assert_eq!(metadata.mode() & 0o777, 0o666 & !umask); let metadata = fs::metadata(src_file_path("example.sh")).unwrap(); assert_eq!(metadata.mode() & 0o777, 0o777 & !umask); } #[cargo_test] fn unpack_again_when_cargo_ok_is_unrecognized() { Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("fetch") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) "#]]) .run(); let src_file_path = |path: &str| { glob::glob( paths::home() .join(".cargo/registry/src/*/bar-1.0.0/") .join(path) .to_str() .unwrap(), ) .unwrap() .next() .unwrap() .unwrap() }; // Change permissions to simulate the old behavior not respecting umask. let lib_rs = src_file_path("src/lib.rs"); let cargo_ok = src_file_path(".cargo-ok"); let mut perms = fs::metadata(&lib_rs).unwrap().permissions(); assert!(!perms.readonly()); perms.set_readonly(true); fs::set_permissions(&lib_rs, perms).unwrap(); let ok = fs::read_to_string(&cargo_ok).unwrap(); assert_eq!(&ok, r#"{"v":1}"#); p.cargo("fetch").with_stderr_data("").run(); // Without changing `.cargo-ok`, a unpack won't be triggered. let perms = fs::metadata(&lib_rs).unwrap().permissions(); assert!(perms.readonly()); // Write "ok" to simulate the old behavior and trigger the unpack again. fs::write(&cargo_ok, "ok").unwrap(); p.cargo("fetch").with_stderr_data("").run(); // Permission has been restored and `.cargo-ok` is in the new format. let perms = fs::metadata(lib_rs).unwrap().permissions(); assert!(!perms.readonly()); let ok = fs::read_to_string(&cargo_ok).unwrap(); assert_eq!(&ok, r#"{"v":1}"#); } #[cargo_test] fn differ_only_by_metadata() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] baz = "=0.0.1" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("baz", "0.0.1+b").publish(); Package::new("baz", "0.0.1+c").yanked(true).publish(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] baz v0.0.1+b (registry `dummy-registry`) [CHECKING] baz v0.0.1+b [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); Package::new("baz", "0.0.1+d").publish(); p.cargo("clean").run(); p.cargo("check") .with_stderr_data(str![[r#" [CHECKING] baz v0.0.1+b [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn differ_only_by_metadata_with_lockfile() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] baz = "=0.0.1" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("baz", "0.0.1+a").publish(); Package::new("baz", "0.0.1+b").publish(); Package::new("baz", "0.0.1+c").publish(); p.cargo("update --package baz --precise 0.0.1+b") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [UPDATING] baz v0.0.1+c -> v0.0.1+b "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [DOWNLOADING] crates ... [DOWNLOADED] baz v0.0.1+b (registry `dummy-registry`) [CHECKING] baz v0.0.1+b [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn builtin_source_replacement() { // errors for builtin source replacement of crates.io // should not include mention of source replacement in the error message. let server = RegistryBuilder::new().build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bad-cksum = ">= 0.0.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); let pkg = Package::new("bad-cksum", "0.0.1"); pkg.publish(); t!(File::create(&pkg.archive_dst())); p.cargo("check -v") .replace_crates_io(&server.index_url()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] crates.io index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bad-cksum v0.0.1 [ERROR] failed to verify the checksum of `bad-cksum v0.0.1` "#]]) .run(); } #[cargo_test] fn builtin_source_replacement_no_vendor_error() { // errors for builtin source replacement of crates.io // should not mention outdated vendor dependencies let server = RegistryBuilder::new().build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2021" [dependencies] dep = "0.2.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); let pkg = Package::new("dep", "0.1.0"); pkg.publish(); p.cargo("check -v") .replace_crates_io(&server.index_url()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] crates.io index [ERROR] failed to select a version for the requirement `dep = "^0.2.0"` candidate versions found which didn't match: 0.1.0 location searched: crates.io index required by package `foo v0.0.1 ([ROOT]/foo)` "#]]) .run(); } cargo-0.91.0/tests/testsuite/registry_auth.rs000064400000000000000000000373641046102023000174550ustar 00000000000000//! Tests for registry authentication. use crate::prelude::*; use cargo_test_support::compare::assert_e2e; use cargo_test_support::registry::{Package, RegistryBuilder, Token}; use cargo_test_support::str; use cargo_test_support::{Execs, Project, project}; fn cargo(p: &Project, s: &str) -> Execs { let mut e = p.cargo(s); e.masquerade_as_nightly_cargo(&["asymmetric-token"]) .arg("-Zasymmetric-token"); e.env( "CARGO_REGISTRY_GLOBAL_CREDENTIAL_PROVIDERS", "cargo:paseto cargo:token", ); e } fn make_project() -> Project { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] version = "0.0.1" registry = "alternative" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("bar", "0.0.1").alternative(true).publish(); p } #[cargo_test] fn requires_credential_provider() { let _registry = RegistryBuilder::new() .alternative() .auth_required() .http_api() .build(); let p = make_project(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [LOCKING] 1 package to latest compatible version [ERROR] failed to download `bar v0.0.1 (registry `alternative`)` Caused by: unable to get packages from source Caused by: authenticated registries require a credential-provider to be configured see https://doc.rust-lang.org/cargo/reference/registry-authentication.html for details "#]]) .run(); } #[cargo_test] fn simple() { let _registry = RegistryBuilder::new() .alternative() .auth_required() .http_index() .build(); let p = make_project(); cargo(&p, "build") .with_stderr_data(str![[r#" [UPDATING] `alternative` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `alternative`) [COMPILING] bar v0.0.1 (registry `alternative`) [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn simple_with_asymmetric() { let _registry = RegistryBuilder::new() .alternative() .auth_required() .http_index() .token(cargo_test_support::registry::Token::rfc_key()) .build(); let p = make_project(); cargo(&p, "build") .with_stderr_data(str![[r#" [UPDATING] `alternative` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `alternative`) [COMPILING] bar v0.0.1 (registry `alternative`) [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn environment_config() { let registry = RegistryBuilder::new() .alternative() .auth_required() .no_configure_registry() .no_configure_token() .http_index() .build(); let p = make_project(); cargo(&p, "build") .env( "CARGO_REGISTRIES_ALTERNATIVE_INDEX", registry.index_url().as_str(), ) .env("CARGO_REGISTRIES_ALTERNATIVE_TOKEN", registry.token()) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `alternative`) [COMPILING] bar v0.0.1 (registry `alternative`) [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn environment_token() { let registry = RegistryBuilder::new() .alternative() .auth_required() .no_configure_token() .http_index() .build(); let p = make_project(); cargo(&p, "build") .env("CARGO_REGISTRIES_ALTERNATIVE_TOKEN", registry.token()) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `alternative`) [COMPILING] bar v0.0.1 (registry `alternative`) [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn environment_token_with_asymmetric() { let registry = RegistryBuilder::new() .alternative() .auth_required() .no_configure_token() .http_index() .token(cargo_test_support::registry::Token::Keys( "k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36" .to_string(), None, )) .build(); let p = make_project(); cargo(&p, "build") .env("CARGO_REGISTRIES_ALTERNATIVE_SECRET_KEY", registry.key()) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `alternative`) [COMPILING] bar v0.0.1 (registry `alternative`) [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn bad_environment_token_with_asymmetric_subject() { let registry = RegistryBuilder::new() .alternative() .auth_required() .no_configure_token() .http_index() .token(cargo_test_support::registry::Token::Keys( "k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36" .to_string(), None, )) .build(); let p = make_project(); cargo(&p, "build") .env("CARGO_REGISTRIES_ALTERNATIVE_SECRET_KEY", registry.key()) .env( "CARGO_REGISTRIES_ALTERNATIVE_SECRET_KEY_SUBJECT", "incorrect", ) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [ERROR] failed to get `bar` as a dependency of package `foo v0.0.1 ([ROOT]/foo)` Caused by: token rejected for `alternative`, please run `cargo login --registry alternative` or use environment variable CARGO_REGISTRIES_ALTERNATIVE_TOKEN Caused by: failed to get successful HTTP response from `http://127.0.0.1:[..]/index/config.json`, got 401 body: Unauthorized message from server. "#]]) .with_status(101) .run(); } #[cargo_test] fn bad_environment_token_with_asymmetric_incorrect_subject() { let registry = RegistryBuilder::new() .alternative() .auth_required() .no_configure_token() .http_index() .token(cargo_test_support::registry::Token::rfc_key()) .build(); let p = make_project(); cargo(&p, "build") .env("CARGO_REGISTRIES_ALTERNATIVE_SECRET_KEY", registry.key()) .env( "CARGO_REGISTRIES_ALTERNATIVE_SECRET_KEY_SUBJECT", "incorrect", ) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [ERROR] failed to get `bar` as a dependency of package `foo v0.0.1 ([ROOT]/foo)` Caused by: token rejected for `alternative`, please run `cargo login --registry alternative` or use environment variable CARGO_REGISTRIES_ALTERNATIVE_TOKEN Caused by: failed to get successful HTTP response from `http://127.0.0.1:[..]/index/config.json`, got 401 body: Unauthorized message from server. "#]]) .with_status(101) .run(); } #[cargo_test] fn bad_environment_token_with_incorrect_asymmetric() { let _registry = RegistryBuilder::new() .alternative() .auth_required() .no_configure_token() .http_index() .token(cargo_test_support::registry::Token::Keys( "k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36" .to_string(), None, )) .build(); let p = make_project(); cargo(&p, "build") .env( "CARGO_REGISTRIES_ALTERNATIVE_SECRET_KEY", "k3.secret.9Vxr5hVlI_g_orBZN54vPz20bmB4O76wB_MVqUSuJJJqHFLwP8kdn_RY5g6J6pQG", ) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [ERROR] failed to get `bar` as a dependency of package `foo v0.0.1 ([ROOT]/foo)` Caused by: token rejected for `alternative`, please run `cargo login --registry alternative` or use environment variable CARGO_REGISTRIES_ALTERNATIVE_TOKEN Caused by: failed to get successful HTTP response from `http://127.0.0.1:[..]/index/config.json`, got 401 body: Unauthorized message from server. "#]]) .with_status(101) .run(); } #[cargo_test] fn missing_token() { let _registry = RegistryBuilder::new() .alternative() .auth_required() .no_configure_token() .http_index() .build(); let p = make_project(); cargo(&p, "build") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [ERROR] failed to get `bar` as a dependency of package `foo v0.0.1 ([ROOT]/foo)` Caused by: no token found for `alternative`, please run `cargo login --registry alternative` or use environment variable CARGO_REGISTRIES_ALTERNATIVE_TOKEN "#]]) .run(); } #[cargo_test] fn missing_token_git() { let _registry = RegistryBuilder::new() .alternative() .auth_required() .no_configure_token() .build(); let p = make_project(); cargo(&p, "build") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [LOCKING] 1 package to latest compatible version [ERROR] failed to download `bar v0.0.1 (registry `alternative`)` Caused by: unable to get packages from source Caused by: no token found for `alternative`, please run `cargo login --registry alternative` or use environment variable CARGO_REGISTRIES_ALTERNATIVE_TOKEN "#]]) .run(); } #[cargo_test] fn incorrect_token() { let _registry = RegistryBuilder::new() .alternative() .auth_required() .no_configure_token() .http_index() .build(); let p = make_project(); cargo(&p, "build") .env("CARGO_REGISTRIES_ALTERNATIVE_TOKEN", "incorrect") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [ERROR] failed to get `bar` as a dependency of package `foo v0.0.1 ([ROOT]/foo)` Caused by: token rejected for `alternative`, please run `cargo login --registry alternative` or use environment variable CARGO_REGISTRIES_ALTERNATIVE_TOKEN Caused by: failed to get successful HTTP response from `http://127.0.0.1:[..]/index/config.json`, got 401 body: Unauthorized message from server. "#]]) .run(); } #[cargo_test] fn incorrect_token_git() { let _registry = RegistryBuilder::new() .alternative() .auth_required() .no_configure_token() .http_api() .build(); let p = make_project(); cargo(&p, "build") .env("CARGO_REGISTRIES_ALTERNATIVE_TOKEN", "incorrect") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [ERROR] failed to download from `http://127.0.0.1:[..]/dl/bar/0.0.1/download` Caused by: failed to get successful HTTP response from `http://127.0.0.1:[..]/dl/bar/0.0.1/download` (127.0.0.1), got 401 body: Unauthorized message from server. "#]]) .run(); } #[cargo_test] fn anonymous_alt_registry() { // An alternative registry that requires auth, but is not in the config. let registry = RegistryBuilder::new() .alternative() .auth_required() .no_configure_token() .no_configure_registry() .http_index() .build(); let p = make_project(); cargo(&p, &format!("install --index {} bar", registry.index_url())) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `sparse+http://127.0.0.1:[..]/index/` index [ERROR] no token found for `sparse+http://127.0.0.1:[..]/index/` consider setting up an alternate registry in Cargo's configuration as described by https://doc.rust-lang.org/cargo/reference/registries.html [registries] my-registry = { index = "sparse+http://127.0.0.1:[..]/index/" } "#]]) .run(); } #[cargo_test] fn login() { let _registry = RegistryBuilder::new() .alternative() .no_configure_token() .auth_required() .http_index() .build(); let p = make_project(); cargo(&p, "login --registry alternative") .with_stdin("sekrit") .run(); } #[cargo_test] fn login_existing_token() { let _registry = RegistryBuilder::new() .alternative() .auth_required() .http_index() .build(); let p = make_project(); cargo(&p, "login --registry alternative") .with_stdin("sekrit") .run(); } #[cargo_test] fn duplicate_index() { let server = RegistryBuilder::new() .alternative() .no_configure_token() .auth_required() .build(); let p = make_project(); // Two alternative registries with the same index. cargo(&p, "build") .env( "CARGO_REGISTRIES_ALTERNATIVE1_INDEX", server.index_url().as_str(), ) .env( "CARGO_REGISTRIES_ALTERNATIVE2_INDEX", server.index_url().as_str(), ) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [LOCKING] 1 package to latest compatible version [ERROR] failed to download `bar v0.0.1 (registry `alternative`)` Caused by: unable to get packages from source Caused by: multiple registries are configured with the same index url 'registry+[ROOTURL]/alternative-registry': alternative1, alternative2 "#]]) .run(); } #[cargo_test] fn token_not_logged() { // Checks that the token isn't displayed in debug output (for both HTTP // index and registry API). Note that this doesn't fully verify the // correct behavior since we don't have an HTTP2 server, and curl behaves // significantly differently when using HTTP2. let crates_io = RegistryBuilder::new() .http_api() .http_index() .auth_required() .token(Token::Plaintext("a-unique_token".to_string())) .build(); Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); let output = cargo(&p, "publish") .replace_crates_io(crates_io.index_url()) .env("CARGO_HTTP_DEBUG", "true") .env("CARGO_LOG", "trace") .run(); let log = String::from_utf8(output.stderr).unwrap(); assert_e2e().eq( &log, str![[r#" ... [PUBLISHED] foo v0.1.0 at registry `crates-io` "#]], ); let authorizations: Vec<_> = log .lines() .filter(|line| { line.contains("http-debug:") && line.to_lowercase().contains("authorization") }) .collect(); assert!(authorizations.iter().all(|line| line.contains("REDACTED"))); // Total authorizations: // 1. Initial config.json // 2. /index/3/f/foo // 3. config.json again for verification // 4. /index/3/b/bar // 5. config.json again for verification // 6. /index/3/b/bar // 7. /dl/bar/1.0.0/download // 8. /api/v1/crates/new // 9. config.json again for verification // 10. /index/3/f/foo for the "wait for publish" assert_eq!(authorizations.len(), 10); assert!(!log.contains("a-unique_token")); } cargo-0.91.0/tests/testsuite/registry_overlay.rs000064400000000000000000000206141046102023000201630ustar 00000000000000//! Tests for local-registry sources. use crate::prelude::*; use cargo_test_support::project; use cargo_test_support::registry::{Package, RegistryBuilder, TestRegistry}; use cargo_test_support::str; fn setup() -> (TestRegistry, String) { let alt = RegistryBuilder::new().alternative().build(); ( RegistryBuilder::new().http_index().build(), alt.index_url() .to_file_path() .unwrap() .into_os_string() .into_string() .unwrap(), ) } #[cargo_test] fn overlay_hit() { let (reg, alt_path) = setup(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] baz = "0.1.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); // baz is only in the local registry, but it gets found Package::new("baz", "0.1.1") .alternative(true) .local(true) .publish(); p.cargo("check") .overlay_registry(®.index_url(), &alt_path) .run(); } #[cargo_test] fn registry_version_wins() { let (reg, alt_path) = setup(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] baz = "0.1.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); // The latest one is in the main registry, so it will get chosen. Package::new("baz", "0.1.1").publish(); Package::new("baz", "0.1.0") .alternative(true) .local(true) .publish(); p.cargo("check") .overlay_registry(®.index_url(), &alt_path) .with_stderr_data(str![[r#" [UPDATING] `sparse+http://127.0.0.1:[..]/index/` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] baz v0.1.1 (registry `sparse+http://127.0.0.1:[..]/index/`) [CHECKING] baz v0.1.1 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn overlay_version_wins() { let (reg, alt_path) = setup(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] baz = "0.1.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); // The latest one is in the overlay registry, so it will get chosen. Package::new("baz", "0.1.0").publish(); Package::new("baz", "0.1.1") .alternative(true) .local(true) .publish(); p.cargo("check") .overlay_registry(®.index_url(), &alt_path) .with_stderr_data(str![[r#" [UPDATING] `sparse+http://127.0.0.1:[..]/index/` index [LOCKING] 1 package to latest compatible version [UNPACKING] baz v0.1.1 (registry `[ROOT]/alternative-registry`) [CHECKING] baz v0.1.1 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn version_precedence() { let (reg, alt_path) = setup(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] baz = "0.1.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); // The one we want is in the main registry. Package::new("baz", "0.1.1").publish(); Package::new("baz", "0.1.1") .alternative(true) .local(true) .publish(); p.cargo("check") .overlay_registry(®.index_url(), &alt_path) .with_stderr_data(str![[r#" [UPDATING] `sparse+http://127.0.0.1:[..]/index/` index [LOCKING] 1 package to latest compatible version [UNPACKING] baz v0.1.1 (registry `[ROOT]/alternative-registry`) [CHECKING] baz v0.1.1 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn local_depends_on_old_registry_package() { let (reg, alt_path) = setup(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] baz = "0.1.0" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("baz", "0.0.1").publish(); // A new local package can depend on an older version in the registry. Package::new("baz", "0.1.1") .dep("baz", "=0.0.1") .alternative(true) .local(true) .publish(); p.cargo("check") .overlay_registry(®.index_url(), &alt_path) .run(); } #[cargo_test] fn registry_dep_depends_on_new_local_package() { let (reg, alt_path) = setup(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] registry-package = "0.1.0" workspace-package = "0.0.1" "#, ) .file("src/main.rs", "fn main() {}") .build(); Package::new("registry-package", "0.1.0") .dep("workspace-package", "0.1.0") .publish(); // The local overlay contains an updated version of workspace-package Package::new("workspace-package", "0.1.1") .alternative(true) .local(true) .publish(); // The registry contains older versions of workspace-package (one of which // we depend on directly). Package::new("workspace-package", "0.1.0").publish(); Package::new("workspace-package", "0.0.1").publish(); p.cargo("check") .overlay_registry(®.index_url(), &alt_path) .with_stderr_data( str![[r#" [UPDATING] `sparse+http://127.0.0.1:[..]/index/` index [LOCKING] 3 packages to latest compatible versions [ADDING] workspace-package v0.0.1 (available: v0.1.1) [DOWNLOADING] crates ... [UNPACKING] workspace-package v0.1.1 (registry `[ROOT]/alternative-registry`) [DOWNLOADED] registry-package v0.1.0 (registry `sparse+http://127.0.0.1:[..]/index/`) [DOWNLOADED] workspace-package v0.0.1 (registry `sparse+http://127.0.0.1:[..]/index/`) [CHECKING] workspace-package v0.1.1 [CHECKING] workspace-package v0.0.1 [CHECKING] registry-package v0.1.0 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } // Test that we can overlay on top of alternate registries, not just crates-io. // Since the test framework only supports a single alternate registry, we repurpose // the dummy crates-io as the registry to overlay on top. #[cargo_test] fn alt_registry() { let alt = RegistryBuilder::new().http_index().alternative().build(); let crates_io = RegistryBuilder::new().build(); let crates_io_path = crates_io .index_url() .to_file_path() .unwrap() .into_os_string() .into_string() .unwrap(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] baz = { version = "0.1.0", registry = "alternative" } "#, ) .file("src/main.rs", "fn main() {}") .build(); // This package isn't used, but publishing it forces the creation of the registry index. Package::new("bar", "0.0.1").local(true).publish(); Package::new("baz", "0.1.1").alternative(true).publish(); p.cargo("check") .overlay_registry(&alt.index_url(), &crates_io_path) .run(); } cargo-0.91.0/tests/testsuite/rename_deps.rs000064400000000000000000000241241046102023000170340ustar 00000000000000//! Tests for renaming dependencies. use crate::prelude::*; use cargo_test_support::git; use cargo_test_support::paths; use cargo_test_support::registry::{self, Package}; use cargo_test_support::{basic_manifest, project, str}; #[cargo_test] fn rename_dependency() { Package::new("bar", "0.1.0").publish(); Package::new("bar", "0.2.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = { version = "0.1.0" } baz = { version = "0.2.0", package = "bar" } "#, ) .file("src/lib.rs", "extern crate bar; extern crate baz;") .build(); p.cargo("build").run(); } #[cargo_test] fn rename_with_different_names() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] baz = { path = "bar", package = "bar" } "#, ) .file("src/lib.rs", "extern crate baz;") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [lib] name = "random_name" "#, ) .file("bar/src/lib.rs", "") .build(); p.cargo("build").run(); } #[cargo_test] fn lots_of_names() { registry::alt_init(); Package::new("foo", "0.1.0") .file("src/lib.rs", "pub fn foo1() {}") .publish(); Package::new("foo", "0.2.0") .file("src/lib.rs", "pub fn foo() {}") .publish(); Package::new("foo", "0.1.0") .file("src/lib.rs", "pub fn foo2() {}") .alternative(true) .publish(); let g = git::repo(&paths::root().join("another")) .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("src/lib.rs", "pub fn foo3() {}") .build(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "test" version = "0.1.0" edition = "2015" authors = [] [dependencies] foo = "0.2" foo1 = {{ version = "0.1", package = "foo" }} foo2 = {{ version = "0.1", registry = "alternative", package = "foo" }} foo3 = {{ git = '{}', package = "foo" }} foo4 = {{ path = "foo", package = "foo" }} "#, g.url() ), ) .file( "src/lib.rs", " extern crate foo; extern crate foo1; extern crate foo2; extern crate foo3; extern crate foo4; pub fn foo() { foo::foo(); foo1::foo1(); foo2::foo2(); foo3::foo3(); foo4::foo4(); } ", ) .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("foo/src/lib.rs", "pub fn foo4() {}") .build(); p.cargo("build -v").run(); } #[cargo_test] fn rename_and_patch() { Package::new("foo", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "test" version = "0.1.0" edition = "2015" authors = [] [dependencies] bar = { version = "0.1", package = "foo" } [patch.crates-io] foo = { path = "foo" } "#, ) .file( "src/lib.rs", "extern crate bar; pub fn foo() { bar::foo(); }", ) .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("foo/src/lib.rs", "pub fn foo() {}") .build(); p.cargo("build -v").run(); } #[cargo_test] fn rename_twice() { Package::new("foo", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "test" version = "0.1.0" edition = "2015" authors = [] [dependencies] bar = { version = "0.1", package = "foo" } [build-dependencies] foo = { version = "0.1" } "#, ) .file("src/lib.rs", "") .build(); p.cargo("build -v") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] foo v0.1.0 (registry `dummy-registry`) [ERROR] the crate `test v0.1.0 ([ROOT]/foo)` depends on crate `foo v0.1.0` multiple times with different names "#]]) .run(); } #[cargo_test] fn rename_affects_fingerprint() { Package::new("foo", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "test" version = "0.1.0" edition = "2015" authors = [] [dependencies] foo = { version = "0.1", package = "foo" } "#, ) .file("src/lib.rs", "extern crate foo;") .build(); p.cargo("build -v").run(); p.change_file( "Cargo.toml", r#" [package] name = "test" version = "0.1.0" edition = "2015" authors = [] [dependencies] bar = { version = "0.1", package = "foo" } "#, ); p.cargo("build -v") .with_status(101) .with_stderr_data(str![[r#" [FRESH] foo v0.1.0 [DIRTY] test v0.1.0 ([ROOT]/foo): name of dependency changed (foo => bar) [COMPILING] test v0.1.0 ([ROOT]/foo) [RUNNING] `rustc [..]` error[E0463]: can't find crate for `foo` ... "#]]) .run(); } #[cargo_test] fn can_run_doc_tests() { Package::new("bar", "0.1.0").publish(); Package::new("bar", "0.2.0").publish(); let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = { version = "0.1.0" } baz = { version = "0.2.0", package = "bar" } "#, ) .file( "src/lib.rs", " extern crate bar; extern crate baz; ", ) .build(); foo.cargo("test -v").with_stderr_data(str![[r#" ... [DOCTEST] foo [RUNNING] `rustdoc [..]--test src/lib.rs [..] --extern bar=[ROOT]/foo/target/debug/deps/libbar-[HASH].rlib --extern baz=[ROOT]/foo/target/debug/deps/libbar-[HASH].rlib [..]` "#]]).run(); } #[cargo_test] fn features_still_work() { Package::new("foo", "0.1.0").publish(); Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "test" version = "0.1.0" edition = "2015" authors = [] [dependencies] p1 = { path = 'a', features = ['b'] } p2 = { path = 'b' } "#, ) .file("src/lib.rs", "") .file( "a/Cargo.toml", r#" [package] name = "p1" version = "0.1.0" edition = "2015" authors = [] [dependencies] b = { version = "0.1", package = "foo", optional = true } "#, ) .file("a/src/lib.rs", "extern crate b;") .file( "b/Cargo.toml", r#" [package] name = "p2" version = "0.1.0" edition = "2015" authors = [] [dependencies] b = { version = "0.1", package = "bar", optional = true } [features] default = ['b'] "#, ) .file("b/src/lib.rs", "extern crate b;") .build(); p.cargo("build -v").run(); } #[cargo_test] fn features_not_working() { Package::new("foo", "0.1.0").publish(); Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "test" version = "0.1.0" edition = "2015" authors = [] [dependencies] a = { path = 'a', package = 'p1', optional = true } [features] default = ['p1'] "#, ) .file("src/lib.rs", "") .file("a/Cargo.toml", &basic_manifest("p1", "0.1.0")) .build(); p.cargo("build -v") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: feature `default` includes `p1` which is neither a dependency nor another feature "#]]) .run(); } #[cargo_test] fn rename_with_dash() { let p = project() .file( "Cargo.toml", r#" [package] name = "qwerty" version = "0.1.0" edition = "2015" [dependencies] foo-bar = { path = 'a', package = 'a' } "#, ) .file("src/lib.rs", "extern crate foo_bar;") .file("a/Cargo.toml", &basic_manifest("a", "0.1.0")) .file("a/src/lib.rs", "") .build(); p.cargo("build").run(); } cargo-0.91.0/tests/testsuite/replace.rs000064400000000000000000001163151046102023000161710ustar 00000000000000//! Tests for `[replace]` table source replacement. use crate::prelude::*; use cargo_test_support::git; use cargo_test_support::paths; use cargo_test_support::registry::Package; use cargo_test_support::{basic_manifest, project, str}; #[cargo_test] fn override_simple() { Package::new("bar", "0.1.0").publish(); let bar = git::repo(&paths::root().join("override")) .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "pub fn bar() {}") .build(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" [replace] "bar:0.1.0" = {{ git = '{}' }} "#, bar.url() ), ) .file( "src/lib.rs", "extern crate bar; pub fn foo() { bar::bar(); }", ) .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [UPDATING] git repository `[ROOTURL]/override` [LOCKING] 2 packages to latest compatible versions [CHECKING] bar v0.1.0 ([ROOTURL]/override#[..]) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn override_with_features() { Package::new("bar", "0.1.0").publish(); let bar = git::repo(&paths::root().join("override")) .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "pub fn bar() {}") .build(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" [replace] "bar:0.1.0" = {{ git = '{}', features = ["some_feature"] }} "#, bar.url() ), ) .file( "src/lib.rs", "extern crate bar; pub fn foo() { bar::bar(); }", ) .build(); p.cargo("check").with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [UPDATING] git repository `[ROOTURL]/override` [LOCKING] 2 packages to latest compatible versions [WARNING] replacement for `bar` uses the features mechanism. default-features and features will not take effect because the replacement dependency does not support this mechanism [CHECKING] bar v0.1.0 ([ROOTURL]/override#[..]) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn override_with_setting_default_features() { Package::new("bar", "0.1.0").publish(); let bar = git::repo(&paths::root().join("override")) .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "pub fn bar() {}") .build(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" [replace] "bar:0.1.0" = {{ git = '{}', default-features = false, features = ["none_default_feature"] }} "#, bar.url() ), ) .file( "src/lib.rs", "extern crate bar; pub fn foo() { bar::bar(); }", ) .build(); p.cargo("check").with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [UPDATING] git repository `[ROOTURL]/override` [LOCKING] 2 packages to latest compatible versions [WARNING] replacement for `bar` uses the features mechanism. default-features and features will not take effect because the replacement dependency does not support this mechanism [CHECKING] bar v0.1.0 ([ROOTURL]/override#[..]) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn missing_version() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" [replace] bar = { git = 'https://example.com' } "#, ) .file("src/lib.rs", "") .build(); p.cargo("check").with_status(101).with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: replacements must specify a version to replace, but `https://github.com/rust-lang/crates.io-index#bar` does not "#]]).run(); } #[cargo_test] fn invalid_semver_version() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "*" [replace] "bar:*" = { git = 'https://example.com' } "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: replacements must specify a valid semver version to replace, but `bar:*` does not ... "#]]) .run(); } #[cargo_test] fn different_version() { Package::new("bar", "0.2.0").publish(); Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" [replace] "bar:0.1.0" = "0.2.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check").with_status(101).with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: replacements cannot specify a version requirement, but found one for `https://github.com/rust-lang/crates.io-index#bar@0.1.0` "#]]).run(); } #[cargo_test] fn transitive() { Package::new("bar", "0.1.0").publish(); Package::new("baz", "0.2.0") .dep("bar", "0.1.0") .file("src/lib.rs", "extern crate bar; fn baz() { bar::bar(); }") .publish(); let foo = git::repo(&paths::root().join("override")) .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "pub fn bar() {}") .build(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] baz = "0.2.0" [replace] "bar:0.1.0" = {{ git = '{}' }} "#, foo.url() ), ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [UPDATING] git repository `[ROOTURL]/override` [LOCKING] 3 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] baz v0.2.0 (registry `dummy-registry`) [CHECKING] bar v0.1.0 ([ROOTURL]/override#[..]) [CHECKING] baz v0.2.0 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn persists_across_rebuilds() { Package::new("bar", "0.1.0").publish(); let foo = git::repo(&paths::root().join("override")) .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "pub fn bar() {}") .build(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" [replace] "bar:0.1.0" = {{ git = '{}' }} "#, foo.url() ), ) .file( "src/lib.rs", "extern crate bar; pub fn foo() { bar::bar(); }", ) .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [UPDATING] git repository `[ROOTURL]/override` [LOCKING] 2 packages to latest compatible versions [CHECKING] bar v0.1.0 ([ROOTURL]/override#[..]) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn replace_registry_with_path() { Package::new("bar", "0.1.0").publish(); let _ = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "pub fn bar() {}") .build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" [replace] "bar:0.1.0" = { path = "../bar" } "#, ) .file( "src/lib.rs", "extern crate bar; pub fn foo() { bar::bar(); }", ) .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [CHECKING] bar v0.1.0 ([ROOT]/bar) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn use_a_spec_to_select() { Package::new("baz", "0.1.1") .file("src/lib.rs", "pub fn baz1() {}") .publish(); Package::new("baz", "0.2.0").publish(); Package::new("bar", "0.1.1") .dep("baz", "0.2") .file( "src/lib.rs", "extern crate baz; pub fn bar() { baz::baz3(); }", ) .publish(); let foo = git::repo(&paths::root().join("override")) .file("Cargo.toml", &basic_manifest("baz", "0.2.0")) .file("src/lib.rs", "pub fn baz3() {}") .build(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1" baz = "0.1" [replace] "baz:0.2.0" = {{ git = '{}' }} "#, foo.url() ), ) .file( "src/lib.rs", " extern crate bar; extern crate baz; pub fn local() { baz::baz1(); bar::bar(); } ", ) .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [UPDATING] git repository `[ROOTURL]/override` [LOCKING] 4 packages to latest compatible versions [ADDING] baz v0.1.1 (available: v0.2.0) [DOWNLOADING] crates ... [DOWNLOADED] baz v0.1.1 (registry `dummy-registry`) [DOWNLOADED] bar v0.1.1 (registry `dummy-registry`) [CHECKING] baz v0.2.0 ([ROOTURL]/override#[..]) [CHECKING] baz v0.1.1 [CHECKING] bar v0.1.1 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn override_adds_some_deps() { Package::new("baz", "0.1.1").publish(); Package::new("bar", "0.1.0").publish(); let foo = git::repo(&paths::root().join("override")) .file( "Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] [dependencies] baz = "0.1" "#, ) .file("src/lib.rs", "") .build(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1" [replace] "bar:0.1.0" = {{ git = '{}' }} "#, foo.url() ), ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [UPDATING] git repository `[ROOTURL]/override` [LOCKING] 3 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] baz v0.1.1 (registry `dummy-registry`) [CHECKING] baz v0.1.1 [CHECKING] bar v0.1.0 ([ROOTURL]/override#[..]) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); Package::new("baz", "0.1.2").publish(); p.cargo("update") .arg(&format!("{}#bar", foo.url())) .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/override` [UPDATING] `dummy-registry` index [LOCKING] 0 packages to latest compatible versions [NOTE] pass `--verbose` to see 1 unchanged dependencies behind latest "#]]) .run(); p.cargo("update https://github.com/rust-lang/crates.io-index#bar") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 0 packages to latest compatible versions [NOTE] pass `--verbose` to see 1 unchanged dependencies behind latest "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn locked_means_locked_yes_no_seriously_i_mean_locked() { // this in theory exercises #2041 Package::new("baz", "0.1.0").publish(); Package::new("baz", "0.2.0").publish(); Package::new("bar", "0.1.0").publish(); let foo = git::repo(&paths::root().join("override")) .file( "Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] [dependencies] baz = "*" "#, ) .file("src/lib.rs", "") .build(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1" baz = "0.1" [replace] "bar:0.1.0" = {{ git = '{}' }} "#, foo.url() ), ) .file("src/lib.rs", "") .build(); p.cargo("check").run(); p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn override_wrong_name() { Package::new("baz", "0.1.0").publish(); let foo = git::repo(&paths::root().join("override")) .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "") .build(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] baz = "0.1" [replace] "baz:0.1.0" = {{ git = '{}' }} "#, foo.url() ), ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [UPDATING] git repository `[ROOTURL]/override` [ERROR] failed to get `baz` as a dependency of package `foo v0.0.1 ([ROOT]/foo)` Caused by: no matching package for override `https://github.com/rust-lang/crates.io-index#baz@0.1.0` found location searched: [ROOTURL]/override version required: =0.1.0 "#]]) .run(); } #[cargo_test] fn override_with_nothing() { Package::new("bar", "0.1.0").publish(); let foo = git::repo(&paths::root().join("override")) .file("src/lib.rs", "") .build(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1" [replace] "bar:0.1.0" = {{ git = '{}' }} "#, foo.url() ), ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [UPDATING] git repository `[ROOTURL]/override` [ERROR] failed to get `bar` as a dependency of package `foo v0.0.1 ([ROOT]/foo)` Caused by: failed to load source for dependency `bar` Caused by: Unable to update [ROOTURL]/override Caused by: Could not find Cargo.toml in `[ROOT]/home/.cargo/git/checkouts/override-[HASH]/[..]` "#]]) .run(); } #[cargo_test] fn override_wrong_version() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [replace] "bar:0.1.0" = { git = 'https://example.com', version = '0.2.0' } "#, ) .file("src/lib.rs", "") .build(); p.cargo("check").with_status(101).with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: replacements cannot specify a version requirement, but found one for `https://github.com/rust-lang/crates.io-index#bar@0.1.0` "#]]).run(); } #[cargo_test] fn multiple_specs() { Package::new("bar", "0.1.0").publish(); let bar = git::repo(&paths::root().join("override")) .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "pub fn bar() {}") .build(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" [replace] "bar:0.1.0" = {{ git = '{0}' }} [replace."https://github.com/rust-lang/crates.io-index#bar:0.1.0"] git = '{0}' "#, bar.url() ), ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [UPDATING] git repository `[ROOTURL]/override` [ERROR] failed to get `bar` as a dependency of package `foo v0.0.1 ([ROOT]/foo)` Caused by: overlapping replacement specifications found: * https://github.com/rust-lang/crates.io-index#bar@0.1.0 * https://github.com/rust-lang/crates.io-index#bar@0.1.0 both specifications match: bar v0.1.0 "#]]) .run(); } #[cargo_test] fn test_override_dep() { Package::new("bar", "0.1.0").publish(); let bar = git::repo(&paths::root().join("override")) .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "pub fn bar() {}") .build(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" [replace] "bar:0.1.0" = {{ git = '{0}' }} "#, bar.url() ), ) .file("src/lib.rs", "") .build(); p.cargo("test -p bar") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [UPDATING] git repository `[ROOTURL]/override` [LOCKING] 2 packages to latest compatible versions [ERROR] There are multiple `bar` packages in your project, and the specification `bar` is ambiguous. Please re-run this command with one of the following specifications: registry+https://github.com/rust-lang/crates.io-index#bar@0.1.0 git+[ROOTURL]/override#bar@0.1.0 "#]]) .run(); } #[cargo_test] fn update() { Package::new("bar", "0.1.0").publish(); let bar = git::repo(&paths::root().join("override")) .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "pub fn bar() {}") .build(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" [replace] "bar:0.1.0" = {{ git = '{0}' }} "#, bar.url() ), ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); p.cargo("update") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [UPDATING] git repository `[ROOTURL]/override` [LOCKING] 0 packages to latest compatible versions "#]]) .run(); } // foo -> near -> far // near is overridden with itself #[cargo_test] fn no_override_self() { let deps = git::repo(&paths::root().join("override")) .file("far/Cargo.toml", &basic_manifest("far", "0.1.0")) .file("far/src/lib.rs", "") .file( "near/Cargo.toml", r#" [package] name = "near" version = "0.1.0" edition = "2015" authors = [] [dependencies] far = { path = "../far" } "#, ) .file("near/src/lib.rs", "#![no_std] pub extern crate far;") .build(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] near = {{ git = '{0}' }} [replace] "near:0.1.0" = {{ git = '{0}' }} "#, deps.url() ), ) .file("src/lib.rs", "#![no_std] pub extern crate near;") .build(); p.cargo("check --verbose").run(); } #[cargo_test] fn override_an_override() { Package::new("chrono", "0.2.0") .dep("serde", "< 0.9") .publish(); Package::new("serde", "0.7.0") .file("src/lib.rs", "pub fn serde07() {}") .publish(); Package::new("serde", "0.8.0") .file("src/lib.rs", "pub fn serde08() {}") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] chrono = "0.2" serde = "0.8" [replace] "chrono:0.2.0" = { path = "chrono" } "serde:0.8.0" = { path = "serde" } "#, ) .file( "Cargo.lock", r#" [[package]] name = "foo" version = "0.0.1" dependencies = [ "chrono 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "chrono" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" replace = "chrono 0.2.0" [[package]] name = "chrono" version = "0.2.0" dependencies = [ "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" replace = "serde 0.8.0" [[package]] name = "serde" version = "0.8.0" "#, ) .file( "src/lib.rs", " extern crate chrono; extern crate serde; pub fn foo() { chrono::chrono(); serde::serde08_override(); } ", ) .file( "chrono/Cargo.toml", r#" [package] name = "chrono" version = "0.2.0" edition = "2015" authors = [] [dependencies] serde = "< 0.9" "#, ) .file( "chrono/src/lib.rs", " extern crate serde; pub fn chrono() { serde::serde07(); } ", ) .file("serde/Cargo.toml", &basic_manifest("serde", "0.8.0")) .file("serde/src/lib.rs", "pub fn serde08_override() {}") .build(); p.cargo("check -v").run(); } #[cargo_test] fn overriding_nonexistent_no_spurious() { Package::new("bar", "0.1.0").dep("baz", "0.1").publish(); Package::new("baz", "0.1.0").publish(); let bar = git::repo(&paths::root().join("override")) .file( "Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] [dependencies] baz = { path = "baz" } "#, ) .file("src/lib.rs", "pub fn bar() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "pub fn baz() {}") .build(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" [replace] "bar:0.1.0" = {{ git = '{url}' }} "baz:0.1.0" = {{ git = '{url}' }} "#, url = bar.url() ), ) .file("src/lib.rs", "") .build(); p.cargo("check").run(); p.cargo("check") .with_stderr_data(str![[r#" [WARNING] package replacement is not used: https://github.com/rust-lang/crates.io-index#baz@0.1.0 [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .with_stdout_data("") .run(); } #[cargo_test] fn no_warnings_when_replace_is_used_in_another_workspace_member() { Package::new("bar", "0.1.0").publish(); Package::new("baz", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = [ "first_crate", "second_crate"] [replace] "bar:0.1.0" = { path = "local_bar" } "#, ) .file( "first_crate/Cargo.toml", r#" [package] name = "first_crate" version = "0.1.0" edition = "2015" [dependencies] bar = "0.1.0" "#, ) .file("first_crate/src/lib.rs", "") .file( "second_crate/Cargo.toml", &basic_manifest("second_crate", "0.1.0"), ) .file("second_crate/src/lib.rs", "") .file("local_bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("local_bar/src/lib.rs", "") .build(); p.cargo("check") .cwd("first_crate") .with_stdout_data("") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [CHECKING] bar v0.1.0 ([ROOT]/foo/local_bar) [CHECKING] first_crate v0.1.0 ([ROOT]/foo/first_crate) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .cwd("second_crate") .with_stdout_data("") .with_stderr_data(str![[r#" [CHECKING] second_crate v0.1.0 ([ROOT]/foo/second_crate) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn replace_to_path_dep() { Package::new("bar", "0.1.0").dep("baz", "0.1").publish(); Package::new("baz", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" [replace] "bar:0.1.0" = { path = "bar" } "#, ) .file("src/lib.rs", "extern crate bar;") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] [dependencies] baz = { path = "baz" } "#, ) .file( "bar/src/lib.rs", "extern crate baz; pub fn bar() { baz::baz(); }", ) .file("bar/baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("bar/baz/src/lib.rs", "pub fn baz() {}") .build(); p.cargo("check").run(); } #[cargo_test] fn override_with_default_feature() { Package::new("another", "0.1.0").publish(); Package::new("another", "0.1.1").dep("bar", "0.1").publish(); Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = { path = "bar", default-features = false } another = "0.1" another2 = { path = "another2" } [replace] 'bar:0.1.0' = { path = "bar" } "#, ) .file("src/main.rs", "extern crate bar; fn main() { bar::bar(); }") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] [features] default = [] "#, ) .file( "bar/src/lib.rs", r#" #[cfg(feature = "default")] pub fn bar() {} "#, ) .file( "another2/Cargo.toml", r#" [package] name = "another2" version = "0.1.0" edition = "2015" authors = [] [dependencies] bar = { version = "0.1", default-features = false } "#, ) .file("another2/src/lib.rs", "") .build(); p.cargo("run").run(); } #[cargo_test] fn override_plus_dep() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1" [replace] 'bar:0.1.0' = { path = "bar" } "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] [dependencies] foo = { path = ".." } "#, ) .file("bar/src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] cyclic package dependency: package `bar v0.1.0 ([ROOT]/foo/bar)` depends on itself. Cycle: package `bar v0.1.0 ([ROOT]/foo/bar)` ... which satisfies dependency `bar = "^0.1"` of package `foo v0.0.1 ([ROOT]/foo)` ... which satisfies path dependency `foo` of package `bar v0.1.0 ([ROOT]/foo/bar)` "#]]) .run(); } #[cargo_test] fn override_generic_matching_other_versions() { Package::new("bar", "0.1.0+a").publish(); let bar = git::repo(&paths::root().join("override")) .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "pub fn bar() {}") .build(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" [replace] "bar:0.1.0" = {{ git = '{}' }} "#, bar.url() ), ) .file( "src/lib.rs", "extern crate bar; pub fn foo() { bar::bar(); }", ) .build(); p.cargo("check").with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [UPDATING] git repository `[ROOTURL]/override` [ERROR] failed to get `bar` as a dependency of package `foo v0.0.1 ([ROOT]/foo)` Caused by: replacement specification `https://github.com/rust-lang/crates.io-index#bar@0.1.0` matched 0.1.0+a and tried to override it with 0.1.0 avoid matching unrelated packages by being more specific "#]]).with_status(101).run(); } #[cargo_test] fn override_respects_spec_metadata() { Package::new("bar", "0.1.0+a").publish(); let bar = git::repo(&paths::root().join("override")) .file("Cargo.toml", &basic_manifest("bar", "0.1.0+a")) .file("src/lib.rs", "pub fn bar() {}") .build(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" [replace] "bar:0.1.0+notTheBuild" = {{ git = '{}' }} "#, bar.url() ), ) .file( "src/lib.rs", "extern crate bar; pub fn foo() { bar::bar(); }", ) .build(); p.cargo("check").with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [WARNING] package replacement is not used: https://github.com/rust-lang/crates.io-index#bar@0.1.0+notTheBuild [DOWNLOADING] crates ... [DOWNLOADED] bar v0.1.0+a (registry `dummy-registry`) [CHECKING] bar v0.1.0+a [CHECKING] foo v0.0.1 ([ROOT]/foo) error[E0425]: cannot find function `bar`[..] ... [ERROR] could not compile `foo` (lib) due to 1 previous error "#]]).with_status(101).run(); } #[cargo_test] fn override_spec_metadata_is_optional() { Package::new("bar", "0.1.0+a").publish(); let bar = git::repo(&paths::root().join("override")) .file("Cargo.toml", &basic_manifest("bar", "0.1.0+a")) .file("src/lib.rs", "pub fn bar() {}") .build(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "0.1.0" [replace] "bar:0.1.0" = {{ git = '{}' }} "#, bar.url() ), ) .file( "src/lib.rs", "extern crate bar; pub fn foo() { bar::bar(); }", ) .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [UPDATING] git repository `[ROOTURL]/override` [LOCKING] 2 packages to latest compatible versions [CHECKING] bar v0.1.0+a ([ROOTURL]/override#[..]) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } cargo-0.91.0/tests/testsuite/required_features.rs000064400000000000000000001256271046102023000203020ustar 00000000000000//! Tests for targets with `required-features`. use crate::prelude::*; use cargo_test_support::install::{assert_has_installed_exe, assert_has_not_installed_exe}; use cargo_test_support::is_nightly; use cargo_test_support::paths; use cargo_test_support::project; use cargo_test_support::str; #[cargo_test] fn build_bin_default_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] default = ["a"] a = [] [[bin]] name = "foo" required-features = ["a"] "#, ) .file( "src/main.rs", r#" extern crate foo; #[cfg(feature = "a")] fn test() { foo::foo(); } fn main() {} "#, ) .file("src/lib.rs", r#"#[cfg(feature = "a")] pub fn foo() {}"#) .build(); p.cargo("build").run(); assert!(p.bin("foo").is_file()); p.cargo("build --no-default-features").run(); p.cargo("build --bin=foo").run(); assert!(p.bin("foo").is_file()); p.cargo("build --bin=foo --no-default-features") .with_status(101) .with_stderr_data(str![[r#" [ERROR] target `foo` in package `foo` requires the features: `a` Consider enabling them by passing, e.g., `--features="a"` "#]]) .run(); } #[cargo_test] fn build_bin_arg_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] a = [] [[bin]] name = "foo" required-features = ["a"] "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("build --features a").run(); assert!(p.bin("foo").is_file()); } #[cargo_test] fn build_bin_multiple_required_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] default = ["a", "b"] a = [] b = ["a"] c = [] [[bin]] name = "foo_1" path = "src/foo_1.rs" required-features = ["b", "c"] [[bin]] name = "foo_2" path = "src/foo_2.rs" required-features = ["a"] "#, ) .file("src/foo_1.rs", "fn main() {}") .file("src/foo_2.rs", "fn main() {}") .build(); p.cargo("build").run(); assert!(!p.bin("foo_1").is_file()); assert!(p.bin("foo_2").is_file()); p.cargo("build --features c").run(); assert!(p.bin("foo_1").is_file()); assert!(p.bin("foo_2").is_file()); p.cargo("build --no-default-features").run(); } #[cargo_test] fn build_example_default_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] default = ["a"] a = [] [[example]] name = "foo" required-features = ["a"] "#, ) .file("examples/foo.rs", "fn main() {}") .build(); p.cargo("build --example=foo").run(); assert!(p.bin("examples/foo").is_file()); p.cargo("build --example=foo --no-default-features") .with_status(101) .with_stderr_data(str![[r#" [ERROR] target `foo` in package `foo` requires the features: `a` Consider enabling them by passing, e.g., `--features="a"` "#]]) .run(); } #[cargo_test] fn build_example_arg_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] a = [] [[example]] name = "foo" required-features = ["a"] "#, ) .file("examples/foo.rs", "fn main() {}") .build(); p.cargo("build --example=foo --features a").run(); assert!(p.bin("examples/foo").is_file()); } #[cargo_test] fn build_example_multiple_required_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] default = ["a", "b"] a = [] b = ["a"] c = [] [[example]] name = "foo_1" required-features = ["b", "c"] [[example]] name = "foo_2" required-features = ["a"] "#, ) .file("examples/foo_1.rs", "fn main() {}") .file("examples/foo_2.rs", "fn main() {}") .build(); p.cargo("build --example=foo_1") .with_status(101) .with_stderr_data(str![[r#" [ERROR] target `foo_1` in package `foo` requires the features: `b`, `c` Consider enabling them by passing, e.g., `--features="b c"` "#]]) .run(); p.cargo("build --example=foo_2").run(); assert!(!p.bin("examples/foo_1").is_file()); assert!(p.bin("examples/foo_2").is_file()); p.cargo("build --example=foo_1 --features c").run(); p.cargo("build --example=foo_2 --features c").run(); assert!(p.bin("examples/foo_1").is_file()); assert!(p.bin("examples/foo_2").is_file()); p.cargo("build --example=foo_1 --no-default-features") .with_status(101) .with_stderr_data(str![[r#" [ERROR] target `foo_1` in package `foo` requires the features: `b`, `c` Consider enabling them by passing, e.g., `--features="b c"` "#]]) .run(); p.cargo("build --example=foo_2 --no-default-features") .with_status(101) .with_stderr_data(str![[r#" [ERROR] target `foo_2` in package `foo` requires the features: `a` Consider enabling them by passing, e.g., `--features="a"` "#]]) .run(); } #[cargo_test] fn test_default_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] default = ["a"] a = [] [[test]] name = "foo" required-features = ["a"] "#, ) .file("tests/foo.rs", "#[test]\nfn test() {}") .build(); p.cargo("test") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] tests/foo.rs (target/debug/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test test ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); p.cargo("test --no-default-features") .with_stderr_data(str![[r#" [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .with_stdout_data("") .run(); p.cargo("test --test=foo") .with_stderr_data(str![[r#" [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] tests/foo.rs (target/debug/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test test ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); p.cargo("test --test=foo --no-default-features") .with_status(101) .with_stderr_data(str![[r#" [ERROR] target `foo` in package `foo` requires the features: `a` Consider enabling them by passing, e.g., `--features="a"` "#]]) .run(); } #[cargo_test] fn test_arg_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] a = [] [[test]] name = "foo" required-features = ["a"] "#, ) .file("tests/foo.rs", "#[test]\nfn test() {}") .build(); p.cargo("test --features a") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] tests/foo.rs (target/debug/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test test ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn test_multiple_required_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] default = ["a", "b"] a = [] b = ["a"] c = [] [[test]] name = "foo_1" required-features = ["b", "c"] [[test]] name = "foo_2" required-features = ["a"] "#, ) .file("tests/foo_1.rs", "#[test]\nfn test() {}") .file("tests/foo_2.rs", "#[test]\nfn test() {}") .build(); p.cargo("test") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] tests/foo_2.rs (target/debug/deps/foo_2-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test test ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); p.cargo("test --features c") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] tests/foo_1.rs (target/debug/deps/foo_1-[HASH][EXE]) [RUNNING] tests/foo_2.rs (target/debug/deps/foo_2-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test test ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test test ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); p.cargo("test --no-default-features") .with_stderr_data(str![[r#" [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .with_stdout_data("") .run(); } #[cargo_test(nightly, reason = "bench")] fn bench_default_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] default = ["a"] a = [] [[bench]] name = "foo" required-features = ["a"] "#, ) .file( "benches/foo.rs", r#" #![feature(test)] extern crate test; #[bench] fn bench(_: &mut test::Bencher) { } "#, ) .build(); p.cargo("bench") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] benches/foo.rs (target/release/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test bench ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); p.cargo("bench --no-default-features") .with_stderr_data(str![[r#" [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s "#]]) .with_stdout_data("") .run(); p.cargo("bench --bench=foo") .with_stderr_data(str![[r#" [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] benches/foo.rs (target/release/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test bench ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); p.cargo("bench --bench=foo --no-default-features") .with_status(101) .with_stderr_data(str![[r#" [ERROR] target `foo` in package `foo` requires the features: `a` Consider enabling them by passing, e.g., `--features="a"` "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn bench_arg_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] a = [] [[bench]] name = "foo" required-features = ["a"] "#, ) .file( "benches/foo.rs", r#" #![feature(test)] extern crate test; #[bench] fn bench(_: &mut test::Bencher) { } "#, ) .build(); p.cargo("bench --features a") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] benches/foo.rs (target/release/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test bench ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "bench")] fn bench_multiple_required_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] default = ["a", "b"] a = [] b = ["a"] c = [] [[bench]] name = "foo_1" required-features = ["b", "c"] [[bench]] name = "foo_2" required-features = ["a"] "#, ) .file( "benches/foo_1.rs", r#" #![feature(test)] extern crate test; #[bench] fn bench(_: &mut test::Bencher) { } "#, ) .file( "benches/foo_2.rs", r#" #![feature(test)] extern crate test; #[bench] fn bench(_: &mut test::Bencher) { } "#, ) .build(); p.cargo("bench") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] benches/foo_2.rs (target/release/deps/foo_2-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test bench ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); p.cargo("bench --features c") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] benches/foo_1.rs (target/release/deps/foo_1-[HASH][EXE]) [RUNNING] benches/foo_2.rs (target/release/deps/foo_2-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test bench ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test bench ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); p.cargo("bench --no-default-features") .with_stderr_data(str![[r#" [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s "#]]) .with_stdout_data("") .run(); } #[cargo_test] fn install_default_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] default = ["a"] a = [] [[bin]] name = "foo" required-features = ["a"] [[example]] name = "foo" required-features = ["a"] "#, ) .file("src/main.rs", "fn main() {}") .file("examples/foo.rs", "fn main() {}") .build(); p.cargo("install --path .").run(); assert_has_installed_exe(paths::cargo_home(), "foo"); p.cargo("uninstall foo").run(); p.cargo("install --path . --no-default-features") .with_stderr_data(str![[r#" [INSTALLING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [WARNING] none of the package's binaries are available for install using the selected features bin "foo" requires the features: `a` example "foo" requires the features: `a` Consider enabling some of the needed features by passing, e.g., `--features="a"` "#]]) .run(); assert_has_not_installed_exe(paths::cargo_home(), "foo"); p.cargo("install --path . --bin=foo").run(); assert_has_installed_exe(paths::cargo_home(), "foo"); p.cargo("uninstall foo").run(); p.cargo("install --path . --bin=foo --no-default-features") .with_status(101) .with_stderr_data(str![[r#" [INSTALLING] foo v0.0.1 ([ROOT]/foo) [ERROR] failed to compile `foo v0.0.1 ([ROOT]/foo)`, intermediate artifacts can be found at `[ROOT]/foo/target`. To reuse those artifacts with a future compilation, set the environment variable `CARGO_TARGET_DIR` to that path. Caused by: target `foo` in package `foo` requires the features: `a` Consider enabling them by passing, e.g., `--features="a"` "#]]) .run(); assert_has_not_installed_exe(paths::cargo_home(), "foo"); p.cargo("install --path . --example=foo").run(); assert_has_installed_exe(paths::cargo_home(), "foo"); p.cargo("uninstall foo").run(); p.cargo("install --path . --example=foo --no-default-features") .with_status(101) .with_stderr_data(str![[r#" [INSTALLING] foo v0.0.1 ([ROOT]/foo) [ERROR] failed to compile `foo v0.0.1 ([ROOT]/foo)`, intermediate artifacts can be found at `[ROOT]/foo/target`. To reuse those artifacts with a future compilation, set the environment variable `CARGO_TARGET_DIR` to that path. Caused by: target `foo` in package `foo` requires the features: `a` Consider enabling them by passing, e.g., `--features="a"` "#]]) .run(); assert_has_not_installed_exe(paths::cargo_home(), "foo"); } #[cargo_test] fn install_arg_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] a = [] [[bin]] name = "foo" required-features = ["a"] "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("install --features a").run(); assert_has_installed_exe(paths::cargo_home(), "foo"); p.cargo("uninstall foo").run(); } #[cargo_test] fn install_multiple_required_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] default = ["a", "b"] a = [] b = ["a"] c = [] [[bin]] name = "foo_1" path = "src/foo_1.rs" required-features = ["b", "c"] [[bin]] name = "foo_2" path = "src/foo_2.rs" required-features = ["a"] [[example]] name = "foo_3" path = "src/foo_3.rs" required-features = ["b", "c"] [[example]] name = "foo_4" path = "src/foo_4.rs" required-features = ["a"] "#, ) .file("src/foo_1.rs", "fn main() {}") .file("src/foo_2.rs", "fn main() {}") .file("src/foo_3.rs", "fn main() {}") .file("src/foo_4.rs", "fn main() {}") .build(); p.cargo("install --path .").run(); assert_has_not_installed_exe(paths::cargo_home(), "foo_1"); assert_has_installed_exe(paths::cargo_home(), "foo_2"); assert_has_not_installed_exe(paths::cargo_home(), "foo_3"); assert_has_not_installed_exe(paths::cargo_home(), "foo_4"); p.cargo("uninstall foo").run(); p.cargo("install --path . --bins --examples").run(); assert_has_not_installed_exe(paths::cargo_home(), "foo_1"); assert_has_installed_exe(paths::cargo_home(), "foo_2"); assert_has_not_installed_exe(paths::cargo_home(), "foo_3"); assert_has_installed_exe(paths::cargo_home(), "foo_4"); p.cargo("uninstall foo").run(); p.cargo("install --path . --features c").run(); assert_has_installed_exe(paths::cargo_home(), "foo_1"); assert_has_installed_exe(paths::cargo_home(), "foo_2"); assert_has_not_installed_exe(paths::cargo_home(), "foo_3"); assert_has_not_installed_exe(paths::cargo_home(), "foo_4"); p.cargo("uninstall foo").run(); p.cargo("install --path . --features c --bins --examples") .run(); assert_has_installed_exe(paths::cargo_home(), "foo_1"); assert_has_installed_exe(paths::cargo_home(), "foo_2"); assert_has_installed_exe(paths::cargo_home(), "foo_3"); assert_has_installed_exe(paths::cargo_home(), "foo_4"); p.cargo("uninstall foo").run(); p.cargo("install --path . --no-default-features") .with_stderr_data(str![[r#" [INSTALLING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [WARNING] none of the package's binaries are available for install using the selected features bin "foo_1" requires the features: `b`, `c` bin "foo_2" requires the features: `a` example "foo_3" requires the features: `b`, `c` example "foo_4" requires the features: `a` Consider enabling some of the needed features by passing, e.g., `--features="b c"` "#]]) .run(); p.cargo("install --path . --no-default-features --bins") .with_stderr_data(str![[r#" [INSTALLING] foo v0.0.1 ([ROOT]/foo) [WARNING] target filter `bins` specified, but no targets matched; this is a no-op [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [WARNING] none of the package's binaries are available for install using the selected features bin "foo_1" requires the features: `b`, `c` bin "foo_2" requires the features: `a` example "foo_3" requires the features: `b`, `c` example "foo_4" requires the features: `a` Consider enabling some of the needed features by passing, e.g., `--features="b c"` "#]]) .run(); p.cargo("install --path . --no-default-features --examples") .with_stderr_data(str![[r#" [INSTALLING] foo v0.0.1 ([ROOT]/foo) [WARNING] target filter `examples` specified, but no targets matched; this is a no-op [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [WARNING] none of the package's binaries are available for install using the selected features bin "foo_1" requires the features: `b`, `c` bin "foo_2" requires the features: `a` example "foo_3" requires the features: `b`, `c` example "foo_4" requires the features: `a` Consider enabling some of the needed features by passing, e.g., `--features="b c"` "#]]) .run(); p.cargo("install --path . --no-default-features --bins --examples") .with_stderr_data(str![[r#" [INSTALLING] foo v0.0.1 ([ROOT]/foo) [WARNING] target filters `bins`, `examples` specified, but no targets matched; this is a no-op [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [WARNING] none of the package's binaries are available for install using the selected features bin "foo_1" requires the features: `b`, `c` bin "foo_2" requires the features: `a` example "foo_3" requires the features: `b`, `c` example "foo_4" requires the features: `a` Consider enabling some of the needed features by passing, e.g., `--features="b c"` "#]]) .run(); assert_has_not_installed_exe(paths::cargo_home(), "foo_1"); assert_has_not_installed_exe(paths::cargo_home(), "foo_2"); assert_has_not_installed_exe(paths::cargo_home(), "foo_3"); assert_has_not_installed_exe(paths::cargo_home(), "foo_4"); } #[cargo_test] fn dep_feature_in_toml() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = { path = "bar", features = ["a"] } [[bin]] name = "foo" required-features = ["bar/a"] [[example]] name = "foo" required-features = ["bar/a"] [[test]] name = "foo" required-features = ["bar/a"] [[bench]] name = "foo" required-features = ["bar/a"] "#, ) .file("src/main.rs", "fn main() {}") .file("examples/foo.rs", "fn main() {}") .file("tests/foo.rs", "#[test]\nfn test() {}") .file( "benches/foo.rs", r#" #![feature(test)] extern crate test; #[bench] fn bench(_: &mut test::Bencher) { } "#, ) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [features] a = [] "#, ) .file("bar/src/lib.rs", "") .build(); p.cargo("build").run(); // bin p.cargo("build --bin=foo").run(); assert!(p.bin("foo").is_file()); // example p.cargo("build --example=foo").run(); assert!(p.bin("examples/foo").is_file()); // test p.cargo("test --test=foo") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] tests/foo.rs (target/debug/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test test ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); // bench if is_nightly() { p.cargo("bench --bench=foo") .with_stderr_data(str![[r#" [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] benches/foo.rs (target/release/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test bench ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } // install p.cargo("install").run(); assert_has_installed_exe(paths::cargo_home(), "foo"); p.cargo("uninstall foo").run(); } #[cargo_test] fn dep_feature_in_cmd_line() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = { path = "bar" } [[bin]] name = "foo" required-features = ["bar/a"] [[example]] name = "foo" required-features = ["bar/a"] [[test]] name = "foo" required-features = ["bar/a"] [[bench]] name = "foo" required-features = ["bar/a"] "#, ) .file("src/main.rs", "fn main() {}") .file("examples/foo.rs", "fn main() {}") .file( "tests/foo.rs", r#" #[test] fn bin_is_built() { let s = format!("target/debug/foo{}", std::env::consts::EXE_SUFFIX); let p = std::path::Path::new(&s); assert!(p.exists(), "foo does not exist"); } "#, ) .file( "benches/foo.rs", r#" #![feature(test)] extern crate test; #[bench] fn bench(_: &mut test::Bencher) { } "#, ) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [features] a = [] "#, ) .file("bar/src/lib.rs", "") .build(); // This is a no-op p.cargo("build") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert!(!p.bin("foo").is_file()); // bin p.cargo("build --bin=foo") .with_status(101) .with_stderr_data(str![[r#" [ERROR] target `foo` in package `foo` requires the features: `bar/a` Consider enabling them by passing, e.g., `--features="bar/a"` "#]]) .run(); p.cargo("build --bin=foo --features bar/a").run(); assert!(p.bin("foo").is_file()); // example p.cargo("build --example=foo") .with_status(101) .with_stderr_data(str![[r#" [ERROR] target `foo` in package `foo` requires the features: `bar/a` Consider enabling them by passing, e.g., `--features="bar/a"` "#]]) .run(); p.cargo("build --example=foo --features bar/a").run(); assert!(p.bin("examples/foo").is_file()); // test // This is a no-op, since no tests are enabled p.cargo("test") .with_stderr_data(str![[r#" [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .with_stdout_data("") .run(); // Delete the target directory so this can check if the main.rs gets built. p.build_dir().rm_rf(); p.cargo("test --test=foo --features bar/a") .with_stderr_data(str![[r#" [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] tests/foo.rs (target/debug/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test bin_is_built ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); // bench if is_nightly() { p.cargo("bench") .with_stderr_data(str![[r#" [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s "#]]) .with_stdout_data("") .run(); p.cargo("bench --bench=foo --features bar/a") .with_stderr_data(str![[r#" [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] benches/foo.rs (target/release/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test bench ... bench: [AVG_ELAPSED] ns/iter (+/- [JITTER]) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } // install p.cargo("install --path .") .with_stderr_data(str![[r#" [INSTALLING] foo v0.0.1 ([ROOT]/foo) [LOCKING] 1 package to latest compatible version [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [WARNING] none of the package's binaries are available for install using the selected features bin "foo" requires the features: `bar/a` example "foo" requires the features: `bar/a` Consider enabling some of the needed features by passing, e.g., `--features="bar/a"` "#]]) .run(); assert_has_not_installed_exe(paths::cargo_home(), "foo"); p.cargo("install --features bar/a").run(); assert_has_installed_exe(paths::cargo_home(), "foo"); p.cargo("uninstall foo").run(); } #[cargo_test] fn test_skips_compiling_bin_with_missing_required_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] a = [] [[bin]] name = "bin_foo" path = "src/bin/foo.rs" required-features = ["a"] "#, ) .file("src/bin/foo.rs", "extern crate bar; fn main() {}") .file("tests/foo.rs", "") .file("benches/foo.rs", "") .build(); p.cargo("test") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] tests/foo.rs (target/debug/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); p.cargo("test --features a -j 1") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) error[E0463]: can't find crate for `bar` ... "#]]) .run(); if is_nightly() { p.cargo("bench") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] benches/foo.rs (target/release/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); p.cargo("bench --features a -j 1") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) error[E0463]: can't find crate for `bar` ... "#]]) .run(); } } #[cargo_test] fn run_default() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] default = [] a = [] [[bin]] name = "foo" required-features = ["a"] "#, ) .file("src/lib.rs", "") .file("src/main.rs", "extern crate foo; fn main() {}") .build(); p.cargo("run") .with_status(101) .with_stderr_data(str![[r#" [ERROR] target `foo` in package `foo` requires the features: `a` Consider enabling them by passing, e.g., `--features="a"` "#]]) .run(); p.cargo("run --features a").run(); } #[cargo_test] fn run_default_multiple_required_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] default = ["a"] a = [] b = [] [[bin]] name = "foo1" path = "src/foo1.rs" required-features = ["a"] [[bin]] name = "foo3" path = "src/foo3.rs" required-features = ["b"] [[bin]] name = "foo2" path = "src/foo2.rs" required-features = ["b"] "#, ) .file("src/lib.rs", "") .file("src/foo1.rs", "extern crate foo; fn main() {}") .file("src/foo3.rs", "extern crate foo; fn main() {}") .file("src/foo2.rs", "extern crate foo; fn main() {}") .build(); p.cargo("run") .with_status(101) .with_stderr_data(str![[r#" [ERROR] `cargo run` could not determine which binary to run. Use the `--bin` option to specify a binary, or the `default-run` manifest key. available binaries: foo1, foo2, foo3 "#]]) .run(); } #[cargo_test] fn renamed_required_features() { // Test that required-features uses renamed package feature names. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [[bin]] name = "x" required-features = ["a1/f1"] [dependencies] a1 = {path="a1", package="a"} a2 = {path="a2", package="a"} "#, ) .file( "src/bin/x.rs", r#" fn main() { a1::f(); a2::f(); } "#, ) .file( "a1/Cargo.toml", r#" [package] name = "a" version = "0.1.0" [features] f1 = [] "#, ) .file( "a1/src/lib.rs", r#" pub fn f() { if cfg!(feature="f1") { println!("a1 f1"); } } "#, ) .file( "a2/Cargo.toml", r#" [package] name = "a" version = "0.2.0" [features] f2 = [] "#, ) .file( "a2/src/lib.rs", r#" pub fn f() { if cfg!(feature="f2") { println!("a2 f2"); } } "#, ) .build(); p.cargo("run") .with_status(101) .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [ERROR] target `x` in package `foo` requires the features: `a1/f1` Consider enabling them by passing, e.g., `--features="a1/f1"` "#]]) .run(); p.cargo("build --features a1/f1").run(); p.rename_run("x", "x_with_f1") .with_stdout_data(str![[r#" a1 f1 "#]]) .run(); p.cargo("build --features a1/f1,a2/f2").run(); p.rename_run("x", "x_with_f1_f2") .with_stdout_data(str![[r#" a1 f1 a2 f2 "#]]) .run(); } #[cargo_test] fn truncated_install_warning_message() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2021" [features] feature1 = [] feature2 = [] feature3 = [] feature4 = [] feature5 = [] [[bin]] name = "foo1" required-features = ["feature1", "feature2", "feature3"] [[bin]] name = "foo2" required-features = ["feature2"] [[bin]] name = "foo3" required-features = ["feature3"] [[bin]] name = "foo4" required-features = ["feature4", "feature1"] [[bin]] name = "foo5" required-features = ["feature1", "feature2", "feature3", "feature4", "feature5"] [[bin]] name = "foo6" required-features = ["feature1", "feature2", "feature3", "feature4", "feature5"] [[bin]] name = "foo7" required-features = ["feature1", "feature2", "feature3", "feature4", "feature5"] [[bin]] name = "foo8" required-features = ["feature1", "feature2", "feature3", "feature4", "feature5"] [[bin]] name = "foo9" required-features = ["feature1", "feature2", "feature3", "feature4", "feature5"] [[bin]] name = "foo10" required-features = ["feature1", "feature2", "feature3", "feature4", "feature5"] [[example]] name = "example1" required-features = ["feature1", "feature2"] "#, ) .file("src/bin/foo1.rs", "fn main() {}") .file("src/bin/foo2.rs", "fn main() {}") .file("src/bin/foo3.rs", "fn main() {}") .file("src/bin/foo4.rs", "fn main() {}") .file("src/bin/foo5.rs", "fn main() {}") .file("src/bin/foo6.rs", "fn main() {}") .file("src/bin/foo7.rs", "fn main() {}") .file("src/bin/foo8.rs", "fn main() {}") .file("src/bin/foo9.rs", "fn main() {}") .file("src/bin/foo10.rs", "fn main() {}") .file("examples/example1.rs", "fn main() {}") .build(); p.cargo("install --path .").with_stderr_data(str![[r#" [INSTALLING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [WARNING] none of the package's binaries are available for install using the selected features bin "foo1" requires the features: `feature1`, `feature2`, `feature3` bin "foo10" requires the features: `feature1`, `feature2`, `feature3`, `feature4`, `feature5` bin "foo2" requires the features: `feature2` bin "foo3" requires the features: `feature3` bin "foo4" requires the features: `feature4`, `feature1` bin "foo5" requires the features: `feature1`, `feature2`, `feature3`, `feature4`, `feature5` bin "foo6" requires the features: `feature1`, `feature2`, `feature3`, `feature4`, `feature5` 4 more targets also requires features not enabled. See them in the Cargo.toml file. Consider enabling some of the needed features by passing, e.g., `--features="feature1 feature2 feature3"` "#]]).run(); } cargo-0.91.0/tests/testsuite/run.rs000064400000000000000000001405441046102023000153630ustar 00000000000000//! Tests for the `cargo run` command. use crate::prelude::*; use cargo_test_support::{ Project, basic_bin_manifest, basic_lib_manifest, basic_manifest, project, str, }; use cargo_util::paths::dylib_path_envvar; #[cargo_test] fn simple() { let p = project() .file("src/main.rs", r#"fn main() { println!("hello"); }"#) .build(); p.cargo("run") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .with_stdout_data(str![[r#" hello "#]]) .run(); assert!(p.bin("foo").is_file()); } #[cargo_test] fn quiet_arg() { let p = project() .file("src/main.rs", r#"fn main() { println!("hello"); }"#) .build(); p.cargo("run -q") .with_stderr_data("") .with_stdout_data(str![[r#" hello "#]]) .run(); p.cargo("run --quiet") .with_stderr_data("") .with_stdout_data(str![[r#" hello "#]]) .run(); } #[cargo_test] fn unsupported_silent_arg() { let p = project() .file("src/main.rs", r#"fn main() { println!("hello"); }"#) .build(); p.cargo("run -s") .with_stderr_data(str![[r#" [ERROR] unexpected argument '--silent' found tip: a similar argument exists: '--quiet' Usage: cargo[EXE] run [OPTIONS] [ARGS]... For more information, try '--help'. "#]]) .with_status(1) .run(); p.cargo("run --silent") .with_stderr_data(str![[r#" [ERROR] unexpected argument '--silent' found tip: a similar argument exists: '--quiet' Usage: cargo[EXE] run [OPTIONS] [ARGS]... For more information, try '--help'. "#]]) .with_status(1) .run(); } #[cargo_test] fn quiet_arg_and_verbose_arg() { let p = project() .file("src/main.rs", r#"fn main() { println!("hello"); }"#) .build(); p.cargo("run -q -v") .with_status(101) .with_stderr_data(str![[r#" [ERROR] cannot set both --verbose and --quiet "#]]) .run(); } #[cargo_test] fn quiet_arg_and_verbose_config() { let p = project() .file( ".cargo/config.toml", r#" [term] verbose = true "#, ) .file("src/main.rs", r#"fn main() { println!("hello"); }"#) .build(); p.cargo("run -q") .with_stderr_data("") .with_stdout_data(str![[r#" hello "#]]) .run(); } #[cargo_test] fn verbose_arg_and_quiet_config() { let p = project() .file( ".cargo/config.toml", r#" [term] quiet = true "#, ) .file("src/main.rs", r#"fn main() { println!("hello"); }"#) .build(); p.cargo("run -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .with_stdout_data(str![[r#" hello "#]]) .run(); } #[cargo_test] fn quiet_config_alone() { let p = project() .file( ".cargo/config.toml", r#" [term] quiet = true "#, ) .file("src/main.rs", r#"fn main() { println!("hello"); }"#) .build(); p.cargo("run") .with_stderr_data("") .with_stdout_data(str![[r#" hello "#]]) .run(); } #[cargo_test] fn verbose_config_alone() { let p = project() .file( ".cargo/config.toml", r#" [term] verbose = true "#, ) .file("src/main.rs", r#"fn main() { println!("hello"); }"#) .build(); p.cargo("run") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .with_stdout_data(str![[r#" hello "#]]) .run(); } #[cargo_test] fn quiet_config_and_verbose_config() { let p = project() .file( ".cargo/config.toml", r#" [term] verbose = true quiet = true "#, ) .file("src/main.rs", r#"fn main() { println!("hello"); }"#) .build(); p.cargo("run") .with_status(101) .with_stderr_data(str![[r#" [ERROR] cannot set both `term.verbose` and `term.quiet` "#]]) .run(); } #[cargo_test] fn simple_with_args() { let p = project() .file( "src/main.rs", r#" fn main() { assert_eq!(std::env::args().nth(1).unwrap(), "hello"); assert_eq!(std::env::args().nth(2).unwrap(), "world"); } "#, ) .build(); p.cargo("run hello world").run(); } #[cfg(unix)] #[cargo_test] fn simple_with_non_utf8_args() { use std::os::unix::ffi::OsStrExt; let p = project() .file( "src/main.rs", r#" use std::ffi::OsStr; use std::os::unix::ffi::OsStrExt; fn main() { assert_eq!(std::env::args_os().nth(1).unwrap(), OsStr::from_bytes(b"hello")); assert_eq!(std::env::args_os().nth(2).unwrap(), OsStr::from_bytes(b"ab\xffcd")); } "#, ) .build(); p.cargo("run") .arg("hello") .arg(std::ffi::OsStr::from_bytes(b"ab\xFFcd")) .run(); } #[cargo_test] fn exit_code() { let p = project() .file("src/main.rs", "fn main() { std::process::exit(2); }") .build(); let expected = if !cfg!(unix) { str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` [ERROR] process didn't exit successfully: `target/debug/foo[EXE]` ([EXIT_STATUS]: 2) "#]] } else { str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo` "#]] }; p.cargo("run") .with_status(2) .with_stderr_data(expected) .run(); } #[cargo_test] fn exit_code_verbose() { let p = project() .file("src/main.rs", "fn main() { std::process::exit(2); }") .build(); let expected = if !cfg!(unix) { str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` [ERROR] process didn't exit successfully: `target/debug/foo[EXE]` ([EXIT_STATUS]: 2) "#]] } else { str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]] }; p.cargo("run -v") .with_status(2) .with_stderr_data(expected) .run(); } #[cargo_test] fn no_main_file() { let p = project().file("src/lib.rs", "").build(); p.cargo("run") .with_status(101) .with_stderr_data(str![[r#" [ERROR] a bin target must be available for `cargo run` "#]]) .run(); } #[cargo_test] fn too_many_bins() { let p = project() .file("src/lib.rs", "") .file("src/bin/a.rs", "") .file("src/bin/b.rs", "") .build(); // Using [..] here because the order is not stable p.cargo("run") .with_status(101) .with_stderr_data(str![[r#" [ERROR] `cargo run` could not determine which binary to run. Use the `--bin` option to specify a binary, or the `default-run` manifest key. available binaries: a, b "#]]) .run(); } #[cargo_test] fn specify_name() { let p = project() .file("src/lib.rs", "") .file( "src/bin/a.rs", r#" #[allow(unused_extern_crates)] extern crate foo; fn main() { println!("hello a.rs"); } "#, ) .file( "src/bin/b.rs", r#" #[allow(unused_extern_crates)] extern crate foo; fn main() { println!("hello b.rs"); } "#, ) .build(); p.cargo("run --bin a -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..] src/lib.rs [..]` [RUNNING] `rustc [..] src/bin/a.rs [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/a[EXE]` "#]]) .with_stdout_data(str![[r#" hello a.rs "#]]) .run(); p.cargo("run --bin b -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..] src/bin/b.rs [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/b[EXE]` "#]]) .with_stdout_data(str![[r#" hello b.rs "#]]) .run(); } #[cargo_test] fn specify_default_run() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] default-run = "a" "#, ) .file("src/lib.rs", "") .file("src/bin/a.rs", r#"fn main() { println!("hello A"); }"#) .file("src/bin/b.rs", r#"fn main() { println!("hello B"); }"#) .build(); p.cargo("run") .with_stdout_data(str![[r#" hello A "#]]) .run(); p.cargo("run --bin a") .with_stdout_data(str![[r#" hello A "#]]) .run(); p.cargo("run --bin b") .with_stdout_data(str![[r#" hello B "#]]) .run(); } #[cargo_test] fn bogus_default_run() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] default-run = "b" "#, ) .file("src/lib.rs", "") .file("src/bin/a.rs", r#"fn main() { println!("hello A"); }"#) .build(); p.cargo("run") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: default-run target `b` not found [HELP] a target with a similar name exists: `a` "#]]) .run(); } #[cargo_test] fn run_example() { let p = project() .file("src/lib.rs", "") .file("examples/a.rs", r#"fn main() { println!("example"); }"#) .file("src/bin/a.rs", r#"fn main() { println!("bin"); }"#) .build(); p.cargo("run --example a") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/examples/a[EXE]` "#]]) .with_stdout_data(str![[r#" example "#]]) .run(); } #[cargo_test] fn run_library_example() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [[example]] name = "bar" crate-type = ["lib"] "#, ) .file("src/lib.rs", "") .file("examples/bar.rs", "fn foo() {}") .build(); p.cargo("run --example bar") .with_status(101) .with_stderr_data(str![[r#" [ERROR] example target `bar` is a library and cannot be executed "#]]) .run(); } #[cargo_test] fn run_bin_example() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [[example]] name = "bar" crate-type = ["bin"] "#, ) .file("src/lib.rs", "") .file("examples/bar.rs", r#"fn main() { println!("example"); }"#) .build(); p.cargo("run --example bar") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/examples/bar[EXE]` "#]]) .with_stdout_data(str![[r#" example "#]]) .run(); } fn autodiscover_examples_project(rust_edition: &str, autoexamples: Option) -> Project { let autoexamples = match autoexamples { None => "".to_string(), Some(bool) => format!("autoexamples = {}", bool), }; project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" authors = [] edition = "{rust_edition}" {autoexamples} [features] magic = [] [[example]] name = "do_magic" required-features = ["magic"] "#, rust_edition = rust_edition, autoexamples = autoexamples ), ) .file("examples/a.rs", r#"fn main() { println!("example"); }"#) .file( "examples/do_magic.rs", r#" fn main() { println!("magic example"); } "#, ) .build() } #[cargo_test] fn run_example_autodiscover_2015() { let p = autodiscover_examples_project("2015", None); p.cargo("run --example a") .with_status(101) .with_stderr_data(str![[r#" [WARNING] An explicit [[example]] section is specified in Cargo.toml which currently disables Cargo from automatically inferring other example targets. This inference behavior will change in the Rust 2018 edition and the following files will be included as a example target: * examples/a.rs This is likely to break cargo build or cargo test as these files may not be ready to be compiled as a example target today. You can future-proof yourself and disable this warning by adding `autoexamples = false` to your [package] section. You may also move the files to a location where Cargo would not automatically infer them to be a target, such as in subfolders. For more information on this warning you can consult https://github.com/rust-lang/cargo/issues/5330 [ERROR] no example target named `a` in default-run packages [HELP] available example targets: do_magic "#]]) .run(); } #[cargo_test] fn run_example_autodiscover_2015_with_autoexamples_enabled() { let p = autodiscover_examples_project("2015", Some(true)); p.cargo("run --example a") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/examples/a[EXE]` "#]]) .with_stdout_data(str![[r#" example "#]]) .run(); } #[cargo_test] fn run_example_autodiscover_2015_with_autoexamples_disabled() { let p = autodiscover_examples_project("2015", Some(false)); p.cargo("run --example a") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no example target named `a` in default-run packages [HELP] available example targets: do_magic "#]]) .run(); } #[cargo_test] fn run_example_autodiscover_2018() { let p = autodiscover_examples_project("2018", None); p.cargo("run --example a") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/examples/a[EXE]` "#]]) .with_stdout_data(str![[r#" example "#]]) .run(); } #[cargo_test] fn autobins_disables() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" autobins = false "#, ) .file("src/lib.rs", "pub mod bin;") .file("src/bin/mod.rs", "// empty") .build(); p.cargo("run") .with_status(101) .with_stderr_data(str![[r#" [ERROR] a bin target must be available for `cargo run` "#]]) .run(); } #[cargo_test] fn run_bins() { let p = project() .file("src/lib.rs", "") .file("examples/a.rs", r#"fn main() { println!("example"); }"#) .file("src/bin/a.rs", r#"fn main() { println!("bin"); }"#) .build(); p.cargo("run --bins") .with_status(1) .with_stderr_data(str![[r#" [ERROR] unexpected argument '--bins' found tip: a similar argument exists: '--bin' ... "#]]) .run(); } #[cargo_test] fn run_with_filename() { let p = project() .file("src/lib.rs", "") .file( "src/bin/a.rs", r#" extern crate foo; fn main() { println!("hello a.rs"); } "#, ) .file("examples/a.rs", r#"fn main() { println!("example"); }"#) .build(); p.cargo("run --bin bin.rs") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no bin target named `bin.rs` in default-run packages [HELP] available bin targets: a "#]]) .run(); p.cargo("run --bin a.rs") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no bin target named `a.rs` in default-run packages [HELP] a target with a similar name exists: `a` "#]]) .run(); p.cargo("run --example example.rs") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no example target named `example.rs` in default-run packages [HELP] available example targets: a "#]]) .run(); p.cargo("run --example a.rs") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no example target named `a.rs` in default-run packages [HELP] a target with a similar name exists: `a` "#]]) .run(); } #[cargo_test] fn ambiguous_bin_name() { let p = project() .file( "Cargo.toml", r#" [workspace] resolver = "3" members = ["crate1", "crate2", "crate3", "crate4"] "#, ) .file("crate1/src/bin/ambiguous.rs", "fn main(){}") .file( "crate1/Cargo.toml", r#" [package] name = "crate1" version = "0.1.0" edition = "2024" "#, ) .file("crate2/src/bin/ambiguous.rs", "fn main(){}") .file( "crate2/Cargo.toml", r#" [package] name = "crate2" version = "0.1.0" edition = "2024" "#, ) .file("crate3/src/bin/ambiguous.rs", "fn main(){}") .file( "crate3/Cargo.toml", r#" [package] name = "crate3" version = "0.1.0" edition = "2024" "#, ) .file("crate4/src/bin/ambiguous.rs", "fn main(){}") .file( "crate4/Cargo.toml", r#" [package] name = "crate4" version = "0.1.0" edition = "2024" "#, ); let p = p.build(); p.cargo("run --bin ambiguous") .with_status(101) .with_stderr_data(str![[r#" [ERROR] `cargo run` can run at most one executable, but multiple were specified [HELP] available targets: bin `ambiguous` in package `crate1` bin `ambiguous` in package `crate2` bin `ambiguous` in package `crate3` bin `ambiguous` in package `crate4` "#]]) .run(); p.cargo("run --bin crate1/ambiguous") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no bin target named `crate1/ambiguous` in default-run packages [HELP] available bin targets: ambiguous in package crate1 ambiguous in package crate2 ambiguous in package crate3 ambiguous in package crate4 "#]]) .run(); } // See rust-lang/cargo#14544 #[cargo_test] fn print_available_targets_within_virtual_workspace() { let p = project() .file( "Cargo.toml", r#" [workspace] resolver = "3" members = ["crate1", "crate2", "pattern1", "pattern2"] default-members = ["crate1"] "#, ) .file("crate1/src/main.rs", "fn main(){}") .file( "crate1/Cargo.toml", r#" [package] name = "crate1" version = "0.1.0" edition = "2024" "#, ) .file("crate2/src/main.rs", "fn main(){}") .file( "crate2/Cargo.toml", r#" [package] name = "crate2" version = "0.1.0" edition = "2024" "#, ) .file("pattern1/src/main.rs", "fn main(){}") .file( "pattern1/Cargo.toml", r#" [package] name = "pattern1" version = "0.1.0" edition = "2024" "#, ) .file("pattern2/src/main.rs", "fn main(){}") .file( "pattern2/Cargo.toml", r#" [package] name = "pattern2" version = "0.1.0" edition = "2024" "#, ) .file("another/src/main.rs", "fn main(){}") .file( "another/Cargo.toml", r#" [package] name = "another" version = "0.1.0" edition = "2024" "#, ); let p = p.build(); p.cargo("run --bin") .with_status(101) .with_stderr_data(str![[r#" [ERROR] "--bin" takes one argument. Available binaries: crate1 "#]]) .run(); p.cargo("run -p crate1 --bin crate2") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no bin target named `crate2` in `crate1` package [HELP] a target with a similar name exists: `crate1` [HELP] available bin in `crate2` package: crate2 "#]]) .run(); p.cargo("check -p crate1 -p pattern1 -p pattern2 --bin crate2") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no bin target named `crate2` in `crate1`, ... packages [HELP] a target with a similar name exists: `crate1` [HELP] available bin in `crate2` package: crate2 "#]]) .run(); p.cargo("run --bin crate2") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no bin target named `crate2` in default-run packages [HELP] a target with a similar name exists: `crate1` [HELP] available bin in `crate2` package: crate2 "#]]) .run(); p.cargo("check --bin pattern*") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no bin target matches pattern `pattern*` in default-run packages [HELP] available bin in `pattern1` package: pattern1 [HELP] available bin in `pattern2` package: pattern2 "#]]) .run(); // This another branch that none of similar name exists, and print available targets in the // default-members. p.change_file( "Cargo.toml", r#" [workspace] resolver = "3" members = ["crate1", "crate2", "another"] default-members = ["another"] "#, ); p.cargo("run --bin crate2") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no bin target named `crate2` in default-run packages [HELP] available bin in `crate2` package: crate2 "#]]) .run(); } #[cargo_test] fn either_name_or_example() { let p = project() .file("src/bin/a.rs", r#"fn main() { println!("hello a.rs"); }"#) .file("examples/b.rs", r#"fn main() { println!("hello b.rs"); }"#) .build(); p.cargo("run --bin a --example b") .with_status(101) .with_stderr_data(str![[r#" [ERROR] `cargo run` can run at most one executable, but multiple were specified [HELP] available targets: bin `a` in package `foo` example `b` in package `foo` "#]]) .run(); } #[cargo_test] fn one_bin_multiple_examples() { let p = project() .file("src/lib.rs", "") .file( "src/bin/main.rs", r#"fn main() { println!("hello main.rs"); }"#, ) .file("examples/a.rs", r#"fn main() { println!("hello a.rs"); }"#) .file("examples/b.rs", r#"fn main() { println!("hello b.rs"); }"#) .build(); p.cargo("run") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/main[EXE]` "#]]) .with_stdout_data(str![[r#" hello main.rs "#]]) .run(); } #[cargo_test] fn example_with_release_flag() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] version = "*" path = "bar" "#, ) .file( "examples/a.rs", r#" extern crate bar; fn main() { if cfg!(debug_assertions) { println!("slow1") } else { println!("fast1") } bar::baz(); } "#, ) .file("bar/Cargo.toml", &basic_lib_manifest("bar")) .file( "bar/src/bar.rs", r#" pub fn baz() { if cfg!(debug_assertions) { println!("slow2") } else { println!("fast2") } } "#, ) .build(); p.cargo("run -v --release --example a") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [RUNNING] `rustc --crate-name bar --edition=2015 bar/src/bar.rs [..]--crate-type lib --emit=[..]link -C opt-level=3[..] -C metadata=[..] --out-dir [ROOT]/foo/target/release/deps -C strip=debuginfo -L dependency=[ROOT]/foo/target/release/deps` [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name a --edition=2015 examples/a.rs [..]--crate-type bin --emit=[..]link -C opt-level=3[..] -C metadata=[..] --out-dir [ROOT]/foo/target/release/examples -C strip=debuginfo -L dependency=[ROOT]/foo/target/release/deps --extern bar=[ROOT]/foo/target/release/deps/libbar-[HASH].rlib` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [RUNNING] `target/release/examples/a[EXE]` "#]]) .with_stdout_data(str![[r#" fast1 fast2 "#]]) .run(); p.cargo("run -v --example a") .with_stderr_data(str![[r#" [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [RUNNING] `rustc --crate-name bar --edition=2015 bar/src/bar.rs [..]--crate-type lib --emit=[..]link [..]-C debuginfo=2 [..]-C metadata=[..] --out-dir [ROOT]/foo/target/debug/deps -L dependency=[ROOT]/foo/target/debug/deps` [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name a --edition=2015 examples/a.rs [..]--crate-type bin --emit=[..]link [..]-C debuginfo=2 [..]-C metadata=[..] --out-dir [ROOT]/foo/target/debug/examples -L dependency=[ROOT]/foo/target/debug/deps --extern bar=[ROOT]/foo/target/debug/deps/libbar-[HASH].rlib` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/examples/a[EXE]` "#]]) .with_stdout_data(str![[r#" slow1 slow2 "#]]) .run(); } #[cargo_test] fn run_dylib_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "bar" "#, ) .file( "src/main.rs", r#"extern crate bar; fn main() { bar::bar(); }"#, ) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" authors = [] [lib] name = "bar" crate-type = ["dylib"] "#, ) .file("bar/src/lib.rs", "pub fn bar() {}") .build(); p.cargo("run hello world").run(); } #[cargo_test] fn run_with_bin_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies.bar] path = "bar" "#, ) .file("src/main.rs", r#"fn main() { println!("hello"); }"#) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [[bin]] name = "bar" "#, ) .file("bar/src/main.rs", r#"fn main() { println!("bar"); }"#) .build(); p.cargo("run") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [WARNING] foo v0.0.1 ([ROOT]/foo) ignoring invalid dependency `bar` which is missing a lib target [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .with_stdout_data(str![[r#" hello "#]]) .run(); } #[cargo_test] fn run_with_bin_deps() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies.bar1] path = "bar1" [dependencies.bar2] path = "bar2" "#, ) .file("src/main.rs", r#"fn main() { println!("hello"); }"#) .file( "bar1/Cargo.toml", r#" [package] name = "bar1" version = "0.0.1" edition = "2015" authors = [] [[bin]] name = "bar1" "#, ) .file("bar1/src/main.rs", r#"fn main() { println!("bar1"); }"#) .file( "bar2/Cargo.toml", r#" [package] name = "bar2" version = "0.0.1" edition = "2015" authors = [] [[bin]] name = "bar2" "#, ) .file("bar2/src/main.rs", r#"fn main() { println!("bar2"); }"#) .build(); p.cargo("run") .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [WARNING] foo v0.0.1 ([ROOT]/foo) ignoring invalid dependency `bar1` which is missing a lib target [WARNING] foo v0.0.1 ([ROOT]/foo) ignoring invalid dependency `bar2` which is missing a lib target [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .with_stdout_data(str![[r#" hello "#]]) .run(); } #[cargo_test] fn run_with_bin_dep_in_workspace() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo1", "foo2"] "#, ) .file( "foo1/Cargo.toml", r#" [package] name = "foo1" version = "0.0.1" edition = "2015" [dependencies.bar1] path = "bar1" "#, ) .file("foo1/src/main.rs", r#"fn main() { println!("hello"); }"#) .file( "foo1/bar1/Cargo.toml", r#" [package] name = "bar1" version = "0.0.1" edition = "2015" authors = [] [[bin]] name = "bar1" "#, ) .file( "foo1/bar1/src/main.rs", r#"fn main() { println!("bar1"); }"#, ) .file( "foo2/Cargo.toml", r#" [package] name = "foo2" version = "0.0.1" edition = "2015" [dependencies.bar2] path = "bar2" "#, ) .file("foo2/src/main.rs", r#"fn main() { println!("hello"); }"#) .file( "foo2/bar2/Cargo.toml", r#" [package] name = "bar2" version = "0.0.1" edition = "2015" authors = [] [[bin]] name = "bar2" "#, ) .file( "foo2/bar2/src/main.rs", r#"fn main() { println!("bar2"); }"#, ) .build(); p.cargo("run") .with_status(101) .with_stderr_data(str![[r#" [ERROR] `cargo run` could not determine which binary to run. Use the `--bin` option to specify a binary, or the `default-run` manifest key. available binaries: bar1, bar2, foo1, foo2 "#]]) .run(); p.cargo("run --bin foo1") .with_stderr_data(str![[r#" [WARNING] foo1 v0.0.1 ([ROOT]/foo/foo1) ignoring invalid dependency `bar1` which is missing a lib target [WARNING] foo2 v0.0.1 ([ROOT]/foo/foo2) ignoring invalid dependency `bar2` which is missing a lib target [COMPILING] foo1 v0.0.1 ([ROOT]/foo/foo1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo1[EXE]` "#]]) .with_stdout_data(str![[r#" hello "#]]) .run(); } #[cargo_test] fn release_works() { let p = project() .file( "src/main.rs", r#" fn main() { if cfg!(debug_assertions) { panic!() } } "#, ) .build(); p.cargo("run --release") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [RUNNING] `target/release/foo[EXE]` "#]]) .run(); assert!(p.release_bin("foo").is_file()); } #[cargo_test] fn release_short_works() { let p = project() .file( "src/main.rs", r#" fn main() { if cfg!(debug_assertions) { panic!() } } "#, ) .build(); p.cargo("run -r") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [RUNNING] `target/release/foo[EXE]` "#]]) .run(); assert!(p.release_bin("foo").is_file()); } #[cargo_test] fn run_bin_different_name() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [[bin]] name = "bar" "#, ) .file("src/bar.rs", "fn main() {}") .build(); p.cargo("run").run(); } #[cargo_test] fn dashes_are_forwarded() { let p = project() .file( "src/bin/bar.rs", r#" fn main() { let s: Vec = std::env::args().collect(); assert_eq!(s[1], "--"); assert_eq!(s[2], "a"); assert_eq!(s[3], "--"); assert_eq!(s[4], "b"); } "#, ) .build(); p.cargo("run -- -- a -- b").run(); } #[cargo_test] fn run_from_executable_folder() { let p = project() .file("src/main.rs", r#"fn main() { println!("hello"); }"#) .build(); let cwd = p.root().join("target").join("debug"); p.cargo("build").run(); p.cargo("run") .cwd(cwd) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `./foo[EXE]` "#]]) .with_stdout_data(str![[r#" hello "#]]) .run(); } #[cargo_test] fn run_with_library_paths() { let p = project(); // Only link search directories within the target output directory are // propagated through to dylib_path_envvar() (see #3366). let mut dir1 = p.target_debug_dir(); dir1.push("foo\\backslash"); let mut dir2 = p.target_debug_dir(); dir2.push("dir=containing=equal=signs"); let p = p .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" "#, ) .file( "build.rs", &format!( r##" fn main() {{ println!(r#"cargo::rustc-link-search=native={}"#); println!(r#"cargo::rustc-link-search={}"#); }} "##, dir1.display(), dir2.display() ), ) .file( "src/main.rs", &format!( r##" fn main() {{ let search_path = std::env::var_os("{}").unwrap(); let paths = std::env::split_paths(&search_path).collect::>(); println!("{{:#?}}", paths); assert!(paths.contains(&r#"{}"#.into())); assert!(paths.contains(&r#"{}"#.into())); }} "##, dylib_path_envvar(), dir1.display(), dir2.display() ), ) .build(); p.cargo("run").run(); } #[cargo_test] fn library_paths_sorted_alphabetically() { let p = project(); let mut dir1 = p.target_debug_dir(); dir1.push("zzzzzzz"); let mut dir2 = p.target_debug_dir(); dir2.push("BBBBBBB"); let mut dir3 = p.target_debug_dir(); dir3.push("aaaaaaa"); let p = p .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" "#, ) .file( "build.rs", &format!( r##" fn main() {{ println!(r#"cargo::rustc-link-search=native={}"#); println!(r#"cargo::rustc-link-search=native={}"#); println!(r#"cargo::rustc-link-search=native={}"#); }} "##, dir1.display(), dir2.display(), dir3.display() ), ) .file( "src/main.rs", &format!( r##" fn main() {{ let search_path = std::env::var_os("{}").unwrap(); let paths = std::env::split_paths(&search_path).collect::>(); // ASCII case-sensitive sort assert_eq!("BBBBBBB", paths[0].file_name().unwrap().to_string_lossy()); assert_eq!("aaaaaaa", paths[1].file_name().unwrap().to_string_lossy()); assert_eq!("zzzzzzz", paths[2].file_name().unwrap().to_string_lossy()); }} "##, dylib_path_envvar() ), ) .build(); p.cargo("run").run(); } #[cargo_test] fn fail_no_extra_verbose() { let p = project() .file("src/main.rs", "fn main() { std::process::exit(1); }") .build(); p.cargo("run -q") .with_status(1) .with_stdout_data("") .with_stderr_data("") .run(); } #[cargo_test] fn run_multiple_packages() { let p = project() .no_manifest() .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [workspace] [dependencies] d1 = { path = "d1" } d2 = { path = "d2" } d3 = { path = "../d3" } # outside of the workspace [[bin]] name = "foo" "#, ) .file("foo/src/foo.rs", "fn main() { println!(\"foo\"); }") .file("foo/d1/Cargo.toml", &basic_bin_manifest("d1")) .file("foo/d1/src/lib.rs", "") .file("foo/d1/src/main.rs", "fn main() { println!(\"d1\"); }") .file("foo/d2/Cargo.toml", &basic_bin_manifest("d2")) .file("foo/d2/src/main.rs", "fn main() { println!(\"d2\"); }") .file("d3/Cargo.toml", &basic_bin_manifest("d3")) .file("d3/src/main.rs", "fn main() { println!(\"d2\"); }") .build(); let cargo = || { let mut process_builder = p.cargo("run"); process_builder.cwd("foo"); process_builder }; cargo() .arg("-p") .arg("d1") .with_stdout_data(str![[r#" d1 "#]]) .run(); cargo() .arg("-p") .arg("d2") .arg("--bin") .arg("d2") .with_stdout_data(str![[r#" d2 "#]]) .run(); cargo() .with_stdout_data(str![[r#" foo "#]]) .run(); cargo() .arg("-p") .arg("d1") .arg("-p") .arg("d2") .with_status(1) .with_stderr_data(str![[r#" [ERROR] the argument '--package []' cannot be used multiple times ... "#]]) .run(); cargo() .arg("-p") .arg("d3") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package(s) `d3` not found in workspace `[ROOT]/foo/foo` "#]]) .run(); cargo() .arg("-p") .arg("d*") .with_status(101) .with_stderr_data(str![[r#" [ERROR] `cargo run` does not support glob pattern `d*` on package selection "#]]) .run(); } #[cargo_test] fn explicit_bin_with_args() { let p = project() .file( "src/main.rs", r#" fn main() { assert_eq!(std::env::args().nth(1).unwrap(), "hello"); assert_eq!(std::env::args().nth(2).unwrap(), "world"); } "#, ) .build(); p.cargo("run --bin foo hello world").run(); } #[cargo_test] fn run_workspace() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] "#, ) .file("a/Cargo.toml", &basic_bin_manifest("a")) .file("a/src/main.rs", r#"fn main() {println!("run-a");}"#) .file("b/Cargo.toml", &basic_bin_manifest("b")) .file("b/src/main.rs", r#"fn main() {println!("run-b");}"#) .build(); p.cargo("run") .with_status(101) .with_stderr_data(str![[r#" [ERROR] `cargo run` could not determine which binary to run. Use the `--bin` option to specify a binary, or the `default-run` manifest key. available binaries: a, b "#]]) .run(); p.cargo("run --bin a") .with_stdout_data(str![[r#" run-a "#]]) .run(); } #[cargo_test] fn default_run_workspace() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" edition = "2015" default-run = "a" "#, ) .file("a/src/main.rs", r#"fn main() {println!("run-a");}"#) .file("b/Cargo.toml", &basic_bin_manifest("b")) .file("b/src/main.rs", r#"fn main() {println!("run-b");}"#) .build(); p.cargo("run") .with_stdout_data(str![[r#" run-a "#]]) .run(); } #[cargo_test] fn print_env_verbose() { let p = project() .file("Cargo.toml", &basic_manifest("a", "0.0.1")) .file("src/main.rs", r#"fn main() {println!("run-a");}"#) .build(); p.cargo("run -vv") .with_stderr_data(str![[r#" [COMPILING] a v0.0.1 ([ROOT]/foo) [RUNNING] `[..]CARGO_MANIFEST_DIR=[ROOT]/foo[..] rustc --crate-name a[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[..]CARGO_MANIFEST_DIR=[ROOT]/foo[..] target/debug/a[EXE]` "#]]) .run(); } #[cargo_test] #[cfg(target_os = "macos")] fn run_link_system_path_macos() { use cargo_test_support::paths; use std::fs; // Check that the default system library path is honored. // First, build a shared library that will be accessed from // DYLD_FALLBACK_LIBRARY_PATH. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [lib] crate-type = ["cdylib"] "#, ) .file( "src/lib.rs", "#[no_mangle] pub extern fn something_shared() {}", ) .build(); p.cargo("build").run(); // This is convoluted. Since this test can't modify things in /usr, // this needs to dance around to check that things work. // // The default DYLD_FALLBACK_LIBRARY_PATH is: // $(HOME)/lib:/usr/local/lib:/lib:/usr/lib // // This will make use of ~/lib in the path, but the default cc link // path is /usr/lib:/usr/local/lib. So first need to build in one // location, and then move it to ~/lib. // // 1. Build with rustc-link-search pointing to libfoo so the initial // binary can be linked. // 2. Move the library to ~/lib // 3. Run `cargo run` to make sure it can still find the library in // ~/lib. // // This should be equivalent to having the library in /usr/local/lib. let p2 = project() .at("bar") .file("Cargo.toml", &basic_bin_manifest("bar")) .file( "src/main.rs", r#" extern { fn something_shared(); } fn main() { unsafe { something_shared(); } } "#, ) .file( "build.rs", &format!( r#" fn main() {{ println!("cargo::rustc-link-lib=foo"); println!("cargo::rustc-link-search={}"); }} "#, p.target_debug_dir().display() ), ) .build(); p2.cargo("build").run(); p2.cargo("test").run(); let libdir = paths::home().join("lib"); fs::create_dir(&libdir).unwrap(); fs::rename( p.target_debug_dir().join("libfoo.dylib"), libdir.join("libfoo.dylib"), ) .unwrap(); p.root().rm_rf(); const VAR: &str = "DYLD_FALLBACK_LIBRARY_PATH"; // Reset DYLD_FALLBACK_LIBRARY_PATH so that we don't inherit anything that // was set by the cargo that invoked the test. p2.cargo("run").env_remove(VAR).run(); p2.cargo("test").env_remove(VAR).run(); // Ensure this still works when DYLD_FALLBACK_LIBRARY_PATH has // a value set. p2.cargo("run").env(VAR, &libdir).run(); p2.cargo("test").env(VAR, &libdir).run(); } #[cargo_test] fn run_binary_with_same_name_as_dependency() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [dependencies] foo = { path = "foo" } "#, ) .file( "src/main.rs", r#" fn main() {} "#, ) .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [lib] name = "foo" path = "foo.rs" "#, ) .file("foo/foo.rs", "") .build(); p.cargo("run").run(); p.cargo("check -p foo@0.5.0").run(); p.cargo("run -p foo@0.5.0").run(); p.cargo("run -p foo@0.5").run(); p.cargo("run -p foo@0.4") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package(s) `foo@0.4` not found in workspace `[ROOT]/foo` "#]]) .run(); } cargo-0.91.0/tests/testsuite/rust_version.rs000064400000000000000000000734011046102023000173160ustar 00000000000000//! Tests for targets with `rust-version`. use crate::prelude::*; use crate::utils::cargo_process; use cargo_test_support::{project, registry::Package, str}; #[cargo_test] fn rust_version_satisfied() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] rust-version = "1.1.1" [[bin]] name = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check").run(); p.cargo("check --ignore-rust-version").run(); } #[cargo_test] fn rust_version_error() { project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] rust-version = "^1.43" [[bin]] name = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build() .cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] unexpected version requirement, expected a version like "1.32" --> Cargo.toml:7:28 | 7 | rust-version = "^1.43" | ^^^^^^^ | "#]]) .run(); } #[cargo_test] fn rust_version_older_than_edition() { project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] rust-version = "1.1" edition = "2018" [[bin]] name = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build() .cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: rust-version 1.1 is older than first version (1.31.0) required by the specified edition (2018) "#]]) .run(); } #[cargo_test] fn lint_self_incompatible_with_rust_version() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] rust-version = "1.9876.0" [[bin]] name = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] rustc [..] is not supported by the following package: foo@0.0.1 requires rustc 1.9876.0 "#]]) .run(); p.cargo("check --ignore-rust-version").run(); } #[cargo_test] fn lint_dep_incompatible_with_rust_version() { Package::new("too_new_parent", "0.0.1") .dep("too_new_child", "0.0.1") .rust_version("1.2345.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); Package::new("too_new_child", "0.0.1") .rust_version("1.2345.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); Package::new("rustc_compatible", "0.0.1") .rust_version("1.60.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" rust-version = "1.50" authors = [] [dependencies] too_new_parent = "0.0.1" rustc_compatible = "0.0.1" "#, ) .file("src/main.rs", "fn main(){}") .build(); p.cargo("generate-lockfile") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 3 packages to latest compatible versions [ADDING] too_new_child v0.0.1 (requires Rust 1.2345.0) [ADDING] too_new_parent v0.0.1 (requires Rust 1.2345.0) "#]]) .run(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [DOWNLOADING] crates ... [DOWNLOADED] too_new_parent v0.0.1 (registry `dummy-registry`) [DOWNLOADED] too_new_child v0.0.1 (registry `dummy-registry`) [DOWNLOADED] rustc_compatible v0.0.1 (registry `dummy-registry`) [ERROR] rustc [..] is not supported by the following packages: too_new_child@0.0.1 requires rustc 1.2345.0 too_new_parent@0.0.1 requires rustc 1.2345.0 Either upgrade rustc or select compatible dependency versions with `cargo update @ --precise ` where `` is the latest version supporting rustc [..] "#]]) .run(); p.cargo("check --ignore-rust-version").run(); } #[cargo_test] fn resolve_with_rust_version() { Package::new("only-newer", "1.6.0") .rust_version("1.65.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); Package::new("newer-and-older", "1.5.0") .rust_version("1.55.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); Package::new("newer-and-older", "1.6.0") .rust_version("1.65.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] rust-version = "1.60.0" [dependencies] only-newer = "1.0.0" newer-and-older = "1.0.0" "#, ) .file("src/main.rs", "fn main(){}") .build(); p.cargo("generate-lockfile --ignore-rust-version") .env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions "#]]) .run(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.0.1 ([ROOT]/foo) ├── newer-and-older v1.6.0 └── only-newer v1.6.0 "#]]) .run(); p.cargo("generate-lockfile") .env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest Rust 1.60.0 compatible versions [ADDING] newer-and-older v1.5.0 (available: v1.6.0, requires Rust 1.65.0) [ADDING] only-newer v1.6.0 (requires Rust 1.65.0) "#]]) .run(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.0.1 ([ROOT]/foo) ├── newer-and-older v1.5.0 └── only-newer v1.6.0 "#]]) .run(); } #[cargo_test] fn resolve_with_rustc() { Package::new("only-newer", "1.6.0") .rust_version("1.2345") .file("src/lib.rs", "fn other_stuff() {}") .publish(); Package::new("newer-and-older", "1.5.0") .rust_version("1.55.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); Package::new("newer-and-older", "1.6.0") .rust_version("1.2345") .file("src/lib.rs", "fn other_stuff() {}") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] rust-version = "1.60.0" [dependencies] only-newer = "1.0.0" newer-and-older = "1.0.0" "#, ) .file("src/main.rs", "fn main(){}") .build(); p.cargo("generate-lockfile --ignore-rust-version") .env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [ADDING] newer-and-older v1.6.0 (requires Rust 1.2345) [ADDING] only-newer v1.6.0 (requires Rust 1.2345) "#]]) .run(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.0.1 ([ROOT]/foo) ├── newer-and-older v1.6.0 └── only-newer v1.6.0 "#]]) .run(); p.cargo("generate-lockfile") .env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest Rust 1.60.0 compatible versions [ADDING] newer-and-older v1.5.0 (available: v1.6.0, requires Rust 1.2345) [ADDING] only-newer v1.6.0 (requires Rust 1.2345) "#]]) .run(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.0.1 ([ROOT]/foo) ├── newer-and-older v1.5.0 └── only-newer v1.6.0 "#]]) .run(); } #[cargo_test] fn resolve_with_backtracking() { Package::new("has-rust-version", "1.6.0") .rust_version("1.65.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); Package::new("no-rust-version", "2.1.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); Package::new("no-rust-version", "2.2.0") .file("src/lib.rs", "fn other_stuff() {}") .dep("has-rust-version", "1.6.0") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] rust-version = "1.60.0" [dependencies] no-rust-version = "2" "#, ) .file("src/main.rs", "fn main(){}") .build(); p.cargo("generate-lockfile --ignore-rust-version") .env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions "#]]) .run(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.0.1 ([ROOT]/foo) └── no-rust-version v2.2.0 └── has-rust-version v1.6.0 "#]]) .run(); // Ideally we'd pick `has-rust-version` 1.6.0 which requires backtracking p.cargo("generate-lockfile") .env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest Rust 1.60.0 compatible versions [ADDING] has-rust-version v1.6.0 (requires Rust 1.65.0) "#]]) .run(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.0.1 ([ROOT]/foo) └── no-rust-version v2.2.0 └── has-rust-version v1.6.0 "#]]) .run(); } #[cargo_test] fn resolve_with_multiple_rust_versions() { Package::new(&format!("shared-only-newer"), "1.65.0") .rust_version("1.65.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); for ver in ["1.45.0", "1.55.0", "1.65.0"] { Package::new(&format!("shared-newer-and-older"), ver) .rust_version(ver) .file("src/lib.rs", "fn other_stuff() {}") .publish(); } Package::new(&format!("lower-only-newer"), "1.65.0") .rust_version("1.65.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); for ver in ["1.45.0", "1.55.0"] { Package::new(&format!("lower-newer-and-older"), ver) .rust_version(ver) .file("src/lib.rs", "fn other_stuff() {}") .publish(); } Package::new(&format!("higher-only-newer"), "1.65.0") .rust_version("1.65.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); for ver in ["1.55.0", "1.65.0"] { Package::new(&format!("higher-newer-and-older"), ver) .rust_version(ver) .file("src/lib.rs", "fn other_stuff() {}") .publish(); } let p = project() .file( "Cargo.toml", r#" [workspace] members = ["lower"] [package] name = "higher" version = "0.0.1" edition = "2015" authors = [] rust-version = "1.60.0" [dependencies] higher-only-newer = "1" higher-newer-and-older = "1" shared-only-newer = "1" shared-newer-and-older = "1" "#, ) .file("src/main.rs", "fn main() {}") .file( "lower/Cargo.toml", r#" [package] name = "lower" version = "0.0.1" edition = "2015" authors = [] rust-version = "1.50.0" [dependencies] lower-only-newer = "1" lower-newer-and-older = "1" shared-only-newer = "1" shared-newer-and-older = "1" "#, ) .file("lower/src/main.rs", "fn main() {}") .build(); p.cargo("generate-lockfile --ignore-rust-version") .env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 6 packages to latest compatible versions "#]]) .run(); p.cargo("tree") .with_stdout_data(str![[r#" higher v0.0.1 ([ROOT]/foo) ├── higher-newer-and-older v1.65.0 ├── higher-only-newer v1.65.0 ├── shared-newer-and-older v1.65.0 └── shared-only-newer v1.65.0 "#]]) .run(); p.cargo("generate-lockfile") .env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 6 packages to latest Rust 1.50.0 compatible versions [ADDING] higher-newer-and-older v1.55.0 (available: v1.65.0, requires Rust 1.65.0) [ADDING] higher-only-newer v1.65.0 (requires Rust 1.65.0) [ADDING] lower-newer-and-older v1.45.0 (available: v1.55.0, requires Rust 1.55.0) [ADDING] lower-only-newer v1.65.0 (requires Rust 1.65.0) [ADDING] shared-newer-and-older v1.45.0 (available: v1.65.0, requires Rust 1.65.0) [ADDING] shared-only-newer v1.65.0 (requires Rust 1.65.0) "#]]) .run(); p.cargo("tree") .with_stdout_data(str![[r#" higher v0.0.1 ([ROOT]/foo) ├── higher-newer-and-older v1.55.0 ├── higher-only-newer v1.65.0 ├── shared-newer-and-older v1.45.0 └── shared-only-newer v1.65.0 "#]]) .run(); } #[cargo_test] fn resolve_edition2024() { Package::new("only-newer", "1.6.0") .rust_version("1.999.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); Package::new("newer-and-older", "1.5.0") .rust_version("1.80.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); Package::new("newer-and-older", "1.6.0") .rust_version("1.999.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2024" authors = [] rust-version = "1.85.0" [dependencies] only-newer = "1.0.0" newer-and-older = "1.0.0" "#, ) .file("src/main.rs", "fn main(){}") .build(); // Edition2024 should resolve for MSRV p.cargo("generate-lockfile") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest Rust 1.85.0 compatible versions [ADDING] newer-and-older v1.5.0 (available: v1.6.0, requires Rust 1.999.0) [ADDING] only-newer v1.6.0 (requires Rust 1.999.0) "#]]) .run(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.0.1 ([ROOT]/foo) ├── newer-and-older v1.5.0 └── only-newer v1.6.0 "#]]) .run(); // `--ignore-rust-version` has precedence over Edition2024 p.cargo("generate-lockfile --ignore-rust-version") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [ADDING] newer-and-older v1.6.0 (requires Rust 1.999.0) [ADDING] only-newer v1.6.0 (requires Rust 1.999.0) "#]]) .run(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.0.1 ([ROOT]/foo) ├── newer-and-older v1.6.0 └── only-newer v1.6.0 "#]]) .run(); // config has precedence over Edition2024 p.cargo("generate-lockfile") .env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "allow") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [ADDING] newer-and-older v1.6.0 (requires Rust 1.999.0) [ADDING] only-newer v1.6.0 (requires Rust 1.999.0) "#]]) .run(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.0.1 ([ROOT]/foo) ├── newer-and-older v1.6.0 └── only-newer v1.6.0 "#]]) .run(); } #[cargo_test] fn resolve_v3() { Package::new("only-newer", "1.6.0") .rust_version("1.999.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); Package::new("newer-and-older", "1.5.0") .rust_version("1.80.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); Package::new("newer-and-older", "1.6.0") .rust_version("1.999.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] rust-version = "1.85.0" resolver = "3" [dependencies] only-newer = "1.0.0" newer-and-older = "1.0.0" "#, ) .file("src/main.rs", "fn main(){}") .build(); // v3 should resolve for MSRV p.cargo("generate-lockfile") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest Rust 1.85.0 compatible versions [ADDING] newer-and-older v1.5.0 (available: v1.6.0, requires Rust 1.999.0) [ADDING] only-newer v1.6.0 (requires Rust 1.999.0) "#]]) .run(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.0.1 ([ROOT]/foo) ├── newer-and-older v1.5.0 └── only-newer v1.6.0 "#]]) .run(); // `--ignore-rust-version` has precedence over v3 p.cargo("generate-lockfile --ignore-rust-version") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [ADDING] newer-and-older v1.6.0 (requires Rust 1.999.0) [ADDING] only-newer v1.6.0 (requires Rust 1.999.0) "#]]) .run(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.0.1 ([ROOT]/foo) ├── newer-and-older v1.6.0 └── only-newer v1.6.0 "#]]) .run(); // config has precedence over v3 p.cargo("generate-lockfile") .env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "allow") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [ADDING] newer-and-older v1.6.0 (requires Rust 1.999.0) [ADDING] only-newer v1.6.0 (requires Rust 1.999.0) "#]]) .run(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.0.1 ([ROOT]/foo) ├── newer-and-older v1.6.0 └── only-newer v1.6.0 "#]]) .run(); } #[cargo_test] fn update_msrv_resolve() { Package::new("bar", "1.5.0") .rust_version("1.55.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); Package::new("bar", "1.6.0") .rust_version("1.65.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] rust-version = "1.60.0" [dependencies] bar = "1.0.0" "#, ) .file("src/main.rs", "fn main(){}") .build(); p.cargo("update") .env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest Rust 1.60.0 compatible version [ADDING] bar v1.5.0 (available: v1.6.0, requires Rust 1.65.0) "#]]) .run(); p.cargo("update --ignore-rust-version") .env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [UPDATING] bar v1.5.0 -> v1.6.0 "#]]) .run(); } #[cargo_test] fn update_precise_overrides_msrv_resolver() { Package::new("bar", "1.5.0") .rust_version("1.55.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); Package::new("bar", "1.6.0") .rust_version("1.65.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] rust-version = "1.60.0" [dependencies] bar = "1.0.0" "#, ) .file("src/main.rs", "fn main(){}") .build(); p.cargo("update") .env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest Rust 1.60.0 compatible version [ADDING] bar v1.5.0 (available: v1.6.0, requires Rust 1.65.0) "#]]) .run(); p.cargo("update --precise 1.6.0 bar") .env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [UPDATING] bar v1.5.0 -> v1.6.0 (requires Rust 1.65.0) "#]]) .run(); } #[cargo_test] fn check_msrv_resolve() { Package::new("only-newer", "1.6.0") .rust_version("1.65.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); Package::new("newer-and-older", "1.5.0") .rust_version("1.55.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); Package::new("newer-and-older", "1.6.0") .rust_version("1.65.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] rust-version = "1.60.0" [dependencies] only-newer = "1.0.0" newer-and-older = "1.0.0" "#, ) .file("src/main.rs", "fn main(){}") .build(); p.cargo("check --ignore-rust-version") .env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback") .with_stderr_data( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] only-newer v1.6.0 (registry `dummy-registry`) [DOWNLOADED] newer-and-older v1.6.0 (registry `dummy-registry`) [CHECKING] only-newer v1.6.0 [CHECKING] newer-and-older v1.6.0 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.0.1 ([ROOT]/foo) ├── newer-and-older v1.6.0 └── only-newer v1.6.0 "#]]) .run(); std::fs::remove_file(p.root().join("Cargo.lock")).unwrap(); p.cargo("check") .env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest Rust 1.60.0 compatible versions [ADDING] newer-and-older v1.5.0 (available: v1.6.0, requires Rust 1.65.0) [ADDING] only-newer v1.6.0 (requires Rust 1.65.0) [DOWNLOADING] crates ... [DOWNLOADED] newer-and-older v1.5.0 (registry `dummy-registry`) [CHECKING] newer-and-older v1.5.0 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.0.1 ([ROOT]/foo) ├── newer-and-older v1.5.0 └── only-newer v1.6.0 "#]]) .run(); } #[cargo_test] fn cargo_install_ignores_msrv_config() { Package::new("dep", "1.0.0") .rust_version("1.50") .file("src/lib.rs", "fn hello() {}") .publish(); Package::new("dep", "1.1.0") .rust_version("1.70") .file("src/lib.rs", "fn hello() {}") .publish(); Package::new("foo", "0.0.1") .rust_version("1.60") .file("src/main.rs", "fn main() {}") .dep("dep", "1") .publish(); cargo_process("install foo") .env( "CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback", ) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] foo v0.0.1 (registry `dummy-registry`) [INSTALLING] foo v0.0.1 [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] dep v1.1.0 (registry `dummy-registry`) [COMPILING] dep v1.1.0 [COMPILING] foo v0.0.1 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE] [INSTALLED] package `foo v0.0.1` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); } #[cargo_test] fn cargo_install_ignores_resolver_v3_msrv_change() { Package::new("dep", "1.0.0") .rust_version("1.50") .file("src/lib.rs", "fn hello() {}") .publish(); Package::new("dep", "1.1.0") .rust_version("1.70") .file("src/lib.rs", "fn hello() {}") .publish(); Package::new("foo", "0.0.1") .rust_version("1.60") .resolver("3") .file("src/main.rs", "fn main() {}") .dep("dep", "1") .publish(); cargo_process("install foo") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] foo v0.0.1 (registry `dummy-registry`) [INSTALLING] foo v0.0.1 [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] dep v1.1.0 (registry `dummy-registry`) [COMPILING] dep v1.1.0 [COMPILING] foo v0.0.1 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE] [INSTALLED] package `foo v0.0.1` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); } #[cargo_test] fn report_rust_versions() { Package::new("dep-only-low-compatible", "1.55.0") .rust_version("1.55.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); Package::new("dep-only-low-incompatible", "1.75.0") .rust_version("1.75.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); Package::new("dep-only-high-compatible", "1.65.0") .rust_version("1.65.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); Package::new("dep-only-high-incompatible", "1.75.0") .rust_version("1.75.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); Package::new("dep-only-unset-unset", "1.0.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); Package::new("dep-only-unset-compatible", "1.75.0") .rust_version("1.75.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); Package::new("dep-only-unset-incompatible", "1.2345.0") .rust_version("1.2345.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); Package::new("dep-shared-compatible", "1.55.0") .rust_version("1.55.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); Package::new("dep-shared-incompatible", "1.75.0") .rust_version("1.75.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["high", "low", "unset"] "#, ) .file( "high/Cargo.toml", r#" [package] name = "high" edition = "2015" rust-version = "1.70.0" [dependencies] dep-only-high-compatible = "1" dep-only-high-incompatible = "1" dep-shared-compatible = "1" dep-shared-incompatible = "1" "#, ) .file("high/src/main.rs", "fn main(){}") .file( "low/Cargo.toml", r#" [package] name = "low" edition = "2015" rust-version = "1.60.0" [dependencies] dep-only-low-compatible = "1" dep-only-low-incompatible = "1" dep-shared-compatible = "1" dep-shared-incompatible = "1" "#, ) .file("low/src/main.rs", "fn main(){}") .file( "unset/Cargo.toml", r#" [package] name = "unset" edition = "2015" [dependencies] dep-only-unset-unset = "1" dep-only-unset-compatible = "1" dep-only-unset-incompatible = "1" dep-shared-compatible = "1" dep-shared-incompatible = "1" "#, ) .file("unset/src/main.rs", "fn main(){}") .build(); p.cargo("update") .env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 9 packages to latest Rust 1.60.0 compatible versions [ADDING] dep-only-high-incompatible v1.75.0 (requires Rust 1.75.0) [ADDING] dep-only-low-incompatible v1.75.0 (requires Rust 1.75.0) [ADDING] dep-only-unset-incompatible v1.2345.0 (requires Rust 1.2345.0) [ADDING] dep-shared-incompatible v1.75.0 (requires Rust 1.75.0) "#]]) .run(); } cargo-0.91.0/tests/testsuite/rustc.rs000064400000000000000000000620301046102023000157100ustar 00000000000000//! Tests for the `cargo rustc` command. use crate::prelude::*; use cargo_test_support::{basic_bin_manifest, basic_lib_manifest, basic_manifest, project, str}; #[cargo_test] fn build_lib_for_foo() { let p = project() .file("src/main.rs", "fn main() {}") .file("src/lib.rs", r#" "#) .build(); p.cargo("rustc --lib -v").with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C debuginfo=2 [..]-C metadata=[..] [..]--out-dir [ROOT]/foo/target/debug/deps -L dependency=[ROOT]/foo/target/debug/deps` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn lib() { let p = project() .file("src/main.rs", "fn main() {}") .file("src/lib.rs", r#" "#) .build(); p.cargo("rustc --lib -v -- -C debug-assertions=off") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C debuginfo=2 [..]-C metadata=[..] [..]--out-dir [ROOT]/foo/target/debug/deps -L dependency=[ROOT]/foo/target/debug/deps[..]-C debug-assertions=off[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn build_main_and_allow_unstable_options() { let p = project() .file("src/main.rs", "fn main() {}") .file("src/lib.rs", r#" "#) .build(); p.cargo("rustc -v --bin foo -- -C debug-assertions") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C debuginfo=2 [..]-C metadata=[..] --out-dir [ROOT]/foo/target/debug/deps -L dependency=[ROOT]/foo/target/debug/deps` [RUNNING] `rustc --crate-name foo --edition=2015 src/main.rs [..]--crate-type bin --emit=[..]link[..]-C debuginfo=2 [..]-C metadata=[..] --out-dir [ROOT]/foo/target/debug/deps -L dependency=[ROOT]/foo/target/debug/deps --extern foo=[ROOT]/foo/target/debug/deps/libfoo-[HASH].rlib[..]-C debug-assertions[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn fails_when_trying_to_build_main_and_lib_with_args() { let p = project() .file("src/main.rs", "fn main() {}") .file("src/lib.rs", r#" "#) .build(); p.cargo("rustc -v -- -C debug-assertions") .with_status(101) .with_stderr_data(str![[r#" [ERROR] extra arguments to `rustc` can only be passed to one target, consider filtering the package by passing, e.g., `--lib` or `--bin NAME` to specify a single target "#]]) .run(); } #[cargo_test] fn build_with_args_to_one_of_multiple_binaries() { let p = project() .file("src/bin/foo.rs", "fn main() {}") .file("src/bin/bar.rs", "fn main() {}") .file("src/bin/baz.rs", "fn main() {}") .file("src/lib.rs", r#" "#) .build(); p.cargo("rustc -v --bin bar -- -C debug-assertions") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C debuginfo=2 [..]-C metadata=[..] --out-dir [..]` [RUNNING] `rustc --crate-name bar --edition=2015 src/bin/bar.rs [..]--crate-type bin --emit=[..]link[..]-C debuginfo=2 [..]-C debug-assertions[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn fails_with_args_to_all_binaries() { let p = project() .file("src/bin/foo.rs", "fn main() {}") .file("src/bin/bar.rs", "fn main() {}") .file("src/bin/baz.rs", "fn main() {}") .file("src/lib.rs", r#" "#) .build(); p.cargo("rustc -v -- -C debug-assertions") .with_status(101) .with_stderr_data(str![[r#" [ERROR] extra arguments to `rustc` can only be passed to one target, consider filtering the package by passing, e.g., `--lib` or `--bin NAME` to specify a single target "#]]) .run(); } #[cargo_test] fn fails_with_crate_type_to_multi_binaries() { let p = project() .file("src/bin/foo.rs", "fn main() {}") .file("src/bin/bar.rs", "fn main() {}") .file("src/bin/baz.rs", "fn main() {}") .file("src/lib.rs", r#" "#) .build(); p.cargo("rustc --crate-type lib") .with_status(101) .with_stderr_data(str![[r#" [ERROR] crate types to rustc can only be passed to one target, consider filtering the package by passing, e.g., `--lib` or `--example` to specify a single target "#]]) .run(); } #[cargo_test] fn fails_with_crate_type_to_multi_examples() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [[example]] name = "ex1" crate-type = ["rlib"] [[example]] name = "ex2" crate-type = ["rlib"] "#, ) .file("src/lib.rs", "") .file("examples/ex1.rs", "") .file("examples/ex2.rs", "") .build(); p.cargo("rustc -v --example ex1 --example ex2 --crate-type lib,cdylib") .with_status(101) .with_stderr_data(str![[r#" [ERROR] crate types to rustc can only be passed to one target, consider filtering the package by passing, e.g., `--lib` or `--example` to specify a single target "#]]) .run(); } #[cargo_test] fn fails_with_crate_type_to_binary() { let p = project().file("src/bin/foo.rs", "fn main() {}").build(); p.cargo("rustc --crate-type lib") .with_status(101) .with_stderr_data(str![[r#" [ERROR] crate types can only be specified for libraries and example libraries. Binaries, tests, and benchmarks are always the `bin` crate type "#]]) .run(); } #[cargo_test] fn build_with_crate_type_for_foo() { let p = project().file("src/lib.rs", "").build(); p.cargo("rustc -v --crate-type cdylib") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type cdylib [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn build_with_crate_type_for_foo_with_deps() { let p = project() .file( "src/lib.rs", r#" extern crate a; pub fn foo() { a::hello(); } "#, ) .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] a = { path = "a" } "#, ) .file("a/Cargo.toml", &basic_manifest("a", "0.1.0")) .file("a/src/lib.rs", "pub fn hello() {}") .build(); p.cargo("rustc -v --crate-type cdylib") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] a v0.1.0 ([ROOT]/foo/a) [RUNNING] `rustc --crate-name a --edition=2015 a/src/lib.rs [..]--crate-type lib [..]` [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type cdylib [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn build_with_crate_types_for_foo() { let p = project().file("src/lib.rs", "").build(); p.cargo("rustc -v --crate-type lib,cdylib") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --crate-type cdylib [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn build_with_crate_type_to_example() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [[example]] name = "ex" crate-type = ["rlib"] "#, ) .file("src/lib.rs", "") .file("examples/ex.rs", "") .build(); p.cargo("rustc -v --example ex --crate-type cdylib") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib [..]` [RUNNING] `rustc --crate-name ex --edition=2015 examples/ex.rs [..]--crate-type cdylib [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn build_with_crate_types_to_example() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [[example]] name = "ex" crate-type = ["rlib"] "#, ) .file("src/lib.rs", "") .file("examples/ex.rs", "") .build(); p.cargo("rustc -v --example ex --crate-type lib,cdylib") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib [..]` [RUNNING] `rustc --crate-name ex --edition=2015 examples/ex.rs [..]--crate-type lib --crate-type cdylib [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn build_with_crate_types_to_one_of_multi_examples() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [[example]] name = "ex1" crate-type = ["rlib"] [[example]] name = "ex2" crate-type = ["rlib"] "#, ) .file("src/lib.rs", "") .file("examples/ex1.rs", "") .file("examples/ex2.rs", "") .build(); p.cargo("rustc -v --example ex1 --crate-type lib,cdylib") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib [..]` [RUNNING] `rustc --crate-name ex1 --edition=2015 examples/ex1.rs [..]--crate-type lib --crate-type cdylib [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn build_with_args_to_one_of_multiple_tests() { let p = project() .file("tests/foo.rs", r#" "#) .file("tests/bar.rs", r#" "#) .file("tests/baz.rs", r#" "#) .file("src/lib.rs", r#" "#) .build(); p.cargo("rustc -v --test bar -- -C debug-assertions") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C debuginfo=2 [..]-C metadata=[..] --out-dir [..]` [RUNNING] `rustc --crate-name bar --edition=2015 tests/bar.rs [..]--emit=[..]link[..]-C debuginfo=2 [..]--test[..]-C debug-assertions[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn build_foo_with_bar_dependency() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "../bar" "#, ) .file("src/main.rs", "extern crate bar; fn main() { bar::baz() }") .build(); let _bar = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "pub fn baz() {}") .build(); foo.cargo("rustc -v -- -C debug-assertions") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.1.0 ([ROOT]/bar) [RUNNING] `rustc --crate-name bar [..] -C debuginfo=2[..]` [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] -C debuginfo=2 [..]-C debug-assertions[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn build_only_bar_dependency() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "../bar" "#, ) .file("src/main.rs", "extern crate bar; fn main() { bar::baz() }") .build(); let _bar = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("src/lib.rs", "pub fn baz() {}") .build(); foo.cargo("rustc -v -p bar -- -C debug-assertions") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.1.0 ([ROOT]/bar) [RUNNING] `rustc --crate-name bar [..]--crate-type lib [..] -C debug-assertions[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn targets_selected_default() { let p = project().file("src/main.rs", "fn main() {}").build(); p.cargo("rustc -v") // bin .with_stderr_contains( "[RUNNING] `rustc --crate-name foo --edition=2015 src/main.rs [..]--crate-type bin \ --emit=[..]link[..]", ) // bench .with_stderr_does_not_contain( "[RUNNING] `rustc --crate-name foo --edition=2015 src/main.rs [..]--emit=[..]link \ -C opt-level=3 --test [..]", ) // unit test .with_stderr_does_not_contain( "[RUNNING] `rustc --crate-name foo --edition=2015 src/main.rs [..]--emit=[..]link \ -C debuginfo=2 [..]--test [..]", ) .run(); } #[cargo_test] fn targets_selected_all() { let p = project().file("src/main.rs", "fn main() {}").build(); p.cargo("rustc -v --all-targets") // bin and unit test .with_stderr_data( str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/main.rs [..]--crate-type bin --emit=[..]link[..]` [RUNNING] `rustc --crate-name foo --edition=2015 src/main.rs [..]--emit[..]link[..] -C debuginfo=2 [..]--test [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]].unordered() ) .run(); } #[cargo_test] fn fail_with_multiple_packages() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "../bar" [dependencies.baz] path = "../baz" "#, ) .file("src/main.rs", "fn main() {}") .build(); let _bar = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) .file( "src/main.rs", r#" fn main() { if cfg!(flag = "1") { println!("Yeah from bar!"); } } "#, ) .build(); let _baz = project() .at("baz") .file("Cargo.toml", &basic_manifest("baz", "0.1.0")) .file( "src/main.rs", r#" fn main() { if cfg!(flag = "1") { println!("Yeah from baz!"); } } "#, ) .build(); foo.cargo("rustc -v -p bar -p baz") .with_status(1) .with_stderr_data(str![[r#" [ERROR] the argument '--package []' cannot be used multiple times ... "#]]) .run(); } #[cargo_test] fn fail_with_bad_bin_no_package() { let p = project() .file( "src/main.rs", r#" fn main() { println!("hello a.rs"); } "#, ) .build(); p.cargo("rustc --bin main") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no bin target named `main` [HELP] available bin targets: foo ... "#]]) .run(); } #[cargo_test] fn fail_with_glob() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() { break_the_build(); }") .build(); p.cargo("rustc -p '*z'") .with_status(101) .with_stderr_data(str![[r#" [ERROR] Glob patterns on package selection are not supported. "#]]) .run(); } #[cargo_test] fn rustc_with_other_profile() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dev-dependencies] a = { path = "a" } "#, ) .file( "src/main.rs", r#" #[cfg(test)] extern crate a; #[test] fn foo() {} "#, ) .file("a/Cargo.toml", &basic_manifest("a", "0.1.0")) .file("a/src/lib.rs", "") .build(); p.cargo("rustc --profile test").run(); } #[cargo_test] fn rustc_fingerprint() { // Verify that the fingerprint includes the rustc args. let p = project() .file("Cargo.toml", &basic_lib_manifest("foo")) .file("src/lib.rs", "") .build(); p.cargo("rustc -v -- -C debug-assertions") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]-C debug-assertions[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("rustc -v -- -C debug-assertions") .with_stderr_data(str![[r#" [FRESH] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("rustc -v") .with_stderr_does_not_contain("-C debug-assertions") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("rustc -v") .with_stderr_data(str![[r#" [FRESH] foo v0.5.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn rustc_test_with_implicit_bin() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file( "src/main.rs", r#" #[cfg(foo)] fn f() { compile_fail!("Foo shouldn't be set."); } fn main() {} "#, ) .file( "tests/test1.rs", r#" #[cfg(not(foo))] fn f() { compile_fail!("Foo should be set."); } "#, ) .build(); p.cargo("rustc --test test1 -v -- --cfg foo") .with_stderr_data( str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name test1 --edition=2015 tests/test1.rs [..] --cfg foo[..]` [RUNNING] `rustc --crate-name foo --edition=2015 src/main.rs [..]` ... [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn rustc_with_print_cfg_single_target() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", r#"fn main() {} "#) .build(); p.cargo("rustc -Z unstable-options --target x86_64-pc-windows-msvc --print cfg") .masquerade_as_nightly_cargo(&["print"]) .with_stdout_data( str![[r#" debug_assertions target_arch="x86_64" target_endian="little" target_env="msvc" target_family="windows" target_os="windows" target_pointer_width="64" target_vendor="pc" windows ... "#]] .unordered(), ) .run(); } #[cargo_test] fn rustc_with_print_cfg_multiple_targets() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", r#"fn main() {} "#) .build(); p.cargo("rustc -Z unstable-options --target x86_64-pc-windows-msvc --target i686-unknown-linux-gnu --print cfg") .masquerade_as_nightly_cargo(&["print"]) .with_stdout_data(str![[r#" debug_assertions target_arch="x86" target_endian="little" target_env="gnu" target_family="unix" target_os="linux" target_pointer_width="32" target_vendor="unknown" unix debug_assertions target_arch="x86_64" target_endian="little" target_env="msvc" target_family="windows" target_os="windows" target_pointer_width="64" target_vendor="pc" windows ... "#]].unordered()) .run(); } #[cargo_test] fn rustc_with_print_cfg_rustflags_env_var() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", r#"fn main() {} "#) .build(); p.cargo("rustc -Z unstable-options --target x86_64-pc-windows-msvc --print cfg") .masquerade_as_nightly_cargo(&["print"]) .env("RUSTFLAGS", "-C target-feature=+crt-static") .with_stdout_data( str![[r#" debug_assertions target_arch="x86_64" target_endian="little" target_env="msvc" target_family="windows" target_feature="crt-static" target_os="windows" target_pointer_width="64" target_vendor="pc" windows ... "#]] .unordered(), ) .run(); } #[cargo_test] fn rustc_with_print_cfg_config_toml() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file( ".cargo/config.toml", r#" [target.x86_64-pc-windows-msvc] rustflags = ["-C", "target-feature=+crt-static"] "#, ) .file("src/main.rs", r#"fn main() {} "#) .build(); p.cargo("rustc -Z unstable-options --target x86_64-pc-windows-msvc --print cfg") .masquerade_as_nightly_cargo(&["print"]) .env("RUSTFLAGS", "-C target-feature=+crt-static") .with_stdout_data( str![[r#" debug_assertions target_arch="x86_64" target_endian="little" target_env="msvc" target_family="windows" target_feature="crt-static" target_os="windows" target_pointer_width="64" target_vendor="pc" windows ... "#]] .unordered(), ) .run(); } #[cargo_test] fn rustc_with_print_cfg_config_toml_env() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file( "targets/best-target.json", r#"{ "llvm-target": "x86_64-unknown-none", "target-pointer-width": "64", "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", "arch": "x86_64" }"#, ) .file( ".cargo/config.toml", r#" [build] target = "best-target" [env] RUST_TARGET_PATH = { value = "./targets", relative = true } "#, ) .file("src/main.rs", r#"fn main() {} "#) .build(); p.cargo("rustc -Z unstable-options --print cfg") .masquerade_as_nightly_cargo(&["print"]) .with_stdout_data(str!["..."].unordered()) .run(); } #[cargo_test] fn precedence() { // Ensure that the precedence of cargo-rustc is only lower than RUSTFLAGS, // but higher than most flags set by cargo. // // See rust-lang/cargo#14346 let p = project() .file( "Cargo.toml", r#" [package] name = "foo" edition = "2021" [lints.rust] unexpected_cfgs = "allow" "#, ) .file("src/lib.rs", "") .build(); p.cargo("rustc --release -v -- --cfg cargo_rustc -C strip=symbols") .env("RUSTFLAGS", "--cfg from_rustflags") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.0 ([ROOT]/foo) [RUNNING] `rustc [..]-C strip=debuginfo [..]--cfg cargo_rustc -C strip=symbols --cfg from_rustflags` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn build_with_duplicate_crate_types() { let p = project().file("src/lib.rs", "").build(); p.cargo("rustc -v --crate-type staticlib --crate-type staticlib") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..] --crate-type staticlib --emit[..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("rustc -v --crate-type staticlib --crate-type staticlib") .with_stderr_data(str![[r#" [FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } cargo-0.91.0/tests/testsuite/rustc_info_cache.rs000064400000000000000000000147031046102023000200520ustar 00000000000000//! Tests for the cache file for the rustc version info. use std::env; use crate::prelude::*; use cargo_test_support::basic_bin_manifest; use cargo_test_support::{basic_manifest, project}; const MISS: &str = "[..] rustc info cache miss[..]"; const HIT: &str = "[..]rustc info cache hit[..]"; const UPDATE: &str = "[..]updated rustc info cache[..]"; #[cargo_test] fn rustc_info_cache() { let p = project() .file("src/main.rs", r#"fn main() { println!("hello"); }"#) .build(); p.cargo("build") .env("CARGO_LOG", "cargo::util::rustc=debug") .with_stderr_contains("[..]failed to read rustc info cache[..]") .with_stderr_contains(MISS) .with_stderr_does_not_contain(HIT) .with_stderr_contains(UPDATE) .run(); p.cargo("build") .env("CARGO_LOG", "cargo::util::rustc=debug") .with_stderr_contains("[..]reusing existing rustc info cache[..]") .with_stderr_contains(HIT) .with_stderr_does_not_contain(MISS) .with_stderr_does_not_contain(UPDATE) .run(); p.cargo("build") .env("CARGO_LOG", "cargo::util::rustc=debug") .env("CARGO_CACHE_RUSTC_INFO", "0") .with_stderr_contains("[..]rustc info cache disabled[..]") .with_stderr_does_not_contain(UPDATE) .run(); let other_rustc = { let p = project() .at("compiler") .file("Cargo.toml", &basic_manifest("compiler", "0.1.0")) .file( "src/main.rs", r#" use std::process::Command; use std::env; fn main() { let mut cmd = Command::new("rustc"); for arg in env::args_os().skip(1) { cmd.arg(arg); } std::process::exit(cmd.status().unwrap().code().unwrap()); } "#, ) .build(); p.cargo("build").run(); p.root() .join("target/debug/compiler") .with_extension(env::consts::EXE_EXTENSION) }; p.cargo("build") .env("CARGO_LOG", "cargo::util::rustc=debug") .env("RUSTC", other_rustc.display().to_string()) .with_stderr_contains("[..]different compiler, creating new rustc info cache[..]") .with_stderr_contains(MISS) .with_stderr_does_not_contain(HIT) .with_stderr_contains(UPDATE) .run(); p.cargo("build") .env("CARGO_LOG", "cargo::util::rustc=debug") .env("RUSTC", other_rustc.display().to_string()) .with_stderr_contains("[..]reusing existing rustc info cache[..]") .with_stderr_contains(HIT) .with_stderr_does_not_contain(MISS) .with_stderr_does_not_contain(UPDATE) .run(); other_rustc.move_into_the_future(); p.cargo("build") .env("CARGO_LOG", "cargo::util::rustc=debug") .env("RUSTC", other_rustc.display().to_string()) .with_stderr_contains("[..]different compiler, creating new rustc info cache[..]") .with_stderr_contains(MISS) .with_stderr_does_not_contain(HIT) .with_stderr_contains(UPDATE) .run(); p.cargo("build") .env("CARGO_LOG", "cargo::util::rustc=debug") .env("RUSTC", other_rustc.display().to_string()) .with_stderr_contains("[..]reusing existing rustc info cache[..]") .with_stderr_contains(HIT) .with_stderr_does_not_contain(MISS) .with_stderr_does_not_contain(UPDATE) .run(); } #[cargo_test] fn rustc_info_cache_with_wrappers() { let wrapper_project = project() .at("wrapper") .file("Cargo.toml", &basic_bin_manifest("wrapper")) .file("src/main.rs", r#"fn main() { }"#) .build(); let wrapper = wrapper_project.bin("wrapper"); let p = project() .file( "Cargo.toml", r#" [package] name = "test" version = "0.0.0" authors = [] [workspace] "#, ) .file("src/main.rs", r#"fn main() { println!("hello"); }"#) .build(); for &wrapper_env in ["RUSTC_WRAPPER", "RUSTC_WORKSPACE_WRAPPER"].iter() { p.cargo("clean").with_status(0).run(); wrapper_project.change_file( "src/main.rs", r#" fn main() { let mut args = std::env::args_os(); let _me = args.next().unwrap(); let rustc = args.next().unwrap(); let status = std::process::Command::new(rustc).args(args).status().unwrap(); std::process::exit(if status.success() { 0 } else { 1 }) } "#, ); wrapper_project.cargo("build").with_status(0).run(); p.cargo("build") .env("CARGO_LOG", "cargo::util::rustc=debug") .env(wrapper_env, &wrapper) .with_stderr_contains("[..]failed to read rustc info cache[..]") .with_stderr_contains(MISS) .with_stderr_contains(UPDATE) .with_stderr_does_not_contain(HIT) .with_status(0) .run(); p.cargo("build") .env("CARGO_LOG", "cargo::util::rustc=debug") .env(wrapper_env, &wrapper) .with_stderr_contains("[..]reusing existing rustc info cache[..]") .with_stderr_contains(HIT) .with_stderr_does_not_contain(UPDATE) .with_stderr_does_not_contain(MISS) .with_status(0) .run(); wrapper_project.change_file("src/main.rs", r#"fn main() { panic!() }"#); wrapper_project.cargo("build").with_status(0).run(); p.cargo("build") .env("CARGO_LOG", "cargo::util::rustc=debug") .env(wrapper_env, &wrapper) .with_stderr_contains("[..]different compiler, creating new rustc info cache[..]") .with_stderr_contains(MISS) .with_stderr_contains(UPDATE) .with_stderr_does_not_contain(HIT) .with_status(101) .run(); p.cargo("build") .env("CARGO_LOG", "cargo::util::rustc=debug") .env(wrapper_env, &wrapper) .with_stderr_contains("[..]reusing existing rustc info cache[..]") .with_stderr_contains(HIT) .with_stderr_does_not_contain(UPDATE) .with_stderr_does_not_contain(MISS) .with_status(101) .run(); } } cargo-0.91.0/tests/testsuite/rustdoc.rs000064400000000000000000000223551046102023000162410ustar 00000000000000//! Tests for the `cargo rustdoc` command. use crate::prelude::*; use cargo_test_support::str; use cargo_test_support::{basic_manifest, cross_compile, project}; use crate::utils::cross_compile::disabled as cross_compile_disabled; #[cargo_test] fn rustdoc_simple() { let p = project().file("src/lib.rs", "").build(); p.cargo("rustdoc -v") .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustdoc [..] --crate-name foo src/lib.rs -o [ROOT]/foo/target/doc [..] -L dependency=[ROOT]/foo/target/debug/deps [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); } #[cargo_test] fn rustdoc_simple_html() { let p = project().file("src/lib.rs", "").build(); p.cargo("rustdoc --output-format html --open -v") .with_status(101) .with_stderr_data(str![[r#" [ERROR] the `--output-format` flag is unstable, and only available on the nightly channel of Cargo, but this is the `stable` channel See https://doc.rust-lang.org/book/[..].html for more information about Rust release channels. See https://github.com/rust-lang/cargo/issues/12103 for more information about the `--output-format` flag. "#]]) .run(); } #[cargo_test(nightly, reason = "--output-format is unstable")] fn rustdoc_simple_json() { let p = project().file("src/lib.rs", "").build(); p.cargo("rustdoc -Z unstable-options --output-format json -v") .masquerade_as_nightly_cargo(&["rustdoc-output-format"]) .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustdoc [..] --crate-name foo [..]-o [ROOT]/foo/target/doc [..] --output-format=json[..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo.json "#]]) .run(); assert!(p.root().join("target/doc/foo.json").is_file()); } #[cargo_test] fn rustdoc_invalid_output_format() { let p = project().file("src/lib.rs", "").build(); p.cargo("rustdoc -Z unstable-options --output-format pdf -v") .masquerade_as_nightly_cargo(&["rustdoc-output-format"]) .with_status(1) .with_stderr_data(str![[r#" [ERROR] invalid value 'pdf' for '--output-format ' [possible values: html, json] For more information, try '--help'. "#]]) .run(); } #[cargo_test] fn rustdoc_json_stable() { let p = project().file("src/lib.rs", "").build(); p.cargo("rustdoc -Z unstable-options --output-format json -v") .with_status(101) .with_stderr_data( str![[r#" [ERROR] the `-Z` flag is only accepted on the nightly channel of Cargo, but this is the `stable` channel See https://doc.rust-lang.org/book/[..].html for more information about Rust release channels. "#]] ) .run(); } #[cargo_test] fn rustdoc_json_without_unstable_options() { let p = project().file("src/lib.rs", "").build(); p.cargo("rustdoc --output-format json -v") .masquerade_as_nightly_cargo(&["rustdoc-output-format"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] the `--output-format` flag is unstable, pass `-Z unstable-options` to enable it See https://github.com/rust-lang/cargo/issues/12103 for more information about the `--output-format` flag. "#]]) .run(); } #[cargo_test] fn rustdoc_args() { let p = project().file("src/lib.rs", "").build(); p.cargo("rustdoc -v -- --cfg=foo") .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustdoc [..] --crate-name foo src/lib.rs -o [ROOT]/foo/target/doc [..]-C metadata=[..] -L dependency=[ROOT]/foo/target/debug/deps [..]--cfg=foo[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); } #[cargo_test] fn rustdoc_binary_args_passed() { let p = project().file("src/main.rs", "").build(); p.cargo("rustdoc -v") .arg("--") .arg("--markdown-no-toc") .with_stderr_data(str![[r#" ... [RUNNING] `rustdoc [..] --markdown-no-toc[..]` ... "#]]) .run(); } #[cargo_test] fn rustdoc_foo_with_bar_dependency() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "../bar" "#, ) .file("src/lib.rs", "extern crate bar; pub fn foo() {}") .build(); let _bar = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("src/lib.rs", "pub fn baz() {}") .build(); foo.cargo("rustdoc -v -- --cfg=foo") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [CHECKING] bar v0.0.1 ([ROOT]/bar) [RUNNING] `rustc [..] [ROOT]/bar/src/lib.rs [..]` [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustdoc [..] --crate-name foo src/lib.rs -o [ROOT]/foo/target/doc [..]-C metadata=[..] -L dependency=[ROOT]/foo/target/debug/deps --extern [..]--cfg=foo[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); } #[cargo_test] fn rustdoc_only_bar_dependency() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "../bar" "#, ) .file("src/main.rs", "extern crate bar; fn main() { bar::baz() }") .build(); let _bar = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("src/lib.rs", "pub fn baz() {}") .build(); foo.cargo("rustdoc -v -p bar -- --cfg=foo") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [DOCUMENTING] bar v0.0.1 ([ROOT]/bar) [RUNNING] `rustdoc [..] --crate-name bar [ROOT]/bar/src/lib.rs -o [ROOT]/foo/target/doc [..]-C metadata=[..] -L dependency=[ROOT]/foo/target/debug/deps [..]--cfg=foo[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/bar/index.html "#]]) .run(); } #[cargo_test] fn rustdoc_same_name_documents_lib() { let p = project() .file("src/main.rs", "fn main() {}") .file("src/lib.rs", r#" "#) .build(); p.cargo("rustdoc -v -- --cfg=foo") .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustdoc [..] --crate-name foo src/lib.rs -o [ROOT]/foo/target/doc [..]-C metadata=[..] -L dependency=[ROOT]/foo/target/debug/deps [..]--cfg=foo[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); } #[cargo_test] fn features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] quux = [] "#, ) .file("src/lib.rs", "") .build(); p.cargo("rustdoc --verbose --features quux") .with_stderr_data(str![[r#" ... [RUNNING] `rustdoc [..]feature=[..]quux[..]` ... "#]]) .run(); } #[cargo_test] fn proc_macro_crate_type() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lib] proc-macro = true "#, ) .file("src/lib.rs", "") .build(); p.cargo("rustdoc --verbose") .with_stderr_data(str![[r#" ... [RUNNING] `rustdoc --edition=2015 --crate-type proc-macro [..]` ... "#]]) .run(); } #[cargo_test] fn rustdoc_target() { if cross_compile_disabled() { return; } let p = project().file("src/lib.rs", "").build(); p.cargo("rustdoc --verbose --target") .arg(cross_compile::alternate()) .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustdoc [..]--crate-name foo src/lib.rs [..]--target [ALT_TARGET] -o [ROOT]/foo/target/[ALT_TARGET]/doc [..] -L dependency=[ROOT]/foo/target/[ALT_TARGET]/debug/deps -L dependency=[ROOT]/foo/target/debug/deps[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/[..]/doc/foo/index.html "#]]) .run(); } #[cargo_test] fn fail_with_glob() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() { break_the_build(); }") .build(); p.cargo("rustdoc -p '*z'") .with_status(101) .with_stderr_data(str![[r#" [ERROR] Glob patterns on package selection are not supported. "#]]) .run(); } cargo-0.91.0/tests/testsuite/rustdoc_extern_html.rs000064400000000000000000000373521046102023000206550ustar 00000000000000//! Tests for the -Zrustdoc-map feature. use crate::prelude::*; use cargo_test_support::registry::{self, Package}; use cargo_test_support::{Project, paths, project, str}; fn basic_project() -> Project { Package::new("bar", "1.0.0") .file("src/lib.rs", "pub struct Straw;") .publish(); project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [dependencies] bar = "1.0" "#, ) .file( "src/lib.rs", r#" pub fn myfun() -> Option { None } "#, ) .build() } #[cargo_test] fn ignores_on_stable() { // Requires -Zrustdoc-map to use. let p = basic_project(); p.cargo("doc -v --no-deps") .with_stderr_does_not_contain("[..]--extern-html-root-url[..]") .run(); } #[cargo_test(nightly, reason = "--extern-html-root-url is unstable")] fn simple() { // Basic test that it works with crates.io. let p = basic_project(); p.cargo("doc -v --no-deps -Zrustdoc-map") .masquerade_as_nightly_cargo(&["rustdoc-map"]) .with_stderr_data(str![[r#" ... [RUNNING] `rustdoc [..]--crate-name foo [..]--extern-html-root-url [..]bar=https://docs.rs/bar/1.0.0/[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); let myfun = p.read_file("target/doc/foo/fn.myfun.html"); assert!(myfun.contains(r#"href="https://docs.rs/bar/1.0.0/bar/struct.Straw.html""#)); } #[ignore = "Broken, temporarily disabled until https://github.com/rust-lang/rust/pull/82776 is resolved."] #[cargo_test] // #[cargo_test(nightly, reason = "--extern-html-root-url is unstable")] fn std_docs() { // Mapping std docs somewhere else. // For local developers, skip this test if docs aren't installed. let docs = std::path::Path::new(&paths::sysroot()).join("share/doc/rust/html"); if !docs.exists() { if cargo_util::is_ci() { panic!("std docs are not installed, check that the rust-docs component is installed"); } else { eprintln!( "documentation not found at {}, \ skipping test (run `rustdoc component add rust-docs` to install", docs.display() ); return; } } let p = basic_project(); p.change_file( ".cargo/config.toml", r#" [doc.extern-map] std = "local" "#, ); p.cargo("doc -v --no-deps -Zrustdoc-map") .masquerade_as_nightly_cargo(&["rustdoc-map"]) .with_stderr_contains("[RUNNING] `rustdoc [..]--crate-name foo [..]std=file://[..]") .run(); let myfun = p.read_file("target/doc/foo/fn.myfun.html"); assert!(myfun.contains(r#"share/doc/rust/html/core/option/enum.Option.html""#)); p.change_file( ".cargo/config.toml", r#" [doc.extern-map] std = "https://example.com/rust/" "#, ); p.cargo("doc -v --no-deps -Zrustdoc-map") .masquerade_as_nightly_cargo(&["rustdoc-map"]) .with_stderr_contains( "[RUNNING] `rustdoc [..]--crate-name foo [..]std=https://example.com/rust/[..]", ) .run(); let myfun = p.read_file("target/doc/foo/fn.myfun.html"); assert!(myfun.contains(r#"href="https://example.com/rust/core/option/enum.Option.html""#)); } #[cargo_test(nightly, reason = "--extern-html-root-url is unstable")] fn renamed_dep() { // Handles renamed dependencies. Package::new("bar", "1.0.0") .file("src/lib.rs", "pub struct Straw;") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [dependencies] groovy = { version = "1.0", package = "bar" } "#, ) .file( "src/lib.rs", r#" pub fn myfun() -> Option { None } "#, ) .build(); p.cargo("doc -v --no-deps -Zrustdoc-map") .masquerade_as_nightly_cargo(&["rustdoc-map"]) .with_stderr_data(str![[r#" ... [RUNNING] `rustdoc [..]--crate-name foo [..]--extern-html-root-url [..]bar=https://docs.rs/bar/1.0.0/[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); let myfun = p.read_file("target/doc/foo/fn.myfun.html"); assert!(myfun.contains(r#"href="https://docs.rs/bar/1.0.0/bar/struct.Straw.html""#)); } #[cargo_test(nightly, reason = "--extern-html-root-url is unstable")] fn lib_name() { // Handles lib name != package name. Package::new("bar", "1.0.0") .file( "Cargo.toml", r#" [package] name = "bar" version = "1.0.0" [lib] name = "rumpelstiltskin" "#, ) .file("src/lib.rs", "pub struct Straw;") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] bar = "1.0" "#, ) .file( "src/lib.rs", r#" pub fn myfun() -> Option { None } "#, ) .build(); p.cargo("doc -v --no-deps -Zrustdoc-map") .masquerade_as_nightly_cargo(&["rustdoc-map"]) .with_stderr_data(str![[r#" ... [RUNNING] `rustdoc [..]--crate-name foo [..]--extern-html-root-url [..]rumpelstiltskin=https://docs.rs/bar/1.0.0/[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); let myfun = p.read_file("target/doc/foo/fn.myfun.html"); assert!( myfun.contains(r#"href="https://docs.rs/bar/1.0.0/rumpelstiltskin/struct.Straw.html""#) ); } #[cargo_test(nightly, reason = "--extern-html-root-url is unstable")] fn alt_registry() { // Supports other registry names. registry::alt_init(); Package::new("bar", "1.0.0") .alternative(true) .file( "src/lib.rs", r#" extern crate baz; pub struct Queen; pub use baz::King; "#, ) .registry_dep("baz", "1.0") .publish(); Package::new("baz", "1.0.0") .alternative(true) .file("src/lib.rs", "pub struct King;") .publish(); Package::new("grimm", "1.0.0") .file("src/lib.rs", "pub struct Gold;") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [dependencies] bar = { version = "1.0", registry="alternative" } grimm = "1.0" "#, ) .file( "src/lib.rs", r#" pub fn queen() -> bar::Queen { bar::Queen } pub fn king() -> bar::King { bar::King } pub fn gold() -> grimm::Gold { grimm::Gold } "#, ) .file( ".cargo/config.toml", r#" [doc.extern-map.registries] alternative = "https://example.com/{pkg_name}/{version}/" crates-io = "https://docs.rs/" "#, ) .build(); p.cargo("doc -v --no-deps -Zrustdoc-map") .masquerade_as_nightly_cargo(&["rustdoc-map"]) .with_stderr_data(str![[r#" ... [RUNNING] `rustdoc [..]--crate-name foo [..]--extern-html-root-url [..]bar=https://example.com/bar/1.0.0/[..] --extern-html-root-url [..]baz=https://example.com/baz/1.0.0/[..] --extern-html-root-url [..]grimm=https://docs.rs/grimm/1.0.0/[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); let queen = p.read_file("target/doc/foo/fn.queen.html"); assert!(queen.contains(r#"href="https://example.com/bar/1.0.0/bar/struct.Queen.html""#)); let king = p.read_file("target/doc/foo/fn.king.html"); assert!(king.contains(r#"href="https://example.com/baz/1.0.0/baz/struct.King.html""#)); let gold = p.read_file("target/doc/foo/fn.gold.html"); assert!(gold.contains(r#"href="https://docs.rs/grimm/1.0.0/grimm/struct.Gold.html""#)); } #[cargo_test(nightly, reason = "--extern-html-root-url is unstable")] fn multiple_versions() { // What happens when there are multiple versions. // NOTE: This is currently broken behavior. Rustdoc does not provide a way // to match renamed dependencies. Package::new("bar", "1.0.0") .file("src/lib.rs", "pub struct Spin;") .publish(); Package::new("bar", "2.0.0") .file("src/lib.rs", "pub struct Straw;") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [dependencies] bar = "1.0" bar2 = {version="2.0", package="bar"} "#, ) .file( "src/lib.rs", " pub fn fn1() -> bar::Spin {bar::Spin} pub fn fn2() -> bar2::Straw {bar2::Straw} ", ) .build(); p.cargo("doc -v --no-deps -Zrustdoc-map") .masquerade_as_nightly_cargo(&["rustdoc-map"]) .with_stderr_data(str![[r#" ... [RUNNING] `rustdoc [..]--crate-name foo [..]--extern-html-root-url [..]bar=https://docs.rs/bar/1.0.0/[..] --extern-html-root-url [..]bar=https://docs.rs/bar/2.0.0/[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); let fn1 = p.read_file("target/doc/foo/fn.fn1.html"); // This should be 1.0.0, rustdoc seems to use the last entry when there // are duplicates. assert!(fn1.contains(r#"href="https://docs.rs/bar/2.0.0/bar/struct.Spin.html""#)); let fn2 = p.read_file("target/doc/foo/fn.fn2.html"); assert!(fn2.contains(r#"href="https://docs.rs/bar/2.0.0/bar/struct.Straw.html""#)); } #[cargo_test(nightly, reason = "--extern-html-root-url is unstable")] fn rebuilds_when_changing() { // Make sure it rebuilds if the map changes. let p = basic_project(); p.cargo("doc -v --no-deps -Zrustdoc-map") .masquerade_as_nightly_cargo(&["rustdoc-map"]) .with_stderr_data(str![[r#" ... [RUNNING] `rustdoc [..]--extern-html-root-url[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); // This also tests that the map for docs.rs can be overridden. p.change_file( ".cargo/config.toml", r#" [doc.extern-map.registries] crates-io = "https://example.com/" "#, ); p.cargo("doc -v --no-deps -Zrustdoc-map") .masquerade_as_nightly_cargo(&["rustdoc-map"]) .with_stderr_data(str![[r#" ... [RUNNING] `rustdoc [..]--extern-html-root-url [..]bar=https://example.com/bar/1.0.0/[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); } #[cargo_test(nightly, reason = "--extern-html-root-url is unstable")] fn alt_sparse_registry() { // Supports other registry names. registry::init(); let _registry = registry::RegistryBuilder::new() .http_index() .alternative() .build(); Package::new("bar", "1.0.0") .alternative(true) .file( "src/lib.rs", r#" extern crate baz; pub struct Queen; pub use baz::King; "#, ) .registry_dep("baz", "1.0") .publish(); Package::new("baz", "1.0.0") .alternative(true) .file("src/lib.rs", "pub struct King;") .publish(); Package::new("grimm", "1.0.0") .file("src/lib.rs", "pub struct Gold;") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [dependencies] bar = { version = "1.0", registry="alternative" } grimm = "1.0" "#, ) .file( "src/lib.rs", r#" pub fn queen() -> bar::Queen { bar::Queen } pub fn king() -> bar::King { bar::King } pub fn gold() -> grimm::Gold { grimm::Gold } "#, ) .file( ".cargo/config.toml", r#" [doc.extern-map.registries] alternative = "https://example.com/{pkg_name}/{version}/" crates-io = "https://docs.rs/" "#, ) .build(); p.cargo("doc -v --no-deps -Zrustdoc-map") .masquerade_as_nightly_cargo(&["rustdoc-map"]) .with_stderr_data(str![[r#" ... [RUNNING] `rustdoc [..]--crate-name foo [..]--extern-html-root-url [..]bar=https://example.com/bar/1.0.0/[..] --extern-html-root-url [..]baz=https://example.com/baz/1.0.0/[..] --extern-html-root-url [..]grimm=https://docs.rs/grimm/1.0.0/[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); let queen = p.read_file("target/doc/foo/fn.queen.html"); assert!(queen.contains(r#"href="https://example.com/bar/1.0.0/bar/struct.Queen.html""#)); let king = p.read_file("target/doc/foo/fn.king.html"); assert!(king.contains(r#"href="https://example.com/baz/1.0.0/baz/struct.King.html""#)); let gold = p.read_file("target/doc/foo/fn.gold.html"); assert!(gold.contains(r#"href="https://docs.rs/grimm/1.0.0/grimm/struct.Gold.html""#)); } #[cargo_test(nightly, reason = "--extern-html-root-url is unstable")] fn same_deps_multi_occurrence_in_dep_tree() { // rust-lang/cargo#13543 Package::new("baz", "1.0.0") .file("src/lib.rs", "") .publish(); Package::new("bar", "1.0.0") .file("src/lib.rs", "") .dep("baz", "1.0") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" edition = "2018" [dependencies] bar = "1.0" baz = "1.0" "#, ) .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [doc.extern-map.registries] crates-io = "https://docs.rs/" "#, ) .build(); p.cargo("doc -v --no-deps -Zrustdoc-map") .masquerade_as_nightly_cargo(&["rustdoc-map"]) .with_stderr_does_not_contain( "[..]--extern-html-root-url[..]bar=https://docs.rs\ [..]--extern-html-root-url[..]baz=https://docs.rs\ [..]--extern-html-root-url[..]baz=https://docs.rs[..]", ) .with_stderr_contains( "[..]--extern-html-root-url[..]bar=https://docs.rs\ [..]--extern-html-root-url[..]baz=https://docs.rs[..]", ) .run(); } cargo-0.91.0/tests/testsuite/rustdocflags.rs000064400000000000000000000146201046102023000172520ustar 00000000000000//! Tests for setting custom rustdoc flags. use crate::prelude::*; use cargo_test_support::project; use cargo_test_support::rustc_host; use cargo_test_support::rustc_host_env; use cargo_test_support::str; #[cargo_test] fn parses_env() { let p = project().file("src/lib.rs", "").build(); p.cargo("doc -v") .env("RUSTDOCFLAGS", "--cfg=foo") .with_stderr_data(str![[r#" ... [RUNNING] `rustdoc [..] --cfg=foo[..]` ... "#]]) .run(); } #[cargo_test] fn parses_config() { let p = project() .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [build] rustdocflags = ["--cfg", "foo"] "#, ) .build(); p.cargo("doc -v") .with_stderr_data(str![[r#" ... [RUNNING] `rustdoc [..] --cfg foo [..]` ... "#]]) .run(); } #[cargo_test] fn bad_flags() { let p = project().file("src/lib.rs", "").build(); p.cargo("doc") .env("RUSTDOCFLAGS", "--bogus") .with_status(101) .with_stderr_data(str![[r#" ... [ERROR] Unrecognized option: 'bogus' ... "#]]) .run(); } #[cargo_test] fn rerun() { let p = project().file("src/lib.rs", "").build(); p.cargo("doc").env("RUSTDOCFLAGS", "--cfg=foo").run(); p.cargo("doc") .env("RUSTDOCFLAGS", "--cfg=foo") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); p.cargo("doc") .env("RUSTDOCFLAGS", "--cfg=bar") .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .run(); } #[cargo_test] fn rustdocflags_passed_to_rustdoc_through_cargo_test() { let p = project() .file( "src/lib.rs", r#" //! ``` //! assert!(cfg!(do_not_choke)); //! ``` "#, ) .build(); p.cargo("test --doc") .env("RUSTDOCFLAGS", "--cfg do_not_choke") .run(); } #[cargo_test] fn rustdocflags_passed_to_rustdoc_through_cargo_test_only_once() { let p = project().file("src/lib.rs", "").build(); p.cargo("test --doc") .env("RUSTDOCFLAGS", "--markdown-no-toc") .run(); } #[cargo_test] fn rustdocflags_misspelled() { let p = project().file("src/main.rs", "fn main() { }").build(); p.cargo("doc") .env("RUSTDOC_FLAGS", "foo") .with_stderr_data(str![[r#" [WARNING] Cargo does not read `RUSTDOC_FLAGS` environment variable. Did you mean `RUSTDOCFLAGS`? ... "#]]) .run(); } #[cargo_test] fn whitespace() { // Checks behavior of different whitespace characters. let p = project().file("src/lib.rs", "").build(); // "too many operands" p.cargo("doc") .env("RUSTDOCFLAGS", "--crate-version this has spaces") .with_stderr_data(str![[r#" ... [ERROR] could not document `foo` ... "#]]) .with_status(101) .run(); p.cargo("doc") .env_remove("__CARGO_TEST_FORCE_ARGFILE") // Not applicable for argfile. .env( "RUSTDOCFLAGS", "--crate-version 1111\n2222\t3333\u{00a0}4444", ) .run(); let contents = p.read_file("target/doc/foo/index.html"); assert!(contents.contains("1111")); assert!(contents.contains("2222")); assert!(contents.contains("3333")); assert!(contents.contains("4444")); } #[cargo_test] fn not_affected_by_target_rustflags() { let cfg = if cfg!(windows) { "windows" } else { "unix" }; let p = project() .file("src/lib.rs", "") .file( ".cargo/config.toml", &format!( r#" [target.'cfg({cfg})'] rustflags = ["-D", "missing-docs"] [build] rustdocflags = ["--cfg", "foo"] "#, ), ) .build(); // `cargo build` should fail due to missing docs. p.cargo("build -v") .with_status(101) .with_stderr_data(str![[r#" ... [RUNNING] `rustc [..] -D missing-docs` ... "#]]) .run(); // `cargo doc` shouldn't fail. p.cargo("doc -v") .with_stderr_data(str![[r#" ... [RUNNING] `rustdoc [..] --cfg foo[..]` ... "#]]) .run(); } #[cargo_test] fn target_triple_rustdocflags_works() { let host = rustc_host(); let host_env = rustc_host_env(); let p = project().file("src/lib.rs", "").build(); // target.triple.rustdocflags in env works p.cargo("doc -v") .env( &format!("CARGO_TARGET_{host_env}_RUSTDOCFLAGS"), "--cfg=foo", ) .with_stderr_data(str![[r#" ... [RUNNING] `rustdoc[..]--cfg[..]foo[..]` ... "#]]) .run(); // target.triple.rustdocflags in config works p.cargo("doc -v") .arg("--config") .arg(format!("target.{host}.rustdocflags=['--cfg', 'foo']")) .with_stderr_data(str![[r#" ... [RUNNING] `rustdoc [..] --cfg foo [..]` ... "#]]) .run(); } #[cargo_test] fn target_triple_rustdocflags_works_through_cargo_test() { let host = rustc_host(); let host_env = rustc_host_env(); let p = project() .file( "src/lib.rs", r#" //! ``` //! assert!(cfg!(foo)); //! ``` "#, ) .build(); // target.triple.rustdocflags in env works p.cargo("test --doc -v") .env( &format!("CARGO_TARGET_{host_env}_RUSTDOCFLAGS"), "--cfg=foo", ) .with_stderr_data(str![[r#" ... [RUNNING] `rustdoc[..]--test[..]--cfg[..]foo[..]` "#]]) .with_stdout_data(str![[r#" running 1 test test src/lib.rs - (line 2) ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); // target.triple.rustdocflags in config works p.cargo("test --doc -v") .arg("--config") .arg(format!("target.{host}.rustdocflags=['--cfg', 'foo']")) .with_stderr_data(str![[r#" ... [RUNNING] `rustdoc[..]--test[..]--cfg[..]foo[..]` "#]]) .with_stdout_data(str![[r#" running 1 test test src/lib.rs - (line 2) ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); } cargo-0.91.0/tests/testsuite/rustflags.rs000064400000000000000000001326751046102023000165770ustar 00000000000000//! Tests for setting custom rustc flags. use std::fs; use crate::prelude::*; use cargo_test_support::registry::Package; use cargo_test_support::{ RawOutput, basic_manifest, paths, project, project_in_home, rustc_host, str, }; use snapbox::assert_data_eq; #[cargo_test] fn env_rustflags_normal_source() { let p = project() .file("src/lib.rs", "") .file("src/bin/a.rs", "fn main() {}") .file("examples/b.rs", "fn main() {}") .file("tests/c.rs", "#[test] fn f() { }") .file( "benches/d.rs", r#" #![feature(test)] extern crate test; #[bench] fn run1(_ben: &mut test::Bencher) { } "#, ) .build(); // Use RUSTFLAGS to pass an argument that will generate an error p.cargo("check --lib") .env("RUSTFLAGS", "-Z bogus") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); p.cargo("check --bin=a") .env("RUSTFLAGS", "-Z bogus") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); p.cargo("check --example=b") .env("RUSTFLAGS", "-Z bogus") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); p.cargo("test") .env("RUSTFLAGS", "-Z bogus") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); p.cargo("bench") .env("RUSTFLAGS", "-Z bogus") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); } #[cargo_test] fn env_rustflags_build_script() { // RUSTFLAGS should be passed to rustc for build scripts // when --target is not specified. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { assert!(cfg!(foo)); } "#, ) .build(); p.cargo("check").env("RUSTFLAGS", "--cfg foo").run(); } #[cargo_test] fn env_rustflags_build_script_dep() { // RUSTFLAGS should be passed to rustc for build scripts // when --target is not specified. // In this test if --cfg foo is not passed the build will fail. let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" build = "build.rs" [build-dependencies.bar] path = "../bar" "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .build(); let _bar = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.0.1")) .file( "src/lib.rs", r#" fn bar() { } #[cfg(not(foo))] fn bar() { } "#, ) .build(); foo.cargo("check").env("RUSTFLAGS", "--cfg foo").run(); } #[cargo_test] fn env_rustflags_normal_source_with_target() { let p = project() .file("src/lib.rs", "") .file("src/bin/a.rs", "fn main() {}") .file("examples/b.rs", "fn main() {}") .file("tests/c.rs", "#[test] fn f() { }") .file( "benches/d.rs", r#" #![feature(test)] extern crate test; #[bench] fn run1(_ben: &mut test::Bencher) { } "#, ) .build(); let host = &rustc_host(); // Use RUSTFLAGS to pass an argument that will generate an error p.cargo("check --lib --target") .arg(host) .env("RUSTFLAGS", "-Z bogus") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); p.cargo("check --bin=a --target") .arg(host) .env("RUSTFLAGS", "-Z bogus") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); p.cargo("check --example=b --target") .arg(host) .env("RUSTFLAGS", "-Z bogus") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); p.cargo("test --target") .arg(host) .env("RUSTFLAGS", "-Z bogus") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); p.cargo("bench --target") .arg(host) .env("RUSTFLAGS", "-Z bogus") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); } #[cargo_test] fn env_rustflags_build_script_with_target() { // RUSTFLAGS should not be passed to rustc for build scripts // when --target is specified. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { assert!(!cfg!(foo)); } "#, ) .build(); let host = rustc_host(); p.cargo("check --target") .arg(host) .env("RUSTFLAGS", "--cfg foo") .run(); } #[cargo_test] fn env_rustflags_build_script_with_target_doesnt_apply_to_host_kind() { // RUSTFLAGS should *not* be passed to rustc for build scripts when --target is specified as the // host triple even if target-applies-to-host-kind is enabled, to match legacy Cargo behavior. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { assert!(!cfg!(foo)); } "#, ) .file( ".cargo/config.toml", r#" target-applies-to-host = true "#, ) .build(); let host = rustc_host(); p.cargo("check --target") .masquerade_as_nightly_cargo(&["target-applies-to-host"]) .arg(host) .arg("-Ztarget-applies-to-host") .env("RUSTFLAGS", "--cfg foo") .run(); } #[cargo_test] fn env_rustflags_build_script_dep_with_target() { // RUSTFLAGS should not be passed to rustc for build scripts // when --target is specified. // In this test if --cfg foo is passed the build will fail. let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" build = "build.rs" [build-dependencies.bar] path = "../bar" "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .build(); let _bar = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.0.1")) .file( "src/lib.rs", r#" fn bar() { } #[cfg(foo)] fn bar() { } "#, ) .build(); let host = rustc_host(); foo.cargo("check --target") .arg(host) .env("RUSTFLAGS", "--cfg foo") .run(); } #[cargo_test] fn env_rustflags_recompile() { let p = project().file("src/lib.rs", "").build(); p.cargo("check").run(); // Setting RUSTFLAGS forces a recompile p.cargo("check") .env("RUSTFLAGS", "-Z bogus") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); } #[cargo_test] fn env_rustflags_recompile2() { let p = project().file("src/lib.rs", "").build(); p.cargo("check").env("RUSTFLAGS", "--cfg foo").run(); // Setting RUSTFLAGS forces a recompile p.cargo("check") .env("RUSTFLAGS", "-Z bogus") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); } #[cargo_test] fn env_rustflags_no_recompile() { let p = project().file("src/lib.rs", "").build(); p.cargo("check").env("RUSTFLAGS", "--cfg foo").run(); p.cargo("check") .env("RUSTFLAGS", "--cfg foo") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn build_rustflags_normal_source() { let p = project() .file("src/lib.rs", "") .file("src/bin/a.rs", "fn main() {}") .file("examples/b.rs", "fn main() {}") .file("tests/c.rs", "#[test] fn f() { }") .file( "benches/d.rs", r#" #![feature(test)] extern crate test; #[bench] fn run1(_ben: &mut test::Bencher) { } "#, ) .file( ".cargo/config.toml", r#" [build] rustflags = ["-Z", "bogus"] "#, ) .build(); p.cargo("check --lib") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); p.cargo("check --bin=a") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); p.cargo("check --example=b") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); p.cargo("test") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); p.cargo("bench") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); } #[cargo_test] fn build_rustflags_build_script() { // RUSTFLAGS should be passed to rustc for build scripts // when --target is not specified. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { assert!(cfg!(foo)); } "#, ) .file( ".cargo/config.toml", r#" [build] rustflags = ["--cfg", "foo"] "#, ) .build(); p.cargo("check").run(); } #[cargo_test] fn build_rustflags_build_script_dep() { // RUSTFLAGS should be passed to rustc for build scripts // when --target is not specified. // In this test if --cfg foo is not passed the build will fail. let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" build = "build.rs" [build-dependencies.bar] path = "../bar" "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .file( ".cargo/config.toml", r#" [build] rustflags = ["--cfg", "foo"] "#, ) .build(); let _bar = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.0.1")) .file( "src/lib.rs", r#" fn bar() { } #[cfg(not(foo))] fn bar() { } "#, ) .build(); foo.cargo("check").run(); } #[cargo_test] fn build_rustflags_normal_source_with_target() { let p = project() .file("src/lib.rs", "") .file("src/bin/a.rs", "fn main() {}") .file("examples/b.rs", "fn main() {}") .file("tests/c.rs", "#[test] fn f() { }") .file( "benches/d.rs", r#" #![feature(test)] extern crate test; #[bench] fn run1(_ben: &mut test::Bencher) { } "#, ) .file( ".cargo/config.toml", r#" [build] rustflags = ["-Z", "bogus"] "#, ) .build(); let host = &rustc_host(); // Use build.rustflags to pass an argument that will generate an error p.cargo("check --lib --target") .arg(host) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); p.cargo("check --bin=a --target") .arg(host) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); p.cargo("check --example=b --target") .arg(host) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); p.cargo("test --target") .arg(host) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); p.cargo("bench --target") .arg(host) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); } #[cargo_test] fn build_rustflags_build_script_with_target() { // RUSTFLAGS should not be passed to rustc for build scripts // when --target is specified. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { assert!(!cfg!(foo)); } "#, ) .file( ".cargo/config.toml", r#" [build] rustflags = ["--cfg", "foo"] "#, ) .build(); let host = rustc_host(); p.cargo("check --target").arg(host).run(); } #[cargo_test] fn build_rustflags_build_script_dep_with_target() { // RUSTFLAGS should not be passed to rustc for build scripts // when --target is specified. // In this test if --cfg foo is passed the build will fail. let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" build = "build.rs" [build-dependencies.bar] path = "../bar" "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .file( ".cargo/config.toml", r#" [build] rustflags = ["--cfg", "foo"] "#, ) .build(); let _bar = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.0.1")) .file( "src/lib.rs", r#" fn bar() { } #[cfg(foo)] fn bar() { } "#, ) .build(); let host = rustc_host(); foo.cargo("check --target").arg(host).run(); } #[cargo_test] fn build_rustflags_recompile() { let p = project().file("src/lib.rs", "").build(); p.cargo("check").run(); // Setting RUSTFLAGS forces a recompile let config = r#" [build] rustflags = ["-Z", "bogus"] "#; let config_file = paths::root().join("foo/.cargo/config.toml"); fs::create_dir_all(config_file.parent().unwrap()).unwrap(); fs::write(config_file, config).unwrap(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); } #[cargo_test] fn build_rustflags_recompile2() { let p = project().file("src/lib.rs", "").build(); p.cargo("check").env("RUSTFLAGS", "--cfg foo").run(); // Setting RUSTFLAGS forces a recompile let config = r#" [build] rustflags = ["-Z", "bogus"] "#; let config_file = paths::root().join("foo/.cargo/config.toml"); fs::create_dir_all(config_file.parent().unwrap()).unwrap(); fs::write(config_file, config).unwrap(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); } #[cargo_test] fn build_rustflags_no_recompile() { let p = project() .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [build] rustflags = ["--cfg", "foo"] "#, ) .build(); p.cargo("check").env("RUSTFLAGS", "--cfg foo").run(); p.cargo("check") .env("RUSTFLAGS", "--cfg foo") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn build_rustflags_with_home_config() { // We need a config file inside the home directory let home = paths::home(); let home_config = home.join(".cargo"); fs::create_dir(&home_config).unwrap(); fs::write( &home_config.join("config"), r#" [build] rustflags = ["-Cllvm-args=-x86-asm-syntax=intel"] "#, ) .unwrap(); // And we need the project to be inside the home directory // so the walking process finds the home project twice. let p = project_in_home("foo").file("src/lib.rs", "").build(); p.cargo("check -v").run(); } #[cargo_test] fn target_rustflags_normal_source() { let p = project() .file("src/lib.rs", "") .file("src/bin/a.rs", "fn main() {}") .file("examples/b.rs", "fn main() {}") .file("tests/c.rs", "#[test] fn f() { }") .file( "benches/d.rs", r#" #![feature(test)] extern crate test; #[bench] fn run1(_ben: &mut test::Bencher) { } "#, ) .file( ".cargo/config.toml", &format!( " [target.{}] rustflags = [\"-Z\", \"bogus\"] ", rustc_host() ), ) .build(); p.cargo("check --lib") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); p.cargo("check --bin=a") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); p.cargo("check --example=b") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); p.cargo("test") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); p.cargo("bench") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); } #[cargo_test] fn target_rustflags_also_for_build_scripts() { let p = project() .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { assert!(cfg!(foo)); } "#, ) .file( ".cargo/config.toml", &format!( " [target.{}] rustflags = [\"--cfg=foo\"] ", rustc_host() ), ) .build(); p.cargo("check").run(); } #[cargo_test] fn target_rustflags_not_for_build_scripts_with_target() { let host = rustc_host(); let p = project() .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { assert!(!cfg!(foo)); } "#, ) .file( ".cargo/config.toml", &format!( " [target.{}] rustflags = [\"--cfg=foo\"] ", host ), ) .build(); p.cargo("check --target").arg(host).run(); // Enabling -Ztarget-applies-to-host should not make a difference without the config setting p.cargo("check --target") .arg(host) .masquerade_as_nightly_cargo(&["target-applies-to-host"]) .arg("-Ztarget-applies-to-host") .run(); // Even with the setting, the rustflags from `target.` should not apply, to match the legacy // Cargo behavior. p.change_file( ".cargo/config.toml", &format!( " target-applies-to-host = true [target.{}] rustflags = [\"--cfg=foo\"] ", host ), ); p.cargo("check --target") .arg(host) .masquerade_as_nightly_cargo(&["target-applies-to-host"]) .arg("-Ztarget-applies-to-host") .run(); } #[cargo_test] fn build_rustflags_for_build_scripts() { let host = rustc_host(); let p = project() .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { assert!(cfg!(foo), "CFG FOO!"); } "#, ) .file( ".cargo/config.toml", " [build] rustflags = [\"--cfg=foo\"] ", ) .build(); // With "legacy" behavior, build.rustflags should apply to build scripts without --target p.cargo("check").run(); // But should _not_ apply _with_ --target p.cargo("check --target") .arg(host) .with_status(101) .with_stderr_data("...\n[..]CFG FOO![..]\n...") .run(); // Enabling -Ztarget-applies-to-host should not make a difference without the config setting p.cargo("check") .masquerade_as_nightly_cargo(&["target-applies-to-host"]) .arg("-Ztarget-applies-to-host") .run(); p.cargo("check --target") .arg(host) .masquerade_as_nightly_cargo(&["target-applies-to-host"]) .arg("-Ztarget-applies-to-host") .with_status(101) .with_stderr_data("...\n[..]CFG FOO![..]\n...") .run(); // When set to false though, the "proper" behavior where host artifacts _only_ pick up on // [host] should be applied. p.change_file( ".cargo/config.toml", " target-applies-to-host = false [build] rustflags = [\"--cfg=foo\"] ", ); p.cargo("check") .masquerade_as_nightly_cargo(&["target-applies-to-host"]) .arg("-Ztarget-applies-to-host") .with_status(101) .with_stderr_data("...\n[..]CFG FOO![..]\n...") .run(); p.cargo("check --target") .arg(host) .masquerade_as_nightly_cargo(&["target-applies-to-host"]) .arg("-Ztarget-applies-to-host") .with_status(101) .with_stderr_data("...\n[..]CFG FOO![..]\n...") .run(); } #[cargo_test] fn host_rustflags_for_build_scripts() { let host = rustc_host(); let p = project() .file("src/lib.rs", "") .file( "build.rs", r#" // Ensure that --cfg=foo is passed. fn main() { assert!(cfg!(foo)); } "#, ) .file( ".cargo/config.toml", &format!( " target-applies-to-host = false [host.{}] rustflags = [\"--cfg=foo\"] ", host ), ) .build(); p.cargo("check --target") .arg(host) .masquerade_as_nightly_cargo(&["target-applies-to-host", "host-config"]) .arg("-Ztarget-applies-to-host") .arg("-Zhost-config") .run(); } // target.{}.rustflags takes precedence over build.rustflags #[cargo_test] fn target_rustflags_precedence() { let p = project() .file("src/lib.rs", "") .file("src/bin/a.rs", "fn main() {}") .file("examples/b.rs", "fn main() {}") .file("tests/c.rs", "#[test] fn f() { }") .file( ".cargo/config.toml", &format!( " [build] rustflags = [\"--cfg\", \"foo\"] [target.{}] rustflags = [\"-Z\", \"bogus\"] ", rustc_host() ), ) .build(); p.cargo("check --lib") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); p.cargo("check --bin=a") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); p.cargo("check --example=b") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); p.cargo("test") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); p.cargo("bench") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: [..]bogus[..] ... "#]]) .run(); } #[cargo_test] fn cfg_rustflags_normal_source() { let p = project() .file("src/lib.rs", "pub fn t() {}") .file("src/bin/a.rs", "fn main() {}") .file("examples/b.rs", "fn main() {}") .file("tests/c.rs", "#[test] fn f() { }") .file( ".cargo/config.toml", &format!( r#" [target.'cfg({})'] rustflags = ["--cfg", "bar"] "#, if rustc_host().contains("-windows-") { "windows" } else { "not(windows)" } ), ) .build(); p.cargo("build --lib -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] --cfg bar` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build --bin=a -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name a [..] --cfg bar` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build --example=b -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name b [..] --cfg bar` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("test --no-run -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..] --cfg bar` [RUNNING] `rustc [..] --cfg bar` [RUNNING] `rustc [..] --cfg bar` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [EXECUTABLE] `[ROOT]/foo/target/debug/deps/foo-[HASH][EXE]` [EXECUTABLE] `[ROOT]/foo/target/debug/deps/a-[HASH][EXE]` [EXECUTABLE] `[ROOT]/foo/target/debug/deps/c-[HASH][EXE]` "#]]) .run(); p.cargo("bench --no-run -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..] --cfg bar` [RUNNING] `rustc [..] --cfg bar` [RUNNING] `rustc [..] --cfg bar` [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [EXECUTABLE] `[ROOT]/foo/target/release/deps/foo-[HASH][EXE]` [EXECUTABLE] `[ROOT]/foo/target/release/deps/a-[HASH][EXE]` "#]]) .run(); } // target.'cfg(...)'.rustflags takes precedence over build.rustflags #[cargo_test] fn cfg_rustflags_precedence() { let p = project() .file("src/lib.rs", "pub fn t() {}") .file("src/bin/a.rs", "fn main() {}") .file("examples/b.rs", "fn main() {}") .file("tests/c.rs", "#[test] fn f() { }") .file( ".cargo/config.toml", &format!( r#" [build] rustflags = ["--cfg", "foo"] [target.'cfg({})'] rustflags = ["--cfg", "bar"] "#, if rustc_host().contains("-windows-") { "windows" } else { "not(windows)" } ), ) .build(); p.cargo("build --lib -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] --cfg bar` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build --bin=a -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name a [..] --cfg bar` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build --example=b -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name b [..] --cfg bar` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("test --no-run -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..] --cfg bar` [RUNNING] `rustc [..] --cfg bar` [RUNNING] `rustc [..] --cfg bar` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [EXECUTABLE] `[ROOT]/foo/target/debug/deps/foo-[HASH][EXE]` [EXECUTABLE] `[ROOT]/foo/target/debug/deps/a-[HASH][EXE]` [EXECUTABLE] `[ROOT]/foo/target/debug/deps/c-[HASH][EXE]` "#]]) .run(); p.cargo("bench --no-run -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..] --cfg bar` [RUNNING] `rustc [..] --cfg bar` [RUNNING] `rustc [..] --cfg bar` [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [EXECUTABLE] `[ROOT]/foo/target/release/deps/foo-[HASH][EXE]` [EXECUTABLE] `[ROOT]/foo/target/release/deps/a-[HASH][EXE]` "#]]) .run(); } #[cargo_test] fn target_rustflags_string_and_array_form1() { let p1 = project() .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [build] rustflags = ["--cfg", "foo"] "#, ) .build(); p1.cargo("check -v") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] --cfg foo` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let p2 = project() .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [build] rustflags = "--cfg foo" "#, ) .build(); p2.cargo("check -v") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] --cfg foo` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn target_rustflags_string_and_array_form2() { let p1 = project() .file( ".cargo/config.toml", &format!( r#" [target.{}] rustflags = ["--cfg", "foo"] "#, rustc_host() ), ) .file("src/lib.rs", "") .build(); p1.cargo("check -v") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] --cfg foo` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let p2 = project() .file( ".cargo/config.toml", &format!( r#" [target.{}] rustflags = "--cfg foo" "#, rustc_host() ), ) .file("src/lib.rs", "") .build(); p2.cargo("check -v") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] --cfg foo` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn two_matching_in_config() { let p1 = project() .file( ".cargo/config.toml", r#" [target.'cfg(unix)'] rustflags = ["--cfg", 'foo="a"'] [target.'cfg(windows)'] rustflags = ["--cfg", 'foo="a"'] [target.'cfg(target_pointer_width = "32")'] rustflags = ["--cfg", 'foo="b"'] [target.'cfg(target_pointer_width = "64")'] rustflags = ["--cfg", 'foo="b"'] "#, ) .file( "src/main.rs", r#" #![allow(unexpected_cfgs)] fn main() { if cfg!(foo = "a") { println!("a"); } else if cfg!(foo = "b") { println!("b"); } else { panic!() } } "#, ) .build(); p1.cargo("run").run(); p1.cargo("build") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn env_rustflags_misspelled() { let p = project().file("src/main.rs", "fn main() { }").build(); for cmd in &["check", "build", "run", "test", "bench"] { p.cargo(cmd) .env("RUST_FLAGS", "foo") .with_stderr_data( "\ [WARNING] Cargo does not read `RUST_FLAGS` environment variable. Did you mean `RUSTFLAGS`? ... ", ) .run(); } } #[cargo_test] fn env_rustflags_misspelled_build_script() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2021" build = "build.rs" "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() { }") .build(); p.cargo("check") .env("RUST_FLAGS", "foo") .with_stderr_data(str![[r#" [WARNING] Cargo does not read `RUST_FLAGS` environment variable. Did you mean `RUSTFLAGS`? [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn remap_path_prefix_works() { // Check that remap-path-prefix works. Package::new("bar", "0.1.0") .file("src/lib.rs", "pub fn f() -> &'static str { file!() }") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] bar = "0.1" "#, ) .file( "src/main.rs", r#" fn main() { println!("{}", bar::f()); } "#, ) .build(); p.cargo("run") .env( "RUSTFLAGS", format!("--remap-path-prefix={}=/foo", paths::root().display()), ) .with_stdout_data(str![[r#" /foo/home/.cargo/registry/src/-[HASH]/bar-0.1.0/src/lib.rs "#]]) .run(); } #[cargo_test] fn rustflags_remap_path_prefix_ignored_for_c_metadata() { let p = project().file("src/lib.rs", "").build(); let build_output = p .cargo("build -v") .env( "RUSTFLAGS", "--remap-path-prefix=/abc=/zoo --remap-path-prefix /spaced=/zoo", ) .run(); let first_c_metadata = dbg!(get_c_metadata(build_output)); p.cargo("clean").run(); let build_output = p .cargo("build -v") .env( "RUSTFLAGS", "--remap-path-prefix=/def=/zoo --remap-path-prefix /earth=/zoo", ) .run(); let second_c_metadata = dbg!(get_c_metadata(build_output)); assert_data_eq!(first_c_metadata, second_c_metadata); } #[cargo_test] fn rustc_remap_path_prefix_ignored_for_c_metadata() { let p = project().file("src/lib.rs", "").build(); let build_output = p .cargo("rustc -v -- --remap-path-prefix=/abc=/zoo --remap-path-prefix /spaced=/zoo") .run(); let first_c_metadata = dbg!(get_c_metadata(build_output)); p.cargo("clean").run(); let build_output = p .cargo("rustc -v -- --remap-path-prefix=/def=/zoo --remap-path-prefix /earth=/zoo") .run(); let second_c_metadata = dbg!(get_c_metadata(build_output)); assert_data_eq!(first_c_metadata, second_c_metadata); } // `--remap-path-prefix` is meant to take two different binaries and make them the same but the // rlib name, including `-Cextra-filename`, can still end up in the binary so it can't change #[cargo_test] fn rustflags_remap_path_prefix_ignored_for_c_extra_filename() { let p = project().file("src/lib.rs", "").build(); let build_output = p .cargo("build -v") .env( "RUSTFLAGS", "--remap-path-prefix=/abc=/zoo --remap-path-prefix /spaced=/zoo", ) .run(); let first_c_extra_filename = dbg!(get_c_extra_filename(build_output)); p.cargo("clean").run(); let build_output = p .cargo("build -v") .env( "RUSTFLAGS", "--remap-path-prefix=/def=/zoo --remap-path-prefix /earth=/zoo", ) .run(); let second_c_extra_filename = dbg!(get_c_extra_filename(build_output)); assert_data_eq!(first_c_extra_filename, second_c_extra_filename); } // `--remap-path-prefix` is meant to take two different binaries and make them the same but the // rlib name, including `-Cextra-filename`, can still end up in the binary so it can't change #[cargo_test] fn rustc_remap_path_prefix_ignored_for_c_extra_filename() { let p = project().file("src/lib.rs", "").build(); let build_output = p .cargo("rustc -v -- --remap-path-prefix=/abc=/zoo --remap-path-prefix /spaced=/zoo") .run(); let first_c_extra_filename = dbg!(get_c_extra_filename(build_output)); p.cargo("clean").run(); let build_output = p .cargo("rustc -v -- --remap-path-prefix=/def=/zoo --remap-path-prefix /earth=/zoo") .run(); let second_c_extra_filename = dbg!(get_c_extra_filename(build_output)); assert_data_eq!(first_c_extra_filename, second_c_extra_filename); } fn get_c_metadata(output: RawOutput) -> String { let get_c_metadata_re = regex::Regex::new(r".* (--crate-name [^ ]+).* (-C ?metadata=[^ ]+).*").unwrap(); let stderr = String::from_utf8(output.stderr).unwrap(); let mut c_metadata = get_c_metadata_re .captures_iter(&stderr) .map(|c| { let (_, [name, c_metadata]) = c.extract(); format!("{name} {c_metadata}") }) .collect::>(); assert!( !c_metadata.is_empty(), "`{get_c_metadata_re:?}` did not match:\n```\n{stderr}\n```" ); c_metadata.sort(); c_metadata.join("\n") } fn get_c_extra_filename(output: RawOutput) -> String { let get_c_extra_filename_re = regex::Regex::new(r".* (--crate-name [^ ]+).* (-C ?extra-filename=[^ ]+).*").unwrap(); let stderr = String::from_utf8(output.stderr).unwrap(); let mut c_extra_filename = get_c_extra_filename_re .captures_iter(&stderr) .map(|c| { let (_, [name, c_extra_filename]) = c.extract(); format!("{name} {c_extra_filename}") }) .collect::>(); assert!( !c_extra_filename.is_empty(), "`{get_c_extra_filename_re:?}` did not match:\n```\n{stderr}\n```" ); c_extra_filename.sort(); c_extra_filename.join("\n") } #[cargo_test] fn host_config_rustflags_with_target() { // regression test for https://github.com/rust-lang/cargo/issues/10206 let p = project() .file("src/lib.rs", "") .file("build.rs", "fn main() { assert!(cfg!(foo)); }") .file(".cargo/config.toml", "target-applies-to-host = false") .build(); p.cargo("check") .masquerade_as_nightly_cargo(&["target-applies-to-host", "host-config"]) .arg("-Zhost-config") .arg("-Ztarget-applies-to-host") .arg("-Zunstable-options") .arg("--config") .arg("host.rustflags=[\"--cfg=foo\"]") .run(); } #[cargo_test] fn target_applies_to_host_rustflags_works() { // Ensures that rustflags are passed to the target when // target_applies_to_host=false let p = project() .file( "src/lib.rs", r#"#[cfg(feature = "flag")] compile_error!("flag passed");"#, ) .build(); // Use RUSTFLAGS to pass an argument that will generate an error. p.cargo("check") .masquerade_as_nightly_cargo(&["target-applies-to-host"]) .arg("-Ztarget-applies-to-host") .env("CARGO_TARGET_APPLIES_TO_HOST", "false") .env("RUSTFLAGS", r#"--cfg feature="flag""#) .with_status(101) .with_stderr_data( "[CHECKING] foo v0.0.1 ([ROOT]/foo) [ERROR] flag passed ...", ) .run(); } #[cargo_test] fn target_applies_to_host_rustdocflags_works() { // Ensures that rustflags are passed to the target when // target_applies_to_host=false let p = project() .file( "src/lib.rs", r#"#[cfg(feature = "flag")] compile_error!("flag passed");"#, ) .build(); // Use RUSTFLAGS to pass an argument that would generate an error // but it is ignored. p.cargo("doc") .masquerade_as_nightly_cargo(&["target-applies-to-host"]) .arg("-Ztarget-applies-to-host") .env("CARGO_TARGET_APPLIES_TO_HOST", "false") .env("RUSTDOCFLAGS", r#"--cfg feature="flag""#) .with_status(101) .with_stderr_data( "[DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [ERROR] flag passed ...", ) .run(); } #[cargo_test] fn host_config_shared_build_dep() { // rust-lang/cargo#14253 Package::new("cc", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bootstrap" edition = "2021" [dependencies] cc = "1.0.0" [build-dependencies] cc = "1.0.0" [profile.dev] debug = 0 "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .file( ".cargo/config.toml", " target-applies-to-host=false [host] rustflags = ['--cfg', 'from_host'] [build] rustflags = ['--cfg', 'from_target'] ", ) .build(); p.cargo("build -v") .masquerade_as_nightly_cargo(&["target-applies-to-host"]) .arg("-Ztarget-applies-to-host") .arg("-Zhost-config") .with_stderr_data( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] cc v1.0.0 (registry `dummy-registry`) [COMPILING] cc v1.0.0 [RUNNING] `rustc --crate-name cc [..]--cfg from_host[..]` [RUNNING] `rustc --crate-name cc [..]--cfg from_target[..]` [COMPILING] bootstrap v0.0.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name build_script_build [..]--cfg from_host[..]` [RUNNING] `[ROOT]/foo/target/debug/build/bootstrap-[HASH]/build-script-build` [RUNNING] `rustc --crate-name bootstrap[..]--cfg from_target[..]` [FINISHED] `dev` profile [unoptimized] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } cargo-0.91.0/tests/testsuite/rustup.rs000064400000000000000000000215021046102023000161110ustar 00000000000000//! Tests for Cargo's behavior under Rustup. use std::env; use std::env::consts::EXE_EXTENSION; use std::ffi::OsString; use std::fs; use std::path::{Path, PathBuf}; use crate::prelude::*; use crate::utils::cargo_process; use cargo_test_support::paths::{home, root}; use cargo_test_support::{process, project, str}; /// Helper to generate an executable. fn make_exe(dest: &Path, name: &str, contents: &str, env: &[(&str, PathBuf)]) -> PathBuf { let rs_name = format!("{name}.rs"); fs::write( root().join(&rs_name), &format!("fn main() {{ {contents} }}"), ) .unwrap(); let mut pb = process("rustc"); env.iter().for_each(|(key, value)| { pb.env(key, value); }); pb.arg("--edition=2021") .arg(root().join(&rs_name)) .exec() .unwrap(); let exe = Path::new(name).with_extension(EXE_EXTENSION); let output = dest.join(&exe); fs::rename(root().join(&exe), &output).unwrap(); output } fn prepend_path(path: &Path) -> OsString { let mut paths = vec![path.to_path_buf()]; paths.extend(env::split_paths(&env::var_os("PATH").unwrap_or_default())); env::join_paths(paths).unwrap() } struct RustupEnvironment { /// Path for ~/.cargo/bin cargo_bin: PathBuf, /// Path for ~/.rustup rustup_home: PathBuf, /// Path to the cargo executable in the toolchain directory /// (~/.rustup/toolchain/test-toolchain/bin/cargo.exe). cargo_toolchain_exe: PathBuf, } /// Creates an executable which prints a message and then runs the *real* rustc. fn real_rustc_wrapper(bin_dir: &Path, message: &str) -> PathBuf { let real_rustc = cargo_util::paths::resolve_executable("rustc".as_ref()).unwrap(); // The toolchain rustc needs to call the real rustc. In order to do that, // it needs to restore or clear the RUSTUP environment variables so that // if rustup is installed, it will call the correct rustc. let rustup_toolchain_setup = match std::env::var_os("RUSTUP_TOOLCHAIN") { Some(t) => format!( ".env(\"RUSTUP_TOOLCHAIN\", \"{}\")", t.into_string().unwrap() ), None => format!(".env_remove(\"RUSTUP_TOOLCHAIN\")"), }; let mut env = vec![("CARGO_RUSTUP_TEST_real_rustc", real_rustc)]; let rustup_home_setup = match std::env::var_os("RUSTUP_HOME") { Some(h) => { env.push(("CARGO_RUSTUP_TEST_RUSTUP_HOME", h.into())); format!(".env(\"RUSTUP_HOME\", env!(\"CARGO_RUSTUP_TEST_RUSTUP_HOME\"))") } None => format!(".env_remove(\"RUSTUP_HOME\")"), }; make_exe( bin_dir, "rustc", &format!( r#" eprintln!("{message}"); let r = std::process::Command::new(env!("CARGO_RUSTUP_TEST_real_rustc")) .args(std::env::args_os().skip(1)) {rustup_toolchain_setup} {rustup_home_setup} .status(); std::process::exit(r.unwrap().code().unwrap_or(2)); "# ), &env, ) } /// Creates a simulation of a rustup environment with `~/.cargo/bin` and /// `~/.rustup` directories populated with some executables that simulate /// rustup. fn simulated_rustup_environment() -> RustupEnvironment { // Set up ~/.rustup/toolchains/test-toolchain/bin with a custom rustc and cargo. let rustup_home = home().join(".rustup"); let toolchain_bin = rustup_home .join("toolchains") .join("test-toolchain") .join("bin"); toolchain_bin.mkdir_p(); let rustc_toolchain_exe = real_rustc_wrapper(&toolchain_bin, "real rustc running"); let cargo_toolchain_exe = make_exe( &toolchain_bin, "cargo", r#"panic!("cargo toolchain should not be called");"#, &[], ); // Set up ~/.cargo/bin with a typical set of rustup proxies. let cargo_bin = home().join(".cargo").join("bin"); cargo_bin.mkdir_p(); let rustc_proxy = make_exe( &cargo_bin, "rustc", &format!( r#" match std::env::args().next().unwrap().as_ref() {{ "rustc" => {{}} arg => panic!("proxy only supports rustc, got {{arg:?}}"), }} eprintln!("rustc proxy running"); let r = std::process::Command::new(env!("CARGO_RUSTUP_TEST_rustc_toolchain_exe")) .args(std::env::args_os().skip(1)) .status(); std::process::exit(r.unwrap().code().unwrap_or(2)); "# ), &[("CARGO_RUSTUP_TEST_rustc_toolchain_exe", rustc_toolchain_exe)], ); fs::hard_link( &rustc_proxy, cargo_bin.join("cargo").with_extension(EXE_EXTENSION), ) .unwrap(); fs::hard_link( &rustc_proxy, cargo_bin.join("rustup").with_extension(EXE_EXTENSION), ) .unwrap(); RustupEnvironment { cargo_bin, rustup_home, cargo_toolchain_exe, } } #[cargo_test] fn typical_rustup() { // Test behavior under a typical rustup setup with a normal toolchain. let RustupEnvironment { cargo_bin, rustup_home, cargo_toolchain_exe, } = simulated_rustup_environment(); // Set up a project and run a normal cargo build. let p = project().file("src/lib.rs", "").build(); // The path is modified so that cargo will call `rustc` from // `~/.cargo/bin/rustc to use our custom rustup proxies. let path = prepend_path(&cargo_bin); p.cargo("check") .env("RUSTUP_TOOLCHAIN", "test-toolchain") .env("RUSTUP_HOME", &rustup_home) .env("PATH", &path) .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) real rustc running [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Do a similar test, but with a toolchain link that does not have cargo // (which normally would do a fallback to nightly/beta/stable). cargo_toolchain_exe.rm_rf(); p.build_dir().rm_rf(); p.cargo("check") .env("RUSTUP_TOOLCHAIN", "test-toolchain") .env("RUSTUP_HOME", &rustup_home) .env("PATH", &path) .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) real rustc running [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } // This doesn't work on Windows because Cargo forces the PATH to contain the // sysroot_libdir, which is actually `bin`, preventing the test from // overriding the bin directory. #[cargo_test(ignore_windows = "PATH can't be overridden on Windows")] fn custom_calls_other_cargo() { // Test behavior when a custom subcommand tries to manipulate PATH to use // a different toolchain. let RustupEnvironment { cargo_bin, rustup_home, cargo_toolchain_exe: _, } = simulated_rustup_environment(); // Create a directory with a custom toolchain (outside of the rustup universe). let custom_bin = root().join("custom-bin"); custom_bin.mkdir_p(); // `cargo` points to the real cargo. let cargo_exe = crate::utils::cargo_exe(); fs::hard_link(&cargo_exe, custom_bin.join(cargo_exe.file_name().unwrap())).unwrap(); // `rustc` executes the real rustc. real_rustc_wrapper(&custom_bin, "custom toolchain rustc running"); // A project that cargo-custom will try to build. let p = project().file("src/lib.rs", "").build(); // Create a custom cargo subcommand. // This will modify PATH to a custom toolchain and call cargo from that. make_exe( &cargo_bin, "cargo-custom", r#" use std::env; use std::process::Command; eprintln!("custom command running"); let mut paths = vec![std::path::PathBuf::from(env!("CARGO_RUSTUP_TEST_custom_bin"))]; paths.extend(env::split_paths(&env::var_os("PATH").unwrap_or_default())); let path = env::join_paths(paths).unwrap(); let status = Command::new("cargo") .arg("check") .current_dir(env!("CARGO_RUSTUP_TEST_project_dir")) .env("PATH", path) .status() .unwrap(); assert!(status.success()); "#, &[ ("CARGO_RUSTUP_TEST_custom_bin", custom_bin), ("CARGO_RUSTUP_TEST_project_dir", p.root()), ], ); cargo_process("custom") // Set these to simulate what would happen when running under rustup. // We want to make sure that cargo-custom does not try to use the // rustup proxies. .env("RUSTUP_TOOLCHAIN", "test-toolchain") .env("RUSTUP_HOME", &rustup_home) .with_stderr_data(str![[r#" custom command running [CHECKING] foo v0.0.1 ([ROOT]/foo) custom toolchain rustc running [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } cargo-0.91.0/tests/testsuite/sbom.rs000064400000000000000000000365241046102023000155210ustar 00000000000000//! Tests for cargo-sbom precursor files. use std::path::PathBuf; use crate::prelude::*; use cargo_test_support::basic_bin_manifest; use cargo_test_support::cargo_test; use cargo_test_support::compare::assert_e2e; use cargo_test_support::project; use cargo_test_support::registry::Package; use snapbox::IntoData; const SBOM_FILE_EXTENSION: &str = ".cargo-sbom.json"; fn append_sbom_suffix(link: &PathBuf) -> PathBuf { let mut link_buf = link.clone().into_os_string(); link_buf.push(SBOM_FILE_EXTENSION); PathBuf::from(link_buf) } #[cargo_test] fn warn_without_passing_unstable_flag() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", r#"fn main() {}"#) .build(); p.cargo("build") .env("CARGO_BUILD_SBOM", "true") .masquerade_as_nightly_cargo(&["sbom"]) .with_stderr_data( "\ [WARNING] ignoring 'sbom' config, pass `-Zsbom` to enable it\n\ [COMPILING] foo v0.5.0 ([..])\n\ [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [..]\n", ) .run(); let file = append_sbom_suffix(&p.bin("foo")); assert!(!file.exists()); } #[cargo_test] fn simple() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", r#"fn main() {}"#) .build(); p.cargo("build -Zsbom") .env("CARGO_BUILD_SBOM", "true") .masquerade_as_nightly_cargo(&["sbom"]) .run(); let file = append_sbom_suffix(&p.bin("foo")); let output = std::fs::read_to_string(file).unwrap(); // The expected test does contain the "rustc" section // but other tests omit them for brevity. assert_e2e().eq( output, snapbox::str![[r#" { "crates": [ { "dependencies": [], "features": [], "id": "path+[ROOTURL]/foo#0.5.0", "kind": [ "bin" ] } ], "root": 0, "rustc": { "commit_hash": "{...}", "host": "[HOST_TARGET]", "verbose_version": "{...}", "version": "{...}", "workspace_wrapper": null, "wrapper": null }, "target": "[HOST_TARGET]", "version": 1 } "#]] .is_json(), ); } #[cargo_test] fn with_multiple_crate_types() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.2.3" [lib] crate-type = ["dylib", "rlib"] "#, ) .file("src/main.rs", r#"fn main() { let _i = foo::give_five(); }"#) .file("src/lib.rs", r#"pub fn give_five() -> i32 { 5 }"#) .build(); p.cargo("build -Zsbom") .env("CARGO_BUILD_SBOM", "true") .masquerade_as_nightly_cargo(&["sbom"]) .run(); assert_eq!( 3, p.glob(p.target_debug_dir().join("*.cargo-sbom.json")) .count() ); let sbom_path = append_sbom_suffix(&p.dylib("foo")); assert!(sbom_path.is_file()); let output = std::fs::read_to_string(sbom_path).unwrap(); assert_e2e().eq( output, snapbox::str![[r#" { "crates": [ { "dependencies": [], "features": [], "id": "path+[ROOTURL]/foo#1.2.3", "kind": [ "dylib", "rlib" ] } ], "root": 0, "rustc": "{...}", "target": "[HOST_TARGET]", "version": 1 } "#]] .is_json(), ); } #[cargo_test] fn with_simple_build_script() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" build = "build.rs" "#, ) .file("src/main.rs", "#[cfg(foo)] fn main() {}") .file( "build.rs", r#"fn main() { println!("cargo::rustc-check-cfg=cfg(foo)"); println!("cargo::rustc-cfg=foo"); }"#, ) .build(); p.cargo("build -Zsbom") .env("CARGO_BUILD_SBOM", "true") .masquerade_as_nightly_cargo(&["sbom"]) .run(); let path = append_sbom_suffix(&p.bin("foo")); assert!(path.is_file()); let output = std::fs::read_to_string(path).unwrap(); assert_e2e().eq( output, snapbox::str![[r#" { "crates": [ { "dependencies": [ { "index": 1, "kind": "build" } ], "features": [], "id": "path+[ROOTURL]/foo#0.0.1", "kind": [ "bin" ] }, { "dependencies": [], "features": [], "id": "path+[ROOTURL]/foo#0.0.1", "kind": [ "custom-build" ] } ], "root": 0, "rustc": "{...}", "target": "[HOST_TARGET]", "version": 1 } "#]] .is_json(), ); } #[cargo_test] fn with_build_dependencies() { Package::new("baz", "0.1.0").publish(); Package::new("bar", "0.1.0") .build_dep("baz", "0.1.0") .file( "Cargo.toml", r#" [package] name = "bar" version = "0.1.0" build = "build.rs" [build-dependencies] baz = "0.1.0" "#, ) .file("src/lib.rs", "pub fn bar() -> i32 { 2 }") .file( "build.rs", r#"fn main() { println!("cargo::rustc-check-cfg=cfg(foo)"); println!("cargo::rustc-cfg=foo"); }"#, ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" [dependencies] bar = "0.1.0" "#, ) .file("src/main.rs", "fn main() { let _i = bar::bar(); }") .build(); p.cargo("build -Zsbom") .env("CARGO_BUILD_SBOM", "true") .masquerade_as_nightly_cargo(&["sbom"]) .run(); let path = append_sbom_suffix(&p.bin("foo")); let output = std::fs::read_to_string(path).unwrap(); assert_e2e().eq( output, snapbox::str![[r#" { "crates": [ { "dependencies": [ { "index": 1, "kind": "build" } ], "features": [], "id": "registry+https://github.com/rust-lang/crates.io-index#bar@0.1.0", "kind": [ "lib" ] }, { "dependencies": [ { "index": 2, "kind": "normal" } ], "features": [], "id": "registry+https://github.com/rust-lang/crates.io-index#bar@0.1.0", "kind": [ "custom-build" ] }, { "dependencies": [], "features": [], "id": "registry+https://github.com/rust-lang/crates.io-index#baz@0.1.0", "kind": [ "lib" ] }, { "dependencies": [ { "index": 0, "kind": "normal" } ], "features": [], "id": "path+[ROOTURL]/foo#0.0.1", "kind": [ "bin" ] } ], "root": 3, "rustc": "{...}", "target": "[HOST_TARGET]", "version": 1 } "#]] .is_json(), ); } #[cargo_test] fn crate_uses_different_features_for_build_and_normal_dependencies() { let p = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2021" [dependencies] b = { path = "b/", features = ["f1"] } [build-dependencies] b = { path = "b/", features = ["f2"] } "#, ) .file( "src/main.rs", r#" fn main() { b::f1(); } "#, ) .file( "build.rs", r#" fn main() { b::f2(); } "#, ) .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.0.1" edition = "2021" [features] f1 = [] f2 = [] "#, ) .file( "b/src/lib.rs", r#" #[cfg(feature = "f1")] pub fn f1() {} #[cfg(feature = "f2")] pub fn f2() {} "#, ) .build(); p.cargo("build -Zsbom") .env("CARGO_BUILD_SBOM", "true") .masquerade_as_nightly_cargo(&["sbom"]) .run(); let path = append_sbom_suffix(&p.bin("a")); assert!(path.is_file()); let output = std::fs::read_to_string(path).unwrap(); assert_e2e().eq( output, snapbox::str![[r#" { "crates": [ { "dependencies": [ { "index": 1, "kind": "build" }, { "index": 3, "kind": "normal" } ], "features": [], "id": "path+[ROOTURL]/foo#a@0.1.0", "kind": [ "bin" ] }, { "dependencies": [ { "index": 2, "kind": "normal" } ], "features": [], "id": "path+[ROOTURL]/foo#a@0.1.0", "kind": [ "custom-build" ] }, { "dependencies": [], "features": [ "f2" ], "id": "path+[ROOTURL]/foo/b#0.0.1", "kind": [ "lib" ] }, { "dependencies": [], "features": [ "f1" ], "id": "path+[ROOTURL]/foo/b#0.0.1", "kind": [ "lib" ] } ], "root": 0, "rustc": "{...}", "target": "[HOST_TARGET]", "version": 1 } "#]] .is_json(), ); } #[cargo_test] fn artifact_dep() { Package::new("bar", "0.5.0") .file("src/main.rs", "fn main() {}") .file("Cargo.toml", &basic_bin_manifest("bar")) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2021" [lib] crate-type = ["dylib"] [dependencies] bar = { version = "0.5.0", artifact = "bin" } [build-dependencies] bar = { version = "0.5.0", artifact = "bin" } "#, ) .file("src/lib.rs", "") .file("build.rs", r#" fn main() { let bar: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR").expect("CARGO_BIN_FILE_BAR").into(); assert!(&bar.is_file()); }"#) .build(); p.cargo("build -Z bindeps -Z sbom") .env("CARGO_BUILD_SBOM", "true") .masquerade_as_nightly_cargo(&["bindeps", "sbom"]) .run(); let output = std::fs::read_to_string(append_sbom_suffix(&p.dylib("foo"))).unwrap(); assert_e2e().eq( output, snapbox::str![[r#" { "crates": [ { "dependencies": [], "features": [], "id": "registry+https://github.com/rust-lang/crates.io-index#bar@0.5.0", "kind": [ "bin" ] }, { "dependencies": [ { "index": 0, "kind": "normal" }, { "index": 0, "kind": "build" }, { "index": 2, "kind": "build" } ], "features": [], "id": "path+[ROOTURL]/foo#0.0.0", "kind": [ "dylib" ] }, { "dependencies": [], "features": [], "id": "path+[ROOTURL]/foo#0.0.0", "kind": [ "custom-build" ] } ], "root": 1, "rustc": "{...}", "target": "[HOST_TARGET]", "version": 1 } "#]] .is_json(), ); } #[cargo_test] fn proc_macro() { Package::new("noop", "0.0.1") .file( "Cargo.toml", r#" [package] name = "noop" version = "0.0.1" edition = "2021" [lib] proc-macro = true "#, ) .file( "src/lib.rs", r#" extern crate proc_macro; use proc_macro::TokenStream; #[proc_macro_derive(Noop)] pub fn noop(_input: TokenStream) -> TokenStream { "".parse().unwrap() } "#, ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2021" [dependencies] noop = "0.0.1" "#, ) .file( "src/main.rs", r#" #[macro_use] extern crate noop; #[derive(Noop)] struct X; fn main() {} "#, ) .build(); p.cargo("build -Z sbom") .env("CARGO_BUILD_SBOM", "true") .masquerade_as_nightly_cargo(&["sbom"]) .run(); let output = std::fs::read_to_string(append_sbom_suffix(&p.bin("foo"))).unwrap(); assert_e2e().eq( output, snapbox::str![[r#" { "crates": [ { "dependencies": [ { "index": 1, "kind": "build" } ], "features": [], "id": "path+[ROOTURL]/foo#0.0.1", "kind": [ "bin" ] }, { "dependencies": [], "features": [], "id": "registry+https://github.com/rust-lang/crates.io-index#noop@0.0.1", "kind": [ "proc-macro" ] } ], "root": 0, "rustc": "{...}", "target": "[HOST_TARGET]", "version": 1 } "#]] .is_json(), ); } #[cargo_test] fn workspace_wrapper() { let wrapper = project() .at("wrapper") .file("Cargo.toml", &basic_bin_manifest("wrapper")) .file( "src/main.rs", r#" fn main() { let mut args = std::env::args().skip(1); if let Some(sbom) = std::env::var_os("CARGO_SBOM_PATH") { for sbom in std::env::split_paths(&sbom) { eprintln!("found sbom"); assert!(sbom.exists()); } } let status = std::process::Command::new(&args.next().unwrap()) .args(args).status().unwrap(); std::process::exit(status.code().unwrap_or(1)); } "#, ) .build(); wrapper.cargo("build").run(); let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", r#"fn main() {}"#) .build(); p.cargo("build -Zsbom") .env("CARGO_BUILD_SBOM", "true") .env("RUSTC_WRAPPER", wrapper.bin("wrapper")) .masquerade_as_nightly_cargo(&["sbom"]) .with_stderr_data(snapbox::str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) found sbom [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let file = append_sbom_suffix(&p.bin("foo")); let output = std::fs::read_to_string(file).unwrap(); assert_e2e().eq( output, snapbox::str![[r#" { "crates": [ { "dependencies": [], "features": [], "id": "path+[ROOTURL]/foo#0.5.0", "kind": [ "bin" ] } ], "root": 0, "rustc": "{...}", "target": "[HOST_TARGET]", "version": 1 } "#]] .is_json(), ); } cargo-0.91.0/tests/testsuite/script.rs000064400000000000000000001420401046102023000160540ustar 00000000000000use std::fs; use crate::prelude::*; use cargo_test_support::basic_manifest; use cargo_test_support::paths::cargo_home; use cargo_test_support::registry::Package; use cargo_test_support::str; const ECHO_SCRIPT: &str = r#"#!/usr/bin/env cargo fn main() { let mut args = std::env::args_os(); let bin = args.next().unwrap().to_str().unwrap().to_owned(); let args = args.collect::>(); println!("bin: {bin}"); println!("args: {args:?}"); } #[test] fn test () {} "#; #[cfg(unix)] fn path() -> Vec { std::env::split_paths(&std::env::var_os("PATH").unwrap_or_default()).collect() } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn basic_rs() { let p = cargo_test_support::project() .file("echo.rs", ECHO_SCRIPT) .build(); p.cargo("-Zscript -v echo.rs") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" bin: [ROOT]/home/.cargo/target/[HASH]/debug/echo[EXE] args: [] "#]]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] echo v0.0.0 ([ROOT]/foo/echo.rs) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/echo[EXE]` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn basic_path() { let p = cargo_test_support::project() .file("echo", ECHO_SCRIPT) .build(); p.cargo("-Zscript -v ./echo") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" bin: [ROOT]/home/.cargo/target/[HASH]/debug/echo[EXE] args: [] "#]]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] echo v0.0.0 ([ROOT]/foo/echo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/echo[EXE]` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn path_required() { let p = cargo_test_support::project() .file("echo", ECHO_SCRIPT) .build(); p.cargo("-Zscript -v echo") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stdout_data("") .with_stderr_data(str![[r#" [ERROR] no such command: `echo` [HELP] a command with a similar name exists: `bench` [HELP] view all installed commands with `cargo --list` [HELP] find a package to install `echo` with `cargo search cargo-echo` [HELP] To run the file `echo`, provide a relative path like `./echo` "#]]) .run(); } #[cfg(unix)] #[cargo_test(nightly, reason = "-Zscript is unstable")] fn manifest_precedence_over_plugins() { let p = cargo_test_support::project() .file("echo.rs", ECHO_SCRIPT) .executable(std::path::Path::new("path-test").join("cargo-echo.rs"), "") .build(); // With path - fmt is there with known description let mut path = path(); path.push(p.root().join("path-test")); let path = std::env::join_paths(path.iter()).unwrap(); p.cargo("-Zscript -v echo.rs") .env("PATH", &path) .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" bin: [ROOT]/home/.cargo/target/[HASH]/debug/echo[EXE] args: [] "#]]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] echo v0.0.0 ([ROOT]/foo/echo.rs) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/echo[EXE]` "#]]) .run(); } #[cfg(unix)] #[cargo_test(nightly, reason = "-Zscript is unstable")] fn warn_when_plugin_masks_manifest_on_stable() { let p = cargo_test_support::project() .file("echo.rs", ECHO_SCRIPT) .executable(std::path::Path::new("path-test").join("cargo-echo.rs"), "") .build(); let mut path = path(); path.push(p.root().join("path-test")); let path = std::env::join_paths(path.iter()).unwrap(); p.cargo("-v echo.rs") .env("PATH", &path) .with_stdout_data("") .with_stderr_data(str![[r#" [WARNING] external subcommand `echo.rs` has the appearance of a manifest-command This was previously accepted but will be phased out when `-Zscript` is stabilized. For more information, see issue #12207 . "#]]) .run(); } #[cargo_test] fn requires_nightly() { let p = cargo_test_support::project() .file("echo.rs", ECHO_SCRIPT) .build(); p.cargo("-v echo.rs") .with_status(101) .with_stdout_data("") .with_stderr_data(str![[r#" [ERROR] running the file `echo.rs` requires `-Zscript` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn requires_z_flag() { let p = cargo_test_support::project() .file("echo.rs", ECHO_SCRIPT) .build(); p.cargo("-v echo.rs") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stdout_data("") .with_stderr_data(str![[r#" [ERROR] running the file `echo.rs` requires `-Zscript` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn clean_output_with_edition() { let script = r#"#!/usr/bin/env cargo --- [package] edition = "2018" --- fn main() { println!("Hello world!"); }"#; let p = cargo_test_support::project() .file("script.rs", script) .build(); p.cargo("-Zscript -v script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" Hello world! "#]]) .with_stderr_data(str![[r#" [COMPILING] script v0.0.0 ([ROOT]/foo/script.rs) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn warning_without_edition() { let script = r#"#!/usr/bin/env cargo --- [package] --- fn main() { println!("Hello world!"); }"#; let p = cargo_test_support::project() .file("script.rs", script) .build(); p.cargo("-Zscript -v script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" Hello world! "#]]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo/script.rs) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn rebuild() { let script = r#"#!/usr/bin/env cargo-eval fn main() { let msg = option_env!("_MESSAGE").unwrap_or("undefined"); println!("msg = {}", msg); }"#; let p = cargo_test_support::project() .file("script.rs", script) .build(); p.cargo("-Zscript -v script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" msg = undefined "#]]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo/script.rs) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]` "#]]) .run(); // Verify we don't rebuild p.cargo("-Zscript -v script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" msg = undefined "#]]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]` "#]]) .run(); // Verify we do rebuild p.cargo("-Zscript -v script.rs") .env("_MESSAGE", "hello") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" msg = hello "#]]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo/script.rs) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn use_cargo_home_config() { let script = ECHO_SCRIPT; let _ = cargo_test_support::project() .at("script") .file("script.rs", script) .build(); let p = cargo_test_support::project() .file( ".cargo/config.toml", r#" [build] rustc = "non-existent-rustc" "#, ) .file("script.rs", script) .build(); // Verify that the config from the current directory is used p.cargo("-Zscript script.rs -NotAnArg") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" bin: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE] args: ["-NotAnArg"] "#]]) .run(); // Verify that the config from the parent directory is not used p.cargo("-Zscript ../script/script.rs -NotAnArg") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" bin: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE] args: ["-NotAnArg"] "#]]) .run(); // Write a global config.toml in the cargo home directory let cargo_home = cargo_home(); fs::write( &cargo_home.join("config.toml"), r#" [build] rustc = "non-existent-rustc" "#, ) .unwrap(); // Verify the global config is used p.cargo("-Zscript script.rs -NotAnArg") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] could not execute process `non-existent-rustc -vV` (never executed) Caused by: [NOT_FOUND] "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn default_programmatic_verbosity() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() .file("script.rs", script) .build(); p.cargo("-Zscript script.rs -NotAnArg") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" bin: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE] args: ["-NotAnArg"] "#]]) .with_stderr_data("") .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn quiet() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() .file("script.rs", script) .build(); p.cargo("-Zscript -q script.rs -NotAnArg") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" bin: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE] args: ["-NotAnArg"] "#]]) .with_stderr_data("") .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn test_line_numbering_preserved() { let script = r#"#!/usr/bin/env cargo fn main() { println!("line: {}", line!()); } "#; let p = cargo_test_support::project() .file("script.rs", script) .build(); p.cargo("-Zscript -v script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" line: 4 "#]]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo/script.rs) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn test_escaped_hyphen_arg() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() .file("script.rs", script) .build(); p.cargo("-Zscript -v -- script.rs -NotAnArg") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" bin: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE] args: ["-NotAnArg"] "#]]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo/script.rs) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE] -NotAnArg` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn test_unescaped_hyphen_arg() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() .file("script.rs", script) .build(); p.cargo("-Zscript -v script.rs -NotAnArg") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" bin: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE] args: ["-NotAnArg"] "#]]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo/script.rs) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE] -NotAnArg` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn test_same_flags() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() .file("script.rs", script) .build(); p.cargo("-Zscript -v script.rs --help") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" bin: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE] args: ["--help"] "#]]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo/script.rs) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE] --help` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn test_name_has_weird_chars() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() .file("s-h.w§c!.rs", script) .build(); p.cargo("-Zscript -v s-h.w§c!.rs") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" bin: [ROOT]/home/.cargo/target/[HASH]/debug/s-h-w-c-[EXE] args: [] "#]]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] s-h-w-c- v0.0.0 ([ROOT]/foo/s-h.w§c!.rs) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/s-h-w-c-[EXE]` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn test_name_has_leading_number() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() .file("42answer.rs", script) .build(); p.cargo("-Zscript -v 42answer.rs") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" bin: [ROOT]/home/.cargo/target/[HASH]/debug/answer[EXE] args: [] "#]]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] answer v0.0.0 ([ROOT]/foo/42answer.rs) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/answer[EXE]` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn test_name_is_number() { let script = ECHO_SCRIPT; let p = cargo_test_support::project().file("42.rs", script).build(); p.cargo("-Zscript -v 42.rs") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" bin: [ROOT]/home/.cargo/target/[HASH]/debug/package[EXE] args: [] "#]]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] package v0.0.0 ([ROOT]/foo/42.rs) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/package[EXE]` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn script_like_dir() { let p = cargo_test_support::project() .file("foo.rs/foo", "something") .build(); p.cargo("-Zscript -v foo.rs") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] no such file or subcommand `foo.rs`: `foo.rs` is a directory "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn non_existent_rs() { let p = cargo_test_support::project().build(); p.cargo("-Zscript -v foo.rs") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] no such file or subcommand `foo.rs` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn non_existent_rs_stable() { let p = cargo_test_support::project().build(); p.cargo("-v foo.rs") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stdout_data("") .with_stderr_data(str![[r#" [ERROR] no such subcommand `foo.rs` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn did_you_mean_file() { let p = cargo_test_support::project() .file("food.rs", ECHO_SCRIPT) .build(); p.cargo("-Zscript -v foo.rs") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stdout_data("") .with_stderr_data(str![[r#" [ERROR] no such file or subcommand `foo.rs` [HELP] there is a script with a similar name: `./food.rs` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn did_you_mean_file_stable() { let p = cargo_test_support::project() .file("food.rs", ECHO_SCRIPT) .build(); p.cargo("-v foo.rs") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stdout_data("") .with_stderr_data(str![[r#" [ERROR] no such subcommand `foo.rs` [HELP] there is a script with a similar name: `./food.rs` (requires `-Zscript`) "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn did_you_mean_command() { let p = cargo_test_support::project().build(); p.cargo("-Zscript -v build--manifest-path=./Cargo.toml") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stdout_data("") .with_stderr_data(str![[r#" [ERROR] no such file or subcommand `build--manifest-path=./Cargo.toml` [HELP] there is a command with a similar name: `build --manifest-path=./Cargo.toml` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn did_you_mean_command_stable() { let p = cargo_test_support::project().build(); p.cargo("-v build--manifest-path=./Cargo.toml") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stdout_data("") .with_stderr_data(str![[r#" [ERROR] no such subcommand `build--manifest-path=./Cargo.toml` [HELP] there is a command with a similar name: `build --manifest-path=./Cargo.toml` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn test_name_same_as_dependency() { Package::new("script", "1.0.0").publish(); let script = r#"#!/usr/bin/env cargo --- [dependencies] script = "1.0.0" --- fn main() { println!("Hello world!"); }"#; let p = cargo_test_support::project() .file("script.rs", script) .build(); p.cargo("-Zscript -v script.rs --help") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" Hello world! "#]]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest Rust [..] compatible version [DOWNLOADING] crates ... [DOWNLOADED] script v1.0.0 (registry `dummy-registry`) [COMPILING] script v1.0.0 [COMPILING] script v0.0.0 ([ROOT]/foo/script.rs) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE] --help` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn test_path_dep() { let script = r#"#!/usr/bin/env cargo --- [dependencies] bar.path = "./bar" --- fn main() { println!("Hello world!"); }"#; let p = cargo_test_support::project() .file("script.rs", script) .file("src/lib.rs", "pub fn foo() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "pub fn bar() {}") .build(); p.cargo("-Zscript -v script.rs --help") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" Hello world! "#]]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [LOCKING] 1 package to latest Rust [..] compatible version [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [COMPILING] script v0.0.0 ([ROOT]/foo/script.rs) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE] --help` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn test_no_build_rs() { let script = r#"#!/usr/bin/env cargo fn main() { println!("Hello world!"); }"#; let p = cargo_test_support::project() .file("script.rs", script) .file("build.rs", "broken") .build(); p.cargo("-Zscript -v script.rs --help") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" Hello world! "#]]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo/script.rs) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE] --help` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn test_no_autobins() { let script = r#"#!/usr/bin/env cargo fn main() { println!("Hello world!"); }"#; let p = cargo_test_support::project() .file("script.rs", script) .file("src/bin/not-script/main.rs", "fn main() {}") .build(); p.cargo("-Zscript -v script.rs --help") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" Hello world! "#]]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo/script.rs) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE] --help` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn test_no_autolib() { let script = r#"#!/usr/bin/env cargo fn main() { println!("Hello world!"); }"#; let p = cargo_test_support::project() .file("script.rs", script) .file("src/lib.rs", r#"compile_error!{"must not be built"}"#) .build(); p.cargo("-Zscript -v script.rs --help") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" Hello world! "#]]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo/script.rs) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE] --help` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn disallow_explicit_workspace() { let p = cargo_test_support::project() .file( "script.rs", r#" ---- package.edition = "2021" [workspace] ---- fn main() {} "#, ) .build(); p.cargo("-Zscript -v script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/script.rs` Caused by: `workspace` is not allowed in embedded manifests "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn disallow_explicit_lib() { let p = cargo_test_support::project() .file( "script.rs", r#" ---- package.edition = "2021" [lib] name = "script" path = "script.rs" ---- fn main() {} "#, ) .build(); p.cargo("-Zscript -v script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/script.rs` Caused by: `lib` is not allowed in embedded manifests "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn disallow_explicit_bin() { let p = cargo_test_support::project() .file( "script.rs", r#" ---- package.edition = "2021" [[bin]] name = "script" path = "script.rs" ---- fn main() {} "#, ) .build(); p.cargo("-Zscript -v script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/script.rs` Caused by: `bin` is not allowed in embedded manifests "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn disallow_explicit_example() { let p = cargo_test_support::project() .file( "script.rs", r#" ---- package.edition = "2021" [[example]] name = "script" path = "script.rs" ---- fn main() {} "#, ) .build(); p.cargo("-Zscript -v script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/script.rs` Caused by: `example` is not allowed in embedded manifests "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn disallow_explicit_test() { let p = cargo_test_support::project() .file( "script.rs", r#" ---- package.edition = "2021" [[test]] name = "script" path = "script.rs" ---- fn main() {} "#, ) .build(); p.cargo("-Zscript -v script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/script.rs` Caused by: `test` is not allowed in embedded manifests "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn disallow_explicit_bench() { let p = cargo_test_support::project() .file( "script.rs", r#" ---- package.edition = "2021" [[bench]] name = "script" path = "script.rs" ---- fn main() {} "#, ) .build(); p.cargo("-Zscript -v script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/script.rs` Caused by: `bench` is not allowed in embedded manifests "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn disallow_explicit_package_build() { let p = cargo_test_support::project() .file( "script.rs", r#" ---- [package] edition = "2021" build = "script.rs" ---- fn main() {} "#, ) .build(); p.cargo("-Zscript -v script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/script.rs` Caused by: `package.build` is not allowed in embedded manifests "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn disallow_explicit_package_links() { let p = cargo_test_support::project() .file( "script.rs", r#" ---- [package] edition = "2021" links = "script" ---- fn main() {} "#, ) .build(); p.cargo("-Zscript -v script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/script.rs` Caused by: `package.links` is not allowed in embedded manifests "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn disallow_explicit_package_autolib() { let p = cargo_test_support::project() .file( "script.rs", r#" ---- [package] edition = "2021" autolib = true ---- fn main() {} "#, ) .build(); p.cargo("-Zscript -v script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/script.rs` Caused by: `package.autolib` is not allowed in embedded manifests "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn disallow_explicit_package_autobins() { let p = cargo_test_support::project() .file( "script.rs", r#" ---- [package] edition = "2021" autobins = true ---- fn main() {} "#, ) .build(); p.cargo("-Zscript -v script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/script.rs` Caused by: `package.autobins` is not allowed in embedded manifests "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn disallow_explicit_package_autoexamples() { let p = cargo_test_support::project() .file( "script.rs", r#" ---- [package] edition = "2021" autoexamples = true ---- fn main() {} "#, ) .build(); p.cargo("-Zscript -v script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/script.rs` Caused by: `package.autoexamples` is not allowed in embedded manifests "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn disallow_explicit_package_autotests() { let p = cargo_test_support::project() .file( "script.rs", r#" ---- [package] edition = "2021" autotests = true ---- fn main() {} "#, ) .build(); p.cargo("-Zscript -v script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/script.rs` Caused by: `package.autotests` is not allowed in embedded manifests "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn disallow_explicit_package_autobenches() { let p = cargo_test_support::project() .file( "script.rs", r#" ---- [package] edition = "2021" autobenches = true ---- fn main() {} "#, ) .build(); p.cargo("-Zscript -v script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/script.rs` Caused by: `package.autobenches` is not allowed in embedded manifests "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn implicit_target_dir() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() .file("script.rs", script) .build(); p.cargo("-Zscript -v script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" bin: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE] args: [] "#]]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo/script.rs) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn no_local_lockfile() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() .file("script.rs", script) .build(); let local_lockfile_path = p.root().join("Cargo.lock"); assert!(!local_lockfile_path.exists()); p.cargo("-Zscript -v script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" bin: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE] args: [] "#]]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo/script.rs) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]` "#]]) .run(); assert!(!local_lockfile_path.exists()); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn cmd_check_requires_nightly() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() .file("script.rs", script) .build(); p.cargo("check --manifest-path script.rs") .with_status(101) .with_stdout_data("") .with_stderr_data(str![[r#" [ERROR] embedded manifest `[ROOT]/foo/script.rs` requires `-Zscript` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn cmd_check_requires_z_flag() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() .file("script.rs", script) .build(); p.cargo("check --manifest-path script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stdout_data("") .with_stderr_data(str![[r#" [ERROR] embedded manifest `[ROOT]/foo/script.rs` requires `-Zscript` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn cmd_check_with_embedded() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() .file("script.rs", script) .build(); p.cargo("-Zscript check --manifest-path script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data("") .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [CHECKING] script v0.0.0 ([ROOT]/foo/script.rs) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn cmd_check_with_missing_script_rs() { let p = cargo_test_support::project().build(); p.cargo("-Zscript check --manifest-path script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stdout_data("") .with_stderr_data(str![[r#" [ERROR] the manifest-path must be a path to a Cargo.toml file "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn cmd_check_with_missing_script() { let p = cargo_test_support::project().build(); p.cargo("-Zscript check --manifest-path script") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stdout_data("") .with_stderr_data(str![[r#" [ERROR] the manifest-path must be a path to a Cargo.toml file "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn cmd_build_with_embedded() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() .file("script.rs", script) .build(); p.cargo("-Zscript build --manifest-path script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data("") .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo/script.rs) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn cmd_test_with_embedded() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() .file("script.rs", script) .build(); p.cargo("-Zscript test --manifest-path script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" running 1 test test test ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo/script.rs) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests script.rs ([ROOT]/home/.cargo/target/[HASH]/debug/deps/script-[HASH][EXE]) "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn cmd_clean_with_embedded() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() .file("script.rs", script) .build(); // Ensure there is something to clean p.cargo("-Zscript script.rs") .masquerade_as_nightly_cargo(&["script"]) .run(); p.cargo("-Zscript clean --manifest-path script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data("") .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [REMOVED] [FILE_NUM] files, [FILE_SIZE]B total "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn cmd_generate_lockfile_with_embedded() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() .file("script.rs", script) .build(); p.cargo("-Zscript generate-lockfile --manifest-path script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data("") .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn cmd_metadata_with_embedded() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() .file("script.rs", script) .build(); p.cargo("-Zscript metadata --manifest-path script.rs --format-version=1") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2024", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo/script.rs#script@0.0.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/script.rs", "metadata": null, "name": "script", "publish": [], "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "bin" ], "doc": true, "doctest": false, "edition": "2024", "kind": [ "bin" ], "name": "script", "src_path": "[ROOT]/foo/script.rs", "test": true } ], "version": "0.0.0" } ], "resolve": { "nodes": [ { "dependencies": [], "deps": [], "features": [], "id": "path+[ROOTURL]/foo/script.rs#script@0.0.0" } ], "root": "path+[ROOTURL]/foo/script.rs#script@0.0.0" }, "target_directory": "[ROOT]/home/.cargo/target/[HASH]", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo/script.rs#script@0.0.0" ], "workspace_members": [ "path+[ROOTURL]/foo/script.rs#script@0.0.0" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn cmd_read_manifest_with_embedded() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() .file("script.rs", script) .build(); p.cargo("-Zscript read-manifest --manifest-path script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data( str![[r#" { "authors": [], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2024", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo/script.rs#script@0.0.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/script.rs", "metadata": null, "name": "script", "publish": [], "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "bin" ], "doc": true, "doctest": false, "edition": "2024", "kind": [ "bin" ], "name": "script", "src_path": "[ROOT]/foo/script.rs", "test": true } ], "version": "0.0.0" } "#]] .is_json(), ) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn cmd_run_with_embedded() { let p = cargo_test_support::project() .file("script.rs", ECHO_SCRIPT) .build(); p.cargo("-Zscript run --manifest-path script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" bin: [ROOT]/home/.cargo/target/[HASH]/debug/script[EXE] args: [] "#]]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo/script.rs) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn cmd_tree_with_embedded() { let p = cargo_test_support::project() .file("script.rs", ECHO_SCRIPT) .build(); p.cargo("-Zscript tree --manifest-path script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" script v0.0.0 ([ROOT]/foo/script.rs) "#]]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn cmd_update_with_embedded() { let p = cargo_test_support::project() .file("script.rs", ECHO_SCRIPT) .build(); p.cargo("-Zscript update --manifest-path script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data("") .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn cmd_verify_project_with_embedded() { let p = cargo_test_support::project() .file("script.rs", ECHO_SCRIPT) .build(); p.cargo("-Zscript verify-project --manifest-path script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data( str![[r#" { "success": "true" } "#]] .is_json(), ) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn cmd_pkgid_with_embedded() { let p = cargo_test_support::project() .file("script.rs", ECHO_SCRIPT) .build(); p.cargo("-Zscript script.rs") .masquerade_as_nightly_cargo(&["script"]) .run(); p.cargo("-Zscript pkgid --manifest-path script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" path+[ROOTURL]/foo/script.rs#script@0.0.0 "#]]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn cmd_pkgid_with_embedded_no_lock_file() { let p = cargo_test_support::project() .file("script.rs", ECHO_SCRIPT) .build(); p.cargo("-Zscript pkgid --manifest-path script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [ERROR] a Cargo.lock must exist for this command "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn cmd_pkgid_with_embedded_dep() { Package::new("dep", "1.0.0").publish(); let script = r#"#!/usr/bin/env cargo --- [dependencies] dep = "1.0.0" --- fn main() { println!("Hello world!"); }"#; let p = cargo_test_support::project() .file("script.rs", script) .build(); p.cargo("-Zscript script.rs") .masquerade_as_nightly_cargo(&["script"]) .run(); p.cargo("-Zscript pkgid --manifest-path script.rs -p dep") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" registry+https://github.com/rust-lang/crates.io-index#dep@1.0.0 "#]]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn script_as_dep() { let p = cargo_test_support::project() .file("script.rs", ECHO_SCRIPT) .file("src/lib.rs", "pub fn foo() {}") .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] script.path = "script.rs" "#, ) .build(); p.cargo("build") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stderr_data(str![[r#" [WARNING] no edition set: defaulting to the 2015 edition while the latest is 2024 [ERROR] failed to get `script` as a dependency of package `foo v0.1.0 ([ROOT]/foo)` Caused by: failed to load source for dependency `script` Caused by: Unable to update [ROOT]/foo/script.rs Caused by: Single file packages cannot be used as dependencies "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn cmd_install_with_embedded() { let p = cargo_test_support::project() .file("script.rs", ECHO_SCRIPT) .build(); p.cargo("-Zscript install --path script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] `[ROOT]/foo/script.rs` is not a directory. --path must point to a directory containing a Cargo.toml file. "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn cmd_package_with_embedded() { let p = cargo_test_support::project() .file("script.rs", ECHO_SCRIPT) .build(); p.cargo("-Zscript package --manifest-path script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [ERROR] [ROOT]/foo/script.rs is unsupported by `cargo package` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn cmd_publish_with_embedded() { let p = cargo_test_support::project() .file("script.rs", ECHO_SCRIPT) .build(); p.cargo("-Zscript publish --manifest-path script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [ERROR] [ROOT]/foo/script.rs is unsupported by `cargo publish` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn manifest_path_env() { let p = cargo_test_support::project() .file( "script.rs", r#"#!/usr/bin/env cargo fn main() { let path = env!("CARGO_MANIFEST_PATH"); println!("CARGO_MANIFEST_PATH: {}", path); } "#, ) .build(); p.cargo("-Zscript -v script.rs") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" CARGO_MANIFEST_PATH: [ROOT]/foo/script.rs "#]]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo/script.rs) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]` "#]]) .run(); } #[cargo_test(nightly, reason = "-Zscript is unstable")] fn ignore_surrounding_workspace() { let p = cargo_test_support::project() .file( std::path::Path::new(".cargo").join("config.toml"), r#" [registries.test-reg] index = "https://github.com/rust-lang/crates.io-index" "#, ) .file( std::path::Path::new("inner").join("Cargo.toml"), r#" [package] name = "inner" version = "0.1.0" [dependencies] serde = { version = "1.0", registry = "test-reg" } "#, ) .file(std::path::Path::new("inner").join("src").join("lib.rs"), "") .file(std::path::Path::new("script").join("echo.rs"), ECHO_SCRIPT) .file( "Cargo.toml", r#" [workspace] members = [ "inner", ] "#, ) .build(); p.cargo("-Zscript -v script/echo.rs") .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data(str![[r#" bin: [ROOT]/home/.cargo/target/[HASH]/debug/echo[EXE] args: [] "#]]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] echo v0.0.0 ([ROOT]/foo/script/echo.rs) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/echo[EXE]` "#]]) .run(); } cargo-0.91.0/tests/testsuite/search.rs000064400000000000000000000142331046102023000160170ustar 00000000000000//! Tests for the `cargo search` command. use std::collections::HashSet; use crate::prelude::*; use crate::utils::cargo_process; use cargo::util::cache_lock::CacheLockMode; use cargo_test_support::paths; use cargo_test_support::registry::{RegistryBuilder, Response}; use cargo_test_support::str; const SEARCH_API_RESPONSE: &[u8] = br#" { "crates": [{ "created_at": "2014-11-16T20:17:35Z", "description": "Design by contract style assertions for Rust", "documentation": null, "downloads": 2, "homepage": null, "id": "hoare", "keywords": [], "license": null, "links": { "owners": "/api/v1/crates/hoare/owners", "reverse_dependencies": "/api/v1/crates/hoare/reverse_dependencies", "version_downloads": "/api/v1/crates/hoare/downloads", "versions": "/api/v1/crates/hoare/versions" }, "max_version": "0.1.1", "name": "hoare", "repository": "https://github.com/nick29581/libhoare", "updated_at": "2014-11-20T21:49:21Z", "versions": null }, { "id": "postgres", "name": "postgres", "updated_at": "2020-05-01T23:17:54.335921+00:00", "versions": null, "keywords": null, "categories": null, "badges": [ { "badge_type": "circle-ci", "attributes": { "repository": "sfackler/rust-postgres", "branch": null } } ], "created_at": "2014-11-24T02:34:44.756689+00:00", "downloads": 535491, "recent_downloads": 88321, "max_version": "0.17.3", "newest_version": "0.17.3", "description": "A native, synchronous PostgreSQL client", "homepage": null, "documentation": null, "repository": "https://github.com/sfackler/rust-postgres", "links": { "version_downloads": "/api/v1/crates/postgres/downloads", "versions": "/api/v1/crates/postgres/versions", "owners": "/api/v1/crates/postgres/owners", "owner_team": "/api/v1/crates/postgres/owner_team", "owner_user": "/api/v1/crates/postgres/owner_user", "reverse_dependencies": "/api/v1/crates/postgres/reverse_dependencies" }, "exact_match": true } ], "meta": { "total": 2 } }"#; const SEARCH_RESULTS: &str = "\ hoare = \"0.1.1\" # Design by contract style assertions for Rust postgres = \"0.17.3\" # A native, synchronous PostgreSQL client "; #[must_use] fn setup() -> RegistryBuilder { RegistryBuilder::new() .http_api() .add_responder("/api/v1/crates", |_, _| Response { code: 200, headers: vec![], body: SEARCH_API_RESPONSE.to_vec(), }) } #[cargo_test] fn not_update() { let registry = setup().build(); use cargo::core::{Shell, SourceId}; use cargo::sources::RegistrySource; use cargo::sources::source::Source; use cargo::util::GlobalContext; let sid = SourceId::for_registry(registry.index_url()).unwrap(); let gctx = GlobalContext::new( Shell::from_write(Box::new(Vec::new())), paths::root(), paths::home().join(".cargo"), ); let lock = gctx .acquire_package_cache_lock(CacheLockMode::DownloadExclusive) .unwrap(); let mut regsrc = RegistrySource::remote(sid, &HashSet::new(), &gctx).unwrap(); regsrc.invalidate_cache(); regsrc.block_until_ready().unwrap(); drop(lock); cargo_process("search postgres") .replace_crates_io(registry.index_url()) .with_stdout_data(SEARCH_RESULTS) // without "Updating ... index" .with_stderr_data(str![[r#" [NOTE] to learn more about a package, run `cargo info ` "#]]) .run(); } #[cargo_test] fn replace_default() { let registry = setup().build(); cargo_process("search postgres") .replace_crates_io(registry.index_url()) .with_stdout_data(SEARCH_RESULTS) .with_stderr_data(str![[r#" [UPDATING] crates.io index [NOTE] to learn more about a package, run `cargo info ` "#]]) .run(); } #[cargo_test] fn simple() { let registry = setup().build(); cargo_process("search postgres --index") .arg(registry.index_url().as_str()) .with_stdout_data(SEARCH_RESULTS) .with_stderr_data(str![[r#" [UPDATING] `[ROOT]/registry` index [NOTE] to learn more about a package, run `cargo info ` "#]]) .run(); } #[cargo_test] fn multiple_query_params() { let registry = setup().build(); cargo_process("search postgres sql --index") .arg(registry.index_url().as_str()) .with_stdout_data(SEARCH_RESULTS) .with_stderr_data(str![[r#" [UPDATING] `[ROOT]/registry` index [NOTE] to learn more about a package, run `cargo info ` "#]]) .run(); } #[cargo_test] fn ignore_quiet() { let registry = setup().build(); cargo_process("search -q postgres") .replace_crates_io(registry.index_url()) .with_stdout_data(SEARCH_RESULTS) .run(); } #[cargo_test] fn colored_results() { let registry = setup().build(); cargo_process("search --color=never postgres") .replace_crates_io(registry.index_url()) .with_stdout_does_not_contain("[..]\x1b[[..]") .run(); cargo_process("search --color=always postgres") .replace_crates_io(registry.index_url()) .with_stdout_data( "\ ... [..]\x1b[[..] ... ", ) .run(); } #[cargo_test] fn auth_required_failure() { let server = setup().auth_required().no_configure_token().build(); cargo_process("search postgres") .replace_crates_io(server.index_url()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] crates.io index [ERROR] no token found, please run `cargo login` or use environment variable CARGO_REGISTRY_TOKEN "#]]) .run(); } #[cargo_test] fn auth_required() { let server = setup().auth_required().build(); cargo_process("search postgres") .replace_crates_io(server.index_url()) .with_stdout_data(SEARCH_RESULTS) .run(); } cargo-0.91.0/tests/testsuite/shell_quoting.rs000064400000000000000000000022131046102023000174220ustar 00000000000000//! This file tests that when the commands being run are shown //! in the output, their arguments are quoted properly //! so that the command can be run in a terminal. use crate::prelude::*; use cargo_test_support::project; use cargo_test_support::str; #[cargo_test] fn features_are_quoted() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" authors = ["mikeyhew@example.com"] edition = "2015" [features] some_feature = [] default = ["some_feature"] "#, ) .file("src/main.rs", "fn main() {error}") .build(); p.cargo("check -v") .env("MSYSTEM", "1") .with_status(101) .with_stderr_data(str![[r#" [CHECKING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc [..] --cfg 'feature="default"' --cfg 'feature="some_feature"' [..]` ... [ERROR] could not compile `foo` (bin "foo") due to 1 previous error Caused by: process didn't exit successfully: [..] --cfg 'feature="default"' --cfg 'feature="some_feature"' [..] "#]]) .run(); } cargo-0.91.0/tests/testsuite/source_replacement.rs000064400000000000000000000220551046102023000204320ustar 00000000000000//! Tests for `[source]` table (source replacement). use std::fs; use crate::prelude::*; use crate::utils::cargo_process; use cargo_test_support::registry::{Package, RegistryBuilder, TestRegistry}; use cargo_test_support::{paths, project, str, t}; fn setup_replacement(config: &str) -> TestRegistry { let crates_io = RegistryBuilder::new() .no_configure_registry() .http_api() .build(); let root = paths::root(); t!(fs::create_dir(&root.join(".cargo"))); t!(fs::write(root.join(".cargo/config.toml"), config,)); crates_io } #[cargo_test] fn crates_io_token_not_sent_to_replacement() { // verifies that the crates.io token is not sent to a replacement registry during publish. let crates_io = setup_replacement( r#" [source.crates-io] replace-with = 'alternative' "#, ); let _alternative = RegistryBuilder::new() .alternative() .http_api() .no_configure_token() .build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" "#, ) .file("src/lib.rs", "") .build(); p.cargo("publish --no-verify --registry crates-io") .replace_crates_io(crates_io.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index ... "#]]) .run(); } #[cargo_test] fn token_sent_to_correct_registry() { // verifies that the crates.io token is not sent to a replacement registry during yank. let crates_io = setup_replacement( r#" [source.crates-io] replace-with = 'alternative' "#, ); let _alternative = RegistryBuilder::new().alternative().http_api().build(); cargo_process("yank foo@0.0.1 --registry crates-io") .replace_crates_io(crates_io.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [YANK] foo@0.0.1 "#]]) .run(); cargo_process("yank foo@0.0.1 --registry alternative") .replace_crates_io(crates_io.index_url()) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [YANK] foo@0.0.1 "#]]) .run(); } #[cargo_test] fn ambiguous_registry() { // verifies that an error is issued when a source-replacement is configured // and no --registry argument is given. let crates_io = setup_replacement( r#" [source.crates-io] replace-with = 'alternative' "#, ); let _alternative = RegistryBuilder::new() .alternative() .http_api() .no_configure_token() .build(); cargo_process("yank foo@0.0.1") .replace_crates_io(crates_io.index_url()) .with_status(101) .with_stderr_data(str![[r#" [ERROR] crates-io is replaced with remote registry alternative; include `--registry alternative` or `--registry crates-io` "#]]) .run(); } #[cargo_test] fn yank_with_default_crates_io() { // verifies that no error is given when registry.default is used. let crates_io = setup_replacement( r#" [source.crates-io] replace-with = 'alternative' [registry] default = 'crates-io' "#, ); let _alternative = RegistryBuilder::new().alternative().http_api().build(); cargo_process("yank foo@0.0.1") .replace_crates_io(crates_io.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [YANK] foo@0.0.1 "#]]) .run(); } #[cargo_test] fn yank_with_default_alternative() { // verifies that no error is given when registry.default is an alt registry. let crates_io = setup_replacement( r#" [source.crates-io] replace-with = 'alternative' [registry] default = 'alternative' "#, ); let _alternative = RegistryBuilder::new().alternative().http_api().build(); cargo_process("yank foo@0.0.1") .replace_crates_io(crates_io.index_url()) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [YANK] foo@0.0.1 "#]]) .run(); } #[cargo_test] fn publish_with_replacement() { // verifies that the crates.io token is not sent to a replacement registry during publish. let crates_io = setup_replacement( r#" [source.crates-io] replace-with = 'alternative' "#, ); let _alternative = RegistryBuilder::new() .alternative() .http_api() .no_configure_token() .build(); // Publish bar only to alternative. This tests that the publish verification build // does uses the source replacement. Package::new("bar", "1.0.0").alternative(true).publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); // Verifies that the crates.io index is used to find the publishing endpoint // and that the crate is sent to crates.io. The source replacement is only used // for the verification step. p.cargo("publish --registry crates-io") .replace_crates_io(crates_io.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [UPDATING] `alternative` index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `alternative`) [COMPILING] bar v1.0.0 [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] foo v0.0.1 ([ROOT]/foo) [UPLOADED] foo v0.0.1 to registry `crates-io` [NOTE] waiting for foo v0.0.1 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.0.1 at registry `crates-io` "#]]) .run(); } #[cargo_test] fn undefined_default() { // verifies that no error is given when registry.default is used. let crates_io = setup_replacement( r#" [registry] default = 'undefined' "#, ); cargo_process("yank foo@0.0.1") .replace_crates_io(crates_io.index_url()) .with_status(101) .with_stderr_data(str![[r#" [ERROR] registry index was not found in any configuration: `undefined` "#]]) .run(); } #[cargo_test] fn source_replacement_with_registry_url() { let alternative = RegistryBuilder::new().alternative().http_api().build(); Package::new("bar", "0.0.1").alternative(true).publish(); let crates_io = setup_replacement(&format!( r#" [source.crates-io] replace-with = 'using-registry-url' [source.using-registry-url] registry = '{}' "#, alternative.index_url() )); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies.bar] version = "0.0.1" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .replace_crates_io(crates_io.index_url()) .with_stderr_data(str![[r#" [UPDATING] `using-registry-url` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `using-registry-url`) [CHECKING] bar v0.0.1 [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn source_replacement_with_no_package_in_directoy() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2021" [dependencies] bar = { version = "^0.8.9" } "#, ) .file("src/lib.rs", "") .build(); let root = paths::root(); t!(fs::create_dir(&root.join("vendor"))); let crates_io = setup_replacement(&format!( r#" [source.crates-io] replace-with = "vendored-sources" [source.vendored-sources] directory = "vendor" "# )); p.cargo("build") .replace_crates_io(crates_io.index_url()) .with_status(101) .with_stderr_data(str![[r#" [ERROR] no matching package named `bar` found location searched: directory source `[ROOT]/vendor` (which is replacing registry `crates-io`) required by package `foo v0.1.0 ([ROOT]/foo)` "#]]) .run(); } cargo-0.91.0/tests/testsuite/ssh.rs000064400000000000000000000522551046102023000153550ustar 00000000000000//! Network tests for SSH connections. //! //! Note that these tests will generally require setting `CARGO_CONTAINER_TESTS` //! or `CARGO_PUBLIC_NETWORK_TESTS`. //! //! NOTE: The container tests almost certainly won't work on Windows. use std::fs; use std::io::Write; use std::path::PathBuf; use crate::prelude::*; use cargo_test_support::containers::{Container, ContainerHandle, MkFile}; use cargo_test_support::git::cargo_uses_gitoxide; use cargo_test_support::{Project, paths, process, project, str}; fn ssh_repo_url(container: &ContainerHandle, name: &str) -> String { let port = container.port_mappings[&22]; format!("ssh://testuser@127.0.0.1:{port}/repos/{name}.git") } /// The path to the client's private key. fn key_path() -> PathBuf { paths::home().join(".ssh/id_ed25519") } /// Generates the SSH keys for authenticating into the container. fn gen_ssh_keys() -> String { let path = key_path(); process("ssh-keygen") .args(&["-t", "ed25519", "-N", "", "-f"]) .arg(&path) .exec_with_output() .unwrap(); let pub_key = path.with_extension("pub"); fs::read_to_string(pub_key).unwrap() } /// Handler for running ssh-agent for SSH authentication. /// /// Be sure to set `SSH_AUTH_SOCK` when running a process in order to use the /// agent. Keys will need to be copied into the container with the /// `authorized_keys()` method. struct Agent { sock: PathBuf, pid: String, ssh_dir: PathBuf, pub_key: String, } impl Agent { fn launch() -> Agent { let ssh_dir = paths::home().join(".ssh"); fs::create_dir(&ssh_dir).unwrap(); let pub_key = gen_ssh_keys(); let sock = paths::root().join("agent"); let output = process("ssh-agent") .args(&["-s", "-a"]) .arg(&sock) .exec_with_output() .unwrap(); let stdout = std::str::from_utf8(&output.stdout).unwrap(); let start = stdout.find("SSH_AGENT_PID=").unwrap() + 14; let end = &stdout[start..].find(';').unwrap(); let pid = (&stdout[start..start + end]).to_string(); eprintln!("SSH_AGENT_PID={pid}"); process("ssh-add") .arg(key_path()) .env("SSH_AUTH_SOCK", &sock) .exec_with_output() .unwrap(); Agent { sock, pid, ssh_dir, pub_key, } } /// Returns a `MkFile` which can be passed into the `Container` builder to /// copy an `authorized_keys` file containing this agent's public key. fn authorized_keys(&self) -> MkFile { MkFile::path("home/testuser/.ssh/authorized_keys") .contents(self.pub_key.as_bytes()) .mode(0o600) .uid(100) .gid(101) } } impl Drop for Agent { fn drop(&mut self) { if let Err(e) = process("ssh-agent") .args(&["-k", "-a"]) .arg(&self.sock) .env("SSH_AGENT_PID", &self.pid) .exec_with_output() { eprintln!("failed to stop ssh-agent: {e:?}"); } } } /// Common project used for several tests. fn foo_bar_project(url: &str) -> Project { project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = {{ git = "{url}" }} "# ), ) .file("src/lib.rs", "") .build() } #[cargo_test(container_test)] fn no_known_host() { // When host is not known, it should show an error. let sshd = Container::new("sshd").launch(); let url = ssh_repo_url(&sshd, "bar"); let p = foo_bar_project(&url); p.cargo("fetch") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] git repository `ssh://testuser@127.0.0.1:[..]/repos/bar.git` [ERROR] failed to get `bar` as a dependency of package `foo v0.1.0 ([ROOT]/foo)` Caused by: failed to load source for dependency `bar` Caused by: Unable to update ssh://testuser@127.0.0.1:[..]/repos/bar.git Caused by: failed to clone into: [ROOT]/home/.cargo/git/db/bar-[HASH] Caused by: [ERROR] unknown SSH host key The SSH host key for `[127.0.0.1]:[..]` is not known and cannot be validated. To resolve this issue, add the host key to the `net.ssh.known-hosts` array in your Cargo configuration (such as [ROOT]/home/.cargo/config.toml) or in your OpenSSH known_hosts file at [ROOT]/home/.ssh/known_hosts The key to add is: [127.0.0.1]:[..] ecdsa-sha2-nistp256 AAAA[..] The ECDSA key fingerprint is: SHA256:[..] This fingerprint should be validated with the server administrator that it is correct. See https://doc.rust-lang.org/stable/cargo/appendix/git-authentication.html#ssh-known-hosts for more information. "#]]) .run(); } #[cargo_test(container_test)] fn known_host_works() { // The key displayed in the error message should work when added to known_hosts. let agent = Agent::launch(); let sshd = Container::new("sshd") .file(agent.authorized_keys()) .launch(); let url = ssh_repo_url(&sshd, "bar"); let p = foo_bar_project(&url); let output = p .cargo("fetch") .env("SSH_AUTH_SOCK", &agent.sock) .build_command() .output() .unwrap(); let stderr = std::str::from_utf8(&output.stderr).unwrap(); // Validate the fingerprint while we're here. let fingerprint = stderr .lines() .find_map(|line| line.strip_prefix(" The ECDSA key fingerprint is: ")) .unwrap() .trim(); let finger_out = sshd.exec(&["ssh-keygen", "-l", "-f", "/etc/ssh/ssh_host_ecdsa_key.pub"]); let gen_finger = std::str::from_utf8(&finger_out.stdout).unwrap(); // let gen_finger = gen_finger.split_whitespace().nth(1).unwrap(); assert_eq!(fingerprint, gen_finger); // Add the key to known_hosts, and try again. let key = stderr .lines() .find(|line| line.starts_with(" [127.0.0.1]:")) .unwrap() .trim(); fs::write(agent.ssh_dir.join("known_hosts"), key).unwrap(); p.cargo("fetch") .env("SSH_AUTH_SOCK", &agent.sock) .with_stderr_data(str![[r#" [UPDATING] git repository `ssh://testuser@127.0.0.1:[..]/repos/bar.git` [LOCKING] 1 package to latest compatible version "#]]) .run(); } #[cargo_test(container_test)] fn same_key_different_hostname() { // The error message should mention if an identical key was found. let agent = Agent::launch(); let sshd = Container::new("sshd").launch(); let hostkey = sshd.read_file("/etc/ssh/ssh_host_ecdsa_key.pub"); let known_hosts = format!("example.com {hostkey}"); fs::write(agent.ssh_dir.join("known_hosts"), known_hosts).unwrap(); let url = ssh_repo_url(&sshd, "bar"); let p = foo_bar_project(&url); p.cargo("fetch") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] git repository `ssh://testuser@127.0.0.1:[..]/repos/bar.git` [ERROR] failed to get `bar` as a dependency of package `foo v0.1.0 ([ROOT]/foo)` Caused by: failed to load source for dependency `bar` Caused by: Unable to update ssh://testuser@127.0.0.1:[..]/repos/bar.git Caused by: failed to clone into: [ROOT]/home/.cargo/git/db/bar-[HASH] Caused by: [ERROR] unknown SSH host key The SSH host key for `[127.0.0.1]:[..]` is not known and cannot be validated. To resolve this issue, add the host key to the `net.ssh.known-hosts` array in your Cargo configuration (such as [ROOT]/home/.cargo/config.toml) or in your OpenSSH known_hosts file at [ROOT]/home/.ssh/known_hosts The key to add is: [127.0.0.1]:[..] ecdsa-sha2-nistp256 AAAA[..] The ECDSA key fingerprint is: SHA256:[..] This fingerprint should be validated with the server administrator that it is correct. Note: This host key was found, but is associated with a different host: [ROOT]/home/.ssh/known_hosts line 1: example.com See https://doc.rust-lang.org/stable/cargo/appendix/git-authentication.html#ssh-known-hosts for more information. "#]]) .run(); } #[cargo_test(container_test)] fn known_host_without_port() { // A known_host entry without a port should match a connection to a non-standard port. let agent = Agent::launch(); let sshd = Container::new("sshd") .file(agent.authorized_keys()) .launch(); let hostkey = sshd.read_file("/etc/ssh/ssh_host_ecdsa_key.pub"); // The important part of this test is that this line does not have a port. let known_hosts = format!("127.0.0.1 {hostkey}"); fs::write(agent.ssh_dir.join("known_hosts"), known_hosts).unwrap(); let url = ssh_repo_url(&sshd, "bar"); let p = foo_bar_project(&url); p.cargo("fetch") .env("SSH_AUTH_SOCK", &agent.sock) .with_stderr_data(str![[r#" [UPDATING] git repository `ssh://testuser@127.0.0.1:[..]/repos/bar.git` [LOCKING] 1 package to latest compatible version "#]]) .run(); } #[cargo_test(container_test)] fn hostname_case_insensitive() { // hostname checking should be case-insensitive. let agent = Agent::launch(); let sshd = Container::new("sshd") .file(agent.authorized_keys()) .launch(); // Consider using `gethostname-rs` instead? let hostname = process("hostname").exec_with_output().unwrap(); let hostname = std::str::from_utf8(&hostname.stdout).unwrap().trim(); let inv_hostname = if hostname.chars().any(|c| c.is_lowercase()) { hostname.to_uppercase() } else { // There should be *some* chars in the name. assert!(hostname.chars().any(|c| c.is_uppercase())); hostname.to_lowercase() }; eprintln!("converted {hostname} to {inv_hostname}"); let hostkey = sshd.read_file("/etc/ssh/ssh_host_ecdsa_key.pub"); let known_hosts = format!("{inv_hostname} {hostkey}"); fs::write(agent.ssh_dir.join("known_hosts"), known_hosts).unwrap(); let port = sshd.port_mappings[&22]; let url = format!("ssh://testuser@{hostname}:{port}/repos/bar.git"); let p = foo_bar_project(&url); p.cargo("fetch") .env("SSH_AUTH_SOCK", &agent.sock) .with_stderr_data(&format!( "\ [UPDATING] git repository `ssh://testuser@{hostname}:{port}/repos/bar.git` [LOCKING] 1 package to latest compatible version " )) .run(); } #[cargo_test(container_test)] fn invalid_key_error() { // An error when a known_host value doesn't match. let agent = Agent::launch(); let sshd = Container::new("sshd") .file(agent.authorized_keys()) .launch(); let port = sshd.port_mappings[&22]; let known_hosts = format!( "[127.0.0.1]:{port} ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLqLMclVr7MDuaVsm3sEnnq2OrGxTFiHSw90wd6N14BU8xVC9cZldC3rJ58Wmw6bEVKPjk7foNG0lHwS5bCKX+U=\n" ); fs::write(agent.ssh_dir.join("known_hosts"), known_hosts).unwrap(); let url = ssh_repo_url(&sshd, "bar"); let p = foo_bar_project(&url); p.cargo("fetch") .env("SSH_AUTH_SOCK", &agent.sock) .with_status(101) .with_stderr_data(&format!("\ [UPDATING] git repository `ssh://testuser@127.0.0.1:{port}/repos/bar.git` [ERROR] failed to get `bar` as a dependency of package `foo v0.1.0 ([ROOT]/foo)` Caused by: failed to load source for dependency `bar` Caused by: Unable to update ssh://testuser@127.0.0.1:{port}/repos/bar.git Caused by: failed to clone into: [ROOT]/home/.cargo/git/db/bar-[HASH] Caused by: [ERROR] SSH host key has changed for `[127.0.0.1]:{port}` ********************************* * WARNING: HOST KEY HAS CHANGED * ********************************* This may be caused by a man-in-the-middle attack, or the server may have changed its host key. The ECDSA fingerprint for the key from the remote host is: SHA256:[..] You are strongly encouraged to contact the server administrator for `[127.0.0.1]:{port}` to verify that this new key is correct. If you can verify that the server has a new key, you can resolve this error by removing the old ecdsa-sha2-nistp256 key for `[127.0.0.1]:{port}` located at [ROOT]/home/.ssh/known_hosts line 1, and adding the new key to the `net.ssh.known-hosts` array in your Cargo configuration (such as [ROOT]/home/.cargo/config.toml) or in your OpenSSH known_hosts file at [ROOT]/home/.ssh/known_hosts The key provided by the remote host is: [127.0.0.1]:{port} ecdsa-sha2-nistp256 [..] See https://doc.rust-lang.org/stable/cargo/appendix/git-authentication.html#ssh-known-hosts for more information. ")) .run(); // Add the key, it should work even with the old key left behind. let hostkey = sshd.read_file("/etc/ssh/ssh_host_ecdsa_key.pub"); let known_hosts_path = agent.ssh_dir.join("known_hosts"); let mut f = fs::OpenOptions::new() .append(true) .open(known_hosts_path) .unwrap(); write!(f, "[127.0.0.1]:{port} {hostkey}").unwrap(); drop(f); p.cargo("fetch") .env("SSH_AUTH_SOCK", &agent.sock) .with_stderr_data(str![[r#" [UPDATING] git repository `ssh://testuser@127.0.0.1:[..]/repos/bar.git` [LOCKING] 1 package to latest compatible version "#]]) .run(); } // For unknown reasons, this test occasionally fails on Windows with a // LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE error: // failed to start SSH session: Unable to exchange encryption keys; class=Ssh (23) #[cargo_test(public_network_test, ignore_windows = "test is flaky on windows")] fn invalid_github_key() { // A key for github.com in known_hosts should override the built-in key. // This uses a bogus key which should result in an error. let ssh_dir = paths::home().join(".ssh"); fs::create_dir(&ssh_dir).unwrap(); let known_hosts = "\ github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLqLMclVr7MDuaVsm3sEnnq2OrGxTFiHSw90wd6N14BU8xVC9cZldC3rJ58Wmw6bEVKPjk7foNG0lHwS5bCKX+U=\n\ github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDgi+8rMcyFCBq5y7BXrb2aaYGhMjlU3QDy7YDvtNL5KSecYOsaqQHaXr87Bbx0EEkgbhK4kVMkmThlCoNITQS9Vc3zIMQ+Tg6+O4qXx719uCzywl50Tb5tDqPGMj54jcq3VUiu/dvse0yeehyvzoPNWewgGWLx11KI4A4wOwMnc6guhculEWe9DjGEjUQ34lPbmdfu/Hza7ZVu/RhgF/wc43uzXWB2KpMEqtuY1SgRlCZqTASoEtfKZi0AuM7AEdOwE5aTotS4CQZHWimb1bMFpF4DAq92CZ8Jhrm4rWETbO29WmjviCJEA3KNQyd3oA7H9AE9z/22PJaVEmjiZZ+wyLgwyIpOlsnHYNEdGeQMQ4SgLRkARLwcnKmByv1AAxsBW4LI3Os4FpwxVPdXHcBebydtvxIsbtUVkkq99nbsIlnSRFSTvb0alrdzRuKTdWpHtN1v9hagFqmeCx/kJfH76NXYBbtaWZhSOnxfEbhLYuOb+IS4jYzHAIkzy9FjVuk=\n\ ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEeMB6BUAW6FfvfLxRO3kGASe0yXnrRT4kpqncsup2b2\n"; fs::write(ssh_dir.join("known_hosts"), known_hosts).unwrap(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bitflags = { git = "ssh://git@github.com/rust-lang/bitflags.git", tag = "1.3.2" } "#, ) .file("src/lib.rs", "") .build(); p.cargo("fetch") .with_status(101) .with_stderr_data(if cargo_uses_gitoxide() { str![[r#" ... git@github.com: Permission denied (publickey). ... "#]] } else { str![[r#" ... [ERROR] SSH host key has changed for `github.com` ... "#]] }) .run(); } // For unknown reasons, this test occasionally fails on Windows with a // LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE error: // failed to start SSH session: Unable to exchange encryption keys; class=Ssh (23) #[cargo_test(public_network_test, ignore_windows = "test is flaky on windows")] fn bundled_github_works() { // The bundled key for github.com works. // // Use a bogus auth sock to force an authentication error. // On Windows, if the agent service is running, it could allow a // successful authentication. // // If the bundled hostkey did not work, it would result in an "unknown SSH // host key" instead. let bogus_auth_sock = paths::home().join("ssh_auth_sock"); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bitflags = { git = "ssh://git@github.com/rust-lang/bitflags.git", tag = "1.3.2" } "#, ) .file("src/lib.rs", "") .build(); let expected = if cargo_uses_gitoxide() { str![[r#" [UPDATING] git repository `ssh://git@github.com/rust-lang/bitflags.git` [ERROR] failed to get `bitflags` as a dependency of package `foo v0.1.0 ([ROOT]/foo)` Caused by: failed to load source for dependency `bitflags` Caused by: Unable to update ssh://git@github.com/rust-lang/bitflags.git?tag=1.3.2 Caused by: failed to clone into: [ROOT]/home/.cargo/git/db/bitflags-[HASH] Caused by: failed to authenticate when downloading repository * attempted to find username/password via `credential.helper`, but maybe the found credentials were incorrect if the git CLI succeeds then `net.git-fetch-with-cli` may help here https://doc.rust-lang.org/cargo/reference/config.html#netgit-fetch-with-cli Caused by: [CREDENTIAL]s provided for "ssh://git@github.com/rust-lang/bitflags.git" were not accepted by the remote Caused by: git@github.com: Permission denied (publickey). "#]] } else { str![[r#" [UPDATING] git repository `ssh://git@github.com/rust-lang/bitflags.git` [ERROR] failed to get `bitflags` as a dependency of package `foo v0.1.0 ([ROOT]/foo)` Caused by: failed to load source for dependency `bitflags` Caused by: Unable to update ssh://git@github.com/rust-lang/bitflags.git?tag=1.3.2 Caused by: failed to clone into: [ROOT]/home/.cargo/git/db/bitflags-[HASH] Caused by: failed to authenticate when downloading repository * attempted ssh-agent authentication, but no usernames succeeded: `git` if the git CLI succeeds then `net.git-fetch-with-cli` may help here https://doc.rust-lang.org/cargo/reference/config.html#netgit-fetch-with-cli Caused by: no authentication methods succeeded "#]] }; p.cargo("fetch") .env("SSH_AUTH_SOCK", &bogus_auth_sock) .with_status(101) .with_stderr_data(expected) .run(); let expected = if cargo_uses_gitoxide() { str![[r#" [UPDATING] git repository `ssh://git@github.com:22/rust-lang/bitflags.git` [ERROR] failed to get `bitflags` as a dependency of package `foo v0.1.0 ([ROOT]/foo)` Caused by: failed to load source for dependency `bitflags` Caused by: Unable to update ssh://git@github.com:22/rust-lang/bitflags.git?tag=1.3.2 Caused by: failed to clone into: [ROOT]/home/.cargo/git/db/bitflags-[HASH] Caused by: failed to authenticate when downloading repository * attempted to find username/password via `credential.helper`, but maybe the found credentials were incorrect if the git CLI succeeds then `net.git-fetch-with-cli` may help here https://doc.rust-lang.org/cargo/reference/config.html#netgit-fetch-with-cli Caused by: [CREDENTIAL]s provided for "ssh://git@github.com:22/rust-lang/bitflags.git" were not accepted by the remote Caused by: git@github.com: Permission denied (publickey). "#]] } else { str![[r#" [UPDATING] git repository `ssh://git@github.com:22/rust-lang/bitflags.git` [ERROR] failed to get `bitflags` as a dependency of package `foo v0.1.0 ([ROOT]/foo)` Caused by: failed to load source for dependency `bitflags` Caused by: Unable to update ssh://git@github.com:22/rust-lang/bitflags.git?tag=1.3.2 Caused by: failed to clone into: [ROOT]/home/.cargo/git/db/bitflags-[HASH] Caused by: failed to authenticate when downloading repository * attempted ssh-agent authentication, but no usernames succeeded: `git` if the git CLI succeeds then `net.git-fetch-with-cli` may help here https://doc.rust-lang.org/cargo/reference/config.html#netgit-fetch-with-cli Caused by: no authentication methods succeeded "#]] }; // Explicit :22 should also work with bundled. p.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bitflags = { git = "ssh://git@github.com:22/rust-lang/bitflags.git", tag = "1.3.2" } "#, ); p.cargo("fetch") .env("SSH_AUTH_SOCK", &bogus_auth_sock) .with_status(101) .with_stderr_data(expected) .run(); } #[cargo_test(container_test)] fn ssh_key_in_config() { // known_host in config works. let agent = Agent::launch(); let sshd = Container::new("sshd") .file(agent.authorized_keys()) .launch(); let hostkey = sshd.read_file("/etc/ssh/ssh_host_ecdsa_key.pub"); let url = ssh_repo_url(&sshd, "bar"); let p = foo_bar_project(&url); p.change_file( ".cargo/config.toml", &format!( r#" [net.ssh] known-hosts = ['127.0.0.1 {}'] "#, hostkey.trim() ), ); p.cargo("fetch") .env("SSH_AUTH_SOCK", &agent.sock) .with_stderr_data(str![[r#" [UPDATING] git repository `ssh://testuser@127.0.0.1:[..]/repos/bar.git` [LOCKING] 1 package to latest compatible version "#]]) .run(); } cargo-0.91.0/tests/testsuite/standard_lib.rs000064400000000000000000000571501046102023000172050ustar 00000000000000//! Tests for building the standard library (-Zbuild-std). //! //! These tests all use a "mock" standard library so that we don't have to //! rebuild the real one. There is a separate integration test `build-std` //! which builds the real thing, but that should be avoided if possible. use std::path::{Path, PathBuf}; use crate::prelude::*; use cargo_test_support::ProjectBuilder; use cargo_test_support::cross_compile; use cargo_test_support::registry::{Dependency, Package}; use cargo_test_support::{Execs, paths, project, rustc_host, str}; struct Setup { rustc_wrapper: PathBuf, real_sysroot: String, } fn setup() -> Setup { // Our mock sysroot requires a few packages from crates.io, so make sure // they're "published" to crates.io. Also edit their code a bit to make sure // that they have access to our custom crates with custom apis. Package::new("registry-dep-using-core", "1.0.0") .file( "src/lib.rs", " #![no_std] #[cfg(feature = \"mockbuild\")] pub fn custom_api() { } #[cfg(not(feature = \"mockbuild\"))] pub fn non_sysroot_api() { core::custom_api(); } ", ) .add_dep(Dependency::new("rustc-std-workspace-core", "*").optional(true)) .feature("mockbuild", &["rustc-std-workspace-core"]) .publish(); Package::new("registry-dep-using-alloc", "1.0.0") .file( "src/lib.rs", " #![no_std] extern crate alloc; #[cfg(feature = \"mockbuild\")] pub fn custom_api() { } #[cfg(not(feature = \"mockbuild\"))] pub fn non_sysroot_api() { core::custom_api(); alloc::custom_api(); } ", ) .add_dep(Dependency::new("rustc-std-workspace-core", "*").optional(true)) .add_dep(Dependency::new("rustc-std-workspace-alloc", "*").optional(true)) .feature( "mockbuild", &["rustc-std-workspace-core", "rustc-std-workspace-alloc"], ) .publish(); Package::new("registry-dep-using-std", "1.0.0") .file( "src/lib.rs", " #[cfg(feature = \"mockbuild\")] pub fn custom_api() { } #[cfg(not(feature = \"mockbuild\"))] pub fn non_sysroot_api() { std::custom_api(); } ", ) .add_dep(Dependency::new("rustc-std-workspace-std", "*").optional(true)) .feature("mockbuild", &["rustc-std-workspace-std"]) .publish(); let p = ProjectBuilder::new(paths::root().join("rustc-wrapper")) .file( "src/main.rs", &r#" use std::process::Command; use std::env; fn main() { let mut args = env::args().skip(1).collect::>(); let is_sysroot_crate = env::var_os("RUSTC_BOOTSTRAP").is_some(); if is_sysroot_crate { args.push("--sysroot".to_string()); args.push(env::var("REAL_SYSROOT").unwrap()); } else if let Some(pos) = args.iter().position(|arg| arg == "--target") { // build-std target unit // Set --sysroot only when the target is host if args.iter().nth(pos + 1) == Some(&"__HOST_TARGET__".to_string()) { // This `--sysroot` is here to disable the sysroot lookup, // to ensure nothing is required. // See https://github.com/rust-lang/wg-cargo-std-aware/issues/31 // for more information on this. args.push("--sysroot".to_string()); args.push("/path/to/nowhere".to_string()); } } else { // host unit, do not use sysroot } let ret = Command::new(&args[0]).args(&args[1..]).status().unwrap(); std::process::exit(ret.code().unwrap_or(1)); } "# .replace("__HOST_TARGET__", rustc_host()), ) .build(); p.cargo("build").run(); Setup { rustc_wrapper: p.bin("foo"), real_sysroot: paths::sysroot(), } } fn enable_build_std(e: &mut Execs, setup: &Setup) { // First up, force Cargo to use our "mock sysroot" which mimics what // libstd looks like upstream. let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/testsuite/mock-std/library"); e.env("__CARGO_TESTS_ONLY_SRC_ROOT", &root); e.masquerade_as_nightly_cargo(&["build-std"]); // We do various shenanigans to ensure our "mock sysroot" actually links // with the real sysroot, so we don't have to actually recompile std for // each test. Perform all that logic here, namely: // // * RUSTC_WRAPPER - uses our shim executable built above to control rustc // * REAL_SYSROOT - used by the shim executable to swap out to the real // sysroot temporarily for some compilations // * RUST{,DOC}FLAGS - an extra `-L` argument to ensure we can always load // crates from the sysroot, but only indirectly through other crates. e.env("RUSTC_WRAPPER", &setup.rustc_wrapper); e.env("REAL_SYSROOT", &setup.real_sysroot); let libdir = format!("/lib/rustlib/{}/lib", rustc_host()); e.env( "RUSTFLAGS", format!("-Ldependency={}{}", setup.real_sysroot, libdir), ); e.env( "RUSTDOCFLAGS", format!("-Ldependency={}{}", setup.real_sysroot, libdir), ); } // Helper methods used in the tests below trait BuildStd: Sized { fn build_std(&mut self, setup: &Setup) -> &mut Self; fn build_std_arg(&mut self, setup: &Setup, arg: &str) -> &mut Self; fn target_host(&mut self) -> &mut Self; } impl BuildStd for Execs { fn build_std(&mut self, setup: &Setup) -> &mut Self { enable_build_std(self, setup); self.arg("-Zbuild-std"); self } fn build_std_arg(&mut self, setup: &Setup, arg: &str) -> &mut Self { enable_build_std(self, setup); self.arg(format!("-Zbuild-std={}", arg)); self } fn target_host(&mut self) -> &mut Self { self.arg("--target").arg(rustc_host()); self } } #[cargo_test(build_std_mock)] fn basic() { let setup = setup(); let p = project() .file( "src/main.rs", " fn main() { std::custom_api(); foo::f(); } #[test] fn smoke_bin_unit() { std::custom_api(); foo::f(); } ", ) .file( "src/lib.rs", " extern crate alloc; extern crate proc_macro; /// ``` /// foo::f(); /// ``` pub fn f() { core::custom_api(); std::custom_api(); alloc::custom_api(); proc_macro::custom_api(); } #[test] fn smoke_lib_unit() { std::custom_api(); f(); } ", ) .file( "tests/smoke.rs", " #[test] fn smoke_integration() { std::custom_api(); foo::f(); } ", ) .build(); p.cargo("check -v").build_std(&setup).target_host().run(); p.cargo("build").build_std(&setup).target_host().run(); p.cargo("run").build_std(&setup).target_host().run(); p.cargo("test").build_std(&setup).target_host().run(); } #[cargo_test(build_std_mock)] fn shared_std_dependency_rebuild() { let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); let setup = setup(); let p = project() .file( "Cargo.toml", format!( " [package] name = \"foo\" version = \"0.1.0\" edition = \"2021\" [build-dependencies] dep_test = {{ path = \"{}/tests/testsuite/mock-std/dep_test\" }} ", manifest_dir.replace('\\', "/") ) .as_str(), ) .file( "src/main.rs", r#" fn main() { println!("Hello, World!"); } "#, ) .file( "build.rs", r#" fn main() { println!("cargo::rerun-if-changed=build.rs"); } "#, ) .build(); p.cargo("build -v") .build_std(&setup) .target_host() .with_stderr_data(str![[r#" ... [RUNNING] `[..] rustc --crate-name dep_test [..]` ... [RUNNING] `[..] rustc --crate-name dep_test [..]` ... "#]]) .run(); p.cargo("build -v") .build_std(&setup) .with_stderr_does_not_contain(str![[r#" ... [RUNNING] `[..] rustc --crate-name dep_test [..]` ... [RUNNING] `[..] rustc --crate-name dep_test [..]` ... "#]]) .run(); } #[cargo_test(build_std_mock)] fn simple_lib_std() { let setup = setup(); let p = project().file("src/lib.rs", "").build(); p.cargo("build -v") .build_std(&setup) .target_host() .with_stderr_data(str![[r#" ... [RUNNING] `[..] rustc --crate-name std [..]` ... "#]]) .run(); // Check freshness. p.change_file("src/lib.rs", " "); p.cargo("build -v") .build_std(&setup) .target_host() .with_stderr_data(str![[r#" ... [FRESH] std v0.1.0 ([..]/tests/testsuite/mock-std/library/std) ... "#]]) .run(); } #[cargo_test(build_std_mock)] fn simple_bin_std() { let setup = setup(); let p = project().file("src/main.rs", "fn main() {}").build(); p.cargo("run -v").build_std(&setup).target_host().run(); } #[cargo_test(build_std_mock)] fn lib_nostd() { let setup = setup(); let p = project() .file( "src/lib.rs", r#" #![no_std] pub fn foo() { assert_eq!(u8::MIN, 0); } "#, ) .build(); p.cargo("build -v --lib") .build_std_arg(&setup, "core") .target_host() .with_stderr_does_not_contain("[..]libstd[..]") .run(); } #[cargo_test(build_std_mock)] fn check_core() { let setup = setup(); let p = project() .file("src/lib.rs", "#![no_std] fn unused_fn() {}") .build(); p.cargo("check -v") .build_std_arg(&setup, "core") .target_host() .with_stderr_data(str![[r#" ... [WARNING] function `unused_fn` is never used ... "#]]) .run(); } #[cargo_test(build_std_mock)] fn build_std_with_no_arg_for_core_only_target() { let target = "aarch64-unknown-none"; if !cross_compile::requires_target_installed(target) { return; } let setup = setup(); let p = project() .file( "src/lib.rs", r#" #![no_std] pub fn foo() { assert_eq!(u8::MIN, 0); } "#, ) .build(); p.cargo("build -v") .arg("--target") .arg(target) .build_std(&setup) .with_stderr_data( str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] registry-dep-using-std v1.0.0 (registry `dummy-registry`) [DOWNLOADED] registry-dep-using-core v1.0.0 (registry `dummy-registry`) [DOWNLOADED] registry-dep-using-alloc v1.0.0 (registry `dummy-registry`) [COMPILING] compiler_builtins v0.1.0 ([..]/library/compiler_builtins) [COMPILING] core v0.1.0 ([..]/library/core) [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[..] rustc --crate-name compiler_builtins [..]--target aarch64-unknown-none[..]` [RUNNING] `[..] rustc --crate-name core [..]--target aarch64-unknown-none[..]` [RUNNING] `[..] rustc --crate-name foo [..]--target aarch64-unknown-none[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); p.cargo("clean").run(); // Also work for a mix of std and core-only targets, // though not sure how common it is... // // Note that we don't download std dependencies for the second call // because `-Zbuild-std` downloads them all also when building for core only. p.cargo("build -v") .arg("--target") .arg(target) .target_host() .build_std(&setup) .with_stderr_data( str![[r#" [UPDATING] `dummy-registry` index [COMPILING] core v0.1.0 ([..]/library/core) [COMPILING] dep_test v0.1.0 ([..]/dep_test) [COMPILING] compiler_builtins v0.1.0 ([..]/library/compiler_builtins) [COMPILING] proc_macro v0.1.0 ([..]/library/proc_macro) [COMPILING] panic_unwind v0.1.0 ([..]/library/panic_unwind) [COMPILING] rustc-std-workspace-core v1.9.0 ([..]/library/rustc-std-workspace-core) [COMPILING] foo v0.0.1 ([ROOT]/foo) [COMPILING] registry-dep-using-core v1.0.0 [COMPILING] alloc v0.1.0 ([..]/library/alloc) [COMPILING] rustc-std-workspace-alloc v1.9.0 ([..]/library/rustc-std-workspace-alloc) [COMPILING] registry-dep-using-alloc v1.0.0 [COMPILING] std v0.1.0 ([..]/library/std) [RUNNING] `[..]rustc --crate-name compiler_builtins [..]--target aarch64-unknown-none[..]` [RUNNING] `[..]rustc --crate-name core [..]--target aarch64-unknown-none[..]` [RUNNING] `[..]rustc --crate-name foo [..]--target aarch64-unknown-none[..]` [RUNNING] `[..]rustc --crate-name core [..]--target [HOST_TARGET][..]` [RUNNING] `[..]rustc --crate-name dep_test [..]--target [HOST_TARGET][..]` [RUNNING] `[..]rustc --crate-name proc_macro [..]--target [HOST_TARGET][..]` [RUNNING] `[..]rustc --crate-name panic_unwind [..]--target [HOST_TARGET][..]` [RUNNING] `[..]rustc --crate-name compiler_builtins [..]--target [HOST_TARGET][..]` [RUNNING] `[..]rustc --crate-name rustc_std_workspace_core [..]--target [HOST_TARGET][..]` [RUNNING] `[..]rustc --crate-name registry_dep_using_core [..]--target [HOST_TARGET][..]` [RUNNING] `[..]rustc --crate-name alloc [..]--target [HOST_TARGET][..]` [RUNNING] `[..]rustc --crate-name rustc_std_workspace_alloc [..]--target [HOST_TARGET][..]` [RUNNING] `[..]rustc --crate-name registry_dep_using_alloc [..]--target [HOST_TARGET][..]` [RUNNING] `[..]rustc --crate-name std [..]--target [HOST_TARGET][..]` [RUNNING] `[..]rustc --crate-name foo [..]--target [HOST_TARGET][..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test(build_std_mock)] fn depend_same_as_std() { let setup = setup(); let p = project() .file( "src/lib.rs", r#" pub fn f() { registry_dep_using_core::non_sysroot_api(); registry_dep_using_alloc::non_sysroot_api(); registry_dep_using_std::non_sysroot_api(); } "#, ) .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [dependencies] registry-dep-using-core = "1.0" registry-dep-using-alloc = "1.0" registry-dep-using-std = "1.0" "#, ) .build(); p.cargo("build -v").build_std(&setup).target_host().run(); } #[cargo_test(build_std_mock)] fn test() { let setup = setup(); let p = project() .file( "src/lib.rs", r#" #[cfg(test)] mod tests { #[test] fn it_works() { assert_eq!(2 + 2, 4); } } "#, ) .build(); p.cargo("test -v") .build_std(&setup) .target_host() .with_stdout_data(str![[r#" running 1 test test tests::it_works ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s ... "#]]) .run(); } #[cargo_test(build_std_mock)] fn target_proc_macro() { let setup = setup(); let p = project() .file( "src/lib.rs", r#" extern crate proc_macro; pub fn f() { let _ts = proc_macro::TokenStream::new(); } "#, ) .build(); p.cargo("build -v").build_std(&setup).target_host().run(); } #[cargo_test(build_std_mock)] fn bench() { let setup = setup(); let p = project() .file( "src/lib.rs", r#" #![feature(test)] extern crate test; #[bench] fn b1(b: &mut test::Bencher) { b.iter(|| ()) } "#, ) .build(); p.cargo("bench -v").build_std(&setup).target_host().run(); } #[cargo_test(build_std_mock)] fn doc() { let setup = setup(); let p = project() .file( "src/lib.rs", r#" /// Doc pub fn f() -> Result<(), ()> {Ok(())} "#, ) .build(); p.cargo("doc -v").build_std(&setup).target_host().run(); } #[cargo_test(build_std_mock)] fn check_std() { let setup = setup(); let p = project() .file( "src/lib.rs", " extern crate core; extern crate alloc; extern crate proc_macro; pub fn f() {} ", ) .file("src/main.rs", "fn main() {}") .file( "tests/t1.rs", r#" #[test] fn t1() { assert_eq!(1, 2); } "#, ) .build(); p.cargo("check -v --all-targets") .build_std(&setup) .target_host() .run(); p.cargo("check -v --all-targets --profile=test") .build_std(&setup) .target_host() .run(); } #[cargo_test(build_std_mock)] fn doctest() { let setup = setup(); let p = project() .file( "src/lib.rs", r#" /// Doc /// ``` /// std::custom_api(); /// ``` pub fn f() {} "#, ) .build(); p.cargo("test --doc -v") .build_std(&setup) .with_stdout_data(str![[r#" running 1 test test src/lib.rs - f (line 3) ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .target_host() .run(); } #[cargo_test(build_std_mock)] fn no_implicit_alloc() { // Demonstrate that alloc is not implicitly in scope. let setup = setup(); let p = project() .file( "src/lib.rs", r#" pub fn f() { let _: Vec = alloc::vec::Vec::new(); } "#, ) .build(); p.cargo("build -v") .build_std(&setup) .target_host() .with_stderr_data(str![[r#" ... error[E0433]: failed to resolve[..]`alloc` ... "#]]) .with_status(101) .run(); } #[cargo_test(build_std_mock)] fn macro_expanded_shadow() { // This tests a bug caused by the previous use of `--extern` to directly // load sysroot crates. This necessitated the switch to `--sysroot` to // retain existing behavior. See // https://github.com/rust-lang/wg-cargo-std-aware/issues/40 for more // detail. let setup = setup(); let p = project() .file( "src/lib.rs", r#" macro_rules! a { () => (extern crate std as alloc;) } a!(); "#, ) .build(); p.cargo("build -v").build_std(&setup).target_host().run(); } #[cargo_test(build_std_mock)] fn ignores_incremental() { // Incremental is not really needed for std, make sure it is disabled. // Incremental also tends to have bugs that affect std libraries more than // any other crate. let setup = setup(); let p = project().file("src/lib.rs", "").build(); p.cargo("build") .env("CARGO_INCREMENTAL", "1") .build_std(&setup) .target_host() .run(); let incremental: Vec<_> = p .glob(format!("target/{}/debug/incremental/*", rustc_host())) .map(|e| e.unwrap()) .collect(); assert_eq!(incremental.len(), 1); assert!( incremental[0] .file_name() .unwrap() .to_str() .unwrap() .starts_with("foo-") ); } #[cargo_test(build_std_mock)] fn cargo_config_injects_compiler_builtins() { let setup = setup(); let p = project() .file( "src/lib.rs", r#" #![no_std] pub fn foo() { assert_eq!(u8::MIN, 0); } "#, ) .file( ".cargo/config.toml", r#" [unstable] build-std = ['core'] "#, ) .build(); let mut build = p.cargo("build -v --lib"); enable_build_std(&mut build, &setup); build .target_host() .with_stderr_does_not_contain("[..]libstd[..]") .run(); } #[cargo_test(build_std_mock)] fn different_features() { let setup = setup(); let p = project() .file( "src/lib.rs", " pub fn foo() { std::conditional_function(); } ", ) .build(); p.cargo("build") .build_std(&setup) .arg("-Zbuild-std-features=feature1") .target_host() .run(); } #[cargo_test(build_std_mock)] fn no_roots() { // Checks for a bug where it would panic if there are no roots. let setup = setup(); let p = project().file("tests/t1.rs", "").build(); p.cargo("build") .build_std(&setup) .target_host() .with_stderr_data(str![[r#" ... [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(build_std_mock)] fn proc_macro_only() { // Checks for a bug where it would panic if building a proc-macro only let setup = setup(); let p = project() .file( "Cargo.toml", r#" [package] name = "pm" version = "0.1.0" [lib] proc-macro = true "#, ) .file("src/lib.rs", "") .build(); p.cargo("build") .build_std(&setup) .target_host() .with_stderr_data(str![[r#" ... [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test(build_std_mock)] fn fetch() { let setup = setup(); let p = project().file("src/main.rs", "fn main() {}").build(); p.cargo("fetch") .build_std(&setup) .target_host() .with_stderr_contains("[DOWNLOADED] [..]") .run(); p.cargo("build") .build_std(&setup) .target_host() .with_stderr_does_not_contain("[DOWNLOADED] [..]") .run(); } cargo-0.91.0/tests/testsuite/test.rs000064400000000000000000004113601046102023000155330ustar 00000000000000//! Tests for the `cargo test` command. use std::fs; use crate::prelude::*; use crate::utils::cargo_exe; use cargo_test_support::registry::Package; use cargo_test_support::{basic_bin_manifest, basic_lib_manifest, basic_manifest, project, str}; use cargo_test_support::{cross_compile, paths}; use cargo_test_support::{rustc_host, rustc_host_env, sleep_ms}; use cargo_util::paths::dylib_path_envvar; use crate::utils::cross_compile::can_run_on_host as cross_compile_can_run_on_host; #[cargo_test] fn cargo_test_simple() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file( "src/main.rs", r#" fn hello() -> &'static str { "hello" } pub fn main() { println!("{}", hello()) } #[test] fn test_hello() { assert_eq!(hello(), "hello") } "#, ) .build(); p.cargo("build").run(); assert!(p.bin("foo").is_file()); p.process(&p.bin("foo")) .with_stdout_data(str![[r#" hello "#]]) .run(); p.cargo("test") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/main.rs (target/debug/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" ... test test_hello ... ok ... "#]]) .run(); } #[cargo_test] fn cargo_test_release() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.1.0" edition = "2015" [dependencies] bar = { path = "bar" } "#, ) .file( "src/lib.rs", r#" extern crate bar; pub fn foo() { bar::bar(); } #[test] fn test() { foo(); } "#, ) .file( "tests/test.rs", r#" extern crate foo; #[test] fn test() { foo::foo(); } "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "pub fn bar() {}") .build(); p.cargo("test -v --release") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [RUNNING] `rustc [..]-C opt-level=3 [..]` [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc [..]-C opt-level=3 [..]` [RUNNING] `rustc [..]-C opt-level=3 [..]` [RUNNING] `rustc [..]-C opt-level=3 [..]` [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/release/deps/foo-[HASH][EXE]` [RUNNING] `[ROOT]/foo/target/release/deps/test-[HASH][EXE]` [DOCTEST] foo [RUNNING] `rustdoc [..]--test src/lib.rs[..]` "#]]) .with_stdout_data( str![[r#" test test ... ok test test ... ok running 0 tests ... "#]] .unordered(), ) .run(); } #[cargo_test] fn cargo_test_overflow_checks() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = [] [[bin]] name = "foo" [profile.release] overflow-checks = true "#, ) .file( "src/foo.rs", r#" use std::panic; pub fn main() { let r = panic::catch_unwind(|| { [1, i32::MAX].iter().sum::(); }); assert!(r.is_err()); } "#, ) .build(); p.cargo("build --release").run(); assert!(p.release_bin("foo").is_file()); p.process(&p.release_bin("foo")).with_stdout_data("").run(); } #[cargo_test] fn cargo_test_quiet_with_harness() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [[test]] name = "foo" path = "src/foo.rs" harness = true "#, ) .file( "src/foo.rs", r#" fn main() {} #[test] fn test_hello() {} "#, ) .build(); p.cargo("test -q") .with_stdout_data(str![[r#" running 1 test . test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .with_stderr_data("") .run(); } #[cargo_test] fn cargo_test_quiet_no_harness() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [[bin]] name = "foo" test = false [[test]] name = "foo" path = "src/main.rs" harness = false "#, ) .file( "src/main.rs", r#" fn main() {} #[test] fn test_hello() {} "#, ) .build(); p.cargo("test -q") .with_stdout_data("") .with_stderr_data("") .run(); } #[cargo_test] fn cargo_doc_test_quiet() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] "#, ) .file( "src/lib.rs", r#" /// ``` /// let result = foo::add(2, 3); /// assert_eq!(result, 5); /// ``` pub fn add(a: i32, b: i32) -> i32 { a + b } /// ``` /// let result = foo::div(10, 2); /// assert_eq!(result, 5); /// ``` /// /// # Panics /// /// The function panics if the second argument is zero. /// /// ```rust,should_panic /// // panics on division by zero /// foo::div(10, 0); /// ``` pub fn div(a: i32, b: i32) -> i32 { if b == 0 { panic!("Divide-by-zero error"); } a / b } #[test] fn test_hello() {} "#, ) .build(); p.cargo("test -q") .with_stdout_data(str![[r#" running 1 test . test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s running 3 tests ... test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .with_stderr_data("") .run(); } #[cargo_test] fn cargo_test_verbose() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file( "src/main.rs", r#" fn main() {} #[test] fn test_hello() {} "#, ) .build(); p.cargo("test -v hello") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc [..] src/main.rs [..]` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/debug/deps/foo-[HASH][EXE] hello` "#]]) .with_stdout_data(str![[r#" ... test test_hello ... ok ... "#]]) .run(); } #[cargo_test] fn many_similar_names() { let p = project() .file( "src/lib.rs", " pub fn foo() {} #[test] fn lib_test() {} ", ) .file( "src/main.rs", " extern crate foo; fn main() {} #[test] fn bin_test() { foo::foo() } ", ) .file( "tests/foo.rs", r#" extern crate foo; #[test] fn test_test() { foo::foo() } "#, ) .build(); p.cargo("test -v") .with_stdout_data( str![[r#" test bin_test ... ok test lib_test ... ok test test_test ... ok ... "#]] .unordered(), ) .run(); } #[cargo_test] fn cargo_test_failing_test_in_bin() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file( "src/main.rs", r#" fn hello() -> &'static str { "hello" } pub fn main() { println!("{}", hello()) } #[test] fn test_hello() { assert_eq!(hello(), "nope", "NOPE!") } "#, ) .build(); p.cargo("build").run(); assert!(p.bin("foo").is_file()); p.process(&p.bin("foo")) .with_stdout_data(str![[r#" hello "#]]) .run(); p.cargo("test") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/main.rs (target/debug/deps/foo-[HASH][EXE]) [ERROR] test failed, to rerun pass `--bin foo` "#]]) .with_stdout_data("...\n[..]NOPE![..]\n...") .with_status(101) .run(); } #[cargo_test] fn cargo_test_failing_test_in_test() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/main.rs", r#"pub fn main() { println!("hello"); }"#) .file( "tests/footest.rs", r#"#[test] fn test_hello() { assert!(false, "FALSE!") }"#, ) .build(); p.cargo("build").run(); assert!(p.bin("foo").is_file()); p.process(&p.bin("foo")) .with_stdout_data(str![[r#" hello "#]]) .run(); p.cargo("test") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/main.rs (target/debug/deps/foo-[HASH][EXE]) [RUNNING] tests/footest.rs (target/debug/deps/footest-[HASH][EXE]) [ERROR] test failed, to rerun pass `--test footest` "#]]) .with_stdout_data( str![[r#" ... running 0 tests ... running 1 test test test_hello ... FAILED ... [..]FALSE![..] ... "#]] .unordered(), ) .with_status(101) .run(); } #[cargo_test] fn cargo_test_failing_test_in_lib() { let p = project() .file("Cargo.toml", &basic_lib_manifest("foo")) .file( "src/lib.rs", r#"#[test] fn test_hello() { assert!(false, "FALSE!") }"#, ) .build(); p.cargo("test") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) [ERROR] test failed, to rerun pass `--lib` "#]]) .with_stdout_data(str![[r#" ... test test_hello ... FAILED ... [..]FALSE![..] ... "#]]) .with_status(101) .run(); } #[cargo_test] fn test_with_lib_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [[bin]] name = "baz" path = "src/main.rs" "#, ) .file( "src/lib.rs", r#" /// /// ```rust /// extern crate foo; /// fn main() { /// println!("{:?}", foo::foo()); /// } /// ``` /// pub fn foo(){} #[test] fn lib_test() {} "#, ) .file( "src/main.rs", " #[allow(unused_extern_crates)] extern crate foo; fn main() {} #[test] fn bin_test() {} ", ) .build(); p.cargo("test") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) [RUNNING] unittests src/main.rs (target/debug/deps/baz-[HASH][EXE]) [DOCTEST] foo "#]]) .with_stdout_data( str![[r#" test lib_test ... ok test bin_test ... ok test [..] ... ok ... "#]] .unordered(), ) .run(); } #[cargo_test] fn test_with_deep_lib_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.bar] path = "../bar" "#, ) .file( "src/lib.rs", " #[cfg(test)] extern crate bar; /// ``` /// foo::foo(); /// ``` pub fn foo() {} #[test] fn bar_test() { bar::bar(); } ", ) .build(); let _p2 = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("src/lib.rs", "pub fn bar() {} #[test] fn foo_test() {}") .build(); p.cargo("test") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.0.1 ([ROOT]/bar) [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) [DOCTEST] foo "#]]) .with_stdout_data( str![[r#" test bar_test ... ok test [..] ... ok ... "#]] .unordered(), ) .run(); } #[cargo_test] fn external_test_explicit() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [[test]] name = "test" path = "src/test.rs" "#, ) .file( "src/lib.rs", r#" pub fn get_hello() -> &'static str { "Hello" } #[test] fn internal_test() {} "#, ) .file( "src/test.rs", r#" extern crate foo; #[test] fn external_test() { assert_eq!(foo::get_hello(), "Hello") } "#, ) .build(); p.cargo("test") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) [RUNNING] src/test.rs (target/debug/deps/test-[HASH][EXE]) [DOCTEST] foo "#]]) .with_stdout_data( str![[r#" test internal_test ... ok test external_test ... ok running 0 tests ... "#]] .unordered(), ) .run(); } #[cargo_test] fn external_test_named_test() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [[test]] name = "test" "#, ) .file("src/lib.rs", "") .file("tests/test.rs", "#[test] fn foo() {}") .build(); p.cargo("test").run(); } #[cargo_test] fn external_test_implicit() { let p = project() .file( "src/lib.rs", r#" pub fn get_hello() -> &'static str { "Hello" } #[test] fn internal_test() {} "#, ) .file( "tests/external.rs", r#" extern crate foo; #[test] fn external_test() { assert_eq!(foo::get_hello(), "Hello") } "#, ) .build(); p.cargo("test") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) [RUNNING] tests/external.rs (target/debug/deps/external-[HASH][EXE]) [DOCTEST] foo "#]]) .with_stdout_data( str![[r#" test internal_test ... ok test external_test ... ok running 0 tests ... "#]] .unordered(), ) .run(); } #[cargo_test] fn dont_run_examples() { let p = project() .file("src/lib.rs", "") .file( "examples/dont-run-me-i-will-fail.rs", r#" fn main() { panic!("Examples should not be run by 'cargo test'"); } "#, ) .build(); p.cargo("test").run(); } #[cargo_test] fn pass_through_escaped() { let p = project() .file( "src/lib.rs", " /// ```rust /// assert!(foo::foo()); /// ``` pub fn foo() -> bool { true } /// ```rust /// assert!(!foo::bar()); /// ``` pub fn bar() -> bool { false } #[test] fn test_foo() { assert!(foo()); } #[test] fn test_bar() { assert!(!bar()); } ", ) .build(); p.cargo("test -- bar") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) [DOCTEST] foo "#]]) .with_stdout_data(str![[r#" ... running 1 test test test_bar ... ok ... "#]]) .run(); p.cargo("test -- foo") .with_stderr_data(str![[r#" [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) [DOCTEST] foo "#]]) .with_stdout_data(str![[r#" ... running 1 test test test_foo ... ok ... "#]]) .run(); p.cargo("test -- foo bar") .with_stderr_data(str![[r#" [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) [DOCTEST] foo "#]]) .with_stdout_data( str![[r#" running 2 tests test test_foo ... ok test test_bar ... ok ... "#]] .unordered(), ) .run(); } // Unlike `pass_through_escaped`, doctests won't run when using `testname` as an optimization #[cargo_test] fn pass_through_testname() { let p = project() .file( "src/lib.rs", " /// ```rust /// assert!(foo::foo()); /// ``` pub fn foo() -> bool { true } /// ```rust /// assert!(!foo::bar()); /// ``` pub fn bar() -> bool { false } #[test] fn test_foo() { assert!(foo()); } #[test] fn test_bar() { assert!(!bar()); } ", ) .build(); p.cargo("test bar") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" ... running 1 test test test_bar ... ok ... "#]]) .run(); p.cargo("test foo") .with_stderr_data(str![[r#" [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" ... running 1 test test test_foo ... ok ... "#]]) .run(); p.cargo("test foo -- bar") .with_stderr_data(str![[r#" [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data( str![[r#" running 2 tests test test_bar ... ok test test_foo ... ok ... "#]] .unordered(), ) .run(); } // Regression test for running cargo-test twice with // tests in an rlib #[cargo_test] fn cargo_test_twice() { let p = project() .file("Cargo.toml", &basic_lib_manifest("foo")) .file( "src/foo.rs", r#" #![crate_type = "rlib"] #[test] fn dummy_test() { } "#, ) .build(); for _ in 0..2 { p.cargo("test").run(); } } #[cargo_test] fn lib_bin_same_name() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lib] name = "foo" [[bin]] name = "foo" "#, ) .file("src/lib.rs", "#[test] fn lib_test() {}") .file( "src/main.rs", " #[allow(unused_extern_crates)] extern crate foo; #[test] fn bin_test() {} ", ) .build(); p.cargo("test") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) [RUNNING] unittests src/main.rs (target/debug/deps/foo-[HASH][EXE]) [DOCTEST] foo "#]]) .with_stdout_data( str![[r#" test lib_test ... ok test bin_test ... ok running 0 tests ... "#]] .unordered(), ) .run(); } #[cargo_test] fn lib_with_standard_name() { let p = project() .file("Cargo.toml", &basic_manifest("syntax", "0.0.1")) .file( "src/lib.rs", " /// ``` /// syntax::foo(); /// ``` pub fn foo() {} #[test] fn foo_test() {} ", ) .file( "tests/test.rs", " extern crate syntax; #[test] fn test() { syntax::foo() } ", ) .build(); p.cargo("test") .with_stderr_data(str![[r#" [COMPILING] syntax v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/syntax-[HASH][EXE]) [RUNNING] tests/test.rs (target/debug/deps/test-[HASH][EXE]) [DOCTEST] syntax "#]]) .with_stdout_data( str![[r#" test foo_test ... ok test test ... ok test [..] ... ok ... "#]] .unordered(), ) .run(); } #[cargo_test] fn lib_with_standard_name2() { let p = project() .file( "Cargo.toml", r#" [package] name = "syntax" version = "0.0.1" edition = "2015" authors = [] [lib] name = "syntax" test = false doctest = false "#, ) .file("src/lib.rs", "pub fn foo() {}") .file( "src/main.rs", " extern crate syntax; fn main() {} #[test] fn test() { syntax::foo() } ", ) .build(); p.cargo("test") .with_stderr_data(str![[r#" [COMPILING] syntax v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/main.rs (target/debug/deps/syntax-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" ... test test ... ok ... "#]]) .run(); } #[cargo_test] fn lib_without_name() { let p = project() .file( "Cargo.toml", r#" [package] name = "syntax" version = "0.0.1" edition = "2015" authors = [] [lib] test = false doctest = false "#, ) .file("src/lib.rs", "pub fn foo() {}") .file( "src/main.rs", " extern crate syntax; fn main() {} #[test] fn test() { syntax::foo() } ", ) .build(); p.cargo("test") .with_stderr_data(str![[r#" [COMPILING] syntax v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/main.rs (target/debug/deps/syntax-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" ... test test ... ok ... "#]]) .run(); } #[cargo_test] fn bin_without_name() { let p = project() .file( "Cargo.toml", r#" [package] name = "syntax" version = "0.0.1" edition = "2015" authors = [] [lib] test = false doctest = false [[bin]] path = "src/main.rs" "#, ) .file("src/lib.rs", "pub fn foo() {}") .file( "src/main.rs", " extern crate syntax; fn main() {} #[test] fn test() { syntax::foo() } ", ) .build(); p.cargo("test") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: binary target bin.name is required "#]]) .run(); } #[cargo_test] fn bench_without_name() { let p = project() .file( "Cargo.toml", r#" [package] name = "syntax" version = "0.0.1" edition = "2015" authors = [] [lib] test = false doctest = false [[bench]] path = "src/bench.rs" "#, ) .file("src/lib.rs", "pub fn foo() {}") .file( "src/main.rs", " extern crate syntax; fn main() {} #[test] fn test() { syntax::foo() } ", ) .file( "src/bench.rs", " #![feature(test)] extern crate syntax; extern crate test; #[bench] fn external_bench(_b: &mut test::Bencher) {} ", ) .build(); p.cargo("test") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: benchmark target bench.name is required "#]]) .run(); } #[cargo_test] fn test_without_name() { let p = project() .file( "Cargo.toml", r#" [package] name = "syntax" version = "0.0.1" edition = "2015" authors = [] [lib] test = false doctest = false [[test]] path = "src/test.rs" "#, ) .file( "src/lib.rs", r#" pub fn foo() {} pub fn get_hello() -> &'static str { "Hello" } "#, ) .file( "src/main.rs", " extern crate syntax; fn main() {} #[test] fn test() { syntax::foo() } ", ) .file( "src/test.rs", r#" extern crate syntax; #[test] fn external_test() { assert_eq!(syntax::get_hello(), "Hello") } "#, ) .build(); p.cargo("test") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: test target test.name is required "#]]) .run(); } #[cargo_test] fn example_without_name() { let p = project() .file( "Cargo.toml", r#" [package] name = "syntax" version = "0.0.1" edition = "2015" authors = [] [lib] test = false doctest = false [[example]] path = "examples/example.rs" "#, ) .file("src/lib.rs", "pub fn foo() {}") .file( "src/main.rs", " extern crate syntax; fn main() {} #[test] fn test() { syntax::foo() } ", ) .file( "examples/example.rs", r#" extern crate syntax; fn main() { println!("example1"); } "#, ) .build(); p.cargo("test") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: example target example.name is required "#]]) .run(); } #[cargo_test] fn bin_there_for_integration() { let p = project() .file( "src/main.rs", " fn main() { std::process::exit(101); } #[test] fn main_test() {} ", ) .file( "tests/foo.rs", r#" use std::process::Command; #[test] fn test_test() { let status = Command::new("target/debug/foo").status().unwrap(); assert_eq!(status.code(), Some(101)); } "#, ) .build(); p.cargo("test -v") .with_stdout_data( str![[r#" test main_test ... ok test test_test ... ok ... "#]] .unordered(), ) .run(); } #[cargo_test] fn test_dylib() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lib] name = "foo" crate-type = ["dylib"] [dependencies.bar] path = "bar" "#, ) .file( "src/lib.rs", r#" extern crate bar as the_bar; pub fn bar() { the_bar::baz(); } #[test] fn foo() { bar(); } "#, ) .file( "tests/test.rs", r#" extern crate foo as the_foo; #[test] fn foo() { the_foo::bar(); } "#, ) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [lib] name = "bar" crate-type = ["dylib"] "#, ) .file("bar/src/lib.rs", "pub fn baz() {}") .build(); p.cargo("test") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) [RUNNING] tests/test.rs (target/debug/deps/test-[HASH][EXE]) "#]]) .with_stdout_data( str![[r#" test foo ... ok test foo ... ok ... "#]] .unordered(), ) .run(); p.root().move_into_the_past(); p.cargo("test") .with_stderr_data(str![[r#" [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) [RUNNING] tests/test.rs (target/debug/deps/test-[HASH][EXE]) "#]]) .with_stdout_data( str![[r#" test foo ... ok test foo ... ok ... "#]] .unordered(), ) .run(); } #[cargo_test] fn test_twice_with_build_cmd() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" "#, ) .file("build.rs", "fn main() {}") .file("src/lib.rs", "#[test] fn foo() {}") .build(); p.cargo("test") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) [DOCTEST] foo "#]]) .with_stdout_data( str![[r#" test foo ... ok running 0 tests ... "#]] .unordered(), ) .run(); p.cargo("test") .with_stderr_data(str![[r#" [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) [DOCTEST] foo "#]]) .with_stdout_data( str![[r#" test foo ... ok running 0 tests ... "#]] .unordered(), ) .run(); } #[cargo_test] fn test_then_build() { let p = project().file("src/lib.rs", "#[test] fn foo() {}").build(); p.cargo("test") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) [DOCTEST] foo "#]]) .with_stdout_data( str![[r#" test foo ... ok running 0 tests ... "#]] .unordered(), ) .run(); p.cargo("build") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn test_no_run() { let p = project() .file("src/lib.rs", "#[test] fn foo() { panic!() }") .build(); p.cargo("test --no-run") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [EXECUTABLE] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) "#]]) .run(); } #[cargo_test] fn test_no_run_emit_json() { let p = project() .file("src/lib.rs", "#[test] fn foo() { panic!() }") .build(); p.cargo("test --no-run --message-format json") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn test_run_specific_bin_target() { let prj = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [[bin]] name="bin1" path="src/bin1.rs" [[bin]] name="bin2" path="src/bin2.rs" "#, ) .file("src/bin1.rs", "#[test] fn test1() { }") .file("src/bin2.rs", "#[test] fn test2() { }") .build(); prj.cargo("test --bin bin2") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/bin2.rs (target/debug/deps/bin2-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" ... test test2 ... ok ... "#]]) .run(); } #[cargo_test] fn test_run_implicit_bin_target() { let prj = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [[bin]] name="mybin" path="src/mybin.rs" "#, ) .file( "src/mybin.rs", "#[test] fn test_in_bin() { } fn main() { panic!(\"Don't execute me!\"); }", ) .file("tests/mytest.rs", "#[test] fn test_in_test() { }") .file("benches/mybench.rs", "#[test] fn test_in_bench() { }") .file( "examples/myexm.rs", "#[test] fn test_in_exm() { } fn main() { panic!(\"Don't execute me!\"); }", ) .build(); prj.cargo("test --bins") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/mybin.rs (target/debug/deps/mybin-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" ... test test_in_bin ... ok ... "#]]) .run(); } #[cargo_test] fn test_run_specific_test_target() { let prj = project() .file("src/bin/a.rs", "fn main() { }") .file("src/bin/b.rs", "#[test] fn test_b() { } fn main() { }") .file("tests/a.rs", "#[test] fn test_a() { }") .file("tests/b.rs", "#[test] fn test_b() { }") .build(); prj.cargo("test --test b") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] tests/b.rs (target/debug/deps/b-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" ... test test_b ... ok ... "#]]) .run(); } #[cargo_test] fn test_run_implicit_test_target() { let prj = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [[bin]] name="mybin" path="src/mybin.rs" "#, ) .file( "src/mybin.rs", "#[test] fn test_in_bin() { } fn main() { panic!(\"Don't execute me!\"); }", ) .file("tests/mytest.rs", "#[test] fn test_in_test() { }") .file("benches/mybench.rs", "#[test] fn test_in_bench() { }") .file( "examples/myexm.rs", "fn main() { compile_error!(\"Don't build me!\"); }", ) .build(); prj.cargo("test --tests") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/mybin.rs (target/debug/deps/mybin-[HASH][EXE]) [RUNNING] tests/mytest.rs (target/debug/deps/mytest-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" ... test test_in_test ... ok ... "#]]) .run(); } #[cargo_test] fn test_run_implicit_bench_target() { let prj = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [[bin]] name="mybin" path="src/mybin.rs" "#, ) .file( "src/mybin.rs", "#[test] fn test_in_bin() { } fn main() { panic!(\"Don't execute me!\"); }", ) .file("tests/mytest.rs", "#[test] fn test_in_test() { }") .file("benches/mybench.rs", "#[test] fn test_in_bench() { }") .file( "examples/myexm.rs", "fn main() { compile_error!(\"Don't build me!\"); }", ) .build(); prj.cargo("test --benches") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/mybin.rs (target/debug/deps/mybin-[HASH][EXE]) [RUNNING] benches/mybench.rs (target/debug/deps/mybench-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" ... test test_in_bench ... ok ... "#]]) .run(); } #[cargo_test] fn test_run_implicit_example_target() { let prj = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [[bin]] name = "mybin" path = "src/mybin.rs" [[example]] name = "myexm1" [[example]] name = "myexm2" test = true [profile.test] panic = "abort" # this should be ignored by default Cargo targets set. "#, ) .file( "src/mybin.rs", "#[test] fn test_in_bin() { } fn main() { panic!(\"Don't execute me!\"); }", ) .file("tests/mytest.rs", "#[test] fn test_in_test() { }") .file("benches/mybench.rs", "#[test] fn test_in_bench() { }") .file( "examples/myexm1.rs", "#[test] fn test_in_exm() { } fn main() { panic!(\"Don't execute me!\"); }", ) .file( "examples/myexm2.rs", "#[test] fn test_in_exm() { } fn main() { panic!(\"Don't execute me!\"); }", ) .build(); // Compiles myexm1 as normal binary (without --test), but does not run it. prj.cargo("test -v") .with_stderr_contains("[RUNNING] `rustc [..]myexm1.rs [..]--crate-type bin[..]") .with_stderr_contains("[RUNNING] `rustc [..]myexm2.rs [..]--test[..]") .with_stderr_does_not_contain("[RUNNING] [..]myexm1-[..]") // profile.test panic settings shouldn't be applied even to myexm1 .with_stderr_line_without(&["[RUNNING] `rustc --crate-name myexm1"], &["panic=abort"]) .with_stderr_contains("[RUNNING] [..]target/debug/examples/myexm2-[..]") .run(); // Only tests myexm2. prj.cargo("test --tests") .with_stderr_does_not_contain("[RUNNING] [..]myexm1-[..]") .with_stderr_contains("[RUNNING] [..]target/debug/examples/myexm2-[..]") .run(); // Tests all examples. prj.cargo("test --examples") .with_stderr_data(str![[r#" ... [RUNNING] unittests examples/myexm1.rs (target/debug/examples/myexm1-[HASH][EXE]) [RUNNING] unittests examples/myexm2.rs (target/debug/examples/myexm2-[HASH][EXE]) ... "#]]) .run(); // Test an example, even without `test` set. prj.cargo("test --example myexm1") .with_stderr_data(str![[r#" ... [RUNNING] unittests examples/myexm1.rs (target/debug/examples/myexm1-[HASH][EXE]) ... "#]]) .run(); // Tests all examples. prj.cargo("test --all-targets") .with_stderr_data(str![[r#" ... [RUNNING] unittests examples/myexm1.rs (target/debug/examples/myexm1-[HASH][EXE]) [RUNNING] unittests examples/myexm2.rs (target/debug/examples/myexm2-[HASH][EXE]) ... "#]]) .run(); } #[cargo_test] fn test_filtered_excludes_compiling_examples() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [[bin]] name = "mybin" test = false "#, ) .file( "src/lib.rs", "#[cfg(test)] mod tests { #[test] fn test_in_lib() { } }", ) .file( "src/bin/mybin.rs", "#[test] fn test_in_bin() { } fn main() { panic!(\"Don't execute me!\"); }", ) .file("tests/mytest.rs", "#[test] fn test_in_test() { }") .file( "benches/mybench.rs", "#[test] fn test_in_bench() { assert!(false) }", ) .file( "examples/myexm1.rs", "#[test] fn test_in_exm() { assert!(false) } fn main() { panic!(\"Don't execute me!\"); }", ) .build(); p.cargo("test -v test_in_") .with_stdout_data(str![[r#" running 1 test test tests::test_in_lib ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test test_in_test ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .with_stderr_data( str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..] --crate-type lib [..]` [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..] --test [..]` [RUNNING] `rustc --crate-name mybin --edition=2015 src/bin/mybin.rs [..] --crate-type bin [..]` [RUNNING] `rustc --crate-name mytest --edition=2015 tests/mytest.rs [..] --test [..]` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/debug/deps/foo-[HASH][EXE] test_in_` [RUNNING] `[ROOT]/foo/target/debug/deps/mytest-[HASH][EXE] test_in_` "#]] .unordered(), ) .with_stderr_does_not_contain("[RUNNING][..]rustc[..]myexm1[..]") .with_stderr_does_not_contain("[RUNNING][..]deps/mybin-[..] test_in_") .run(); } #[cargo_test] fn test_no_harness() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [[bin]] name = "foo" test = false [[test]] name = "bar" path = "foo.rs" harness = false "#, ) .file("src/main.rs", "fn main() {}") .file("foo.rs", "fn main() {}") .build(); p.cargo("test -- --nocapture") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] foo.rs (target/debug/deps/bar-[HASH][EXE]) "#]]) .run(); } #[cargo_test] fn selective_testing() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.d1] path = "d1" [dependencies.d2] path = "d2" [lib] name = "foo" doctest = false "#, ) .file("src/lib.rs", "") .file( "d1/Cargo.toml", r#" [package] name = "d1" version = "0.0.1" edition = "2015" authors = [] [lib] name = "d1" doctest = false "#, ) .file("d1/src/lib.rs", "") .file( "d1/src/main.rs", "#[allow(unused_extern_crates)] extern crate d1; fn main() {}", ) .file( "d2/Cargo.toml", r#" [package] name = "d2" version = "0.0.1" edition = "2015" authors = [] [lib] name = "d2" doctest = false "#, ) .file("d2/src/lib.rs", "") .file( "d2/src/main.rs", "#[allow(unused_extern_crates)] extern crate d2; fn main() {}", ); let p = p.build(); println!("d1"); p.cargo("test -p d1") .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] d1 v0.0.1 ([ROOT]/foo/d1) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/d1-[HASH][EXE]) [RUNNING] unittests src/main.rs (target/debug/deps/d1-[HASH][EXE]) "#]]) .with_stdout_data( str![[r#" running 0 tests running 0 tests ... "#]] .unordered(), ) .run(); println!("d2"); p.cargo("test -p d2") .with_stderr_data(str![[r#" [COMPILING] d2 v0.0.1 ([ROOT]/foo/d2) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/d2-[HASH][EXE]) [RUNNING] unittests src/main.rs (target/debug/deps/d2-[HASH][EXE]) "#]]) .with_stdout_data( str![[r#" running 0 tests running 0 tests ... "#]] .unordered(), ) .run(); println!("whole"); p.cargo("test") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" ... running 0 tests ... "#]]) .run(); } #[cargo_test] fn almost_cyclic_but_not_quite() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dev-dependencies.b] path = "b" [dev-dependencies.c] path = "c" "#, ) .file( "src/lib.rs", r#" #[cfg(test)] extern crate b; #[cfg(test)] extern crate c; "#, ) .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.0.1" edition = "2015" authors = [] [dependencies.foo] path = ".." "#, ) .file( "b/src/lib.rs", r#" #[allow(unused_extern_crates)] extern crate foo; "#, ) .file("c/Cargo.toml", &basic_manifest("c", "0.0.1")) .file("c/src/lib.rs", "") .build(); p.cargo("build").run(); p.cargo("test").run(); } #[cargo_test] fn build_then_selective_test() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.b] path = "b" "#, ) .file( "src/lib.rs", "#[allow(unused_extern_crates)] extern crate b;", ) .file( "src/main.rs", r#" #[allow(unused_extern_crates)] extern crate b; #[allow(unused_extern_crates)] extern crate foo; fn main() {} "#, ) .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) .file("b/src/lib.rs", "") .build(); p.cargo("build").run(); p.root().move_into_the_past(); p.cargo("test -p b").run(); } #[cargo_test] fn example_dev_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dev-dependencies.bar] path = "bar" "#, ) .file("src/lib.rs", "") .file("examples/e1.rs", "extern crate bar; fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file( "bar/src/lib.rs", r#" // make sure this file takes awhile to compile macro_rules! f0( () => (1) ); macro_rules! f1( () => ({(f0!()) + (f0!())}) ); macro_rules! f2( () => ({(f1!()) + (f1!())}) ); macro_rules! f3( () => ({(f2!()) + (f2!())}) ); macro_rules! f4( () => ({(f3!()) + (f3!())}) ); macro_rules! f5( () => ({(f4!()) + (f4!())}) ); macro_rules! f6( () => ({(f5!()) + (f5!())}) ); macro_rules! f7( () => ({(f6!()) + (f6!())}) ); macro_rules! f8( () => ({(f7!()) + (f7!())}) ); pub fn bar() { f8!(); } "#, ) .build(); p.cargo("test").run(); p.cargo("run --example e1 --release -v").run(); } #[cargo_test] fn selective_testing_with_docs() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.d1] path = "d1" "#, ) .file( "src/lib.rs", r#" /// ``` /// not valid rust /// ``` pub fn foo() {} "#, ) .file( "d1/Cargo.toml", r#" [package] name = "d1" version = "0.0.1" edition = "2015" authors = [] [lib] name = "d1" path = "d1.rs" "#, ) .file("d1/d1.rs", ""); let p = p.build(); p.cargo("test -p d1") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] d1 v0.0.1 ([ROOT]/foo/d1) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests d1.rs (target/debug/deps/d1-[HASH][EXE]) [DOCTEST] d1 "#]]) .with_stdout_data( str![[r#" running 0 tests running 0 tests ... "#]] .unordered(), ) .run(); } #[cargo_test] fn example_bin_same_name() { let p = project() .file("src/bin/foo.rs", r#"fn main() { println!("bin"); }"#) .file("examples/foo.rs", r#"fn main() { println!("example"); }"#) .build(); p.cargo("test --no-run -v") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..]` [RUNNING] `rustc [..]` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [EXECUTABLE] `[ROOT]/foo/target/debug/deps/foo-[HASH][EXE]` "#]]) .run(); assert!(!p.bin("foo").is_file()); assert!(p.bin("examples/foo").is_file()); p.process(&p.bin("examples/foo")) .with_stdout_data(str![[r#" example "#]]) .run(); p.cargo("run") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .with_stdout_data(str![[r#" bin "#]]) .run(); assert!(p.bin("foo").is_file()); } #[cargo_test] fn test_with_example_twice() { let p = project() .file("src/bin/foo.rs", r#"fn main() { println!("bin"); }"#) .file("examples/foo.rs", r#"fn main() { println!("example"); }"#) .build(); println!("first"); p.cargo("test -v").run(); assert!(p.bin("examples/foo").is_file()); println!("second"); p.cargo("test -v").run(); assert!(p.bin("examples/foo").is_file()); } #[cargo_test] fn example_with_dev_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lib] name = "foo" test = false doctest = false [dev-dependencies.a] path = "a" "#, ) .file("src/lib.rs", "") .file( "examples/ex.rs", "#[allow(unused_extern_crates)] extern crate a; fn main() {}", ) .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) .file("a/src/lib.rs", "") .build(); p.cargo("test -v") .with_stderr_data( str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] foo v0.0.1 ([ROOT]/foo) [COMPILING] a v0.0.1 ([ROOT]/foo/a) [RUNNING] `rustc --crate-name foo [..]` [RUNNING] `rustc --crate-name a [..]` [RUNNING] `rustc --crate-name ex [..] --extern a=[..]` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn bin_is_preserved() { let p = project() .file("src/lib.rs", "") .file("src/main.rs", "fn main() {}") .build(); p.cargo("build -v").run(); assert!(p.bin("foo").is_file()); println!("test"); p.cargo("test -v").run(); assert!(p.bin("foo").is_file()); } #[cargo_test] fn bad_example() { let p = project().file("src/lib.rs", ""); let p = p.build(); p.cargo("run --example foo") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no example target named `foo` in default-run packages "#]]) .run(); p.cargo("run --bin foo") .with_status(101) .with_stderr_data(str![[r#" [ERROR] no bin target named `foo` in default-run packages "#]]) .run(); } #[cargo_test] fn doctest_feature() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [features] bar = [] "#, ) .file( "src/lib.rs", r#" /// ```rust /// assert_eq!(foo::foo(), 1); /// ``` #[cfg(feature = "bar")] pub fn foo() -> i32 { 1 } "#, ) .build(); p.cargo("test --features bar") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) [DOCTEST] foo "#]]) .with_stdout_data( str![[r#" running 0 tests test [..] ... ok ... "#]] .unordered(), ) .run(); } #[cargo_test] fn dashes_to_underscores() { let p = project() .file("Cargo.toml", &basic_manifest("foo-bar", "0.0.1")) .file( "src/lib.rs", r#" /// ``` /// assert_eq!(foo_bar::foo(), 1); /// ``` pub fn foo() -> i32 { 1 } "#, ) .build(); p.cargo("test -v").run(); } #[cargo_test] fn doctest_dev_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dev-dependencies] b = { path = "b" } "#, ) .file( "src/lib.rs", r#" /// ``` /// extern crate b; /// ``` pub fn foo() {} "#, ) .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) .file("b/src/lib.rs", "") .build(); p.cargo("test -v").run(); } #[cargo_test] fn filter_no_doc_tests() { let p = project() .file( "src/lib.rs", r#" /// ``` /// extern crate b; /// ``` pub fn foo() {} "#, ) .file("tests/foo.rs", "") .build(); p.cargo("test --test=foo") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] tests/foo.rs (target/debug/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" ... running 0 tests ... "#]]) .run(); } #[cargo_test] fn dylib_doctest() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lib] name = "foo" crate-type = ["rlib", "dylib"] test = false "#, ) .file( "src/lib.rs", r#" /// ``` /// foo::foo(); /// ``` pub fn foo() {} "#, ) .build(); p.cargo("test") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [DOCTEST] foo "#]]) .with_stdout_data(str![[r#" ... test [..] ... ok ... "#]]) .run(); } #[cargo_test] fn dylib_doctest2() { // Can't doc-test dylibs, as they're statically linked together. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lib] name = "foo" crate-type = ["dylib"] test = false "#, ) .file( "src/lib.rs", r#" /// ``` /// foo::foo(); /// ``` pub fn foo() {} "#, ) .build(); p.cargo("test") .with_stderr_data(str![[r#" [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn cyclic_dev_dep_doc_test() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dev-dependencies] bar = { path = "bar" } "#, ) .file( "src/lib.rs", r#" //! ``` //! extern crate bar; //! ``` "#, ) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [dependencies] foo = { path = ".." } "#, ) .file( "bar/src/lib.rs", r#" #[allow(unused_extern_crates)] extern crate foo; "#, ) .build(); p.cargo("test") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] foo v0.0.1 ([ROOT]/foo) [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) [DOCTEST] foo "#]]) .with_stdout_data( str![[r#" running 0 tests test [..] ... ok ... "#]] .unordered(), ) .run(); } #[cargo_test] fn dev_dep_with_build_script() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dev-dependencies] bar = { path = "bar" } "#, ) .file("src/lib.rs", "") .file("examples/foo.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] build = "build.rs" "#, ) .file("bar/src/lib.rs", "") .file("bar/build.rs", "fn main() {}") .build(); p.cargo("test").run(); } #[cargo_test] fn no_fail_fast() { let p = project() .file( "src/lib.rs", r#" pub fn add_one(x: i32) -> i32{ x + 1 } /// ```rust /// use foo::sub_one; /// assert_eq!(sub_one(101), 100); /// ``` pub fn sub_one(x: i32) -> i32{ x - 1 } "#, ) .file( "tests/test_add_one.rs", r#" extern crate foo; use foo::*; #[test] fn add_one_test() { assert_eq!(add_one(1), 2); } #[test] fn fail_add_one_test() { assert_eq!(add_one(1), 1); } "#, ) .file( "tests/test_sub_one.rs", r#" extern crate foo; use foo::*; #[test] fn sub_one_test() { assert_eq!(sub_one(1), 0); } "#, ) .build(); p.cargo("test --no-fail-fast") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) [RUNNING] tests/test_add_one.rs (target/debug/deps/test_add_one-[HASH][EXE]) [ERROR] test failed, to rerun pass `--test test_add_one` [RUNNING] tests/test_sub_one.rs (target/debug/deps/test_sub_one-[HASH][EXE]) [DOCTEST] foo [ERROR] 1 target failed: `--test test_add_one` "#]]) .with_stdout_data(str![[r#" running 0 tests test add_one_test ... ok test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s test sub_one_test ... ok test [..] ... ok ... "#]].unordered()) .run(); } #[cargo_test] fn test_multiple_packages() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies.d1] path = "d1" [dependencies.d2] path = "d2" [lib] name = "foo" doctest = false "#, ) .file("src/lib.rs", "") .file( "d1/Cargo.toml", r#" [package] name = "d1" version = "0.0.1" edition = "2015" authors = [] [lib] name = "d1" doctest = false "#, ) .file("d1/src/lib.rs", "") .file( "d2/Cargo.toml", r#" [package] name = "d2" version = "0.0.1" edition = "2015" authors = [] [lib] name = "d2" doctest = false "#, ) .file("d2/src/lib.rs", ""); let p = p.build(); p.cargo("test -p d1 -p d2") .with_stderr_data(str![[r#" ... [RUNNING] unittests src/lib.rs (target/debug/deps/d1-[HASH][EXE]) [RUNNING] unittests src/lib.rs (target/debug/deps/d2-[HASH][EXE]) ... "#]]) .with_stdout_data( str![[r#" running 0 tests running 0 tests ... "#]] .unordered(), ) .run(); } #[cargo_test] fn bin_does_not_rebuild_tests() { let p = project() .file("src/lib.rs", "") .file("src/main.rs", "fn main() {}") .file("tests/foo.rs", ""); let p = p.build(); p.cargo("test -v").run(); sleep_ms(1000); fs::write(p.root().join("src/main.rs"), "fn main() { 3; }").unwrap(); p.cargo("test -v --no-run") .with_stderr_data(str![[r#" [DIRTY] foo v0.0.1 ([ROOT]/foo): the file `src/main.rs` has changed ([..]) [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..] src/main.rs [..]` [RUNNING] `rustc [..] src/main.rs [..]` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [EXECUTABLE] `[ROOT]/foo/target/debug/deps/foo-[HASH][EXE]` [EXECUTABLE] `[ROOT]/foo/target/debug/deps/foo-[HASH][EXE]` [EXECUTABLE] `[ROOT]/foo/target/debug/deps/foo-[HASH][EXE]` "#]]) .run(); } #[cargo_test] fn selective_test_wonky_profile() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [profile.release] opt-level = 2 [dependencies] a = { path = "a" } "#, ) .file("src/lib.rs", "") .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) .file("a/src/lib.rs", ""); let p = p.build(); p.cargo("test -v --no-run --release -p foo -p a").run(); } #[cargo_test] fn selective_test_optional_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] a = { path = "a", optional = true } "#, ) .file("src/lib.rs", "") .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) .file("a/src/lib.rs", ""); let p = p.build(); p.cargo("test -v --no-run --features a -p a") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] a v0.0.1 ([ROOT]/foo/a) [RUNNING] `rustc [..] a/src/lib.rs [..]` [RUNNING] `rustc [..] a/src/lib.rs [..]` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [EXECUTABLE] `[ROOT]/foo/target/debug/deps/a-[HASH][EXE]` "#]]) .run(); } #[cargo_test] fn only_test_docs() { let p = project() .file( "src/lib.rs", r#" #[test] fn foo() { let a: u32 = "hello"; } /// ``` /// foo::bar(); /// println!("ok"); /// ``` pub fn bar() { } "#, ) .file("tests/foo.rs", "this is not rust"); let p = p.build(); p.cargo("test --doc") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [DOCTEST] foo "#]]) .with_stdout_data(str![[r#" ... test [..] ... ok ... "#]]) .run(); } #[cargo_test] fn doctest_with_library_paths() { let p = project(); // Only link search directories within the target output directory are // propagated through to dylib_path_envvar() (see #3366). let dir1 = p.target_debug_dir().join("foo\\backslash"); let dir2 = p.target_debug_dir().join("dir=containing=equal=signs"); let p = p .file("Cargo.toml", &basic_manifest("foo", "0.0.0")) .file( "build.rs", &format!( r##" fn main() {{ println!(r#"cargo::rustc-link-search=native={}"#); println!(r#"cargo::rustc-link-search={}"#); }} "##, dir1.display(), dir2.display() ), ) .file( "src/lib.rs", &format!( r##" /// ``` /// foo::assert_search_path(); /// ``` pub fn assert_search_path() {{ let search_path = std::env::var_os("{}").unwrap(); let paths = std::env::split_paths(&search_path).collect::>(); assert!(paths.contains(&r#"{}"#.into())); assert!(paths.contains(&r#"{}"#.into())); }} "##, dylib_path_envvar(), dir1.display(), dir2.display() ), ) .build(); p.cargo("test --doc").run(); } #[cargo_test] fn test_panic_abort_with_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = { path = "bar" } [profile.dev] panic = 'abort' "#, ) .file( "src/lib.rs", r#" extern crate bar; #[test] fn foo() {} "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) .file("bar/src/lib.rs", "") .build(); p.cargo("test -v").run(); } #[cargo_test] fn cfg_test_even_with_no_harness() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lib] harness = false doctest = false "#, ) .file( "src/lib.rs", r#"#[cfg(test)] fn main() { println!("hello!"); }"#, ) .build(); p.cargo("test -v") .with_stdout_data(str![[r#" hello! "#]]) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..]` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/debug/deps/foo-[HASH][EXE]` "#]]) .run(); } #[cargo_test] fn panic_abort_multiple() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] a = { path = "a" } [profile.release] panic = 'abort' "#, ) .file( "src/lib.rs", "#[allow(unused_extern_crates)] extern crate a;", ) .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) .file("a/src/lib.rs", "") .build(); p.cargo("test --release -v -p foo -p a").run(); } #[cargo_test] fn pass_correct_cfgs_flags_to_rustdoc() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [features] default = ["feature_a/default"] nightly = ["feature_a/nightly"] [dependencies.feature_a] path = "libs/feature_a" default-features = false "#, ) .file( "src/lib.rs", r#" #[cfg(test)] mod tests { #[test] fn it_works() { assert!(true); } } "#, ) .file( "libs/feature_a/Cargo.toml", r#" [package] name = "feature_a" version = "0.1.0" edition = "2015" authors = [] [features] default = ["mock_serde_codegen"] nightly = ["mock_serde_derive"] [dependencies] mock_serde_derive = { path = "../mock_serde_derive", optional = true } [build-dependencies] mock_serde_codegen = { path = "../mock_serde_codegen", optional = true } "#, ) .file( "libs/feature_a/src/lib.rs", r#" #[cfg(feature = "mock_serde_derive")] const MSG: &'static str = "This is safe"; #[cfg(feature = "mock_serde_codegen")] const MSG: &'static str = "This is risky"; pub fn get() -> &'static str { MSG } "#, ) .file( "libs/mock_serde_derive/Cargo.toml", &basic_manifest("mock_serde_derive", "0.1.0"), ) .file("libs/mock_serde_derive/src/lib.rs", "") .file( "libs/mock_serde_codegen/Cargo.toml", &basic_manifest("mock_serde_codegen", "0.1.0"), ) .file("libs/mock_serde_codegen/src/lib.rs", ""); let p = p.build(); p.cargo("test --package feature_a --verbose") .with_stderr_data(str![[r#" ... [DOCTEST] feature_a [RUNNING] `rustdoc [..]--test [..]mock_serde_codegen[..]` ... "#]]) .run(); p.cargo("test --verbose") .with_stderr_data(str![[r#" ... [DOCTEST] foo [RUNNING] `rustdoc [..]--test [..]feature_a[..]` ... "#]]) .run(); } #[cargo_test] fn test_release_ignore_panic() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] a = { path = "a" } [profile.test] panic = 'abort' [profile.release] panic = 'abort' "#, ) .file( "src/lib.rs", "#[allow(unused_extern_crates)] extern crate a;", ) .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) .file("a/src/lib.rs", ""); let p = p.build(); println!("test"); p.cargo("test -v").run(); println!("bench"); p.cargo("bench -v").run(); } #[cargo_test] fn test_many_with_features() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] a = { path = "a" } [features] foo = [] [workspace] "#, ) .file("src/lib.rs", "") .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) .file("a/src/lib.rs", "") .build(); p.cargo("test -v -p a -p foo --features foo").run(); } #[cargo_test] fn test_all_workspace() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { path = "bar" } [workspace] "#, ) .file("src/main.rs", "#[test] fn foo_test() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "#[test] fn bar_test() {}") .build(); p.cargo("test --workspace") .with_stdout_data( str![[r#" test foo_test ... ok test bar_test ... ok ... "#]] .unordered(), ) .run(); } #[cargo_test] fn test_all_exclude() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [workspace] members = ["bar", "baz"] "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "#[test] pub fn bar() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "#[test] pub fn baz() { assert!(false); }") .build(); p.cargo("test --workspace --exclude baz") .with_stdout_data(str![[r#" ... running 1 test test bar ... ok ... "#]]) .run(); } #[cargo_test] fn test_all_exclude_not_found() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [workspace] members = ["bar"] "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "#[test] pub fn bar() {}") .build(); p.cargo("test --workspace --exclude baz") .with_stderr_data(str![[r#" ... [WARNING] excluded package(s) `baz` not found in workspace `[ROOT]/foo` ... "#]]) .with_stdout_data(str![[r#" ... running 1 test test bar ... ok ... "#]]) .run(); } #[cargo_test] fn test_all_exclude_glob() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [workspace] members = ["bar", "baz"] "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "#[test] pub fn bar() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "#[test] pub fn baz() { assert!(false); }") .build(); p.cargo("test --workspace --exclude '*z'") .with_stdout_data(str![[r#" ... running 1 test test bar ... ok ... "#]]) .run(); } #[cargo_test] fn test_all_exclude_glob_not_found() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [workspace] members = ["bar"] "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "#[test] pub fn bar() {}") .build(); p.cargo("test --workspace --exclude '*z'") .with_stderr_data(str![[r#" ... [WARNING] excluded package pattern(s) `*z` not found in workspace `[ROOT]/foo` ... "#]]) .with_stdout_data(str![[r#" ... running 1 test test bar ... ok ... "#]]) .run(); } #[cargo_test] fn test_all_exclude_broken_glob() { let p = project().file("src/main.rs", "fn main() {}").build(); p.cargo("test --workspace --exclude '[*z'") .with_status(101) .with_stderr_data(str![[r#" ... [ERROR] cannot build glob pattern from `[*z` ... "#]]) .run(); } #[cargo_test] fn test_all_virtual_manifest() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] "#, ) .file("a/Cargo.toml", &basic_manifest("a", "0.1.0")) .file("a/src/lib.rs", "#[test] fn a() {}") .file("b/Cargo.toml", &basic_manifest("b", "0.1.0")) .file("b/src/lib.rs", "#[test] fn b() {}") .build(); p.cargo("test --workspace") .with_stdout_data( str![[r#" running 1 test test a ... ok running 1 test test b ... ok ... "#]] .unordered(), ) .run(); } #[cargo_test] fn test_virtual_manifest_all_implied() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] "#, ) .file("a/Cargo.toml", &basic_manifest("a", "0.1.0")) .file("a/src/lib.rs", "#[test] fn a() {}") .file("b/Cargo.toml", &basic_manifest("b", "0.1.0")) .file("b/src/lib.rs", "#[test] fn b() {}") .build(); p.cargo("test") .with_stdout_data( str![[r#" running 1 test test a ... ok running 1 test test b ... ok ... "#]] .unordered(), ) .run(); } #[cargo_test] fn test_virtual_manifest_one_project() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "#[test] fn bar() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "#[test] fn baz() { assert!(false); }") .build(); p.cargo("test -p bar") .with_stdout_contains("running 1 test\ntest bar ... ok") .with_stdout_does_not_contain("running 1 test\ntest baz ... ok") .run(); } #[cargo_test] fn test_virtual_manifest_glob() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "#[test] fn bar() { assert!(false); }") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "#[test] fn baz() {}") .build(); p.cargo("test -p '*z'") .with_stdout_does_not_contain("running 1 test\ntest bar ... ok") .with_stdout_contains("running 1 test\ntest baz ... ok") .run(); } #[cargo_test] fn test_virtual_manifest_glob_not_found() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "#[test] fn bar() {}") .build(); p.cargo("test -p bar -p '*z'") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package pattern(s) `*z` not found in workspace `[ROOT]/foo` ... "#]]) .run(); } #[cargo_test] fn test_virtual_manifest_broken_glob() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "#[test] fn bar() {}") .build(); p.cargo("test -p '[*z'") .with_status(101) .with_stderr_data(str![[r#" [ERROR] cannot build glob pattern from `[*z` ... "#]]) .run(); } #[cargo_test] fn test_all_member_dependency_same_name() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a"] "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" [dependencies] a = "0.1.0" "#, ) .file("a/src/lib.rs", "#[test] fn a() {}") .build(); Package::new("a", "0.1.0").publish(); p.cargo("test --workspace") .with_stdout_data(str![[r#" ... test a ... ok ... "#]]) .run(); } #[cargo_test] fn doctest_only_with_dev_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" [dev-dependencies] b = { path = "b" } "#, ) .file( "src/lib.rs", r#" /// ``` /// extern crate b; /// /// b::b(); /// ``` pub fn a() {} "#, ) .file("b/Cargo.toml", &basic_manifest("b", "0.1.0")) .file("b/src/lib.rs", "pub fn b() {}") .build(); p.cargo("test --doc -v").run(); } #[cargo_test] fn test_many_targets() { let p = project() .file( "src/bin/a.rs", r#" fn main() {} #[test] fn bin_a() {} "#, ) .file( "src/bin/b.rs", r#" fn main() {} #[test] fn bin_b() {} "#, ) .file( "src/bin/c.rs", r#" fn main() {} #[test] fn bin_c() { panic!(); } "#, ) .file( "examples/a.rs", r#" fn main() {} #[test] fn example_a() {} "#, ) .file( "examples/b.rs", r#" fn main() {} #[test] fn example_b() {} "#, ) .file("examples/c.rs", "#[test] fn example_c() { panic!(); }") .file("tests/a.rs", "#[test] fn test_a() {}") .file("tests/b.rs", "#[test] fn test_b() {}") .file("tests/c.rs", "does not compile") .build(); p.cargo("test --verbose --bin a --bin b --example a --example b --test a --test b") .with_stdout_data( str![[r#" test bin_a ... ok test bin_b ... ok test test_a ... ok test test_b ... ok ... "#]] .unordered(), ) .with_stderr_data( str![[r#" [RUNNING] `rustc --crate-name a --edition=2015 examples/a.rs [..]` [RUNNING] `rustc --crate-name b --edition=2015 examples/b.rs [..]` ... "#]] .unordered(), ) .run(); } #[cargo_test] fn doctest_and_registry() { let p = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" [dependencies] b = { path = "b" } c = { path = "c" } [workspace] "#, ) .file("src/lib.rs", "") .file("b/Cargo.toml", &basic_manifest("b", "0.1.0")) .file( "b/src/lib.rs", " /// ``` /// b::foo(); /// ``` pub fn foo() {} ", ) .file( "c/Cargo.toml", r#" [package] name = "c" version = "0.1.0" edition = "2015" [dependencies] b = "0.1" "#, ) .file("c/src/lib.rs", "") .build(); Package::new("b", "0.1.0").publish(); p.cargo("test --workspace -v").run(); } #[cargo_test] fn cargo_test_env() { let rustc_host = rustc_host(); let src = format!( r#" #![crate_type = "rlib"] #[test] fn env_test() {{ use std::env; eprintln!("{{}}", env::var("{}").unwrap()); }} "#, cargo::CARGO_ENV ); let p = project() .file("Cargo.toml", &basic_lib_manifest("foo")) .file("src/lib.rs", &src) .build(); let cargo = format!( "{}[EXE]", cargo_exe() .with_extension("") .to_str() .unwrap() .replace(rustc_host, "[HOST_TARGET]") ); p.cargo("test --lib -- --nocapture") .with_stderr_contains(cargo) .with_stdout_data(str![[r#" ... test env_test ... ok ... "#]]) .run(); // Check that `cargo test` propagates the environment's $CARGO let cargo_exe = cargo_exe(); let other_cargo_path = p.root().join(cargo_exe.file_name().unwrap()); std::fs::hard_link(&cargo_exe, &other_cargo_path).unwrap(); let stderr_other_cargo = format!( "{}[EXE]", other_cargo_path .with_extension("") .to_str() .unwrap() .replace(p.root().parent().unwrap().to_str().unwrap(), "[ROOT]") ); p.process(other_cargo_path) .args(&["test", "--lib", "--", "--nocapture"]) .with_stderr_contains(stderr_other_cargo) .with_stdout_data(str![[r#" ... test env_test ... ok ... "#]]) .run(); } #[cargo_test] fn test_order() { let p = project() .file("src/lib.rs", "#[test] fn test_lib() {}") .file("tests/a.rs", "#[test] fn test_a() {}") .file("tests/z.rs", "#[test] fn test_z() {}") .build(); p.cargo("test --workspace") .with_stdout_data(str![[r#" running 1 test test test_lib ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test test_a ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test test_z ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s ... "#]]) .run(); } #[cargo_test] fn cyclic_dev() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dev-dependencies] foo = { path = "." } "#, ) .file("src/lib.rs", "#[test] fn test_lib() {}") .file("tests/foo.rs", "extern crate foo;") .build(); p.cargo("test --workspace").run(); } #[cargo_test] fn cyclical_dep_with_missing_feature() { // Checks for error handling when a cyclical dev-dependency specify a // feature that doesn't exist. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dev-dependencies] foo = { path = ".", features = ["missing"] } "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to select a version for `foo`. ... required by package `foo v0.1.0 ([ROOT]/foo)` versions that meet the requirements `*` are: 0.1.0 package `foo` depends on `foo` with feature `missing` but `foo` does not have that feature. failed to select a version for `foo` which could resolve this conflict "#]]) .run(); } #[cargo_test] fn publish_a_crate_without_tests() { Package::new("testless", "0.1.0") .file( "Cargo.toml", r#" [package] name = "testless" version = "0.1.0" edition = "2015" exclude = ["tests/*"] [[test]] name = "a_test" "#, ) .file("src/lib.rs", "") // In real life, the package will have a test, // which would be excluded from .crate file by the // `exclude` field. Our test harness does not honor // exclude though, so let's just not add the file! // .file("tests/a_test.rs", "") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] testless = "0.1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("test").run(); p.cargo("test --package testless").run(); } #[cargo_test] fn find_dependency_of_proc_macro_dependency_with_target() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["root", "proc_macro_dep"] "#, ) .file( "root/Cargo.toml", r#" [package] name = "root" version = "0.1.0" edition = "2015" authors = [] [dependencies] proc_macro_dep = { path = "../proc_macro_dep" } "#, ) .file( "root/src/lib.rs", r#" #[macro_use] extern crate proc_macro_dep; #[derive(Noop)] pub struct X; "#, ) .file( "proc_macro_dep/Cargo.toml", r#" [package] name = "proc_macro_dep" version = "0.1.0" edition = "2015" authors = [] [lib] proc-macro = true [dependencies] baz = "^0.1" "#, ) .file( "proc_macro_dep/src/lib.rs", r#" extern crate baz; extern crate proc_macro; use proc_macro::TokenStream; #[proc_macro_derive(Noop)] pub fn noop(_input: TokenStream) -> TokenStream { "".parse().unwrap() } "#, ) .build(); Package::new("bar", "0.1.0").publish(); Package::new("baz", "0.1.0") .dep("bar", "0.1") .file("src/lib.rs", "extern crate bar;") .publish(); p.cargo("test --workspace --target").arg(rustc_host()).run(); } #[cargo_test] fn test_hint_not_masked_by_doctest() { let p = project() .file( "src/lib.rs", r#" /// ``` /// assert_eq!(1, 1); /// ``` pub fn this_works() {} "#, ) .file( "tests/integ.rs", r#" #[test] fn this_fails() { panic!(); } "#, ) .build(); p.cargo("test --no-fail-fast") .with_status(101) .with_stdout_data( str![[r#" test this_fails ... FAILED test [..]this_works (line [..]) ... ok ... "#]] .unordered(), ) .with_stderr_data(str![[r#" ... [ERROR] test failed, to rerun pass `--test integ` ... "#]]) .run(); } #[cargo_test] fn test_hint_workspace_virtual() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b", "c"] "#, ) .file("a/Cargo.toml", &basic_manifest("a", "0.1.0")) .file("a/src/lib.rs", "#[test] fn t1() {}") .file("b/Cargo.toml", &basic_manifest("b", "0.1.0")) .file("b/src/lib.rs", "#[test] fn t1() {assert!(false)}") .file("c/Cargo.toml", &basic_manifest("c", "0.1.0")) .file( "c/src/lib.rs", r#" /// ```rust /// assert_eq!(1, 2); /// ``` pub fn foo() {} "#, ) .file( "c/src/main.rs", r#" fn main() {} #[test] fn from_main() { assert_eq!(1, 2); } "#, ) .file( "c/tests/t1.rs", r#" #[test] fn from_int_test() { assert_eq!(1, 2); } "#, ) .file( "c/examples/ex1.rs", r#" fn main() {} #[test] fn from_example() { assert_eq!(1, 2); } "#, ) // This does not use #[bench] since it is unstable. #[test] works just // the same for our purpose of checking the hint. .file( "c/benches/b1.rs", r#" #[test] fn from_bench() { assert_eq!(1, 2); } "#, ) .build(); // This depends on Units being sorted so that `b` fails first. p.cargo("test") .with_stderr_data( str![[r#" [COMPILING] c v0.1.0 ([ROOT]/foo/c) [COMPILING] a v0.1.0 ([ROOT]/foo/a) [COMPILING] b v0.1.0 ([ROOT]/foo/b) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/a-[HASH][EXE]) [RUNNING] unittests src/lib.rs (target/debug/deps/b-[HASH][EXE]) [ERROR] test failed, to rerun pass `-p b --lib` "#]] .unordered(), ) .with_status(101) .run(); p.cargo("test") .cwd("b") .with_stderr_data(str![[r#" [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs ([ROOT]/foo/target/debug/deps/b-[HASH][EXE]) [ERROR] test failed, to rerun pass `--lib` "#]]) .with_status(101) .run(); p.cargo("test --no-fail-fast") .with_stderr_data(str![[r#" [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/a-[HASH][EXE]) [RUNNING] unittests src/lib.rs (target/debug/deps/b-[HASH][EXE]) [ERROR] test failed, to rerun pass `-p b --lib` [RUNNING] unittests src/lib.rs (target/debug/deps/c-[HASH][EXE]) [RUNNING] unittests src/main.rs (target/debug/deps/c-[HASH][EXE]) [ERROR] test failed, to rerun pass `-p c --bin c` [RUNNING] tests/t1.rs (target/debug/deps/t1-[HASH][EXE]) [ERROR] test failed, to rerun pass `-p c --test t1` [DOCTEST] a [DOCTEST] b [DOCTEST] c [ERROR] doctest failed, to rerun pass `-p c --doc` [ERROR] 4 targets failed: `-p b --lib` `-p c --bin c` `-p c --test t1` `-p c --doc` "#]]) .with_status(101) .run(); // Check others that are not in the default set. p.cargo("test -p c --examples --benches --no-fail-fast") .with_stderr_data(str![[r#" [COMPILING] c v0.1.0 ([ROOT]/foo/c) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/c-[HASH][EXE]) [RUNNING] unittests src/main.rs (target/debug/deps/c-[HASH][EXE]) [ERROR] test failed, to rerun pass `-p c --bin c` [RUNNING] benches/b1.rs (target/debug/deps/b1-[HASH][EXE]) [ERROR] test failed, to rerun pass `-p c --bench b1` [RUNNING] unittests examples/ex1.rs (target/debug/examples/ex1-[HASH][EXE]) [ERROR] test failed, to rerun pass `-p c --example ex1` [ERROR] 3 targets failed: `-p c --bin c` `-p c --bench b1` `-p c --example ex1` "#]]) .with_status(101) .run(); } #[cargo_test] fn test_hint_workspace_nonvirtual() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [workspace] members = ["a"] "#, ) .file("src/lib.rs", "") .file("a/Cargo.toml", &basic_manifest("a", "0.1.0")) .file("a/src/lib.rs", "#[test] fn t1() {assert!(false)}") .build(); p.cargo("test --workspace") .with_stderr_data(str![[r#" ... [ERROR] test failed, to rerun pass `-p a --lib` ... "#]]) .with_status(101) .run(); p.cargo("test -p a") .with_stderr_data(str![[r#" ... [ERROR] test failed, to rerun pass `-p a --lib` ... "#]]) .with_status(101) .run(); } #[cargo_test] fn json_artifact_includes_test_flag() { // Verify that the JSON artifact output includes `test` flag. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [profile.test] opt-level = 1 "#, ) .file("src/lib.rs", "") .build(); p.cargo("test --lib -v --no-run --message-format=json") .with_stdout_data( str![[r#" [ { "executable": "[ROOT]/foo/target/debug/deps/foo-[HASH][EXE]", "features": [], "filenames": "{...}", "fresh": false, "manifest_path": "[ROOT]/foo/Cargo.toml", "package_id": "path+[ROOTURL]/foo#0.0.1", "profile": "{...}", "reason": "compiler-artifact", "target": { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "foo", "src_path": "[ROOT]/foo/src/lib.rs", "test": true } }, { "reason": "build-finished", "success": true } ] "#]] .is_json() .against_jsonlines(), ) .run(); } #[cargo_test] fn json_artifact_includes_executable_for_library_tests() { let p = project() .file("src/main.rs", "fn main() { }") .file("src/lib.rs", r#"#[test] fn lib_test() {}"#) .build(); p.cargo("test --lib -v --no-run --message-format=json") .with_stdout_data( str![[r#" [ { "executable": "[ROOT]/foo/target/debug/deps/foo-[HASH][EXE]", "features": [], "filenames": "{...}", "fresh": false, "manifest_path": "[ROOT]/foo/Cargo.toml", "package_id": "path+[ROOTURL]/foo#0.0.1", "profile": "{...}", "reason": "compiler-artifact", "target": { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "foo", "src_path": "[ROOT]/foo/src/lib.rs", "test": true } }, { "reason": "build-finished", "success": true } ] "#]] .is_json() .against_jsonlines(), ) .run(); } #[cargo_test] fn json_artifact_includes_executable_for_integration_tests() { let p = project() .file( "tests/integration_test.rs", r#"#[test] fn integration_test() {}"#, ) .build(); p.cargo("test -v --no-run --message-format=json --test integration_test") .with_stdout_data( str![[r#" [ { "executable": "[ROOT]/foo/target/debug/deps/integration_test-[HASH][EXE]", "features": [], "filenames": "{...}", "fresh": false, "manifest_path": "[ROOT]/foo/Cargo.toml", "package_id": "path+[ROOTURL]/foo#0.0.1", "profile": "{...}", "reason": "compiler-artifact", "target": { "crate_types": [ "bin" ], "doc": false, "doctest": false, "edition": "2015", "kind": [ "test" ], "name": "integration_test", "src_path": "[ROOT]/foo/tests/integration_test.rs", "test": true } }, { "reason": "build-finished", "success": true } ] "#]] .is_json() .against_jsonlines(), ) .run(); } #[cargo_test] fn test_build_script_links() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" links = 'something' [lib] test = false "#, ) .file("build.rs", "fn main() {}") .file("src/lib.rs", "") .build(); p.cargo("test --no-run").run(); } #[cargo_test] fn doctest_skip_staticlib() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [lib] crate-type = ["staticlib"] "#, ) .file( "src/lib.rs", r#" //! ``` //! assert_eq!(1,2); //! ``` "#, ) .build(); p.cargo("test --doc") .with_status(101) .with_stderr_data(str![[r#" [WARNING] doc tests are not supported for crate type(s) `staticlib` in package `foo` [ERROR] no library targets found in package `foo` "#]]) .run(); p.cargo("test") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) "#]]) .run(); } #[cargo_test] fn can_not_mix_doc_tests_and_regular_tests() { let p = project() .file( "src/lib.rs", "\ /// ``` /// assert_eq!(1, 1) /// ``` pub fn foo() -> u8 { 1 } #[cfg(test)] mod tests { #[test] fn it_works() { assert_eq!(2 + 2, 4); } } ", ) .build(); p.cargo("test") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) [DOCTEST] foo "#]]) .with_stdout_data(str![[r#" running 1 test test tests::it_works ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s running 1 test test src/lib.rs - foo (line 1) ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); p.cargo("test --lib") .with_stderr_data(str![[r#" [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) "#]]) .with_stdout_data(str![[r#" running 1 test test tests::it_works ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .run(); // This has been modified to attempt to diagnose spurious errors on CI. // For some reason, this is recompiling the lib when it shouldn't. If the // root cause is ever found, the changes here should be reverted. // See https://github.com/rust-lang/cargo/issues/6887 p.cargo("test --doc -vv") .with_stderr_does_not_contain("[COMPILING] foo [..]") .with_stderr_data(str![[r#" ... [DOCTEST] foo ... "#]]) .with_stdout_data(str![[r#" running 1 test test src/lib.rs - foo (line 1) ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s "#]]) .env("CARGO_LOG", "cargo=trace") .run(); p.cargo("test --lib --doc") .with_status(101) .with_stderr_data(str![[r#" [ERROR] Can't mix --doc with other target selecting options "#]]) .run(); } #[cargo_test] fn can_not_no_run_doc_tests() { let p = project() .file( "src/lib.rs", r#" /// ``` /// let _x = 1 + "foo"; /// ``` pub fn foo() -> u8 { 1 } "#, ) .build(); p.cargo("test --doc --no-run") .with_status(101) .with_stderr_data(str![[r#" [ERROR] Can't skip running doc tests with --no-run "#]]) .run(); } #[cargo_test] fn test_all_targets_lib() { let p = project().file("src/lib.rs", "").build(); p.cargo("test --all-targets") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) "#]]) .run(); } #[cargo_test] fn test_dep_with_dev() { Package::new("devdep", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [dependencies] bar = { path = "bar" } "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" [dev-dependencies] devdep = "0.1" "#, ) .file("bar/src/lib.rs", "") .build(); p.cargo("test -p bar") .with_status(101) .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [ERROR] package `bar` cannot be tested because it requires dev-dependencies and is not a member of the workspace "#]]) .run(); } #[cargo_test] fn cargo_test_doctest_xcompile_ignores() { // Check ignore-TARGET syntax. let p = project() .file("Cargo.toml", &basic_lib_manifest("foo")) .file( "src/lib.rs", r#" ///```ignore-x86_64 ///assert!(cfg!(not(target_arch = "x86_64"))); ///``` pub fn foo() -> u8 { 4 } "#, ) .build(); p.cargo("build").run(); #[cfg(not(target_arch = "x86_64"))] p.cargo("test") .with_stdout_data(str![[r#" ... test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s ... "#]]) .run(); #[cfg(target_arch = "x86_64")] p.cargo("test") .with_stdout_data(str![[r#" ... test result: ok. 0 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s ... "#]]) .run(); } #[cargo_test] fn cargo_test_doctest_xcompile_runner() { if !cross_compile_can_run_on_host() { return; } let runner = project() .file("Cargo.toml", &basic_bin_manifest("runner")) .file( "src/main.rs", r#" pub fn main() { eprintln!("this is a runner"); let args: Vec = std::env::args().collect(); std::process::Command::new(&args[1]).spawn(); } "#, ) .build(); runner.cargo("build").run(); assert!(runner.bin("runner").is_file()); let runner_path = paths::root().join("runner"); fs::copy(&runner.bin("runner"), &runner_path).unwrap(); let config = paths::root().join(".cargo/config.toml"); fs::create_dir_all(config.parent().unwrap()).unwrap(); // Escape Windows backslashes for TOML config. let runner_str = runner_path.to_str().unwrap().replace('\\', "\\\\"); fs::write( config, format!( r#" [target.'cfg(target_arch = "{}")'] runner = "{}" "#, cross_compile::alternate_arch(), runner_str ), ) .unwrap(); let p = project() .file("Cargo.toml", &basic_lib_manifest("foo")) .file( "src/lib.rs", &format!( r#" ///``` ///assert!(cfg!(target_arch = "{}")); ///``` pub fn foo() -> u8 {{ 4 }} "#, cross_compile::alternate_arch() ), ) .build(); p.cargo("build").run(); p.cargo(&format!("test --target {}", cross_compile::alternate())) .with_stdout_data(str![[r#" ... running 0 tests ... "#]]) .run(); p.cargo(&format!("test --target {}", cross_compile::alternate())) .with_stdout_data(str![[r#" ... test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s ... "#]]) .with_stderr_data(str![[r#" ... this is a runner ... "#]]) .run(); } #[cargo_test] fn cargo_test_doctest_xcompile_no_runner() { if !cross_compile_can_run_on_host() { return; } let p = project() .file("Cargo.toml", &basic_lib_manifest("foo")) .file( "src/lib.rs", &format!( r#" ///``` ///assert!(cfg!(target_arch = "{}")); ///``` pub fn foo() -> u8 {{ 4 }} "#, cross_compile::alternate_arch() ), ) .build(); p.cargo("build").run(); p.cargo(&format!("test --target {}", cross_compile::alternate())) .with_stdout_data(str![[r#" ... running 0 tests ... "#]]) .run(); p.cargo(&format!("test --target {}", cross_compile::alternate())) .with_stdout_data(str![[r#" ... test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [ELAPSED]s ... "#]]) .run(); } #[cargo_test(nightly, reason = "-Zpanic-abort-tests in rustc is unstable")] fn panic_abort_tests() { let p = project() .file( "Cargo.toml", r#" [package] name = 'foo' version = '0.1.0' edition = "2015" [dependencies] a = { path = 'a' } [profile.dev] panic = 'abort' [profile.test] panic = 'abort' "#, ) .file( "src/lib.rs", r#" #[test] fn foo() { a::foo(); } "#, ) .file("a/Cargo.toml", &basic_lib_manifest("a")) .file("a/src/lib.rs", "pub fn foo() {}") .build(); // This uses -j1 because of a race condition. Otherwise it will build the // two copies of `foo` in parallel, and which one is first is random. If // `--test` is first, then the first line with `[..]` will match, and the // second line with `--test` will fail. p.cargo("test -Z panic-abort-tests -v -j1") .with_stderr_data( str![[r#" [RUNNING] `[..]--crate-name a [..]-C panic=abort[..]` [RUNNING] `[..]--crate-name foo [..]-C panic=abort[..]` [RUNNING] `[..]--crate-name foo [..]-C panic=abort[..]--test[..]` ... "#]] .unordered(), ) .masquerade_as_nightly_cargo(&["panic-abort-tests"]) .run(); } #[cargo_test] // Unlike with rustc, `rustdoc --test -Cpanic=abort` already works on stable fn panic_abort_doc_tests() { let p = project() .file( "Cargo.toml", r#" [package] name = 'foo' version = '0.1.0' edition = "2015" [profile.dev] panic = 'abort' "#, ) .file( "src/lib.rs", r#" //! ```should_panic //! panic!(); //! ``` "#, ) .build(); p.cargo("test --doc -Z panic-abort-tests -v") .with_stderr_data( str![[r#" [RUNNING] `[..]rustc[..] --crate-name foo [..]-C panic=abort[..]` [RUNNING] `[..]rustdoc[..] --crate-name foo [..]--test[..]-C panic=abort[..]` ... "#]] .unordered(), ) .masquerade_as_nightly_cargo(&["panic-abort-tests"]) .run(); } #[cargo_test(nightly, reason = "-Zpanic-abort-tests in rustc is unstable")] fn panic_abort_only_test() { let p = project() .file( "Cargo.toml", r#" [package] name = 'foo' version = '0.1.0' edition = "2015" [dependencies] a = { path = 'a' } [profile.test] panic = 'abort' "#, ) .file( "src/lib.rs", r#" #[test] fn foo() { a::foo(); } "#, ) .file("a/Cargo.toml", &basic_lib_manifest("a")) .file("a/src/lib.rs", "pub fn foo() {}") .build(); p.cargo("test -Z panic-abort-tests -v") .with_stderr_data(str![[r#" [WARNING] `panic` setting is ignored for `test` profile ... "#]]) .masquerade_as_nightly_cargo(&["panic-abort-tests"]) .run(); } #[cargo_test(nightly, reason = "-Zpanic-abort-tests in rustc is unstable")] fn panic_abort_test_profile_inherits() { let p = project() .file( "Cargo.toml", r#" [package] name = 'foo' version = '0.1.0' edition = "2015" [dependencies] a = { path = 'a' } [profile.dev] panic = 'abort' "#, ) .file( "src/lib.rs", r#" #[test] fn foo() { a::foo(); } "#, ) .file("a/Cargo.toml", &basic_lib_manifest("a")) .file("a/src/lib.rs", "pub fn foo() {}") .build(); p.cargo("test -Z panic-abort-tests -v") .masquerade_as_nightly_cargo(&["panic-abort-tests"]) .with_status(0) .run(); } #[cargo_test] fn bin_env_for_test() { // Test for the `CARGO_BIN_EXE_` environment variables for tests. // // Note: The Unicode binary uses a `[[bin]]` definition because different // filesystems normalize utf-8 in different ways. For example, HFS uses // "gru\u{308}ßen" and APFS uses "gr\u{fc}ßen". Defining it in TOML forces // one form to be used. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2018" [[bin]] name = 'grüßen' path = 'src/bin/grussen.rs' "#, ) .file("src/bin/foo.rs", "fn main() {}") .file("src/bin/with-dash.rs", "fn main() {}") .file("src/bin/grussen.rs", "fn main() {}") .build(); let bin_path = |name| p.bin(name).to_string_lossy().replace("\\", "\\\\"); p.change_file( "tests/check_env.rs", &r#" #[test] fn run_bins() { assert_eq!(env!("CARGO_BIN_EXE_foo"), ""); assert_eq!(env!("CARGO_BIN_EXE_with-dash"), ""); assert_eq!(env!("CARGO_BIN_EXE_grüßen"), ""); } "# .replace("", &bin_path("foo")) .replace("", &bin_path("with-dash")) .replace("", &bin_path("grüßen")), ); p.cargo("test --test check_env").run(); p.cargo("check --test check_env").run(); } #[cargo_test] fn test_workspaces_cwd() { // This tests that all the different test types are executed from the // crate directory (manifest_dir), and not from the workspace root. let make_lib_file = |expected| { format!( r#" //! ``` //! assert_eq!("{expected}", std::fs::read_to_string("file.txt").unwrap()); //! assert_eq!("{expected}", include_str!("../file.txt")); //! assert_eq!( //! std::path::PathBuf::from(std::env!("CARGO_MANIFEST_DIR")), //! std::env::current_dir().unwrap(), //! ); //! ``` #[test] fn test_unit_{expected}_cwd() {{ assert_eq!("{expected}", std::fs::read_to_string("file.txt").unwrap()); assert_eq!("{expected}", include_str!("../file.txt")); assert_eq!( std::path::PathBuf::from(std::env!("CARGO_MANIFEST_DIR")), std::env::current_dir().unwrap(), ); }} "#, expected = expected ) }; let make_test_file = |expected| { format!( r#" #[test] fn test_integration_{expected}_cwd() {{ assert_eq!("{expected}", std::fs::read_to_string("file.txt").unwrap()); assert_eq!("{expected}", include_str!("../file.txt")); assert_eq!( std::path::PathBuf::from(std::env!("CARGO_MANIFEST_DIR")), std::env::current_dir().unwrap(), ); }} "#, expected = expected ) }; let p = project() .file( "Cargo.toml", r#" [package] name = "root-crate" version = "0.0.0" [workspace] members = [".", "nested-crate", "very/deeply/nested/deep-crate"] "#, ) .file("file.txt", "root") .file("src/lib.rs", &make_lib_file("root")) .file("tests/integration.rs", &make_test_file("root")) .file( "nested-crate/Cargo.toml", r#" [package] name = "nested-crate" version = "0.0.0" "#, ) .file("nested-crate/file.txt", "nested") .file("nested-crate/src/lib.rs", &make_lib_file("nested")) .file( "nested-crate/tests/integration.rs", &make_test_file("nested"), ) .file( "very/deeply/nested/deep-crate/Cargo.toml", r#" [package] name = "deep-crate" version = "0.0.0" "#, ) .file("very/deeply/nested/deep-crate/file.txt", "deep") .file( "very/deeply/nested/deep-crate/src/lib.rs", &make_lib_file("deep"), ) .file( "very/deeply/nested/deep-crate/tests/integration.rs", &make_test_file("deep"), ) .build(); p.cargo("test --workspace --all") .with_stderr_data( str![[r#" [DOCTEST] root_crate [DOCTEST] nested_crate [DOCTEST] deep_crate ... "#]] .unordered(), ) .with_stdout_data( str![[r#" test test_unit_root_cwd ... ok test test_unit_nested_cwd ... ok test test_unit_deep_cwd ... ok test test_integration_root_cwd ... ok test test_integration_nested_cwd ... ok test test_integration_deep_cwd ... ok ... "#]] .unordered(), ) .run(); p.cargo("test -p root-crate --all") .with_stderr_data(str![[r#" ... [DOCTEST] root_crate ... "#]]) .with_stdout_data( str![[r#" test test_unit_root_cwd ... ok test test_integration_root_cwd ... ok ... "#]] .unordered(), ) .run(); p.cargo("test -p nested-crate --all") .with_stderr_data(str![[r#" ... [DOCTEST] nested_crate ... "#]]) .with_stdout_data( str![[r#" test test_unit_nested_cwd ... ok test test_integration_nested_cwd ... ok ... "#]] .unordered(), ) .run(); p.cargo("test -p deep-crate --all") .with_stderr_data(str![[r#" ... [DOCTEST] deep_crate ... "#]]) .with_stdout_data( str![[r#" test test_unit_deep_cwd ... ok test test_integration_deep_cwd ... ok ... "#]] .unordered(), ) .run(); p.cargo("test --all") .cwd("nested-crate") .with_stderr_data(str![[r#" ... [DOCTEST] nested_crate ... "#]]) .with_stdout_data( str![[r#" test test_unit_nested_cwd ... ok test test_integration_nested_cwd ... ok ... "#]] .unordered(), ) .run(); p.cargo("test --all") .cwd("very/deeply/nested/deep-crate") .with_stderr_data(str![[r#" ... [DOCTEST] deep_crate ... "#]]) .with_stdout_data( str![[r#" test test_unit_deep_cwd ... ok test test_integration_deep_cwd ... ok ... "#]] .unordered(), ) .run(); } #[cargo_test] fn execution_error() { // Checks the behavior when a test fails to launch. let p = project() .file( "tests/t1.rs", r#" #[test] fn foo() {} "#, ) .build(); let key = format!("CARGO_TARGET_{}_RUNNER", rustc_host_env()); p.cargo("test") .env(&key, "does_not_exist") // The actual error is usually "no such file", but on Windows it has a // custom message. Since matching against the error string produced by // Rust is not very reliable, this just uses `[..]`. .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] tests/t1.rs (target/debug/deps/t1-[HASH][EXE]) [ERROR] test failed, to rerun pass `--test t1` Caused by: could not execute process `does_not_exist [ROOT]/foo/target/debug/deps/t1-[HASH][EXE]` (never executed) Caused by: [NOT_FOUND] "#]]) .with_status(101) .run(); } #[cargo_test] fn nonzero_exit_status() { // Tests for nonzero exit codes from tests. let p = project() .file( "tests/t1.rs", r#" #[test] fn t() { panic!("this is a normal error") } "#, ) .file( "tests/t2.rs", r#" #[test] fn t() { std::process::exit(4) } "#, ) .build(); p.cargo("test --test t1") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] tests/t1.rs (target/debug/deps/t1-[HASH][EXE]) [ERROR] test failed, to rerun pass `--test t1` "#]]) .with_stdout_data(str![[r#" ... this is a normal error ... "#]]) .with_status(101) .run(); p.cargo("test --test t2") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] tests/t2.rs (target/debug/deps/t2-[HASH][EXE]) [ERROR] test failed, to rerun pass `--test t2` Caused by: process didn't exit successfully: `[ROOT]/foo/target/debug/deps/t2-[HASH][EXE]` ([EXIT_STATUS]: 4) [NOTE] test exited abnormally; to see the full output pass --nocapture to the harness. "#]]) .with_status(4) .run(); p.cargo("test --test t2 -- --nocapture") .with_stderr_data(str![[r#" [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] tests/t2.rs (target/debug/deps/t2-[HASH][EXE]) [ERROR] test failed, to rerun pass `--test t2` Caused by: process didn't exit successfully: `[ROOT]/foo/target/debug/deps/t2-[HASH][EXE] --nocapture` ([EXIT_STATUS]: 4) "#]]) .with_status(4) .run(); // no-fail-fast always uses 101 p.cargo("test --no-fail-fast") .with_stderr_data(str![[r#" [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] tests/t1.rs (target/debug/deps/t1-[HASH][EXE]) [ERROR] test failed, to rerun pass `--test t1` [RUNNING] tests/t2.rs (target/debug/deps/t2-[HASH][EXE]) [ERROR] test failed, to rerun pass `--test t2` Caused by: process didn't exit successfully: `[ROOT]/foo/target/debug/deps/t2-[HASH][EXE]` ([EXIT_STATUS]: 4) [NOTE] test exited abnormally; to see the full output pass --nocapture to the harness. [ERROR] 2 targets failed: `--test t1` `--test t2` "#]]) .with_status(101) .run(); p.cargo("test --no-fail-fast -- --nocapture") .with_stderr_does_not_contain( "test exited abnormally; to see the full output pass --nocapture to the harness.", ) .with_stderr_data(str![[r#" [..]thread [..]panicked [..] tests/t1.rs[..] [NOTE] run with `RUST_BACKTRACE=1` environment variable to display a backtrace Caused by: process didn't exit successfully: `[ROOT]/foo/target/debug/deps/t2-[HASH][EXE] --nocapture` ([EXIT_STATUS]: 4) ... "#]].unordered()) .with_status(101) .run(); } #[cargo_test] fn cargo_test_print_env_verbose() { let p = project() .file("Cargo.toml", &basic_manifest("foo", "0.0.1")) .file("src/lib.rs", "") .build(); p.cargo("test -vv").with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `[..]CARGO_MANIFEST_DIR=[ROOT]/foo[..] rustc --crate-name foo[..]` [RUNNING] `[..]CARGO_MANIFEST_DIR=[ROOT]/foo[..] rustc --crate-name foo[..]` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[..]CARGO_MANIFEST_DIR=[ROOT]/foo[..] [ROOT]/foo/target/debug/deps/foo-[HASH][EXE]` [DOCTEST] foo [RUNNING] `[..]CARGO_MANIFEST_DIR=[ROOT]/foo[..] rustdoc --edition=2015 --crate-type lib --color auto --crate-name foo[..]` "#]]).run(); } #[cargo_test] fn cargo_test_set_out_dir_env_var() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2021" "#, ) .file( "src/lib.rs", r#" pub fn add(left: u64, right: u64) -> u64 { left + right } "#, ) .file( "build.rs", r#" fn main() {} "#, ) .file( "tests/case.rs", r#" #[cfg(test)] pub mod tests { #[test] fn test_add() { assert!(std::env::var("OUT_DIR").is_ok()); assert_eq!(foo::add(2, 5), 7); } } "#, ) .build(); p.cargo("test").run(); p.cargo("test --package foo --test case -- tests::test_add --exact --nocapture") .run(); } cargo-0.91.0/tests/testsuite/timings.rs000064400000000000000000000025211046102023000162210ustar 00000000000000//! Tests for --timings. use crate::prelude::*; use cargo_test_support::project; use cargo_test_support::registry::Package; use cargo_test_support::str; #[cargo_test] fn timings_works() { Package::new("dep", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] dep = "0.1" "#, ) .file("src/lib.rs", "") .file("src/main.rs", "fn main() {}") .file("tests/t1.rs", "") .file("examples/ex1.rs", "fn main() {}") .build(); p.cargo("build --all-targets --timings") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] dep v0.1.0 (registry `dummy-registry`) [COMPILING] dep v0.1.0 [COMPILING] foo v0.1.0 ([ROOT]/foo) Timing report saved to [ROOT]/foo/target/cargo-timings/cargo-timing-[..].html [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("clean").run(); p.cargo("test --timings").run(); p.cargo("clean").run(); p.cargo("check --timings").run(); p.cargo("clean").run(); p.cargo("doc --timings").run(); } cargo-0.91.0/tests/testsuite/tool_paths.rs000064400000000000000000000330531046102023000167270ustar 00000000000000//! Tests for configuration values that point to programs. use crate::prelude::*; use cargo_test_support::{basic_lib_manifest, project, rustc_host, rustc_host_env, str}; #[cargo_test] fn pathless_tools() { let target = rustc_host(); let foo = project() .file("Cargo.toml", &basic_lib_manifest("foo")) .file("src/lib.rs", "") .file( ".cargo/config.toml", &format!( r#" [target.{}] linker = "nonexistent-linker" "#, target ), ) .build(); foo.cargo("build --verbose") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc [..]-C linker=nonexistent-linker [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } // can set a custom linker via `target.'cfg(..)'.linker` #[cargo_test] fn custom_linker_cfg() { let foo = project() .file("Cargo.toml", &basic_lib_manifest("foo")) .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [target.'cfg(not(target_os = "none"))'] linker = "nonexistent-linker" "#, ) .build(); foo.cargo("build --verbose") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc [..]-C linker=nonexistent-linker [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } // custom linker set via `target.$triple.linker` have precede over `target.'cfg(..)'.linker` #[cargo_test] fn custom_linker_cfg_precedence() { let target = rustc_host(); let foo = project() .file("Cargo.toml", &basic_lib_manifest("foo")) .file("src/lib.rs", "") .file( ".cargo/config.toml", &format!( r#" [target.'cfg(not(target_os = "none"))'] linker = "ignored-linker" [target.{}] linker = "nonexistent-linker" "#, target ), ) .build(); foo.cargo("build --verbose") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc [..]-C linker=nonexistent-linker [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn custom_linker_cfg_collision() { let foo = project() .file("Cargo.toml", &basic_lib_manifest("foo")) .file("src/lib.rs", "") .file( ".cargo/config.toml", r#" [target.'cfg(not(target_arch = "avr"))'] linker = "nonexistent-linker1" [target.'cfg(not(target_os = "none"))'] linker = "nonexistent-linker2" "#, ) .build(); foo.cargo("build --verbose") .with_status(101) .with_stderr_data(str![[r#" [ERROR] several matching instances of `target.'cfg(..)'.linker` in configurations first match `cfg(not(target_arch = "avr"))` located in [ROOT]/foo/.cargo/config.toml second match `cfg(not(target_os = "none"))` located in [ROOT]/foo/.cargo/config.toml "#]]) .run(); } #[cargo_test] fn absolute_tools() { let target = rustc_host(); // Escaped as they appear within a TOML config file let linker = if cfg!(windows) { r#"C:\\bogus\\nonexistent-linker"# } else { r#"/bogus/nonexistent-linker"# }; let foo = project() .file("Cargo.toml", &basic_lib_manifest("foo")) .file("src/lib.rs", "") .file( ".cargo/config.toml", &format!( r#" [target.{target}] linker = "{linker}" "#, target = target, linker = linker ), ) .build(); foo.cargo("build --verbose") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) [RUNNING] `rustc [..]-C linker=[..]/bogus/nonexistent-linker [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn relative_tools() { let target = rustc_host(); // Escaped as they appear within a TOML config file let linker = if cfg!(windows) { r#".\\tools\\nonexistent-linker"# } else { r#"./tools/nonexistent-linker"# }; // Funky directory structure to test that relative tool paths are made absolute // by reference to the `.cargo/..` directory and not to (for example) the CWD. let p = project() .no_manifest() .file("bar/Cargo.toml", &basic_lib_manifest("bar")) .file("bar/src/lib.rs", "") .file( ".cargo/config.toml", &format!( r#" [target.{target}] linker = "{linker}" "#, target = target, linker = linker ), ) .build(); p.cargo("build --verbose") .cwd("bar") .with_stderr_data(str![[r#" [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [RUNNING] `rustc [..]-C linker=[ROOT]/foo/./tools/nonexistent-linker [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn custom_runner() { let target = rustc_host(); let p = project() .file("src/main.rs", "fn main() {}") .file("tests/test.rs", "") .file("benches/bench.rs", "") .file( ".cargo/config.toml", &format!( r#" [target.{}] runner = "nonexistent-runner -r" "#, target ), ) .build(); p.cargo("run -- --param") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `nonexistent-runner -r target/debug/foo[EXE] --param` ... "#]]) .run(); p.cargo("test --test test --verbose -- --param") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..]` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `nonexistent-runner -r [ROOT]/foo/target/debug/deps/test-[HASH][EXE] --param` ... "#]]) .run(); p.cargo("bench --bench bench --verbose -- --param") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..]` [RUNNING] `rustc [..]` [FINISHED] `bench` profile [optimized] target(s) in [ELAPSED]s [RUNNING] `nonexistent-runner -r [ROOT]/foo/target/release/deps/bench-[HASH][EXE] --param --bench` ... "#]]) .run(); } // can set a custom runner via `target.'cfg(..)'.runner` #[cargo_test] fn custom_runner_cfg() { let p = project() .file("src/main.rs", "fn main() {}") .file( ".cargo/config.toml", r#" [target.'cfg(not(target_os = "none"))'] runner = "nonexistent-runner -r" "#, ) .build(); p.cargo("run -- --param") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `nonexistent-runner -r target/debug/foo[EXE] --param` ... "#]]) .run(); } // custom runner set via `target.$triple.runner` have precedence over `target.'cfg(..)'.runner` #[cargo_test] fn custom_runner_cfg_precedence() { let target = rustc_host(); let p = project() .file("src/main.rs", "fn main() {}") .file( ".cargo/config.toml", &format!( r#" [target.'cfg(not(target_os = "none"))'] runner = "ignored-runner" [target.{}] runner = "nonexistent-runner -r" "#, target ), ) .build(); p.cargo("run -- --param") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `nonexistent-runner -r target/debug/foo[EXE] --param` ... "#]]) .run(); } #[cargo_test] fn custom_runner_cfg_collision() { let p = project() .file("src/main.rs", "fn main() {}") .file( ".cargo/config.toml", r#" [target.'cfg(not(target_arch = "avr"))'] runner = "true" [target.'cfg(not(target_os = "none"))'] runner = "false" "#, ) .build(); p.cargo("run -- --param") .with_status(101) .with_stderr_data(str![[r#" [ERROR] several matching instances of `target.'cfg(..)'.runner` in configurations first match `cfg(not(target_arch = "avr"))` located in [ROOT]/foo/.cargo/config.toml second match `cfg(not(target_os = "none"))` located in [ROOT]/foo/.cargo/config.toml "#]]) .run(); } #[cargo_test] fn custom_runner_env() { let p = project().file("src/main.rs", "fn main() {}").build(); let key = format!("CARGO_TARGET_{}_RUNNER", rustc_host_env()); p.cargo("run") .env(&key, "nonexistent-runner --foo") .with_status(101) // FIXME: Update "Caused by" error message once rust/pull/87704 is merged. // On Windows, changing to a custom executable resolver has changed the // error messages. .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `nonexistent-runner --foo target/debug/foo[EXE]` [ERROR] could not execute process `nonexistent-runner --foo target/debug/foo[EXE]` (never executed) Caused by: [NOT_FOUND] "#]]) .run(); } #[cargo_test] fn custom_runner_env_overrides_config() { let target = rustc_host(); let p = project() .file("src/main.rs", "fn main() {}") .file( ".cargo/config.toml", &format!( r#" [target.{}] runner = "should-not-run -r" "#, target ), ) .build(); let key = format!("CARGO_TARGET_{}_RUNNER", rustc_host_env()); p.cargo("run") .env(&key, "should-run --foo") .with_status(101) .with_stderr_data(str![[r#" ... [RUNNING] `should-run --foo target/debug/foo[EXE]` ... "#]]) .run(); } #[cargo_test] #[cfg(unix)] // Assumes `true` is in PATH. fn custom_runner_env_true() { // Check for a bug where "true" was interpreted as a boolean instead of // the executable. let p = project().file("src/main.rs", "fn main() {}").build(); let key = format!("CARGO_TARGET_{}_RUNNER", rustc_host_env()); p.cargo("run") .env(&key, "true") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `true target/debug/foo[EXE]` "#]]) .run(); } #[cargo_test] fn custom_linker_env() { let p = project().file("src/main.rs", "fn main() {}").build(); let key = format!("CARGO_TARGET_{}_LINKER", rustc_host_env()); p.cargo("build -v") .env(&key, "nonexistent-linker") .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..]-C linker=nonexistent-linker [..]` ... "#]]) .run(); } #[cargo_test] fn target_in_environment_contains_lower_case() { let p = project().file("src/main.rs", "fn main() {}").build(); let target = rustc_host(); let env_key = format!( "CARGO_TARGET_{}_LINKER", target.to_lowercase().replace('-', "_") ); p.cargo("build -v --target") .arg(target) .env(&env_key, "nonexistent-linker") .with_stderr_data(format!("\ [WARNING] environment variables are expected to use uppercase letters and underscores, the variable `{env_key}` will be ignored and have no effect [WARNING] environment variables are expected to use uppercase letters and underscores, the variable `{env_key}` will be ignored and have no effect [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/main.rs [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s " )) .run(); } #[cargo_test] fn cfg_ignored_fields() { // Test for some ignored fields in [target.'cfg()'] tables. let p = project() .file( ".cargo/config.toml", r#" # Try some empty tables. [target.'cfg(not(foo))'] [target.'cfg(not(bar))'.somelib] # A bunch of unused fields. [target.'cfg(not(target_os = "none"))'] linker = 'false' ar = 'false' foo = {rustc-flags = "-l foo"} invalid = 1 runner = 'false' rustflags = '' "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [WARNING] unused key `somelib` in [target] config table `cfg(not(bar))` [WARNING] unused key `ar` in [target] config table `cfg(not(target_os = "none"))` [WARNING] unused key `foo` in [target] config table `cfg(not(target_os = "none"))` [WARNING] unused key `invalid` in [target] config table `cfg(not(target_os = "none"))` [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } cargo-0.91.0/tests/testsuite/unit_graph.rs000064400000000000000000000130141046102023000167060ustar 00000000000000//! Tests for --unit-graph option. use crate::prelude::*; use cargo_test_support::project; use cargo_test_support::registry::Package; use cargo_test_support::str; #[cargo_test] fn gated() { let p = project().file("src/lib.rs", "").build(); p.cargo("build --unit-graph") .with_status(101) .with_stderr_data(str![[r#" [ERROR] the `--unit-graph` flag is unstable, and only available on the nightly channel of Cargo, but this is the `stable` channel See https://doc.rust-lang.org/book/[..].html for more information about Rust release channels. See https://github.com/rust-lang/cargo/issues/8002 for more information about the `--unit-graph` flag. "#]]) .run(); } #[cargo_test] fn simple() { Package::new("a", "1.0.0") .dep("b", "1.0") .feature("feata", &["b/featb"]) .publish(); Package::new("b", "1.0.0") .dep("c", "1.0") .feature("featb", &["c/featc"]) .publish(); Package::new("c", "1.0.0").feature("featc", &[]).publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] a = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("build --features a/feata --unit-graph -Zunstable-options") .masquerade_as_nightly_cargo(&["unit-graph"]) .with_stdout_data( str![[r#" { "roots": [ 3 ], "units": [ { "dependencies": [ { "extern_crate_name": "b", "index": 1, "noprelude": false, "public": false } ], "features": [ "feata" ], "mode": "build", "pkg_id": "registry+https://github.com/rust-lang/crates.io-index#a@1.0.0", "platform": null, "profile": { "codegen_backend": null, "codegen_units": null, "debug_assertions": true, "debuginfo": 2, "incremental": false, "lto": "false", "name": "dev", "opt_level": "0", "overflow_checks": true, "panic": "unwind", "rpath": false, "split_debuginfo": "{...}", "strip": "{...}" }, "target": { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "a", "src_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/a-1.0.0/src/lib.rs", "test": true } }, { "dependencies": [ { "extern_crate_name": "c", "index": 2, "noprelude": false, "public": false } ], "features": [ "featb" ], "mode": "build", "pkg_id": "registry+https://github.com/rust-lang/crates.io-index#b@1.0.0", "platform": null, "profile": { "codegen_backend": null, "codegen_units": null, "debug_assertions": true, "debuginfo": 2, "incremental": false, "lto": "false", "name": "dev", "opt_level": "0", "overflow_checks": true, "panic": "unwind", "rpath": false, "split_debuginfo": "{...}", "strip": "{...}" }, "target": { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "b", "src_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/b-1.0.0/src/lib.rs", "test": true } }, { "dependencies": [], "features": [ "featc" ], "mode": "build", "pkg_id": "registry+https://github.com/rust-lang/crates.io-index#c@1.0.0", "platform": null, "profile": { "codegen_backend": null, "codegen_units": null, "debug_assertions": true, "debuginfo": 2, "incremental": false, "lto": "false", "name": "dev", "opt_level": "0", "overflow_checks": true, "panic": "unwind", "rpath": false, "split_debuginfo": "{...}", "strip": "{...}" }, "target": { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "c", "src_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/c-1.0.0/src/lib.rs", "test": true } }, { "dependencies": [ { "extern_crate_name": "a", "index": 0, "noprelude": false, "public": false } ], "features": [], "mode": "build", "pkg_id": "path+[ROOTURL]/foo#0.1.0", "platform": null, "profile": { "codegen_backend": null, "codegen_units": null, "debug_assertions": true, "debuginfo": 2, "incremental": false, "lto": "false", "name": "dev", "opt_level": "0", "overflow_checks": true, "panic": "unwind", "rpath": false, "split_debuginfo": "{...}", "strip": "{...}" }, "target": { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "foo", "src_path": "[ROOT]/foo/src/lib.rs", "test": true } } ], "version": 1 } "#]] .is_json(), ) .run(); } cargo-0.91.0/tests/testsuite/update.rs000064400000000000000000002262251046102023000160420ustar 00000000000000//! Tests for the `cargo update` command. use crate::prelude::*; use cargo_test_support::compare::assert_e2e; use cargo_test_support::registry::{self}; use cargo_test_support::registry::{Dependency, Package}; use cargo_test_support::{basic_lib_manifest, basic_manifest, git, project, str}; #[cargo_test] fn minor_update_two_places() { Package::new("log", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [dependencies] log = "0.1" foo = { path = "foo" } "#, ) .file("src/lib.rs", "") .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] log = "0.1" "#, ) .file("foo/src/lib.rs", "") .build(); p.cargo("check").run(); Package::new("log", "0.1.1").publish(); p.change_file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] log = "0.1.1" "#, ); p.cargo("check").run(); } #[cargo_test] fn transitive_minor_update() { Package::new("log", "0.1.0").publish(); Package::new("serde", "0.1.0").dep("log", "0.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [dependencies] serde = "0.1" log = "0.1" foo = { path = "foo" } "#, ) .file("src/lib.rs", "") .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] serde = "0.1" "#, ) .file("foo/src/lib.rs", "") .build(); p.cargo("check").run(); Package::new("log", "0.1.1").publish(); Package::new("serde", "0.1.1").dep("log", "0.1.1").publish(); // Note that `serde` isn't actually updated here! The default behavior for // `update` right now is to as conservatively as possible attempt to satisfy // an update. In this case we previously locked the dependency graph to `log // 0.1.0`, but nothing on the command line says we're allowed to update // that. As a result the update of `serde` here shouldn't update to `serde // 0.1.1` as that would also force an update to `log 0.1.1`. // // Also note that this is probably counterintuitive and weird. We may wish // to change this one day. p.cargo("update serde") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 0 packages to latest compatible versions [NOTE] pass `--verbose` to see 2 unchanged dependencies behind latest "#]]) .run(); } #[cargo_test] fn conservative() { Package::new("log", "0.1.0").publish(); Package::new("serde", "0.1.0").dep("log", "0.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [dependencies] serde = "0.1" log = "0.1" foo = { path = "foo" } "#, ) .file("src/lib.rs", "") .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] serde = "0.1" "#, ) .file("foo/src/lib.rs", "") .build(); p.cargo("check").run(); Package::new("log", "0.1.1").publish(); Package::new("serde", "0.1.1").dep("log", "0.1").publish(); p.cargo("update serde") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [UPDATING] serde v0.1.0 -> v0.1.1 [NOTE] pass `--verbose` to see 1 unchanged dependencies behind latest "#]]) .run(); } #[cargo_test] fn update_via_new_dep() { Package::new("log", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [dependencies] log = "0.1" # foo = { path = "foo" } "#, ) .file("src/lib.rs", "") .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] log = "0.1.1" "#, ) .file("foo/src/lib.rs", "") .build(); p.cargo("check").run(); Package::new("log", "0.1.1").publish(); p.uncomment_root_manifest(); p.cargo("check").env("CARGO_LOG", "cargo=trace").run(); } #[cargo_test] fn update_via_new_member() { Package::new("log", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [workspace] # members = [ "foo" ] [dependencies] log = "0.1" "#, ) .file("src/lib.rs", "") .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] log = "0.1.1" "#, ) .file("foo/src/lib.rs", "") .build(); p.cargo("check").run(); Package::new("log", "0.1.1").publish(); p.uncomment_root_manifest(); p.cargo("check").run(); } #[cargo_test] fn add_dep_deep_new_requirement() { Package::new("log", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [dependencies] log = "0.1" # bar = "0.1" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check").run(); Package::new("log", "0.1.1").publish(); Package::new("bar", "0.1.0").dep("log", "0.1.1").publish(); p.uncomment_root_manifest(); p.cargo("check").run(); } #[cargo_test] fn everything_real_deep() { Package::new("log", "0.1.0").publish(); Package::new("foo", "0.1.0").dep("log", "0.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [dependencies] foo = "0.1" # bar = "0.1" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check").run(); Package::new("log", "0.1.1").publish(); Package::new("bar", "0.1.0").dep("log", "0.1.1").publish(); p.uncomment_root_manifest(); p.cargo("check").run(); } #[cargo_test] fn change_package_version() { let p = project() .file( "Cargo.toml", r#" [package] name = "a-foo" version = "0.2.0-alpha" edition = "2015" authors = [] [dependencies] bar = { path = "bar", version = "0.2.0-alpha" } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "0.2.0-alpha")) .file("bar/src/lib.rs", "") .file( "Cargo.lock", r#" [[package]] name = "foo" version = "0.2.0" dependencies = ["bar 0.2.0"] [[package]] name = "bar" version = "0.2.0" "#, ) .build(); p.cargo("check").run(); } #[cargo_test] fn update_precise() { Package::new("serde", "0.1.0").publish(); Package::new("serde", "0.2.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [dependencies] serde = "0.2" foo = { path = "foo" } "#, ) .file("src/lib.rs", "") .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] serde = "0.1" "#, ) .file("foo/src/lib.rs", "") .build(); p.cargo("check").run(); Package::new("serde", "0.2.0").publish(); p.cargo("update serde:0.2.1 --precise 0.2.0") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [DOWNGRADING] serde v0.2.1 -> v0.2.0 "#]]) .run(); } #[cargo_test] fn update_precise_mismatched() { Package::new("serde", "1.2.0").publish(); Package::new("serde", "1.2.1").publish(); Package::new("serde", "1.6.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [dependencies] serde = "~1.2" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check").run(); // `1.6.0` does not match `"~1.2"` p.cargo("update serde:1.2 --precise 1.6.0") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for the requirement `serde = "~1.2"` candidate versions found which didn't match: 1.6.0 location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `bar v0.0.1 ([ROOT]/foo)` perhaps a crate was updated and forgotten to be re-vendored? "#]]) .with_status(101) .run(); // `1.9.0` does not exist p.cargo("update serde:1.2 --precise 1.9.0") // This terrible error message has been the same for a long time. A fix is more than welcome! .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] no matching package named `serde` found location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `bar v0.0.1 ([ROOT]/foo)` "#]]) .with_status(101) .run(); } #[cargo_test] fn update_precise_build_metadata() { Package::new("serde", "0.0.1+first").publish(); Package::new("serde", "0.0.1+second").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.0" edition = "2015" [dependencies] serde = "0.0.1" "#, ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); p.cargo("update serde --precise 0.0.1+first").run(); p.cargo("update serde --precise 0.0.1+second") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [UPDATING] serde v0.0.1+first -> v0.0.1+second "#]]) .run(); // This is not considered "Downgrading". Build metadata are not assumed to // be ordered. p.cargo("update serde --precise 0.0.1+first") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [UPDATING] serde v0.0.1+second -> v0.0.1+first "#]]) .run(); } #[cargo_test] fn update_precise_do_not_force_update_deps() { Package::new("log", "0.1.0").publish(); Package::new("serde", "0.2.1").dep("log", "0.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [dependencies] serde = "0.2" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check").run(); Package::new("log", "0.1.1").publish(); Package::new("serde", "0.2.2").dep("log", "0.1").publish(); p.cargo("update serde:0.2.1 --precise 0.2.2") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [UPDATING] serde v0.2.1 -> v0.2.2 [NOTE] pass `--verbose` to see 1 unchanged dependencies behind latest "#]]) .run(); } #[cargo_test] fn update_recursive() { Package::new("log", "0.1.0").publish(); Package::new("serde", "0.2.1").dep("log", "0.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [dependencies] serde = "0.2" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check").run(); Package::new("log", "0.1.1").publish(); Package::new("serde", "0.2.2").dep("log", "0.1").publish(); p.cargo("update serde:0.2.1 --recursive") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [UPDATING] log v0.1.0 -> v0.1.1 [UPDATING] serde v0.2.1 -> v0.2.2 "#]]) .run(); } #[cargo_test] fn update_aggressive_alias_for_recursive() { Package::new("log", "0.1.0").publish(); Package::new("serde", "0.2.1").dep("log", "0.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [dependencies] serde = "0.2" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check").run(); Package::new("log", "0.1.1").publish(); Package::new("serde", "0.2.2").dep("log", "0.1").publish(); p.cargo("update serde:0.2.1 --aggressive") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [UPDATING] log v0.1.0 -> v0.1.1 [UPDATING] serde v0.2.1 -> v0.2.2 "#]]) .run(); } #[cargo_test] fn update_recursive_conflicts_with_precise() { Package::new("log", "0.1.0").publish(); Package::new("serde", "0.2.1").dep("log", "0.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [dependencies] serde = "0.2" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check").run(); Package::new("log", "0.1.1").publish(); Package::new("serde", "0.2.2").dep("log", "0.1").publish(); p.cargo("update serde:0.2.1 --precise 0.2.2 --recursive") .with_status(1) .with_stderr_data(str![[r#" [ERROR] the argument '--precise ' cannot be used with '--recursive' Usage: cargo[EXE] update --precise ]> For more information, try '--help'. "#]]) .run(); } // cargo update should respect its arguments even without a lockfile. // See issue "Running cargo update without a Cargo.lock ignores arguments" // at . #[cargo_test] fn update_precise_first_run() { Package::new("serde", "0.1.0").publish(); Package::new("serde", "0.2.0").publish(); Package::new("serde", "0.2.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" [dependencies] serde = "0.2" "#, ) .file("src/lib.rs", "") .build(); p.cargo("update serde --precise 0.2.0") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [DOWNGRADING] serde v0.2.1 -> v0.2.0 "#]]) .run(); // Assert `cargo metadata` shows serde 0.2.0 p.cargo("metadata") .with_stdout_data( str![[r#" { "metadata": null, "packages": [ { "authors": [], "categories": [], "default_run": null, "dependencies": [ { "features": [], "kind": null, "name": "serde", "optional": false, "registry": null, "rename": null, "req": "^0.2", "source": "registry+https://github.com/rust-lang/crates.io-index", "target": null, "uses_default_features": true } ], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo#bar@0.0.1", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/foo/Cargo.toml", "metadata": null, "name": "bar", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": null, "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "bar", "src_path": "[ROOT]/foo/src/lib.rs", "test": true } ], "version": "0.0.1" }, { "authors": [], "categories": [], "default_run": null, "dependencies": [], "description": null, "documentation": null, "edition": "2015", "features": {}, "homepage": null, "id": "registry+https://github.com/rust-lang/crates.io-index#serde@0.2.0", "keywords": [], "license": null, "license_file": null, "links": null, "manifest_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/serde-0.2.0/Cargo.toml", "metadata": null, "name": "serde", "publish": null, "readme": null, "repository": null, "rust_version": null, "source": "registry+https://github.com/rust-lang/crates.io-index", "targets": [ { "crate_types": [ "lib" ], "doc": true, "doctest": true, "edition": "2015", "kind": [ "lib" ], "name": "serde", "src_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/serde-0.2.0/src/lib.rs", "test": true } ], "version": "0.2.0" } ], "resolve": { "nodes": [ { "dependencies": [ "registry+https://github.com/rust-lang/crates.io-index#serde@0.2.0" ], "deps": [ { "dep_kinds": [ { "kind": null, "target": null } ], "name": "serde", "pkg": "registry+https://github.com/rust-lang/crates.io-index#serde@0.2.0" } ], "features": [], "id": "path+[ROOTURL]/foo#bar@0.0.1" }, { "dependencies": [], "deps": [], "features": [], "id": "registry+https://github.com/rust-lang/crates.io-index#serde@0.2.0" } ], "root": "path+[ROOTURL]/foo#bar@0.0.1" }, "target_directory": "[ROOT]/foo/target", "version": 1, "workspace_default_members": [ "path+[ROOTURL]/foo#bar@0.0.1" ], "workspace_members": [ "path+[ROOTURL]/foo#bar@0.0.1" ], "workspace_root": "[ROOT]/foo" } "#]] .is_json(), ) .run(); p.cargo("update serde --precise 0.2.0") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index "#]]) .run(); } #[cargo_test] fn preserve_top_comment() { let p = project().file("src/lib.rs", "").build(); p.cargo("update").run(); let lockfile = p.read_lockfile(); assert!(lockfile.starts_with("# This file is automatically @generated by Cargo.\n# It is not intended for manual editing.\n")); let mut lines = lockfile.lines().collect::>(); lines.insert(2, "# some other comment"); let mut lockfile = lines.join("\n"); lockfile.push('\n'); // .lines/.join loses the last newline println!("saving Cargo.lock contents:\n{}", lockfile); p.change_file("Cargo.lock", &lockfile); p.cargo("update").run(); let lockfile2 = p.read_lockfile(); println!("loaded Cargo.lock contents:\n{}", lockfile2); assert_eq!(lockfile, lockfile2); } #[cargo_test] fn dry_run_update() { Package::new("log", "0.1.0").publish(); Package::new("serde", "0.1.0").dep("log", "0.1").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [dependencies] serde = "0.1" log = "0.1" foo = { path = "foo" } "#, ) .file("src/lib.rs", "") .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] serde = "0.1" "#, ) .file("foo/src/lib.rs", "") .build(); p.cargo("check").run(); let old_lockfile = p.read_lockfile(); Package::new("log", "0.1.1").publish(); Package::new("serde", "0.1.1").dep("log", "0.1").publish(); p.cargo("update serde --dry-run") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [UPDATING] serde v0.1.0 -> v0.1.1 [NOTE] pass `--verbose` to see 1 unchanged dependencies behind latest [WARNING] not updating lockfile due to dry run "#]]) .run(); let new_lockfile = p.read_lockfile(); assert_eq!(old_lockfile, new_lockfile) } #[cargo_test] fn workspace_only() { let p = project().file("src/main.rs", "fn main() {}").build(); p.cargo("generate-lockfile").run(); let lock1 = p.read_lockfile(); p.change_file( "Cargo.toml", r#" [package] name = "foo" authors = [] version = "0.0.2" edition = "2015" "#, ); p.cargo("update --workspace").run(); let lock2 = p.read_lockfile(); assert_ne!(lock1, lock2); assert!(lock1.contains("0.0.1")); assert!(lock2.contains("0.0.2")); assert!(!lock1.contains("0.0.2")); assert!(!lock2.contains("0.0.1")); } #[cargo_test] fn precise_with_build_metadata() { // +foo syntax shouldn't be necessary with --precise Package::new("bar", "0.1.0+extra-stuff.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "0.1" "#, ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); Package::new("bar", "0.1.1+extra-stuff.1").publish(); Package::new("bar", "0.1.2+extra-stuff.2").publish(); p.cargo("update bar --precise 0.1") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid version format for precise version `0.1` Caused by: unexpected end of input while parsing minor version number "#]]) .run(); p.cargo("update bar --precise 0.1.1+does-not-match") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] no matching package named `bar` found location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.1.0 ([ROOT]/foo)` "#]]) .run(); p.cargo("update bar --precise 0.1.1") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [UPDATING] bar v0.1.0+extra-stuff.0 -> v0.1.1+extra-stuff.1 "#]]) .run(); Package::new("bar", "0.1.3").publish(); p.cargo("update bar --precise 0.1.3+foo") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] no matching package named `bar` found location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.1.0 ([ROOT]/foo)` "#]]) .run(); p.cargo("update bar --precise 0.1.3") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [UPDATING] bar v0.1.1+extra-stuff.1 -> v0.1.3 "#]]) .run(); } #[cargo_test] fn update_only_members_order_one() { let git_project = git::new("rustdns", |project| { project .file("Cargo.toml", &basic_lib_manifest("rustdns")) .file("src/lib.rs", "pub fn bar() {}") }); let workspace_toml = format!( r#" [workspace.package] version = "2.29.8" edition = "2021" publish = false [workspace] members = [ "rootcrate", "subcrate", ] resolver = "2" [workspace.dependencies] # Internal crates subcrate = {{ version = "*", path = "./subcrate" }} # External dependencies rustdns = {{ version = "0.5.0", default-features = false, git = "{}" }} "#, git_project.url() ); let p = project() .file("Cargo.toml", &workspace_toml) .file( "rootcrate/Cargo.toml", r#" [package] name = "rootcrate" version.workspace = true edition.workspace = true publish.workspace = true [dependencies] subcrate.workspace = true "#, ) .file("rootcrate/src/main.rs", "fn main() {}") .file( "subcrate/Cargo.toml", r#" [package] name = "subcrate" version.workspace = true edition.workspace = true publish.workspace = true [dependencies] rustdns.workspace = true "#, ) .file("subcrate/src/lib.rs", "pub foo() {}") .build(); // First time around we should compile both foo and bar p.cargo("generate-lockfile") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/rustdns` [LOCKING] 1 package to latest compatible version "#]]) .run(); // Modify a file manually, shouldn't trigger a recompile git_project.change_file("src/lib.rs", r#"pub fn bar() { println!("hello!"); }"#); // Commit the changes and make sure we don't trigger a recompile because the // lock file says not to change let repo = git2::Repository::open(&git_project.root()).unwrap(); git::add(&repo); git::commit(&repo); p.change_file("Cargo.toml", &workspace_toml.replace("2.29.8", "2.29.81")); p.cargo("update -p rootcrate") .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [UPDATING] rootcrate v2.29.8 ([ROOT]/foo/rootcrate) -> v2.29.81 [UPDATING] subcrate v2.29.8 ([ROOT]/foo/subcrate) -> v2.29.81 "#]]) .run(); } #[cargo_test] fn update_only_members_order_two() { let git_project = git::new("rustdns", |project| { project .file("Cargo.toml", &basic_lib_manifest("rustdns")) .file("src/lib.rs", "pub fn bar() {}") }); let workspace_toml = format!( r#" [workspace.package] version = "2.29.8" edition = "2021" publish = false [workspace] members = [ "crate2", "crate1", ] resolver = "2" [workspace.dependencies] # Internal crates crate1 = {{ version = "*", path = "./crate1" }} # External dependencies rustdns = {{ version = "0.5.0", default-features = false, git = "{}" }} "#, git_project.url() ); let p = project() .file("Cargo.toml", &workspace_toml) .file( "crate2/Cargo.toml", r#" [package] name = "crate2" version.workspace = true edition.workspace = true publish.workspace = true [dependencies] crate1.workspace = true "#, ) .file("crate2/src/main.rs", "fn main() {}") .file( "crate1/Cargo.toml", r#" [package] name = "crate1" version.workspace = true edition.workspace = true publish.workspace = true [dependencies] rustdns.workspace = true "#, ) .file("crate1/src/lib.rs", "pub foo() {}") .build(); // First time around we should compile both foo and bar p.cargo("generate-lockfile") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/rustdns` [LOCKING] 1 package to latest compatible version "#]]) .run(); // Modify a file manually, shouldn't trigger a recompile git_project.change_file("src/lib.rs", r#"pub fn bar() { println!("hello!"); }"#); // Commit the changes and make sure we don't trigger a recompile because the // lock file says not to change let repo = git2::Repository::open(&git_project.root()).unwrap(); git::add(&repo); git::commit(&repo); p.change_file("Cargo.toml", &workspace_toml.replace("2.29.8", "2.29.81")); p.cargo("update -p crate2") .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [UPDATING] crate1 v2.29.8 ([ROOT]/foo/crate1) -> v2.29.81 [UPDATING] crate2 v2.29.8 ([ROOT]/foo/crate2) -> v2.29.81 "#]]) .run(); } #[cargo_test] fn update_only_members_with_workspace() { let git_project = git::new("rustdns", |project| { project .file("Cargo.toml", &basic_lib_manifest("rustdns")) .file("src/lib.rs", "pub fn bar() {}") }); let workspace_toml = format!( r#" [workspace.package] version = "2.29.8" edition = "2021" publish = false [workspace] members = [ "crate2", "crate1", ] resolver = "2" [workspace.dependencies] # Internal crates crate1 = {{ version = "*", path = "./crate1" }} # External dependencies rustdns = {{ version = "0.5.0", default-features = false, git = "{}" }} "#, git_project.url() ); let p = project() .file("Cargo.toml", &workspace_toml) .file( "crate2/Cargo.toml", r#" [package] name = "crate2" version.workspace = true edition.workspace = true publish.workspace = true [dependencies] crate1.workspace = true "#, ) .file("crate2/src/main.rs", "fn main() {}") .file( "crate1/Cargo.toml", r#" [package] name = "crate1" version.workspace = true edition.workspace = true publish.workspace = true [dependencies] rustdns.workspace = true "#, ) .file("crate1/src/lib.rs", "pub foo() {}") .build(); // First time around we should compile both foo and bar p.cargo("generate-lockfile") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/rustdns` [LOCKING] 1 package to latest compatible version "#]]) .run(); // Modify a file manually, shouldn't trigger a recompile git_project.change_file("src/lib.rs", r#"pub fn bar() { println!("hello!"); }"#); // Commit the changes and make sure we don't trigger a recompile because the // lock file says not to change let repo = git2::Repository::open(&git_project.root()).unwrap(); git::add(&repo); git::commit(&repo); p.change_file("Cargo.toml", &workspace_toml.replace("2.29.8", "2.29.81")); p.cargo("update --workspace") .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [UPDATING] crate1 v2.29.8 ([ROOT]/foo/crate1) -> v2.29.81 [UPDATING] crate2 v2.29.8 ([ROOT]/foo/crate2) -> v2.29.81 "#]]) .run(); } #[cargo_test] fn update_precise_git_revisions() { let (git_project, git_repo) = git::new_repo("git", |p| { p.file("Cargo.toml", &basic_lib_manifest("git")) .file("src/lib.rs", "") }); let tag_name = "Nazgûl"; git::tag(&git_repo, tag_name); let tag_commit_id = git_repo.head().unwrap().target().unwrap().to_string(); git_project.change_file("src/lib.rs", "fn f() {}"); git::add(&git_repo); let head_id = git::commit(&git_repo).to_string(); let short_id = &head_id[..8]; let url = git_project.url(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] git = {{ git = '{url}' }} "# ), ) .file("src/lib.rs", "") .build(); p.cargo("fetch") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/git` [LOCKING] 1 package to latest compatible version "#]]) .run(); assert!(p.read_lockfile().contains(&head_id)); p.cargo("update git --precise") .arg(tag_name) .with_stderr_data(format!( "\ [UPDATING] git repository `[ROOTURL]/git` [UPDATING] git v0.5.0 ([ROOTURL]/git#[..]) -> #{} ", &tag_commit_id[..8], )) .run(); assert!(p.read_lockfile().contains(&tag_commit_id)); assert!(!p.read_lockfile().contains(&head_id)); p.cargo("update git --precise") .arg(short_id) .with_stderr_data(format!( "\ [UPDATING] git repository `[ROOTURL]/git` [UPDATING] git v0.5.0 ([ROOTURL]/git[..]) -> #{short_id} ", )) .run(); assert!(p.read_lockfile().contains(&head_id)); assert!(!p.read_lockfile().contains(&tag_commit_id)); // updating back to tag still requires a git fetch, // as the ref may change over time. p.cargo("update git --precise") .arg(tag_name) .with_stderr_data(format!( "\ [UPDATING] git repository `[ROOTURL]/git` [UPDATING] git v0.5.0 ([ROOTURL]/git#[..]) -> #{} ", &tag_commit_id[..8], )) .run(); assert!(p.read_lockfile().contains(&tag_commit_id)); assert!(!p.read_lockfile().contains(&head_id)); // Now make a tag looks like an oid. // It requires a git fetch, as the oid cannot be found in preexisting git db. let arbitrary_tag: String = "a".repeat(head_id.len()); git::tag(&git_repo, &arbitrary_tag); p.cargo("update git --precise") .arg(&arbitrary_tag) .with_stderr_data(format!( "\ [UPDATING] git repository `[ROOTURL]/git` [UPDATING] git v0.5.0 ([ROOTURL]/git#[..]) -> #{} ", &head_id[..8], )) .run(); assert!(p.read_lockfile().contains(&head_id)); assert!(!p.read_lockfile().contains(&tag_commit_id)); } #[cargo_test] fn precise_yanked() { Package::new("bar", "0.1.0").publish(); Package::new("bar", "0.1.1").yanked(true).publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" [dependencies] bar = "0.1" "#, ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); // Use non-yanked version. let lockfile = p.read_lockfile(); assert!(lockfile.contains("\nname = \"bar\"\nversion = \"0.1.0\"")); p.cargo("update --precise 0.1.1 bar") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [WARNING] selected package `bar@0.1.1` was yanked by the author [NOTE] if possible, try a compatible non-yanked version [UPDATING] bar v0.1.0 -> v0.1.1 "#]]) .run(); // Use yanked version. let lockfile = p.read_lockfile(); assert!(lockfile.contains("\nname = \"bar\"\nversion = \"0.1.1\"")); } #[cargo_test] fn precise_yanked_multiple_presence() { Package::new("bar", "0.1.0").publish(); Package::new("bar", "0.1.1").yanked(true).publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" [dependencies] bar = "0.1" baz = { package = "bar", version = "0.1" } "#, ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); // Use non-yanked version. let lockfile = p.read_lockfile(); assert!(lockfile.contains("\nname = \"bar\"\nversion = \"0.1.0\"")); p.cargo("update --precise 0.1.1 bar") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [WARNING] selected package `bar@0.1.1` was yanked by the author [NOTE] if possible, try a compatible non-yanked version [UPDATING] bar v0.1.0 -> v0.1.1 "#]]) .run(); // Use yanked version. let lockfile = p.read_lockfile(); assert!(lockfile.contains("\nname = \"bar\"\nversion = \"0.1.1\"")); } #[cargo_test] fn report_behind() { Package::new("two-ver", "0.1.0").publish(); Package::new("two-ver", "0.2.0").publish(); Package::new("pre", "1.0.0-alpha.0").publish(); Package::new("pre", "1.0.0-alpha.1").publish(); Package::new("breaking", "0.1.0").publish(); Package::new("breaking", "0.2.0").publish(); Package::new("breaking", "0.2.1-alpha.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" [dependencies] breaking = "0.1" pre = "=1.0.0-alpha.0" two-ver = "0.2.0" two-ver-one = { version = "0.1.0", package = "two-ver" } "#, ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); Package::new("breaking", "0.1.1").publish(); p.cargo("update --dry-run") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [UPDATING] breaking v0.1.0 -> v0.1.1 (available: v0.2.0) [NOTE] pass `--verbose` to see 2 unchanged dependencies behind latest [WARNING] not updating lockfile due to dry run "#]]) .run(); p.cargo("update --dry-run --verbose") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [UPDATING] breaking v0.1.0 -> v0.1.1 (available: v0.2.0) [UNCHANGED] pre v1.0.0-alpha.0 (available: v1.0.0-alpha.1) [UNCHANGED] two-ver v0.1.0 (available: v0.2.0) [NOTE] to see how you depend on a package, run `cargo tree --invert --package @` [WARNING] not updating lockfile due to dry run "#]]) .run(); p.cargo("update").run(); p.cargo("update --dry-run") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 0 packages to latest compatible versions [NOTE] pass `--verbose` to see 3 unchanged dependencies behind latest [WARNING] not updating lockfile due to dry run "#]]) .run(); p.cargo("update --dry-run --verbose") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 0 packages to latest compatible versions [UNCHANGED] breaking v0.1.1 (available: v0.2.0) [UNCHANGED] pre v1.0.0-alpha.0 (available: v1.0.0-alpha.1) [UNCHANGED] two-ver v0.1.0 (available: v0.2.0) [NOTE] to see how you depend on a package, run `cargo tree --invert --package @` [WARNING] not updating lockfile due to dry run "#]]) .run(); } #[cargo_test] fn update_with_missing_feature() { // Attempting to update a package to a version with a missing feature // should produce a warning. Package::new("bar", "0.1.0").feature("feat1", &[]).publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = {version="0.1", features=["feat1"]} "#, ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); // Publish an update that is missing the feature. Package::new("bar", "0.1.1").publish(); p.cargo("update") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 0 packages to latest compatible versions [NOTE] pass `--verbose` to see 1 unchanged dependencies behind latest "#]]) .run(); // Publish a fixed version, should not warn. Package::new("bar", "0.1.2").feature("feat1", &[]).publish(); p.cargo("update") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [UPDATING] bar v0.1.0 -> v0.1.2 "#]]) .run(); } #[cargo_test] fn update_breaking_unstable() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] "#, ) .file("src/lib.rs", "") .build(); p.cargo("update --breaking") .masquerade_as_nightly_cargo(&["update-breaking"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] the `--breaking` flag is unstable, pass `-Z unstable-options` to enable it See https://github.com/rust-lang/cargo/issues/12425 for more information about the `--breaking` flag. "#]]) .run(); } #[cargo_test] fn update_breaking_dry_run() { Package::new("incompatible", "1.0.0").publish(); Package::new("ws", "1.0.0").publish(); let root_manifest = r#" # Check if formatting is preserved. Nothing here should change, due to dry-run. [workspace] members = ["foo"] [workspace.dependencies] ws = "1.0" # Preserve formatting "#; let crate_manifest = r#" # Check if formatting is preserved. Nothing here should change, due to dry-run. [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] incompatible = "1.0" # Preserve formatting ws.workspace = true # Preserve formatting "#; let p = project() .file("Cargo.toml", root_manifest) .file("foo/Cargo.toml", crate_manifest) .file("foo/src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); let lock_file = p.read_file("Cargo.lock"); Package::new("incompatible", "1.0.1").publish(); Package::new("ws", "1.0.1").publish(); Package::new("incompatible", "2.0.0").publish(); Package::new("ws", "2.0.0").publish(); p.cargo("update -Zunstable-options --dry-run --breaking") .masquerade_as_nightly_cargo(&["update-breaking"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [UPGRADING] incompatible ^1.0 -> ^2.0 [UPGRADING] ws ^1.0 -> ^2.0 [LOCKING] 2 packages to latest compatible versions [UPDATING] incompatible v1.0.0 -> v2.0.0 [UPDATING] ws v1.0.0 -> v2.0.0 [WARNING] aborting update due to dry run "#]]) .run(); let root_manifest_after = p.read_file("Cargo.toml"); assert_e2e().eq(&root_manifest_after, root_manifest); let crate_manifest_after = p.read_file("foo/Cargo.toml"); assert_e2e().eq(&crate_manifest_after, crate_manifest); let lock_file_after = p.read_file("Cargo.lock"); assert_e2e().eq(&lock_file_after, lock_file); } #[cargo_test] fn update_breaking() { registry::alt_init(); Package::new("compatible", "1.0.0").publish(); Package::new("incompatible", "1.0.0").publish(); Package::new("pinned", "1.0.0").publish(); Package::new("less-than", "1.0.0").publish(); Package::new("renamed-from", "1.0.0").publish(); Package::new("pre-release", "1.0.0").publish(); Package::new("yanked", "1.0.0").publish(); Package::new("ws", "1.0.0").publish(); Package::new("shared", "1.0.0").publish(); Package::new("multiple-locations", "1.0.0").publish(); Package::new("multiple-versions", "1.0.0").publish(); Package::new("multiple-versions", "2.0.0").publish(); Package::new("alternative-1", "1.0.0") .alternative(true) .publish(); Package::new("alternative-2", "1.0.0") .alternative(true) .publish(); Package::new("bar", "1.0.0").alternative(true).publish(); Package::new("multiple-registries", "1.0.0").publish(); Package::new("multiple-registries", "2.0.0") .alternative(true) .publish(); Package::new("multiple-source-types", "1.0.0").publish(); Package::new("platform-specific", "1.0.0").publish(); Package::new("dev", "1.0.0").publish(); Package::new("build", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" # Check if formatting is preserved [workspace] members = ["foo", "bar"] [workspace.dependencies] ws = "1.0" # This line gets partially rewritten "#, ) .file( "foo/Cargo.toml", r#" # Check if formatting is preserved [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] compatible = "1.0" # Comment incompatible = "1.0" # Comment pinned = "=1.0" # Comment less-than = "<99.0" # Comment renamed-to = { package = "renamed-from", version = "1.0" } # Comment pre-release = "1.0" # Comment yanked = "1.0" # Comment ws.workspace = true # Comment shared = "1.0" # Comment multiple-locations = { path = "../multiple-locations", version = "1.0" } # Comment multiple-versions = "1.0" # Comment alternative-1 = { registry = "alternative", version = "1.0" } # Comment multiple-registries = "1.0" # Comment bar = { path = "../bar", registry = "alternative", version = "1.0.0" } # Comment multiple-source-types = { path = "../multiple-source-types", version = "1.0.0" } # Comment [dependencies.alternative-2] # Comment version = "1.0" # Comment registry = "alternative" # Comment [target.'cfg(unix)'.dependencies] platform-specific = "1.0" # Comment [dev-dependencies] dev = "1.0" # Comment [build-dependencies] build = "1.0" # Comment "#, ) .file("foo/src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "1.0.0" edition = "2015" authors = [] [dependencies] shared = "1.0" multiple-versions = "2.0" multiple-registries = { registry = "alternative", version = "2.0" } # Comment multiple-source-types = "1.0" # Comment "#, ) .file("bar/src/lib.rs", "") .file( "multiple-locations/Cargo.toml", r#" [package] name = "multiple-locations" version = "1.0.0" edition = "2015" authors = [] "#, ) .file("multiple-locations/src/lib.rs", "") .file( "multiple-source-types/Cargo.toml", r#" [package] name = "multiple-source-types" version = "1.0.0" edition = "2015" authors = [] "#, ) .file("multiple-source-types/src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); Package::new("compatible", "1.0.1").publish(); Package::new("incompatible", "1.0.1").publish(); Package::new("pinned", "1.0.1").publish(); Package::new("less-than", "1.0.1").publish(); Package::new("renamed-from", "1.0.1").publish(); Package::new("ws", "1.0.1").publish(); Package::new("multiple-locations", "1.0.1").publish(); Package::new("multiple-versions", "1.0.1").publish(); Package::new("multiple-versions", "2.0.1").publish(); Package::new("alternative-1", "1.0.1") .alternative(true) .publish(); Package::new("alternative-2", "1.0.1") .alternative(true) .publish(); Package::new("platform-specific", "1.0.1").publish(); Package::new("dev", "1.0.1").publish(); Package::new("build", "1.0.1").publish(); Package::new("incompatible", "2.0.0").publish(); Package::new("pinned", "2.0.0").publish(); Package::new("less-than", "2.0.0").publish(); Package::new("renamed-from", "2.0.0").publish(); Package::new("pre-release", "2.0.0-alpha").publish(); Package::new("yanked", "2.0.0").yanked(true).publish(); Package::new("ws", "2.0.0").publish(); Package::new("shared", "2.0.0").publish(); Package::new("multiple-locations", "2.0.0").publish(); Package::new("multiple-versions", "3.0.0").publish(); Package::new("alternative-1", "2.0.0") .alternative(true) .publish(); Package::new("alternative-2", "2.0.0") .alternative(true) .publish(); Package::new("bar", "2.0.0").alternative(true).publish(); Package::new("multiple-registries", "2.0.0").publish(); Package::new("multiple-registries", "3.0.0") .alternative(true) .publish(); Package::new("multiple-source-types", "2.0.0").publish(); Package::new("platform-specific", "2.0.0").publish(); Package::new("dev", "2.0.0").publish(); Package::new("build", "2.0.0").publish(); p.cargo("update -Zunstable-options --breaking") .masquerade_as_nightly_cargo(&["update-breaking"]) .with_stderr_data(str![[r#" [UPDATING] `alternative` index [UPGRADING] multiple-registries ^2.0 -> ^3.0 [UPDATING] `dummy-registry` index [UPGRADING] multiple-source-types ^1.0 -> ^2.0 [UPGRADING] multiple-versions ^2.0 -> ^3.0 [UPGRADING] shared ^1.0 -> ^2.0 [UPGRADING] alternative-1 ^1.0 -> ^2.0 [UPGRADING] alternative-2 ^1.0 -> ^2.0 [UPGRADING] incompatible ^1.0 -> ^2.0 [UPGRADING] multiple-registries ^1.0 -> ^2.0 [UPGRADING] multiple-versions ^1.0 -> ^3.0 [UPGRADING] ws ^1.0 -> ^2.0 [UPGRADING] dev ^1.0 -> ^2.0 [UPGRADING] build ^1.0 -> ^2.0 [UPGRADING] platform-specific ^1.0 -> ^2.0 [LOCKING] 12 packages to latest compatible versions [UPDATING] alternative-1 v1.0.0 (registry `alternative`) -> v2.0.0 [UPDATING] alternative-2 v1.0.0 (registry `alternative`) -> v2.0.0 [UPDATING] build v1.0.0 -> v2.0.0 [UPDATING] dev v1.0.0 -> v2.0.0 [UPDATING] incompatible v1.0.0 -> v2.0.0 [UPDATING] multiple-registries v2.0.0 (registry `alternative`) -> v3.0.0 [UPDATING] multiple-registries v1.0.0 -> v2.0.0 [UPDATING] multiple-source-types v1.0.0 -> v2.0.0 [ADDING] multiple-versions v3.0.0 [UPDATING] platform-specific v1.0.0 -> v2.0.0 [UPDATING] shared v1.0.0 -> v2.0.0 [UPDATING] ws v1.0.0 -> v2.0.0 "#]]) .run(); let root_manifest = p.read_file("Cargo.toml"); assert_e2e().eq( &root_manifest, str![[r#" # Check if formatting is preserved [workspace] members = ["foo", "bar"] [workspace.dependencies] ws = "2.0" # This line gets partially rewritten "#]], ); let foo_manifest = p.read_file("foo/Cargo.toml"); assert_e2e().eq( &foo_manifest, str![[r#" # Check if formatting is preserved [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] compatible = "1.0" # Comment incompatible = "2.0" # Comment pinned = "=1.0" # Comment less-than = "<99.0" # Comment renamed-to = { package = "renamed-from", version = "1.0" } # Comment pre-release = "1.0" # Comment yanked = "1.0" # Comment ws.workspace = true # Comment shared = "2.0" # Comment multiple-locations = { path = "../multiple-locations", version = "1.0" } # Comment multiple-versions = "3.0" # Comment alternative-1 = { registry = "alternative", version = "2.0" } # Comment multiple-registries = "2.0" # Comment bar = { path = "../bar", registry = "alternative", version = "1.0.0" } # Comment multiple-source-types = { path = "../multiple-source-types", version = "1.0.0" } # Comment [dependencies.alternative-2] # Comment version = "2.0" # Comment registry = "alternative" # Comment [target.'cfg(unix)'.dependencies] platform-specific = "2.0" # Comment [dev-dependencies] dev = "2.0" # Comment [build-dependencies] build = "2.0" # Comment "#]], ); let bar_manifest = p.read_file("bar/Cargo.toml"); assert_e2e().eq( &bar_manifest, str![[r#" [package] name = "bar" version = "1.0.0" edition = "2015" authors = [] [dependencies] shared = "2.0" multiple-versions = "3.0" multiple-registries = { registry = "alternative", version = "3.0" } # Comment multiple-source-types = "2.0" # Comment "#]], ); p.cargo("update") .with_stderr_data(str![[r#" [UPDATING] `alternative` index [UPDATING] `dummy-registry` index [LOCKING] 4 packages to latest compatible versions [UPDATING] compatible v1.0.0 -> v1.0.1 [UPDATING] less-than v1.0.0 -> v2.0.0 [UPDATING] pinned v1.0.0 -> v1.0.1 (available: v2.0.0) [UPDATING] renamed-from v1.0.0 -> v1.0.1 (available: v2.0.0) "#]]) .run(); } #[cargo_test] fn update_breaking_specific_packages() { Package::new("just-foo", "1.0.0") .add_dep(Dependency::new("transitive-compatible", "1.0.0").build()) .add_dep(Dependency::new("transitive-incompatible", "1.0.0").build()) .publish(); Package::new("just-bar", "1.0.0").publish(); Package::new("shared", "1.0.0").publish(); Package::new("ws", "1.0.0").publish(); Package::new("transitive-compatible", "1.0.0").publish(); Package::new("transitive-incompatible", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo", "bar"] [workspace.dependencies] ws = "1.0" "#, ) .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] just-foo = "1.0" shared = "1.0" ws.workspace = true "#, ) .file("foo/src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [dependencies] just-bar = "1.0" shared = "1.0" ws.workspace = true "#, ) .file("bar/src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); Package::new("just-foo", "1.0.1") .add_dep(Dependency::new("transitive-compatible", "1.0.0").build()) .add_dep(Dependency::new("transitive-incompatible", "1.0.0").build()) .publish(); Package::new("just-bar", "1.0.1").publish(); Package::new("shared", "1.0.1").publish(); Package::new("ws", "1.0.1").publish(); Package::new("transitive-compatible", "1.0.1").publish(); Package::new("transitive-incompatible", "1.0.1").publish(); Package::new("just-foo", "2.0.0") // Upgrading just-foo implies accepting an update of transitive-compatible. .add_dep(Dependency::new("transitive-compatible", "1.0.1").build()) // Upgrading just-foo implies accepting a major update of transitive-incompatible. .add_dep(Dependency::new("transitive-incompatible", "2.0.0").build()) .publish(); Package::new("just-bar", "2.0.0").publish(); Package::new("shared", "2.0.0").publish(); Package::new("ws", "2.0.0").publish(); Package::new("transitive-incompatible", "2.0.0").publish(); p.cargo("update -Zunstable-options --breaking just-foo shared ws") .masquerade_as_nightly_cargo(&["update-breaking"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [UPGRADING] shared ^1.0 -> ^2.0 [UPGRADING] ws ^1.0 -> ^2.0 [UPGRADING] just-foo ^1.0 -> ^2.0 [LOCKING] 5 packages to latest compatible versions [UPDATING] just-foo v1.0.0 -> v2.0.0 [UPDATING] shared v1.0.0 -> v2.0.0 [UPDATING] transitive-compatible v1.0.0 -> v1.0.1 [UPDATING] transitive-incompatible v1.0.0 -> v2.0.0 [UPDATING] ws v1.0.0 -> v2.0.0 "#]]) .run(); } #[cargo_test] fn update_breaking_specific_packages_that_wont_update() { Package::new("compatible", "1.0.0").publish(); Package::new("renamed-from", "1.0.0").publish(); Package::new("non-semver", "1.0.0").publish(); Package::new("bar", "1.0.0") .add_dep(Dependency::new("transitive-compatible", "1.0.0").build()) .add_dep(Dependency::new("transitive-incompatible", "1.0.0").build()) .publish(); Package::new("transitive-compatible", "1.0.0").publish(); Package::new("transitive-incompatible", "1.0.0").publish(); let crate_manifest = r#" # Check if formatting is preserved [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] compatible = "1.0" # Comment renamed-to = { package = "renamed-from", version = "1.0" } # Comment non-semver = "~1.0" # Comment bar = "1.0" # Comment "#; let p = project() .file("Cargo.toml", crate_manifest) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); let lock_file = p.read_file("Cargo.lock"); Package::new("compatible", "1.0.1").publish(); Package::new("renamed-from", "1.0.1").publish(); Package::new("non-semver", "1.0.1").publish(); Package::new("transitive-compatible", "1.0.1").publish(); Package::new("transitive-incompatible", "1.0.1").publish(); Package::new("renamed-from", "2.0.0").publish(); Package::new("non-semver", "2.0.0").publish(); Package::new("transitive-incompatible", "2.0.0").publish(); p.cargo("update -Zunstable-options --breaking compatible renamed-from non-semver transitive-compatible transitive-incompatible") .masquerade_as_nightly_cargo(&["update-breaking"]) .with_stderr_data(str![[r#" [UPDATING] `[..]` index "#]]) .run(); let crate_manifest_after = p.read_file("Cargo.toml"); assert_e2e().eq(&crate_manifest_after, crate_manifest); let lock_file_after = p.read_file("Cargo.lock"); assert_e2e().eq(&lock_file_after, lock_file); p.cargo( "update compatible renamed-from non-semver transitive-compatible transitive-incompatible", ) .with_stderr_data(str![[r#" [UPDATING] `[..]` index [LOCKING] 5 packages to latest compatible versions [UPDATING] compatible v1.0.0 -> v1.0.1 [UPDATING] non-semver v1.0.0 -> v1.0.1 (available: v2.0.0) [UPDATING] renamed-from v1.0.0 -> v1.0.1 (available: v2.0.0) [UPDATING] transitive-compatible v1.0.0 -> v1.0.1 [UPDATING] transitive-incompatible v1.0.0 -> v1.0.1 "#]]) .run(); } #[cargo_test] fn update_breaking_without_lock_file() { Package::new("compatible", "1.0.0").publish(); Package::new("incompatible", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] compatible = "1.0" # Comment incompatible = "1.0" # Comment "#, ) .file("src/lib.rs", "") .build(); Package::new("compatible", "1.0.1").publish(); Package::new("incompatible", "1.0.1").publish(); Package::new("incompatible", "2.0.0").publish(); p.cargo("update -Zunstable-options --breaking") .masquerade_as_nightly_cargo(&["update-breaking"]) .with_stderr_data(str![[r#" [UPDATING] `[..]` index [UPGRADING] incompatible ^1.0 -> ^2.0 [LOCKING] 2 packages to latest compatible versions "#]]) .run(); } #[cargo_test] fn update_breaking_spec_version() { Package::new("compatible", "1.0.0").publish(); Package::new("incompatible", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] compatible = "1.0" # Comment incompatible = "1.0" # Comment "#, ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); Package::new("compatible", "1.0.1").publish(); Package::new("incompatible", "1.0.1").publish(); Package::new("incompatible", "2.0.0").publish(); // Invalid spec p.cargo("update -Zunstable-options --breaking incompatible@foo") .masquerade_as_nightly_cargo(&["update-breaking"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid package ID specification: `incompatible@foo` Caused by: expected a version like "1.32" "#]]) .run(); // Spec version not matching our current dependencies p.cargo("update -Zunstable-options --breaking incompatible@2.0.0") .masquerade_as_nightly_cargo(&["update-breaking"]) .with_stderr_data(str![[r#""#]]) .run(); // Spec source not matching our current dependencies p.cargo("update -Zunstable-options --breaking https://alternative.com#incompatible@1.0.0") .masquerade_as_nightly_cargo(&["update-breaking"]) .with_stderr_data(str![[r#""#]]) .run(); // Accepted spec p.cargo("update -Zunstable-options --breaking incompatible@1.0.0") .masquerade_as_nightly_cargo(&["update-breaking"]) .with_stderr_data(str![[r#" [UPDATING] `[..]` index [UPGRADING] incompatible ^1.0 -> ^2.0 [LOCKING] 1 package to latest compatible version [UPDATING] incompatible v1.0.0 -> v2.0.0 "#]]) .run(); // Accepted spec, full format Package::new("incompatible", "3.0.0").publish(); p.cargo("update -Zunstable-options --breaking https://github.com/rust-lang/crates.io-index#incompatible@2.0.0") .masquerade_as_nightly_cargo(&["update-breaking"]) .with_stderr_data(str![[r#" [UPDATING] `[..]` index [UPGRADING] incompatible ^2.0 -> ^3.0 [LOCKING] 1 package to latest compatible version [UPDATING] incompatible v2.0.0 -> v3.0.0 "#]]) .run(); // Spec matches a dependency that will not be upgraded p.cargo("update -Zunstable-options --breaking compatible@1.0.0") .masquerade_as_nightly_cargo(&["update-breaking"]) .with_stderr_data(str![[r#" [UPDATING] `[..]` index "#]]) .run(); // Non-existing versions p.cargo("update -Zunstable-options --breaking incompatible@9.0.0") .masquerade_as_nightly_cargo(&["update-breaking"]) .with_stderr_data(str![[r#""#]]) .run(); p.cargo("update -Zunstable-options --breaking compatible@9.0.0") .masquerade_as_nightly_cargo(&["update-breaking"]) .with_stderr_data(str![[r#""#]]) .run(); } #[cargo_test] fn update_breaking_spec_version_transitive() { Package::new("dep", "1.0.0").publish(); Package::new("dep", "1.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] dep = "1.0" bar = { path = "bar", version = "0.0.1" } "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [dependencies] dep = "1.1" "#, ) .file("bar/src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); Package::new("dep", "1.1.1").publish(); Package::new("dep", "2.0.0").publish(); // Will upgrade the direct dependency p.cargo("update -Zunstable-options --breaking dep@1.0") .masquerade_as_nightly_cargo(&["update-breaking"]) .with_stderr_data(str![[r#" [UPDATING] `[..]` index [UPGRADING] dep ^1.0 -> ^2.0 [LOCKING] 1 package to latest compatible version [ADDING] dep v2.0.0 "#]]) .run(); // But not the transitive one, because bar is not a workspace member p.cargo("update -Zunstable-options --breaking dep@1.1") .masquerade_as_nightly_cargo(&["update-breaking"]) .with_stderr_data(str![[r#" [UPDATING] `[..]` index "#]]) .run(); // A non-breaking update is different, as it will update transitive dependencies p.cargo("update dep@1.1") .with_stderr_data(str![[r#" [UPDATING] `[..]` index [LOCKING] 1 package to latest compatible version [UPDATING] dep v1.1.0 -> v1.1.1 "#]]) .run(); } #[cargo_test] fn update_breaking_mixed_compatibility() { Package::new("mixed-compatibility", "1.0.0").publish(); Package::new("mixed-compatibility", "2.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo", "bar"] "#, ) .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] mixed-compatibility = "1.0" "#, ) .file("foo/src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [dependencies] mixed-compatibility = "2.0" "#, ) .file("bar/src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); Package::new("mixed-compatibility", "2.0.1").publish(); p.cargo("update -Zunstable-options --breaking") .masquerade_as_nightly_cargo(&["update-breaking"]) .with_stderr_data(str![[r#" [UPDATING] `[..]` index [UPGRADING] mixed-compatibility ^1.0 -> ^2.0 [LOCKING] 1 package to latest compatible version [ADDING] mixed-compatibility v2.0.1 "#]]) .run(); } #[cargo_test] fn update_breaking_mixed_pinning_renaming() { Package::new("mixed-pinned", "1.0.0").publish(); Package::new("mixed-ws-pinned", "1.0.0").publish(); Package::new("renamed-from", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["pinned", "unpinned", "mixed"] [workspace.dependencies] mixed-ws-pinned = "=1.0" "#, ) .file( "pinned/Cargo.toml", r#" [package] name = "pinned" version = "0.0.1" edition = "2015" authors = [] [dependencies] mixed-pinned = "=1.0" mixed-ws-pinned.workspace = true renamed-to = { package = "renamed-from", version = "1.0" } "#, ) .file("pinned/src/lib.rs", "") .file( "unpinned/Cargo.toml", r#" [package] name = "unpinned" version = "0.0.1" edition = "2015" authors = [] [dependencies] mixed-pinned = "1.0" mixed-ws-pinned = "1.0" renamed-from = "1.0" "#, ) .file("unpinned/src/lib.rs", "") .file( "mixed/Cargo.toml", r#" [package] name = "mixed" version = "0.0.1" edition = "2015" authors = [] [target.'cfg(windows)'.dependencies] mixed-pinned = "1.0" [target.'cfg(unix)'.dependencies] mixed-pinned = "=1.0" "#, ) .file("mixed/src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); Package::new("mixed-pinned", "2.0.0").publish(); Package::new("mixed-ws-pinned", "2.0.0").publish(); Package::new("renamed-from", "2.0.0").publish(); p.cargo("update -Zunstable-options --breaking") .masquerade_as_nightly_cargo(&["update-breaking"]) .with_stderr_data(str![[r#" [UPDATING] `[..]` index [UPGRADING] mixed-pinned ^1.0 -> ^2.0 [UPGRADING] mixed-ws-pinned ^1.0 -> ^2.0 [UPGRADING] renamed-from ^1.0 -> ^2.0 [LOCKING] 3 packages to latest compatible versions [ADDING] mixed-pinned v2.0.0 [ADDING] mixed-ws-pinned v2.0.0 [ADDING] renamed-from v2.0.0 "#]]) .run(); let root_manifest = p.read_file("Cargo.toml"); assert_e2e().eq( &root_manifest, str![[r#" [workspace] members = ["pinned", "unpinned", "mixed"] [workspace.dependencies] mixed-ws-pinned = "=1.0" "#]], ); let pinned_manifest = p.read_file("pinned/Cargo.toml"); assert_e2e().eq( &pinned_manifest, str![[r#" [package] name = "pinned" version = "0.0.1" edition = "2015" authors = [] [dependencies] mixed-pinned = "=1.0" mixed-ws-pinned.workspace = true renamed-to = { package = "renamed-from", version = "1.0" } "#]], ); let unpinned_manifest = p.read_file("unpinned/Cargo.toml"); assert_e2e().eq( &unpinned_manifest, str![[r#" [package] name = "unpinned" version = "0.0.1" edition = "2015" authors = [] [dependencies] mixed-pinned = "2.0" mixed-ws-pinned = "2.0" renamed-from = "2.0" "#]], ); let mixed_manifest = p.read_file("mixed/Cargo.toml"); assert_e2e().eq( &mixed_manifest, str![[r#" [package] name = "mixed" version = "0.0.1" edition = "2015" authors = [] [target.'cfg(windows)'.dependencies] mixed-pinned = "2.0" [target.'cfg(unix)'.dependencies] mixed-pinned = "=1.0" "#]], ); } #[cargo_test] fn update_breaking_pre_release_downgrade() { Package::new("bar", "2.0.0-beta.21").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "2.0.0-beta.21" "#, ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); // The purpose of this test is // to demonstrate that `update --breaking` will not try to downgrade to the latest stable version (1.7.0), // but will rather keep the latest pre-release (2.0.0-beta.21). Package::new("bar", "1.7.0").publish(); p.cargo("update -Zunstable-options --breaking bar") .masquerade_as_nightly_cargo(&["update-breaking"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index "#]]) .run(); } #[cargo_test] fn update_breaking_pre_release_upgrade() { Package::new("bar", "2.0.0-beta.21").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "2.0.0-beta.21" "#, ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); // TODO: `2.0.0-beta.21` can be upgraded to `2.0.0-beta.22` Package::new("bar", "2.0.0-beta.22").publish(); p.cargo("update -Zunstable-options --breaking bar") .masquerade_as_nightly_cargo(&["update-breaking"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index "#]]) .run(); // TODO: `2.0.0-beta.21` can be upgraded to `2.0.0` Package::new("bar", "2.0.0").publish(); p.cargo("update -Zunstable-options --breaking bar") .masquerade_as_nightly_cargo(&["update-breaking"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index "#]]) .run(); Package::new("bar", "3.0.0").publish(); p.cargo("update -Zunstable-options --breaking bar") .masquerade_as_nightly_cargo(&["update-breaking"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [UPGRADING] bar ^2.0.0-beta.21 -> ^3.0.0 [LOCKING] 1 package to latest compatible version [UPDATING] bar v2.0.0-beta.21 -> v3.0.0 "#]]) .run(); } #[cargo_test] fn prefixed_v_in_version() { Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "1.0.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); Package::new("bar", "1.0.1").publish(); p.cargo("update bar --precise v1.0.1") .with_status(101) .with_stderr_data(str![[r#" [ERROR] the version provided, `v1.0.1` is not a valid SemVer version [HELP] try changing the version to `1.0.1` Caused by: unexpected character 'v' while parsing major version number "#]]) .run(); } cargo-0.91.0/tests/testsuite/utils/cross_compile.rs000064400000000000000000000167471046102023000205670ustar 00000000000000//! Support for cross-compile tests with the `--target` flag. //! //! Note that cross-testing is very limited. You need to install the //! "alternate" target to the host (32-bit for 64-bit hosts or vice-versa). //! //! Set `CFG_DISABLE_CROSS_TESTS=1` environment variable to disable these tests //! if you are unable to use the alternate target. Unfortunately 32-bit //! support on macOS is going away, so macOS users are out of luck. //! //! These tests are all disabled on rust-lang/rust's CI, but run in Cargo's CI. use crate::prelude::*; use cargo_test_support::{basic_manifest, cross_compile::alternate, main_file, project}; use cargo_util::ProcessError; use std::fmt::Write; use std::{ process::{Command, Output}, sync::{ Once, atomic::{AtomicBool, Ordering}, }, }; /// Whether or not the resulting cross binaries can run on the host. static CAN_RUN_ON_HOST: AtomicBool = AtomicBool::new(false); pub fn disabled() -> bool { // First, disable if requested. match std::env::var("CFG_DISABLE_CROSS_TESTS") { Ok(ref s) if *s == "1" => return true, _ => {} } // It requires setting `target.linker` for cross-compilation to work on aarch64, // so not going to bother now. if cfg!(all(target_arch = "aarch64", target_os = "linux")) { return true; } // Cross tests are only tested to work on macos, linux, and MSVC windows. if !(cfg!(target_os = "macos") || cfg!(target_os = "linux") || cfg!(target_env = "msvc")) { return true; } // It's not particularly common to have a cross-compilation setup, so // try to detect that before we fail a bunch of tests through no fault // of the user. static CAN_BUILD_CROSS_TESTS: AtomicBool = AtomicBool::new(false); static CHECK: Once = Once::new(); let cross_target = alternate(); let run_cross_test = || -> anyhow::Result { let p = project() .at("cross_test") .file("Cargo.toml", &basic_manifest("cross_test", "1.0.0")) .file("src/main.rs", &main_file(r#""testing!""#, &[])) .build(); let build_result = p .cargo("build --target") .arg(&cross_target) .exec_with_output(); if build_result.is_ok() { CAN_BUILD_CROSS_TESTS.store(true, Ordering::SeqCst); } let result = p .cargo("run --target") .arg(&cross_target) .exec_with_output(); if result.is_ok() { CAN_RUN_ON_HOST.store(true, Ordering::SeqCst); } build_result }; CHECK.call_once(|| { drop(run_cross_test()); }); if CAN_BUILD_CROSS_TESTS.load(Ordering::SeqCst) { // We were able to compile a simple project, so the user has the // necessary `std::` bits installed. Therefore, tests should not // be disabled. return false; } // We can't compile a simple cross project. We want to warn the user // by failing a single test and having the remainder of the cross tests // pass. We don't use `std::sync::Once` here because panicking inside its // `call_once` method would poison the `Once` instance, which is not what // we want. static HAVE_WARNED: AtomicBool = AtomicBool::new(false); if HAVE_WARNED.swap(true, Ordering::SeqCst) { // We are some other test and somebody else is handling the warning. // Just disable the current test. return true; } // We are responsible for warning the user, which we do by panicking. let mut message = format!( " Cannot cross compile to {}. This failure can be safely ignored. If you would prefer to not see this failure, you can set the environment variable CFG_DISABLE_CROSS_TESTS to \"1\". Alternatively, you can install the necessary libraries to enable cross compilation tests. Cross compilation tests depend on your host platform. ", cross_target ); if cfg!(target_os = "linux") { message.push_str( " Linux cross tests target i686-unknown-linux-gnu, which requires the ability to build and run 32-bit targets. This requires the 32-bit libraries to be installed. For example, on Ubuntu, run `sudo apt install gcc-multilib` to install the necessary libraries. ", ); } else if cfg!(all(target_os = "macos", target_arch = "aarch64")) { message.push_str( " macOS on aarch64 cross tests to target x86_64-apple-darwin. This should be natively supported via Xcode, nothing additional besides the rustup target should be needed. ", ); } else if cfg!(target_os = "macos") { message.push_str( " macOS on x86_64 cross tests to target x86_64-apple-ios, which requires the iOS SDK to be installed. This should be included with Xcode automatically. If you are using the Xcode command line tools, you'll need to install the full Xcode app (from the Apple App Store), and switch to it with this command: sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer Some cross-tests want to *run* the executables on the host. These tests will be ignored if this is not possible. On macOS, this means you need an iOS simulator installed to run these tests. To install a simulator, open Xcode, go to preferences > Components, and download the latest iOS simulator. ", ); } else if cfg!(target_os = "windows") { message.push_str( " Windows cross tests target i686-pc-windows-msvc, which requires the ability to build and run 32-bit targets. This should work automatically if you have properly installed Visual Studio build tools. ", ); } else { // The check at the top should prevent this. panic!("platform should have been skipped"); } let rustup_available = Command::new("rustup").output().is_ok(); if rustup_available { write!( message, " Make sure that the appropriate `rustc` target is installed with rustup: rustup target add {} ", cross_target ) .unwrap(); } else { write!( message, " rustup does not appear to be installed. Make sure that the appropriate `rustc` target is installed for the target `{}`. ", cross_target ) .unwrap(); } // Show the actual error message. match run_cross_test() { Ok(_) => message.push_str("\nUh oh, second run succeeded?\n"), Err(err) => match err.downcast_ref::() { Some(proc_err) => write!(message, "\nTest error: {}\n", proc_err).unwrap(), None => write!(message, "\nUnexpected non-process error: {}\n", err).unwrap(), }, } panic!("{}", message); } /// Whether or not the host can run cross-compiled executables. pub fn can_run_on_host() -> bool { if disabled() { return false; } // macos is currently configured to cross compile to x86_64-apple-ios // which requires a simulator to run. Azure's CI image appears to have the // SDK installed, but are not configured to launch iOS images with a // simulator. if cfg!(target_os = "macos") { if CAN_RUN_ON_HOST.load(Ordering::SeqCst) { return true; } else { println!("Note: Cannot run on host, skipping."); return false; } } else { assert!(CAN_RUN_ON_HOST.load(Ordering::SeqCst)); return true; } } cargo-0.91.0/tests/testsuite/utils/ext.rs000064400000000000000000000023341046102023000165110ustar 00000000000000use std::path::PathBuf; use cargo_test_support::{ArgLineCommandExt, Execs, Project, TestEnvCommandExt, compare}; pub trait CargoProjectExt { /// Creates a `ProcessBuilder` to run cargo. /// /// Arguments can be separated by spaces. /// /// For `cargo run`, see [`Project::rename_run`]. /// /// # Example: /// /// ```no_run /// # let p = cargo_test_support::project().build(); /// p.cargo("build --bin foo").run(); /// ``` fn cargo(&self, cmd: &str) -> Execs; } impl CargoProjectExt for Project { fn cargo(&self, cmd: &str) -> Execs { let cargo = cargo_exe(); let mut execs = self.process(&cargo); execs.env("CARGO", cargo); execs.arg_line(cmd); execs } } /// Path to the cargo binary pub fn cargo_exe() -> PathBuf { snapbox::cmd::cargo_bin!("cargo").to_path_buf() } /// Test the cargo command pub trait CargoCommandExt { fn cargo_ui() -> Self; } impl CargoCommandExt for snapbox::cmd::Command { fn cargo_ui() -> Self { Self::new(cargo_exe()) .with_assert(compare::assert_ui()) .env("CARGO_TERM_COLOR", "always") .env("CARGO_TERM_HYPERLINKS", "true") .test_env() } } cargo-0.91.0/tests/testsuite/utils/mod.rs000064400000000000000000000007701046102023000164720ustar 00000000000000use std::path::PathBuf; use cargo_test_support::{ArgLineCommandExt, Execs, execs, process}; pub mod cross_compile; pub mod ext; pub mod tools; /// Run `cargo $arg_line`, see [`Execs`] pub fn cargo_process(arg_line: &str) -> Execs { let cargo = cargo_exe(); let mut p = process(&cargo); p.env("CARGO", cargo); p.arg_line(arg_line); execs().with_process_builder(p) } /// Path to the cargo binary pub fn cargo_exe() -> PathBuf { snapbox::cmd::cargo_bin!("cargo").to_path_buf() } cargo-0.91.0/tests/testsuite/utils/tools.rs000064400000000000000000000112461046102023000170530ustar 00000000000000//! Common executables that can be reused by various tests. use crate::prelude::*; use cargo_test_support::{Project, basic_manifest, paths, project}; use std::path::{Path, PathBuf}; use std::sync::Mutex; use std::sync::OnceLock; static ECHO_WRAPPER: OnceLock>> = OnceLock::new(); static ECHO: OnceLock>> = OnceLock::new(); static CLIPPY_DRIVER: OnceLock>> = OnceLock::new(); /// Returns the path to an executable that works as a wrapper around rustc. /// /// The wrapper will echo the command line it was called with to stderr. pub fn echo_wrapper() -> PathBuf { let mut lock = ECHO_WRAPPER .get_or_init(|| Default::default()) .lock() .unwrap(); if let Some(path) = &*lock { return path.clone(); } let p = project() .at(paths::global_root().join("rustc-echo-wrapper")) .file("Cargo.toml", &basic_manifest("rustc-echo-wrapper", "1.0.0")) .file( "src/main.rs", r#" use std::fs::read_to_string; use std::path::PathBuf; fn main() { // Handle args from `@path` argfile for rustc let args = std::env::args() .flat_map(|p| if let Some(p) = p.strip_prefix("@") { read_to_string(p).unwrap().lines().map(String::from).collect() } else { vec![p] }) .collect::>(); eprintln!("WRAPPER CALLED: {}", args[1..].join(" ")); let status = std::process::Command::new(&args[1]) .args(&args[2..]).status().unwrap(); std::process::exit(status.code().unwrap_or(1)); } "#, ) .build(); p.cargo("build").run(); let path = p.bin("rustc-echo-wrapper"); *lock = Some(path.clone()); path } /// Returns the path to an executable that prints its arguments. /// /// Do not expect this to be anything fancy. pub fn echo() -> PathBuf { let mut lock = ECHO.get_or_init(|| Default::default()).lock().unwrap(); if let Some(path) = &*lock { return path.clone(); } if let Ok(path) = cargo_util::paths::resolve_executable(Path::new("echo")) { *lock = Some(path.clone()); return path; } // Often on Windows, `echo` is not available. let p = project() .at(paths::global_root().join("basic-echo")) .file("Cargo.toml", &basic_manifest("basic-echo", "1.0.0")) .file( "src/main.rs", r#" fn main() { let mut s = String::new(); let mut it = std::env::args().skip(1).peekable(); while let Some(n) = it.next() { s.push_str(&n); if it.peek().is_some() { s.push(' '); } } println!("{}", s); } "#, ) .build(); p.cargo("build").run(); let path = p.bin("basic-echo"); *lock = Some(path.clone()); path } /// Returns a project which builds a cargo-echo simple subcommand pub fn echo_subcommand() -> Project { let p = project() .at("cargo-echo") .file("Cargo.toml", &basic_manifest("cargo-echo", "0.0.1")) .file( "src/main.rs", r#" fn main() { let args: Vec<_> = ::std::env::args().skip(1).collect(); println!("{}", args.join(" ")); } "#, ) .build(); p.cargo("build").run(); p } /// A wrapper around `rustc` instead of calling `clippy`. pub fn wrapped_clippy_driver() -> PathBuf { let mut lock = CLIPPY_DRIVER .get_or_init(|| Default::default()) .lock() .unwrap(); if let Some(path) = &*lock { return path.clone(); } let clippy_driver = project() .at(paths::global_root().join("clippy-driver")) .file("Cargo.toml", &basic_manifest("clippy-driver", "0.0.1")) .file( "src/main.rs", r#" fn main() { let mut args = std::env::args_os(); let _me = args.next().unwrap(); let rustc = args.next().unwrap(); let status = std::process::Command::new(rustc).args(args).status().unwrap(); std::process::exit(status.code().unwrap_or(1)); } "#, ) .build(); clippy_driver.cargo("build").run(); let path = clippy_driver.bin("clippy-driver"); *lock = Some(path.clone()); path } cargo-0.91.0/tests/testsuite/vendor.rs000064400000000000000000001442141046102023000160520ustar 00000000000000//! Tests for the `cargo vendor` command. //! //! Note that every test here uses `--respect-source-config` so that the //! "fake" crates.io is used. Otherwise `vendor` would download the crates.io //! index from the network. use std::fs; use crate::prelude::*; use cargo_test_support::compare::assert_e2e; use cargo_test_support::git; use cargo_test_support::registry::{self, Package, RegistryBuilder}; use cargo_test_support::str; use cargo_test_support::{Project, basic_lib_manifest, basic_manifest, paths, project}; #[cargo_test] fn vendor_simple() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] log = "0.3.5" "#, ) .file("src/lib.rs", "") .build(); Package::new("log", "0.3.5").publish(); p.cargo("vendor --respect-source-config").run(); let lock = p.read_file("vendor/log/Cargo.toml"); assert!(lock.contains("version = \"0.3.5\"")); add_crates_io_vendor_config(&p); p.cargo("check").run(); } #[cargo_test] fn vendor_sample_config() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] log = "0.3.5" "#, ) .file("src/lib.rs", "") .build(); Package::new("log", "0.3.5").publish(); p.cargo("vendor --respect-source-config") .with_stdout_data(str![[r#" [source.crates-io] replace-with = "vendored-sources" [source.vendored-sources] directory = "vendor" "#]]) .run(); } #[cargo_test] fn vendor_sample_config_alt_registry() { let registry = RegistryBuilder::new().alternative().http_index().build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] log = { version = "0.3.5", registry = "alternative" } "#, ) .file("src/lib.rs", "") .build(); Package::new("log", "0.3.5").alternative(true).publish(); p.cargo("vendor --respect-source-config") .with_stdout_data(format!( r#"[source."{0}"] registry = "{0}" replace-with = "vendored-sources" [source.vendored-sources] directory = "vendor" "#, registry.index_url() )) .run(); } #[cargo_test] fn vendor_path_specified() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] log = "0.3.5" "#, ) .file("src/lib.rs", "") .build(); Package::new("log", "0.3.5").publish(); let path = if cfg!(windows) { r#"deps\.vendor"# } else { "deps/.vendor" }; let output = p.cargo("vendor --respect-source-config").arg(path).run(); // Assert against original output to ensure that // path is normalized by `ops::vendor` on Windows. assert_eq!( &String::from_utf8(output.stdout).unwrap(), r#"[source.crates-io] replace-with = "vendored-sources" [source.vendored-sources] directory = "deps/.vendor" "# ); let lock = p.read_file("deps/.vendor/log/Cargo.toml"); assert!(lock.contains("version = \"0.3.5\"")); } fn add_crates_io_vendor_config(p: &Project) { p.change_file( ".cargo/config.toml", r#" [source.crates-io] replace-with = 'vendor' [source.vendor] directory = 'vendor' "#, ); } fn add_git_vendor_config(p: &Project, git_project: &Project) { p.change_file( ".cargo/config.toml", &format!( r#" [source."git+{url}"] git = "{url}" replace-with = 'vendor' [source.vendor] directory = 'vendor' "#, url = git_project.url() ), ); } #[cargo_test] fn package_exclude() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .build(); Package::new("bar", "0.1.0") .file( "Cargo.toml", r#" [package] name = "bar" version = "0.1.0" exclude = [".*", "!.include", "!.dotdir/include"] "#, ) .file("src/lib.rs", "") .file(".exclude", "") .file(".include", "") .file(".dotdir/exclude", "") .file(".dotdir/include", "") .publish(); p.cargo("vendor --respect-source-config").run(); let csum = p.read_file("vendor/bar/.cargo-checksum.json"); // Everything is included because `cargo-vendor` // do direct extractions from tarballs // (Some are excluded like `.git` or `.cargo-ok` though.) assert!(csum.contains(".include")); assert!(csum.contains(".exclude")); assert!(csum.contains(".dotdir/exclude")); assert!(csum.contains(".dotdir/include")); } #[cargo_test] fn discovery_inferred_build_rs_included() { let git_project = git::new("dep", |project| { project .file( "Cargo.toml", r#" [package] name = "dep" version = "0.0.1" edition = "2015" license = "MIT" description = "foo" documentation = "docs.rs/foo" authors = [] include = ["src/lib.rs", "build.rs"] "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" [dependencies.dep] git = '{}' "#, git_project.url() ), ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("vendor --respect-source-config").run(); add_git_vendor_config(&p, &git_project); let lock = p.read_file("vendor/dep/Cargo.toml"); assert_e2e().eq( lock, str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "dep" version = "0.0.1" authors = [] build = "build.rs" include = [ "src/lib.rs", "build.rs", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "docs.rs/foo" readme = false license = "MIT" [lib] name = "dep" path = "src/lib.rs" "##]], ); p.cargo("check").run(); } #[cargo_test] fn discovery_inferred_build_rs_excluded() { let git_project = git::new("dep", |project| { project .file( "Cargo.toml", r#" [package] name = "dep" version = "0.0.1" edition = "2015" license = "MIT" description = "foo" documentation = "docs.rs/foo" authors = [] include = ["src/lib.rs"] "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" [dependencies.dep] git = '{}' "#, git_project.url() ), ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("vendor --respect-source-config").run(); add_git_vendor_config(&p, &git_project); let lock = p.read_file("vendor/dep/Cargo.toml"); assert_e2e().eq( lock, str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "dep" version = "0.0.1" authors = [] build = false include = ["src/lib.rs"] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "docs.rs/foo" readme = false license = "MIT" [lib] name = "dep" path = "src/lib.rs" "##]], ); p.cargo("check").run(); } #[cargo_test] fn discovery_inferred_lib_included() { let git_project = git::new("dep", |project| { project .file( "Cargo.toml", r#" [package] name = "dep" version = "0.0.1" edition = "2015" license = "MIT" description = "foo" documentation = "docs.rs/foo" authors = [] include = ["src/main.rs", "src/lib.rs"] "#, ) .file("src/main.rs", "fn main() {}") .file("src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" [dependencies.dep] git = '{}' "#, git_project.url() ), ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("vendor --respect-source-config").run(); add_git_vendor_config(&p, &git_project); let lock = p.read_file("vendor/dep/Cargo.toml"); assert_e2e().eq( lock, str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "dep" version = "0.0.1" authors = [] build = false include = [ "src/main.rs", "src/lib.rs", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "docs.rs/foo" readme = false license = "MIT" [lib] name = "dep" path = "src/lib.rs" [[bin]] name = "dep" path = "src/main.rs" "##]], ); p.cargo("check").run(); } #[cargo_test] fn discovery_inferred_lib_excluded() { let git_project = git::new("dep", |project| { project .file( "Cargo.toml", r#" [package] name = "dep" version = "0.0.1" edition = "2015" license = "MIT" description = "foo" documentation = "docs.rs/foo" authors = [] include = ["src/main.rs"] "#, ) .file("src/main.rs", "fn main() {}") .file("src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" [dependencies.dep] git = '{}' "#, git_project.url() ), ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("vendor --respect-source-config").run(); add_git_vendor_config(&p, &git_project); let lock = p.read_file("vendor/dep/Cargo.toml"); assert_e2e().eq( lock, str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "dep" version = "0.0.1" authors = [] build = false include = ["src/main.rs"] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "docs.rs/foo" readme = false license = "MIT" [[bin]] name = "dep" path = "src/main.rs" "##]], ); p.cargo("check").run(); } #[cargo_test] fn discovery_inferred_other_included() { let git_project = git::new("dep", |project| { project .file( "Cargo.toml", r#" [package] name = "dep" version = "0.0.1" edition = "2015" license = "MIT" description = "foo" documentation = "docs.rs/foo" authors = [] include = ["src/lib.rs", "src/bin/foo/main.rs", "examples/example_foo.rs", "tests/test_foo.rs", "benches/bench_foo.rs"] "#, ) .file("src/lib.rs", "") .file("src/bin/foo/main.rs", "fn main() {}") .file("examples/example_foo.rs", "fn main() {}") .file("tests/test_foo.rs", "fn main() {}") .file("benches/bench_foo.rs", "fn main() {}") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" [dependencies.dep] git = '{}' "#, git_project.url() ), ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("vendor --respect-source-config").run(); add_git_vendor_config(&p, &git_project); let lock = p.read_file("vendor/dep/Cargo.toml"); assert_e2e().eq( lock, str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "dep" version = "0.0.1" authors = [] build = false include = [ "src/lib.rs", "src/bin/foo/main.rs", "examples/example_foo.rs", "tests/test_foo.rs", "benches/bench_foo.rs", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "docs.rs/foo" readme = false license = "MIT" [lib] name = "dep" path = "src/lib.rs" [[bin]] name = "foo" path = "src/bin/foo/main.rs" [[example]] name = "example_foo" path = "examples/example_foo.rs" [[test]] name = "test_foo" path = "tests/test_foo.rs" [[bench]] name = "bench_foo" path = "benches/bench_foo.rs" "##]], ); p.cargo("check").run(); } #[cargo_test] fn discovery_inferred_other_excluded() { let git_project = git::new("dep", |project| { project .file( "Cargo.toml", r#" [package] name = "dep" version = "0.0.1" edition = "2015" license = "MIT" description = "foo" documentation = "docs.rs/foo" authors = [] include = ["src/lib.rs"] "#, ) .file("src/lib.rs", "") .file("src/bin/foo/main.rs", "fn main() {}") .file("examples/example_foo.rs", "fn main() {}") .file("tests/test_foo.rs", "fn main() {}") .file("benches/bench_foo.rs", "fn main() {}") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" [dependencies.dep] git = '{}' "#, git_project.url() ), ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("vendor --respect-source-config").run(); add_git_vendor_config(&p, &git_project); let lock = p.read_file("vendor/dep/Cargo.toml"); assert_e2e().eq( lock, str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "dep" version = "0.0.1" authors = [] build = false include = ["src/lib.rs"] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "docs.rs/foo" readme = false license = "MIT" [lib] name = "dep" path = "src/lib.rs" "##]], ); p.cargo("check").run(); } #[cargo_test] fn two_versions() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] bitflags = "0.8.0" bar = { path = "bar" } "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" [dependencies] bitflags = "0.7.0" "#, ) .file("bar/src/lib.rs", "") .build(); Package::new("bitflags", "0.7.0").publish(); Package::new("bitflags", "0.8.0").publish(); p.cargo("vendor --respect-source-config").run(); let lock = p.read_file("vendor/bitflags/Cargo.toml"); assert!(lock.contains("version = \"0.8.0\"")); let lock = p.read_file("vendor/bitflags-0.7.0/Cargo.toml"); assert!(lock.contains("version = \"0.7.0\"")); add_crates_io_vendor_config(&p); p.cargo("check").run(); } #[cargo_test] fn two_explicit_versions() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] bitflags = "0.8.0" bar = { path = "bar" } "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" [dependencies] bitflags = "0.7.0" "#, ) .file("bar/src/lib.rs", "") .build(); Package::new("bitflags", "0.7.0").publish(); Package::new("bitflags", "0.8.0").publish(); p.cargo("vendor --respect-source-config --versioned-dirs") .run(); let lock = p.read_file("vendor/bitflags-0.8.0/Cargo.toml"); assert!(lock.contains("version = \"0.8.0\"")); let lock = p.read_file("vendor/bitflags-0.7.0/Cargo.toml"); assert!(lock.contains("version = \"0.7.0\"")); add_crates_io_vendor_config(&p); p.cargo("check").run(); } #[cargo_test] fn help() { let p = project().build(); p.cargo("vendor -h").run(); } #[cargo_test] fn update_versions() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] bitflags = "0.7.0" "#, ) .file("src/lib.rs", "") .build(); Package::new("bitflags", "0.7.0").publish(); Package::new("bitflags", "0.8.0").publish(); p.cargo("vendor --respect-source-config").run(); let lock = p.read_file("vendor/bitflags/Cargo.toml"); assert!(lock.contains("version = \"0.7.0\"")); p.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] bitflags = "0.8.0" "#, ); p.cargo("vendor --respect-source-config").run(); let lock = p.read_file("vendor/bitflags/Cargo.toml"); assert!(lock.contains("version = \"0.8.0\"")); } #[cargo_test] fn two_lockfiles() { let p = project() .no_manifest() .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] bitflags = "=0.7.0" "#, ) .file("foo/src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" [dependencies] bitflags = "=0.8.0" "#, ) .file("bar/src/lib.rs", "") .build(); Package::new("bitflags", "0.7.0").publish(); Package::new("bitflags", "0.8.0").publish(); p.cargo("vendor --respect-source-config -s bar/Cargo.toml --manifest-path foo/Cargo.toml") .run(); let lock = p.read_file("vendor/bitflags/Cargo.toml"); assert!(lock.contains("version = \"0.8.0\"")); let lock = p.read_file("vendor/bitflags-0.7.0/Cargo.toml"); assert!(lock.contains("version = \"0.7.0\"")); add_crates_io_vendor_config(&p); p.cargo("check").cwd("foo").run(); p.cargo("check").cwd("bar").run(); } #[cargo_test] fn test_sync_argument() { let p = project() .no_manifest() .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] bitflags = "=0.7.0" "#, ) .file("foo/src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" [dependencies] bitflags = "=0.8.0" "#, ) .file("bar/src/lib.rs", "") .file( "baz/Cargo.toml", r#" [package] name = "baz" version = "0.1.0" [dependencies] bitflags = "=0.8.0" "#, ) .file("baz/src/lib.rs", "") .build(); Package::new("bitflags", "0.7.0").publish(); Package::new("bitflags", "0.8.0").publish(); p.cargo("vendor --respect-source-config --manifest-path foo/Cargo.toml -s bar/Cargo.toml baz/Cargo.toml test_vendor") .with_stderr_data(str![[r#" [ERROR] unexpected argument 'test_vendor' found Usage: cargo[EXE] vendor [OPTIONS] [path] For more information, try '--help'. "#]] ) .with_status(1) .run(); p.cargo("vendor --respect-source-config --manifest-path foo/Cargo.toml -s bar/Cargo.toml -s baz/Cargo.toml test_vendor") .run(); let lock = p.read_file("test_vendor/bitflags/Cargo.toml"); assert!(lock.contains("version = \"0.8.0\"")); let lock = p.read_file("test_vendor/bitflags-0.7.0/Cargo.toml"); assert!(lock.contains("version = \"0.7.0\"")); } #[cargo_test] fn delete_old_crates() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] bitflags = "=0.7.0" "#, ) .file("src/lib.rs", "") .build(); Package::new("bitflags", "0.7.0").publish(); Package::new("log", "0.3.5").publish(); p.cargo("vendor --respect-source-config").run(); p.read_file("vendor/bitflags/Cargo.toml"); p.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] log = "=0.3.5" "#, ); p.cargo("vendor --respect-source-config").run(); let lock = p.read_file("vendor/log/Cargo.toml"); assert!(lock.contains("version = \"0.3.5\"")); assert!(!p.root().join("vendor/bitflags/Cargo.toml").exists()); } #[cargo_test] fn ignore_files() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] url = "1.4.1" "#, ) .file("src/lib.rs", "") .build(); Package::new("url", "1.4.1") // These will be vendored .file(".cargo_vcs_info.json", "") .file("Cargo.toml.orig", "") .file("foo.orig", "") .file("foo.rej", "") .file("src/lib.rs", "") // These will not be vendored .file(".cargo-ok", "") .file(".gitattributes", "") .file(".gitignore", "") .publish(); p.cargo("vendor --respect-source-config").run(); let csum = p.read_file("vendor/url/.cargo-checksum.json"); assert_e2e().eq( csum, str![[r#" { "files": { ".cargo_vcs_info.json": "[..]", "Cargo.toml": "[..]", "Cargo.toml.orig": "[..]", "foo.orig": "[..]", "foo.rej": "[..]", "src/lib.rs": "[..]" }, "package": "[..]" } "#]] .is_json(), ); } #[cargo_test] fn included_files_only() { let git = git::new("a", |p| { p.file("Cargo.toml", &basic_lib_manifest("a")) .file("src/lib.rs", "") .file(".gitignore", "a") .file("a/b.md", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [dependencies] a = {{ git = '{}' }} "#, git.url() ), ) .file("src/lib.rs", "") .build(); p.cargo("vendor --respect-source-config").run(); let csum = p.read_file("vendor/a/.cargo-checksum.json"); assert!(!csum.contains("a/b.md")); } #[cargo_test] fn dependent_crates_in_crates() { let git = git::new("a", |p| { p.file( "Cargo.toml", r#" [package] name = "a" version = "0.1.0" [dependencies] b = { path = 'b' } "#, ) .file("src/lib.rs", "") .file("b/Cargo.toml", &basic_lib_manifest("b")) .file("b/src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [dependencies] a = {{ git = '{}' }} "#, git.url() ), ) .file("src/lib.rs", "") .build(); p.cargo("vendor --respect-source-config").run(); p.read_file("vendor/a/.cargo-checksum.json"); p.read_file("vendor/b/.cargo-checksum.json"); } #[cargo_test] fn vendoring_git_crates() { let git = git::new("git", |p| { p.file("Cargo.toml", &basic_lib_manifest("serde_derive")) .file("src/lib.rs", "") .file("src/wut.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [dependencies.serde] version = "0.5.0" [dependencies.serde_derive] version = "0.5.0" [patch.crates-io] serde_derive = {{ git = '{}' }} "#, git.url() ), ) .file("src/lib.rs", "") .build(); Package::new("serde", "0.5.0") .dep("serde_derive", "0.5") .publish(); Package::new("serde_derive", "0.5.0").publish(); p.cargo("vendor --respect-source-config").run(); p.read_file("vendor/serde_derive/src/wut.rs"); add_crates_io_vendor_config(&p); p.cargo("check").run(); } #[cargo_test] fn git_simple() { let git = git::new("git", |p| { p.file("Cargo.toml", &basic_lib_manifest("a")) .file("src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [dependencies] a = {{ git = '{}' }} "#, git.url() ), ) .file("src/lib.rs", "") .build(); p.cargo("vendor --respect-source-config").run(); let csum = p.read_file("vendor/a/.cargo-checksum.json"); assert!(csum.contains("\"package\":null")); } #[cargo_test] fn git_diff_rev() { let (git_project, git_repo) = git::new_repo("git", |p| { p.file("Cargo.toml", &basic_manifest("a", "0.1.0")) .file("src/lib.rs", "") }); let url = git_project.url(); let ref_1 = "v0.1.0"; let ref_2 = "v0.2.0"; git::tag(&git_repo, ref_1); git_project.change_file("Cargo.toml", &basic_manifest("a", "0.2.0")); git::add(&git_repo); git::commit(&git_repo); git::tag(&git_repo, ref_2); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [dependencies] a_1 = {{ package = "a", git = '{url}', rev = '{ref_1}' }} a_2 = {{ package = "a", git = '{url}', rev = '{ref_2}' }} "# ), ) .file("src/lib.rs", "") .build(); p.cargo("vendor --respect-source-config") .with_stdout_data(str![[r#" [source."git+[ROOTURL]/git?rev=v0.1.0"] git = "[ROOTURL]/git" rev = "v0.1.0" replace-with = "vendored-sources" [source."git+[ROOTURL]/git?rev=v0.2.0"] git = "[ROOTURL]/git" rev = "v0.2.0" replace-with = "vendored-sources" [source.vendored-sources] directory = "vendor" "#]]) .run(); } #[cargo_test] fn git_duplicate() { let git = git::new("a", |p| { p.file( "Cargo.toml", r#" [package] name = "a" version = "0.1.0" [dependencies] b = { path = 'b' } "#, ) .file("src/lib.rs", "") .file("b/Cargo.toml", &basic_lib_manifest("b")) .file("b/src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [dependencies] a = {{ git = '{}' }} b = '0.5.0' "#, git.url() ), ) .file("src/lib.rs", "") .build(); Package::new("b", "0.5.0").publish(); p.cargo("vendor --respect-source-config") .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/a` [UPDATING] `dummy-registry` index [LOCKING] 3 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] b v0.5.0 (registry `dummy-registry`) [ERROR] failed to sync Caused by: found duplicate version of package `b v0.5.0` vendored from two sources: source 1: registry `crates-io` source 2: [ROOTURL]/a#[..] "#]]) .with_status(101) .run(); } #[cargo_test] fn git_complex() { let git_b = git::new("git_b", |p| { p.file( "Cargo.toml", r#" [package] name = "b" version = "0.1.0" edition = "2021" [dependencies] dep_b = { path = 'dep_b' } "#, ) .file("src/lib.rs", "") .file("dep_b/Cargo.toml", &basic_lib_manifest("dep_b")) .file("dep_b/src/lib.rs", "") }); let git_a = git::new("git_a", |p| { p.file( "Cargo.toml", &format!( r#" [package] name = "a" version = "0.1.0" edition = "2021" [dependencies] b = {{ git = '{}' }} dep_a = {{ path = 'dep_a' }} "#, git_b.url() ), ) .file("src/lib.rs", "") .file("dep_a/Cargo.toml", &basic_lib_manifest("dep_a")) .file("dep_a/src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2021" [dependencies] a = {{ git = '{}' }} "#, git_a.url() ), ) .file("src/lib.rs", "") .build(); let output = p.cargo("vendor --respect-source-config").run(); let output = String::from_utf8(output.stdout).unwrap(); p.change_file(".cargo/config.toml", &output); p.cargo("check -v") .with_stderr_data( str![[r#" [CHECKING] dep_b v0.5.0 ([ROOTURL]/git_b#[..]) [CHECKING] dep_a v0.5.0 ([ROOTURL]/git_a#[..]) [RUNNING] `rustc [..] [ROOT]/foo/vendor/dep_b/src/lib.rs [..]` [RUNNING] `rustc [..] [ROOT]/foo/vendor/dep_a/src/lib.rs [..]` [CHECKING] b v0.1.0 ([ROOTURL]/git_b#[..]) [RUNNING] `rustc [..] [ROOT]/foo/vendor/b/src/lib.rs [..]` [CHECKING] a v0.1.0 ([ROOTURL]/git_a#[..]) [RUNNING] `rustc [..] [ROOT]/foo/vendor/a/src/lib.rs [..]` [CHECKING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc [..] src/lib.rs [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn git_deterministic() { let git_dep = git::new("git_dep", |p| { p.file( "Cargo.toml", r#" [package] name = "git_dep" version = "0.0.1" edition = "2021" license = "MIT" description = "foo" documentation = "docs.rs/foo" authors = [] [[example]] name = "c" [[example]] name = "b" [[example]] name = "a" "#, ) .file("src/lib.rs", "") .file("examples/z.rs", "fn main() {}") .file("examples/y.rs", "fn main() {}") .file("examples/x.rs", "fn main() {}") .file("examples/c.rs", "fn main() {}") .file("examples/b.rs", "fn main() {}") .file("examples/a.rs", "fn main() {}") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [dependencies] git_dep = {{ git = '{}' }} "#, git_dep.url() ), ) .file("src/lib.rs", "") .build(); let output = p.cargo("vendor --respect-source-config").run(); let output = String::from_utf8(output.stdout).unwrap(); p.change_file(".cargo/config.toml", &output); let git_dep_manifest = p.read_file("vendor/git_dep/Cargo.toml"); assert_e2e().eq( git_dep_manifest, str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "git_dep" version = "0.0.1" authors = [] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "docs.rs/foo" readme = false license = "MIT" [lib] name = "git_dep" path = "src/lib.rs" [[example]] name = "a" path = "examples/a.rs" [[example]] name = "b" path = "examples/b.rs" [[example]] name = "c" path = "examples/c.rs" [[example]] name = "x" path = "examples/x.rs" [[example]] name = "y" path = "examples/y.rs" [[example]] name = "z" path = "examples/z.rs" "##]], ); } #[cargo_test] fn git_update_rev() { let (git_project, git_repo) = git::new_repo("git", |p| { p.file("Cargo.toml", &basic_manifest("a", "0.1.0")) .file("src/lib.rs", "") }); let url = git_project.url(); let ref_1 = "initial"; let ref_2 = "update"; git::tag(&git_repo, ref_1); git_project.change_file("src/lib.rs", "pub fn f() {}"); git::add(&git_repo); git::commit(&git_repo); git::tag(&git_repo, ref_2); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [dependencies] a = {{ git = '{url}', rev = '{ref_1}' }} "# ), ) .file("src/lib.rs", "") .build(); p.cargo("vendor --respect-source-config --versioned-dirs") .run(); let lib = p.read_file("vendor/a-0.1.0/src/lib.rs"); assert_e2e().eq(lib, ""); p.change_file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [dependencies] a = {{ git = '{url}', rev = '{ref_2}' }} "# ), ); p.cargo("vendor --respect-source-config --versioned-dirs") .run(); let lib = p.read_file("vendor/a-0.1.0/src/lib.rs"); assert_e2e().eq(lib, "pub fn f() {}"); } #[cargo_test] fn depend_on_vendor_dir_not_deleted() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] libc = "0.2.30" "#, ) .file("src/lib.rs", "") .build(); Package::new("libc", "0.2.30").publish(); p.cargo("vendor --respect-source-config").run(); assert!(p.root().join("vendor/libc").is_dir()); p.change_file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] libc = "0.2.30" [patch.crates-io] libc = { path = 'vendor/libc' } "#, ); p.cargo("vendor --respect-source-config").run(); assert!(p.root().join("vendor/libc").is_dir()); } #[cargo_test] fn ignore_hidden() { // Don't delete files starting with `.` Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("vendor --respect-source-config").run(); // Add a `.git` directory. let repo = git::init(&p.root().join("vendor")); git::add(&repo); git::commit(&repo); assert!(p.root().join("vendor/.git").exists()); // Vendor again, shouldn't change anything. p.cargo("vendor --respect-source-config").run(); // .git should not be removed. assert!(p.root().join("vendor/.git").exists()); // And just for good measure, make sure no files changed. let mut opts = git2::StatusOptions::new(); assert!( repo.statuses(Some(&mut opts)) .unwrap() .iter() .all(|status| status.status() == git2::Status::CURRENT) ); } #[cargo_test] fn config_instructions_works() { // Check that the config instructions work for all dependency kinds. registry::alt_init(); Package::new("dep", "0.1.0").publish(); Package::new("altdep", "0.1.0").alternative(true).publish(); let git_project = git::new("gitdep", |project| { project .file("Cargo.toml", &basic_lib_manifest("gitdep")) .file("src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2021" [dependencies] dep = "0.1" altdep = {{version="0.1", registry="alternative"}} gitdep = {{git='{}'}} "#, git_project.url() ), ) .file("src/lib.rs", "") .build(); let output = p.cargo("vendor --respect-source-config").run(); let output = String::from_utf8(output.stdout).unwrap(); p.change_file(".cargo/config.toml", &output); p.cargo("check -v") .with_stderr_data( str![[r#" [CHECKING] altdep v0.1.0 (registry `alternative`) [CHECKING] dep v0.1.0 [RUNNING] `rustc [..] [ROOT]/foo/vendor/altdep/src/lib.rs [..]` [RUNNING] `rustc [..] [ROOT]/foo/vendor/gitdep/src/lib.rs [..]` [RUNNING] `rustc [..] [ROOT]/foo/vendor/dep/src/lib.rs [..]` [CHECKING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc [..] src/lib.rs [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [CHECKING] gitdep v0.5.0 ([ROOTURL]/gitdep#[..]) "#]] .unordered(), ) .run(); } #[cargo_test] fn git_crlf_preservation() { // Check that newlines don't get changed when you vendor // (will only fail if your system is setup with core.autocrlf=true on windows) let input = "hello \nthere\nmy newline\nfriends"; let git_project = git::new("git", |p| { p.file("Cargo.toml", &basic_lib_manifest("a")) .file("src/lib.rs", input) }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [dependencies] a = {{ git = '{}' }} "#, git_project.url() ), ) .file("src/lib.rs", "") .build(); fs::write( paths::home().join(".gitconfig"), r#" [core] autocrlf = true "#, ) .unwrap(); p.cargo("vendor --respect-source-config").run(); let output = p.read_file("vendor/a/src/lib.rs"); assert_eq!(input, output); } #[cargo_test] #[cfg(unix)] fn vendor_preserves_permissions() { use std::os::unix::fs::MetadataExt; Package::new("bar", "1.0.0") .file_with_mode("example.sh", 0o755, "#!/bin/sh") .file("src/lib.rs", "") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("vendor --respect-source-config").run(); let umask = cargo::util::get_umask(); let metadata = fs::metadata(p.root().join("vendor/bar/src/lib.rs")).unwrap(); assert_eq!(metadata.mode() & 0o777, 0o644 & !umask); let metadata = fs::metadata(p.root().join("vendor/bar/example.sh")).unwrap(); assert_eq!(metadata.mode() & 0o777, 0o755 & !umask); } #[cargo_test] fn no_remote_dependency_no_vendor() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] bar = { path = "bar" } "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" "#, ) .file("bar/src/lib.rs", "") .build(); p.cargo("vendor") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version There is no dependency to vendor in this project. "#]]) .run(); assert!(!p.root().join("vendor").exists()); } #[cargo_test] fn vendor_crate_with_ws_inherit() { let git = git::new("ws", |p| { p.file( "Cargo.toml", r#" [workspace] members = ["bar"] [workspace.package] version = "0.1.0" "#, ) .file( "bar/Cargo.toml", r#" [package] name = "bar" version.workspace = true edition = "2021" "#, ) .file("bar/src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2021" [dependencies] bar = {{ git = '{}' }} "#, git.url() ), ) .file("src/lib.rs", "") .build(); p.cargo("vendor --respect-source-config").run(); p.change_file( ".cargo/config.toml", &format!( r#" [source."{}"] git = "{}" replace-with = "vendor" [source.vendor] directory = "vendor" "#, git.url(), git.url() ), ); p.cargo("check -v") .with_stderr_data(str![[r#" [CHECKING] bar v0.1.0 ([ROOTURL]/ws#[..]) [RUNNING] `rustc [..] [ROOT]/foo/vendor/bar/src/lib.rs [..]` [CHECKING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc [..] src/lib.rs [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn dont_delete_non_registry_sources_with_respect_source_config() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] log = "0.3.5" "#, ) .file("src/lib.rs", "") .build(); Package::new("log", "0.3.5").publish(); p.cargo("vendor --respect-source-config").run(); let lock = p.read_file("vendor/log/Cargo.toml"); assert!(lock.contains("version = \"0.3.5\"")); add_crates_io_vendor_config(&p); p.cargo("vendor --respect-source-config new-vendor-dir") .with_stderr_data(str![[r#" Vendoring log v0.3.5 ([ROOT]/foo/vendor/log) to new-vendor-dir/log To use vendored sources, add this to your .cargo/config.toml for this project: "#]]) .with_stdout_data(str![[r#" [source.crates-io] replace-with = "vendored-sources" [source.vendored-sources] directory = "new-vendor-dir" "#]]) .run(); } #[cargo_test] fn error_loading_which_lock() { // Tests an error message to make sure it is clear which // manifest/workspace caused the problem. In this particular case, it was // because the 2024 edition wants to know which rust version is in use. let p = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.1.0" "#, ) .file("src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" edition = "2024" "#, ) .file("b/src/lib.rs", "") .build(); p.cargo("vendor --respect-source-config -s b/Cargo.toml") .env("RUSTC", "does-not-exist") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to sync Caused by: failed to load lockfile for [ROOT]/foo/b Caused by: could not execute process `does-not-exist -vV` (never executed) Caused by: [NOT_FOUND] "#]]) .run(); } #[cargo_test] fn error_downloading() { // Tests the error message when downloading packages. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); Package::new("bar", "1.0.0").publish(); p.cargo("generate-lockfile").run(); std::fs::remove_file(cargo_test_support::paths::root().join("dl/bar/1.0.0/download")).unwrap(); p.cargo("vendor --respect-source-config") .with_status(101) .with_stderr_data(str![[r#" [DOWNLOADING] crates ... [ERROR] failed to sync Caused by: failed to download packages for [ROOT]/foo Caused by: failed to download from `[ROOTURL]/dl/bar/1.0.0/download` Caused by: [37] Could[..]t read a file:// file (Couldn't open file [ROOT]/dl/bar/1.0.0/download) "#]]) .run(); } cargo-0.91.0/tests/testsuite/verify_project.rs000064400000000000000000000037441046102023000176110ustar 00000000000000//! Tests for the `cargo verify-project` command. use crate::prelude::*; use cargo_test_support::{basic_bin_manifest, main_file, project, str}; #[cargo_test] fn cargo_verify_project_path_to_cargo_toml_relative() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("verify-project --manifest-path foo/Cargo.toml") .cwd(p.root().parent().unwrap()) .with_stdout_data(str![[r#" {"success":"true"} "#]]) .run(); } #[cargo_test] fn cargo_verify_project_path_to_cargo_toml_absolute() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("verify-project --manifest-path") .arg(p.root().join("Cargo.toml")) .cwd(p.root().parent().unwrap()) .with_stdout_data(str![[r#" {"success":"true"} "#]]) .run(); } #[cargo_test] fn cargo_verify_project_cwd() { let p = project() .file("Cargo.toml", &basic_bin_manifest("foo")) .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) .build(); p.cargo("verify-project") .with_stdout_data(str![[r#" {"success":"true"} "#]]) .run(); } #[cargo_test] fn cargo_verify_project_honours_unstable_features() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["test-dummy-unstable"] [package] name = "foo" version = "0.0.1" "#, ) .file("src/lib.rs", "") .build(); p.cargo("verify-project") .masquerade_as_nightly_cargo(&["test-dummy-unstable"]) .with_stdout_data(str![[r#" {"success":"true"} "#]]) .run(); p.cargo("verify-project") .with_status(1) .with_stdout_data(str![[r#" {"invalid":"failed to parse manifest at `[..]`"} "#]]) .run(); } cargo-0.91.0/tests/testsuite/version.rs000064400000000000000000000027021046102023000162350ustar 00000000000000//! Tests for displaying the cargo version. use crate::prelude::*; use crate::utils::cargo_process; use cargo_test_support::project; #[cargo_test] fn simple() { let p = project().build(); p.cargo("version") .with_stdout_data(&format!("cargo {}\n", cargo::version())) .run(); p.cargo("--version") .with_stdout_data(&format!("cargo {}\n", cargo::version())) .run(); p.cargo("-V") .with_stdout_data(&format!("cargo {}\n", cargo::version())) .run(); } #[cargo_test] fn version_works_without_rustc() { let p = project().build(); p.cargo("version").env("PATH", "").run(); } #[cargo_test] fn version_works_with_bad_config() { let p = project() .file(".cargo/config.toml", "this is not toml") .build(); p.cargo("version").run(); } #[cargo_test] fn version_works_with_bad_target_dir() { let p = project() .file( ".cargo/config.toml", r#" [build] target-dir = 4 "#, ) .build(); p.cargo("version").run(); } #[cargo_test] fn verbose() { // This is mainly to check that it doesn't explode. cargo_process("-vV") .with_stdout_data(format!( "\ cargo {} release: [..] commit-hash: [..] commit-date: [..] host: [HOST_TARGET] libgit2: [..] (sys:[..] [..]) libcurl: [..] (sys:[..] [..]) ... os: [..] ", cargo::version() )) .run(); } cargo-0.91.0/tests/testsuite/warn_on_failure.rs000064400000000000000000000065731046102023000177340ustar 00000000000000//! Tests for whether or not warnings are displayed for build scripts. use crate::prelude::*; use cargo_test_support::registry::Package; use cargo_test_support::{Project, project, str}; static WARNING1: &str = "Hello! I'm a warning. :)"; static WARNING2: &str = "And one more!"; fn make_lib(lib_src: &str) { Package::new("bar", "0.0.1") .file( "Cargo.toml", r#" [package] name = "bar" authors = [] version = "0.0.1" edition = "2015" build = "build.rs" "#, ) .file( "build.rs", &format!( r#" fn main() {{ use std::io::Write; println!("cargo::warning={{}}", "{}"); println!("hidden stdout"); write!(&mut ::std::io::stderr(), "hidden stderr"); println!("cargo::warning={{}}", "{}"); }} "#, WARNING1, WARNING2 ), ) .file("src/lib.rs", &format!("fn f() {{ {} }}", lib_src)) .publish(); } fn make_upstream(main_src: &str) -> Project { project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar = "*" "#, ) .file("src/main.rs", &format!("fn main() {{ {} }}", main_src)) .build() } #[cargo_test] fn no_warning_on_success() { make_lib(""); let upstream = make_upstream(""); upstream .cargo("build") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) [COMPILING] bar v0.0.1 [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn no_warning_on_bin_failure() { make_lib(""); let upstream = make_upstream("hi()"); upstream .cargo("build") .with_status(101) .with_stdout_does_not_contain("hidden stdout") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) [COMPILING] bar v0.0.1 [COMPILING] foo v0.0.1 ([ROOT]/foo) error[E0425]: cannot find function `hi` in this scope ... [ERROR] could not compile `foo` (bin "foo") due to 1 previous error "#]]) .run(); } #[cargo_test] fn warning_on_lib_failure() { make_lib("err()"); let upstream = make_upstream(""); upstream .cargo("build") .with_status(101) .with_stdout_does_not_contain("hidden stdout") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) [COMPILING] bar v0.0.1 error[E0425]: cannot find function `err` in this scope ... [WARNING] bar@0.0.1: Hello! I'm a warning. :) [WARNING] bar@0.0.1: And one more! [ERROR] could not compile `bar` (lib) due to 1 previous error "#]]) .run(); } cargo-0.91.0/tests/testsuite/warning_override.rs000064400000000000000000000142771046102023000201260ustar 00000000000000//! Tests for overriding warning behavior using `build.warnings` config option. use crate::prelude::*; use crate::utils::tools; use cargo_test_support::{Project, cargo_test, project, str}; fn make_project_with_rustc_warning() -> Project { project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.0.1" edition = "2021" "# ), ) .file("src/main.rs", "fn main() { let x = 3; }") .build() } #[cargo_test] fn rustc_caching_allow_first() { let p = make_project_with_rustc_warning(); p.cargo("check") .masquerade_as_nightly_cargo(&["warnings"]) .arg("-Zwarnings") .arg("--config") .arg("build.warnings='allow'") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .masquerade_as_nightly_cargo(&["warnings"]) .arg("-Zwarnings") .arg("--config") .arg("build.warnings='deny'") .with_stderr_data(str![[r#" [WARNING] unused variable: `x` --> src/main.rs:1:17 | 1 | fn main() { let x = 3; } | ^ [HELP] if this is intentional, prefix it with an underscore: `_x` | = [NOTE] `#[warn(unused_variables)]` on by default [WARNING] `foo` (bin "foo") generated 1 warning [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [ERROR] warnings are denied by `build.warnings` configuration "#]]) .with_status(101) .run(); } #[cargo_test] fn rustc_caching_deny_first() { let p = make_project_with_rustc_warning(); p.cargo("check") .masquerade_as_nightly_cargo(&["warnings"]) .arg("-Zwarnings") .arg("--config") .arg("build.warnings='deny'") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [WARNING] unused variable: `x` --> src/main.rs:1:17 | 1 | fn main() { let x = 3; } | ^ [HELP] if this is intentional, prefix it with an underscore: `_x` | = [NOTE] `#[warn(unused_variables)]` on by default [WARNING] `foo` (bin "foo") generated 1 warning [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [ERROR] warnings are denied by `build.warnings` configuration "#]]) .with_status(101) .run(); p.cargo("check") .masquerade_as_nightly_cargo(&["warnings"]) .arg("-Zwarnings") .arg("--config") .arg("build.warnings='allow'") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn config() { let p = make_project_with_rustc_warning(); p.cargo("check") .masquerade_as_nightly_cargo(&["warnings"]) .arg("-Zwarnings") .env("CARGO_BUILD_WARNINGS", "deny") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [WARNING] unused variable: `x` --> src/main.rs:1:17 | 1 | fn main() { let x = 3; } | ^ [HELP] if this is intentional, prefix it with an underscore: `_x` | = [NOTE] `#[warn(unused_variables)]` on by default [WARNING] `foo` (bin "foo") generated 1 warning [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [ERROR] warnings are denied by `build.warnings` configuration "#]]) .with_status(101) .run(); // CLI has precedence over env p.cargo("check") .masquerade_as_nightly_cargo(&["warnings"]) .arg("-Zwarnings") .arg("--config") .arg("build.warnings='warn'") .env("CARGO_BUILD_WARNINGS", "deny") .with_stderr_data(str![[r#" [WARNING] unused variable: `x` --> src/main.rs:1:17 | 1 | fn main() { let x = 3; } | ^ [HELP] if this is intentional, prefix it with an underscore: `_x` | = [NOTE] `#[warn(unused_variables)]` on by default [WARNING] `foo` (bin "foo") generated 1 warning [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn requires_nightly() { // build.warnings has no effect without -Zwarnings. let p = make_project_with_rustc_warning(); p.cargo("check") .arg("--config") .arg("build.warnings='deny'") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [WARNING] unused variable: `x` --> src/main.rs:1:17 | 1 | fn main() { let x = 3; } | ^ [HELP] if this is intentional, prefix it with an underscore: `_x` | = [NOTE] `#[warn(unused_variables)]` on by default [WARNING] `foo` (bin "foo") generated 1 warning [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn clippy() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" "#, ) .file("src/lib.rs", "use std::io;") // <-- unused import .build(); p.cargo("check") .masquerade_as_nightly_cargo(&["warnings"]) .arg("-Zwarnings") .arg("--config") .arg("build.warnings='deny'") .env("RUSTC_WORKSPACE_WRAPPER", tools::wrapped_clippy_driver()) .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [WARNING] unused import: `std::io` ... [WARNING] `foo` (lib) generated 1 warning (run `cargo clippy --fix --lib -p foo` to apply 1 suggestion) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [ERROR] warnings are denied by `build.warnings` configuration "#]]) .with_status(101) .run(); } #[cargo_test] fn unknown_value() { let p = make_project_with_rustc_warning(); p.cargo("check") .masquerade_as_nightly_cargo(&["warnings"]) .arg("-Zwarnings") .arg("--config") .arg("build.warnings='forbid'") .with_stderr_data(str![[r#" [ERROR] error in --config cli option: could not load config key `build.warnings` Caused by: unknown variant `forbid`, expected one of `warn`, `allow`, `deny` "#]]) .with_status(101) .run(); } cargo-0.91.0/tests/testsuite/weak_dep_features.rs000064400000000000000000000430031046102023000202240ustar 00000000000000//! Tests for weak-dep-features. use std::fmt::Write; use crate::prelude::*; use cargo_test_support::registry::{Dependency, Package, RegistryBuilder}; use cargo_test_support::str; use cargo_test_support::{project, publish}; use super::features2::switch_to_resolver_2; // Helper to create lib.rs files that check features. fn require(enabled_features: &[&str], disabled_features: &[&str]) -> String { let mut s = String::new(); writeln!(s, "#![allow(unexpected_cfgs)]").unwrap(); for feature in enabled_features { writeln!(s, "#[cfg(not(feature=\"{feature}\"))] compile_error!(\"expected feature {feature} to be enabled\");", feature=feature).unwrap(); } for feature in disabled_features { writeln!(s, "#[cfg(feature=\"{feature}\")] compile_error!(\"did not expect feature {feature} to be enabled\");", feature=feature).unwrap(); } s } #[cargo_test] fn simple() { Package::new("bar", "1.0.0") .feature("feat", &[]) .file("src/lib.rs", &require(&["feat"], &[])) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { version = "1.0", optional = true } [features] f1 = ["bar?/feat"] "#, ) .file("src/lib.rs", &require(&["f1"], &[])) .build(); // It's a bit unfortunate that this has to download `bar`, but avoiding // that is extremely difficult. p.cargo("check --features f1") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check --features f1,bar") .with_stderr_data(str![[r#" [CHECKING] bar v1.0.0 [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn deferred() { // A complex chain that requires deferring enabling the feature due to // another dependency getting enabled. Package::new("bar", "1.0.0") .feature("feat", &[]) .file("src/lib.rs", &require(&["feat"], &[])) .publish(); Package::new("dep", "1.0.0") .add_dep(Dependency::new("bar", "1.0").optional(true)) .feature("feat", &["bar?/feat"]) .publish(); Package::new("bar_activator", "1.0.0") .feature_dep("dep", "1.0", &["bar"]) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] dep = { version = "1.0", features = ["feat"] } bar_activator = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 3 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] dep v1.0.0 (registry `dummy-registry`) [DOWNLOADED] bar_activator v1.0.0 (registry `dummy-registry`) [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [CHECKING] bar v1.0.0 [CHECKING] dep v1.0.0 [CHECKING] bar_activator v1.0.0 [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn not_optional_dep() { // Attempt to use dep_name?/feat where dep_name is not optional. Package::new("dep", "1.0.0").feature("feat", &[]).publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] dep = "1.0" [features] feat = ["dep?/feat"] "#, ) .file("src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: feature `feat` includes `dep?/feat` with a `?`, but `dep` is not an optional dependency A non-optional dependency of the same name is defined; consider removing the `?` or changing the dependency to be optional "#]]) .run(); } #[cargo_test] fn optional_cli_syntax() { // --features bar?/feat Package::new("bar", "1.0.0") .feature("feat", &[]) .file("src/lib.rs", &require(&["feat"], &[])) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { version = "1.0", optional = true } "#, ) .file("src/lib.rs", "") .build(); // Does not build bar. p.cargo("check --features bar?/feat") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Builds bar. p.cargo("check --features bar?/feat,bar") .with_stderr_data(str![[r#" [CHECKING] bar v1.0.0 [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); eprintln!("check V2 resolver"); switch_to_resolver_2(&p); p.build_dir().rm_rf(); // Does not build bar. p.cargo("check --features bar?/feat") .with_stderr_data(str![[r#" [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Builds bar. p.cargo("check --features bar?/feat,bar") .with_stderr_data(str![[r#" [CHECKING] bar v1.0.0 [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn required_features() { // required-features doesn't allow ? Package::new("bar", "1.0.0").feature("feat", &[]).publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { version = "1.0", optional = true } [[bin]] name = "foo" required-features = ["bar?/feat"] "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [ERROR] invalid feature `bar?/feat` in required-features of target `foo`: optional dependency with `?` is not allowed in required-features "#]]) .run(); } #[cargo_test] fn weak_with_host_decouple() { // weak-dep-features with new resolver // // foo v0.1.0 // └── common v1.0.0 // └── bar v1.0.0 <-- does not have `feat` enabled // [build-dependencies] // └── bar_activator v1.0.0 // └── common v1.0.0 // └── bar v1.0.0 <-- does have `feat` enabled Package::new("bar", "1.0.0") .feature("feat", &[]) .file( "src/lib.rs", r#" pub fn feat() -> bool { cfg!(feature = "feat") } "#, ) .publish(); Package::new("common", "1.0.0") .add_dep(Dependency::new("bar", "1.0").optional(true)) .feature("feat", &["bar?/feat"]) .file( "src/lib.rs", r#" #[cfg(feature = "bar")] pub fn feat() -> bool { bar::feat() } #[cfg(not(feature = "bar"))] pub fn feat() -> bool { false } "#, ) .publish(); Package::new("bar_activator", "1.0.0") .feature_dep("common", "1.0", &["bar", "feat"]) .file( "src/lib.rs", r#" pub fn feat() -> bool { common::feat() } "#, ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" resolver = "2" [dependencies] common = { version = "1.0", features = ["feat"] } [build-dependencies] bar_activator = "1.0" "#, ) .file( "src/main.rs", r#" fn main() { assert!(!common::feat()); } "#, ) .file( "build.rs", r#" fn main() { assert!(bar_activator::feat()); } "#, ) .build(); p.cargo("run") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 3 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] common v1.0.0 (registry `dummy-registry`) [DOWNLOADED] bar_activator v1.0.0 (registry `dummy-registry`) [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [COMPILING] bar v1.0.0 [COMPILING] common v1.0.0 [COMPILING] bar_activator v1.0.0 [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/debug/foo[EXE]` "#]]) .run(); } #[cargo_test] fn weak_namespaced() { // Behavior with a dep: dependency. Package::new("bar", "1.0.0") .feature("feat", &[]) .file("src/lib.rs", &require(&["feat"], &[])) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { version = "1.0", optional = true } [features] f1 = ["bar?/feat"] f2 = ["dep:bar"] "#, ) .file("src/lib.rs", &require(&["f1"], &["f2", "bar"])) .build(); p.cargo("check --features f1") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("tree -f") .arg("{p} feats:{f}") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) feats: "#]]) .run(); p.cargo("tree --features f1 -f") .arg("{p} feats:{f}") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) feats:f1 "#]]) .run(); p.cargo("tree --features f1,f2 -f") .arg("{p} feats:{f}") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) feats:f1,f2 └── bar v1.0.0 feats:feat "#]]) .run(); // "bar" remains not-a-feature p.change_file("src/lib.rs", &require(&["f1", "f2"], &["bar"])); p.cargo("check --features f1,f2") .with_stderr_data(str![[r#" [CHECKING] bar v1.0.0 [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn tree() { Package::new("bar", "1.0.0") .feature("feat", &[]) .file("src/lib.rs", &require(&["feat"], &[])) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = { version = "1.0", optional = true } [features] f1 = ["bar?/feat"] "#, ) .file("src/lib.rs", &require(&["f1"], &[])) .build(); p.cargo("tree --features f1") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) "#]]) .run(); p.cargo("tree --features f1,bar") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── bar v1.0.0 "#]]) .run(); p.cargo("tree --features f1,bar -e features") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── bar feature "default" └── bar v1.0.0 "#]]) .run(); p.cargo("tree --features f1,bar -e features -i bar") .with_stdout_data(str![[r#" bar v1.0.0 ├── bar feature "default" │ └── foo v0.1.0 ([ROOT]/foo) │ ├── foo feature "bar" (command-line) │ ├── foo feature "default" (command-line) │ └── foo feature "f1" (command-line) └── bar feature "feat" └── foo feature "f1" (command-line) "#]]) .run(); p.cargo("tree -e features --features bar?/feat") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) "#]]) .run(); // This is a little strange in that it produces no output. // Maybe `cargo tree` should print a note about why? p.cargo("tree -e features -i bar --features bar?/feat") .with_stdout_data("") .run(); p.cargo("tree -e features -i bar --features bar?/feat,bar") .with_stdout_data(str![[r#" bar v1.0.0 ├── bar feature "default" │ └── foo v0.1.0 ([ROOT]/foo) │ ├── foo feature "bar" (command-line) │ └── foo feature "default" (command-line) └── bar feature "feat" (command-line) "#]]) .run(); } #[cargo_test] fn publish() { let registry = RegistryBuilder::new().http_api().http_index().build(); // Publish behavior with /? syntax. Package::new("bar", "1.0.0").feature("feat", &[]).publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" description = "foo" license = "MIT" homepage = "https://example.com/" [dependencies] bar = { version = "1.0", optional = true } [features] feat1 = [] feat2 = ["bar?/feat"] "#, ) .file("src/lib.rs", "") .build(); p.cargo("publish") .replace_crates_io(registry.index_url()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [PACKAGING] foo v0.1.0 ([ROOT]/foo) [UPDATING] crates.io index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.1.0 ([ROOT]/foo) [COMPILING] foo v0.1.0 ([ROOT]/foo/target/package/foo-0.1.0) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] foo v0.1.0 ([ROOT]/foo) [UPLOADED] foo v0.1.0 to registry `crates-io` [NOTE] waiting for foo v0.1.0 to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.1.0 at registry `crates-io` "#]]) .run(); publish::validate_upload_with_contents( r#" { "authors": [], "badges": {}, "categories": [], "deps": [ { "default_features": true, "features": [], "kind": "normal", "name": "bar", "optional": true, "target": null, "version_req": "^1.0" } ], "description": "foo", "documentation": null, "features": { "feat1": [], "feat2": ["bar?/feat"] }, "homepage": "https://example.com/", "keywords": [], "license": "MIT", "license_file": null, "links": null, "name": "foo", "readme": null, "readme_file": null, "repository": null, "rust_version": null, "vers": "0.1.0" } "#, "foo-0.1.0.crate", &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs", "Cargo.lock"], [( "Cargo.toml", str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "foo" version = "0.1.0" build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" homepage = "https://example.com/" readme = false license = "MIT" [features] feat1 = [] feat2 = ["bar?/feat"] [lib] name = "foo" path = "src/lib.rs" [dependencies.bar] version = "1.0" optional = true "##]], )], ); } cargo-0.91.0/tests/testsuite/workspaces.rs000064400000000000000000002135111046102023000167330ustar 00000000000000//! Tests for workspaces. use std::env; use std::fs; use crate::prelude::*; use cargo_test_support::registry::Package; use cargo_test_support::str; use cargo_test_support::{basic_lib_manifest, basic_manifest, git, project, sleep_ms}; #[cargo_test] fn simple_explicit() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [workspace] members = ["bar"] "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] workspace = ".." "#, ) .file("bar/src/main.rs", "fn main() {}"); let p = p.build(); p.cargo("build").run(); assert!(p.bin("foo").is_file()); assert!(!p.bin("bar").is_file()); p.cargo("build").cwd("bar").run(); assert!(p.bin("foo").is_file()); assert!(p.bin("bar").is_file()); assert!(p.root().join("Cargo.lock").is_file()); assert!(!p.root().join("bar/Cargo.lock").is_file()); } #[cargo_test] fn simple_explicit_default_members() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [workspace] members = ["bar"] default-members = ["bar"] "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] workspace = ".." "#, ) .file("bar/src/main.rs", "fn main() {}"); let p = p.build(); p.cargo("build").run(); assert!(p.bin("bar").is_file()); assert!(!p.bin("foo").is_file()); } #[cargo_test] fn non_virtual_default_members_build_other_member() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [workspace] members = [".", "bar", "baz"] default-members = ["baz"] "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "pub fn baz() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [CHECKING] baz v0.1.0 ([ROOT]/foo/baz) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check --manifest-path bar/Cargo.toml") .with_stderr_data(str![[r#" [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn non_virtual_default_members_build_root_project() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [workspace] members = ["bar"] default-members = ["."] "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .build(); p.cargo("check") .with_stderr_data(str![[r#" [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn inferred_root() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [workspace] members = ["bar"] "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/main.rs", "fn main() {}"); let p = p.build(); p.cargo("build").run(); assert!(p.bin("foo").is_file()); assert!(!p.bin("bar").is_file()); p.cargo("build").cwd("bar").run(); assert!(p.bin("foo").is_file()); assert!(p.bin("bar").is_file()); assert!(p.root().join("Cargo.lock").is_file()); assert!(!p.root().join("bar/Cargo.lock").is_file()); } #[cargo_test] fn inferred_path_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [dependencies] bar = { path = "bar" } [workspace] "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/main.rs", "fn main() {}") .file("bar/src/lib.rs", ""); let p = p.build(); p.cargo("build").run(); assert!(p.bin("foo").is_file()); assert!(!p.bin("bar").is_file()); p.cargo("build").cwd("bar").run(); assert!(p.bin("foo").is_file()); assert!(p.bin("bar").is_file()); assert!(p.root().join("Cargo.lock").is_file()); assert!(!p.root().join("bar/Cargo.lock").is_file()); } #[cargo_test] fn transitive_path_dep() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [dependencies] bar = { path = "bar" } [workspace] "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] [dependencies] baz = { path = "../baz" } "#, ) .file("bar/src/main.rs", "fn main() {}") .file("bar/src/lib.rs", "") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/main.rs", "fn main() {}") .file("baz/src/lib.rs", ""); let p = p.build(); p.cargo("build").run(); assert!(p.bin("foo").is_file()); assert!(!p.bin("bar").is_file()); assert!(!p.bin("baz").is_file()); p.cargo("build").cwd("bar").run(); assert!(p.bin("foo").is_file()); assert!(p.bin("bar").is_file()); assert!(!p.bin("baz").is_file()); p.cargo("build").cwd("baz").run(); assert!(p.bin("foo").is_file()); assert!(p.bin("bar").is_file()); assert!(p.bin("baz").is_file()); assert!(p.root().join("Cargo.lock").is_file()); assert!(!p.root().join("bar/Cargo.lock").is_file()); assert!(!p.root().join("baz/Cargo.lock").is_file()); } #[cargo_test] fn parent_pointer_works() { let p = project() .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [dependencies] bar = { path = "../bar" } [workspace] "#, ) .file("foo/src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] workspace = "../foo" "#, ) .file("bar/src/main.rs", "fn main() {}") .file("bar/src/lib.rs", ""); let p = p.build(); p.cargo("build").cwd("foo").run(); p.cargo("build").cwd("bar").run(); assert!(p.root().join("foo/Cargo.lock").is_file()); assert!(!p.root().join("bar/Cargo.lock").is_file()); } #[cargo_test] fn same_names_in_workspace() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [workspace] members = ["bar"] "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] workspace = ".." "#, ) .file("bar/src/main.rs", "fn main() {}"); let p = p.build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] two packages named `foo` in this workspace: - [ROOT]/foo/bar/Cargo.toml - [ROOT]/foo/Cargo.toml "#]]) .run(); } #[cargo_test] fn parent_doesnt_point_to_child() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [workspace] "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/main.rs", "fn main() {}"); let p = p.build(); p.cargo("check") .cwd("bar") .with_status(101) .with_stderr_data(str![[r#" [ERROR] current package believes it's in a workspace when it's not: current: [ROOT]/foo/bar/Cargo.toml workspace: [ROOT]/foo/Cargo.toml this may be fixable by ensuring that this crate is depended on by the workspace root: [ROOT]/foo/Cargo.toml Alternatively, to keep it out of the workspace, add the package to the `workspace.exclude` array, or add an empty `[workspace]` table to the package's manifest. "#]]) .run(); } #[cargo_test] fn invalid_parent_pointer() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] workspace = "foo" "#, ) .file("src/main.rs", "fn main() {}"); let p = p.build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to read `[ROOT]/foo/foo/Cargo.toml` Caused by: [NOT_FOUND] "#]]) .run(); } #[cargo_test] fn invalid_members() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [workspace] members = ["foo"] "#, ) .file("src/main.rs", "fn main() {}"); let p = p.build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to load manifest for workspace member `[ROOT]/foo/foo` referenced by workspace at `[ROOT]/foo/Cargo.toml` Caused by: failed to read `[ROOT]/foo/foo/Cargo.toml` Caused by: [NOT_FOUND] "#]]) .run(); } #[cargo_test] fn bare_workspace_ok() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [workspace] "#, ) .file("src/main.rs", "fn main() {}"); let p = p.build(); p.cargo("check").run(); } #[cargo_test] fn two_roots() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [workspace] members = ["bar"] "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] [workspace] members = [".."] "#, ) .file("bar/src/main.rs", "fn main() {}"); let p = p.build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] multiple workspace roots found in the same workspace: [ROOT]/foo/bar [ROOT]/foo "#]]) .run(); } #[cargo_test] fn workspace_isnt_root() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] workspace = "bar" "#, ) .file("src/main.rs", "fn main() {}") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/main.rs", "fn main() {}"); let p = p.build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] root of a workspace inferred but wasn't a root: [ROOT]/foo/bar/Cargo.toml "#]]) .run(); } #[cargo_test] fn dangling_member() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [workspace] members = ["bar"] "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] workspace = "../baz" "#, ) .file("bar/src/main.rs", "fn main() {}") .file( "baz/Cargo.toml", r#" [package] name = "baz" version = "0.1.0" edition = "2015" authors = [] workspace = "../baz" "#, ) .file("baz/src/main.rs", "fn main() {}"); let p = p.build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package `[ROOT]/foo/bar/Cargo.toml` is a member of the wrong workspace expected: [ROOT]/foo/Cargo.toml actual: [ROOT]/foo/baz/Cargo.toml "#]]) .run(); } #[cargo_test] fn cycle() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] workspace = "bar" "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] workspace = ".." "#, ) .file("bar/src/main.rs", "fn main() {}"); let p = p.build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] root of a workspace inferred but wasn't a root: [ROOT]/foo/bar/Cargo.toml "#]]) .run(); } #[cargo_test] fn share_dependencies() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [dependencies] dep1 = "0.1" [workspace] members = ["bar"] "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] [dependencies] dep1 = "< 0.1.5" "#, ) .file("bar/src/main.rs", "fn main() {}"); let p = p.build(); Package::new("dep1", "0.1.3").publish(); Package::new("dep1", "0.1.8").publish(); p.cargo("check") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [ADDING] dep1 v0.1.3 (available: v0.1.8) [DOWNLOADING] crates ... [DOWNLOADED] dep1 v0.1.3 (registry `dummy-registry`) [CHECKING] dep1 v0.1.3 [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn fetch_fetches_all() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [workspace] members = ["bar"] "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] [dependencies] dep1 = "*" "#, ) .file("bar/src/main.rs", "fn main() {}"); let p = p.build(); Package::new("dep1", "0.1.3").publish(); p.cargo("fetch") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] dep1 v0.1.3 (registry `dummy-registry`) "#]]) .run(); } #[cargo_test] fn lock_works_for_everyone() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [dependencies] dep2 = "0.1" [workspace] members = ["bar"] "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] [dependencies] dep1 = "0.1" "#, ) .file("bar/src/main.rs", "fn main() {}"); let p = p.build(); Package::new("dep1", "0.1.0").publish(); Package::new("dep2", "0.1.0").publish(); p.cargo("generate-lockfile") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions "#]]) .run(); Package::new("dep1", "0.1.1").publish(); Package::new("dep2", "0.1.1").publish(); p.cargo("check") .with_stderr_data(str![[r#" [DOWNLOADING] crates ... [DOWNLOADED] dep2 v0.1.0 (registry `dummy-registry`) [CHECKING] dep2 v0.1.0 [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("check") .cwd("bar") .with_stderr_data(str![[r#" [DOWNLOADING] crates ... [DOWNLOADED] dep1 v0.1.0 (registry `dummy-registry`) [CHECKING] dep1 v0.1.0 [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn virtual_works() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/main.rs", "fn main() {}"); let p = p.build(); p.cargo("build").cwd("bar").run(); assert!(p.root().join("Cargo.lock").is_file()); assert!(p.bin("bar").is_file()); assert!(!p.root().join("bar/Cargo.lock").is_file()); } #[cargo_test] fn explicit_package_argument_works_with_virtual_manifest() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/main.rs", "fn main() {}"); let p = p.build(); p.cargo("build --package bar").run(); assert!(p.root().join("Cargo.lock").is_file()); assert!(p.bin("bar").is_file()); assert!(!p.root().join("bar/Cargo.lock").is_file()); } #[cargo_test] fn virtual_misconfigure() { let p = project() .file( "Cargo.toml", r#" [workspace] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/main.rs", "fn main() {}"); let p = p.build(); p.cargo("check") .cwd("bar") .with_status(101) .with_stderr_data(str![[r#" [ERROR] current package believes it's in a workspace when it's not: current: [ROOT]/foo/bar/Cargo.toml workspace: [ROOT]/foo/Cargo.toml this may be fixable by adding `bar` to the `workspace.members` array of the manifest located at: [ROOT]/foo/Cargo.toml Alternatively, to keep it out of the workspace, add the package to the `workspace.exclude` array, or add an empty `[workspace]` table to the package's manifest. "#]]) .run(); } #[cargo_test] fn virtual_build_all_implied() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/main.rs", "fn main() {}"); let p = p.build(); p.cargo("check").run(); } #[cargo_test] fn virtual_default_members() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] default-members = ["bar"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("bar/src/main.rs", "fn main() {}") .file("baz/src/main.rs", "fn main() {}"); let p = p.build(); p.cargo("build").run(); assert!(p.bin("bar").is_file()); assert!(!p.bin("baz").is_file()); } #[cargo_test] fn virtual_default_member_is_not_a_member() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar"] default-members = ["something-else"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/main.rs", "fn main() {}"); let p = p.build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package `[ROOT]/foo/something-else` is listed in default-members but is not a member for workspace at `[ROOT]/foo/Cargo.toml`. "#]]) .run(); } #[cargo_test] fn virtual_default_members_build_other_member() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["bar", "baz"] default-members = ["baz"] "#, ) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", "pub fn bar() {}") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", "pub fn baz() {}") .build(); p.cargo("check --manifest-path bar/Cargo.toml") .with_stderr_data(str![[r#" [CHECKING] bar v0.1.0 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn virtual_build_no_members() { let p = project().file( "Cargo.toml", r#" [workspace] "#, ); let p = p.build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] manifest path `[ROOT]/foo` contains no package: The manifest is virtual, and the workspace has no members. "#]]) .run(); } #[cargo_test] fn include_virtual() { let p = project() .file( "Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] [workspace] members = ["bar"] "#, ) .file("src/main.rs", "") .file( "bar/Cargo.toml", r#" [workspace] "#, ); let p = p.build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] multiple workspace roots found in the same workspace: [ROOT]/foo/bar [ROOT]/foo "#]]) .run(); } #[cargo_test] fn members_include_path_deps() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [workspace] members = ["p1"] [dependencies] p3 = { path = "p3" } "#, ) .file("src/lib.rs", "") .file( "p1/Cargo.toml", r#" [package] name = "p1" version = "0.1.0" edition = "2015" authors = [] [dependencies] p2 = { path = "../p2" } "#, ) .file("p1/src/lib.rs", "") .file("p2/Cargo.toml", &basic_manifest("p2", "0.1.0")) .file("p2/src/lib.rs", "") .file("p3/Cargo.toml", &basic_manifest("p3", "0.1.0")) .file("p3/src/lib.rs", ""); let p = p.build(); p.cargo("check").cwd("p1").run(); p.cargo("check").cwd("p2").run(); p.cargo("check").cwd("p3").run(); p.cargo("check").run(); assert!(p.root().join("target").is_dir()); assert!(!p.root().join("p1/target").is_dir()); assert!(!p.root().join("p2/target").is_dir()); assert!(!p.root().join("p3/target").is_dir()); } #[cargo_test] fn new_creates_members_list() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [workspace] "#, ) .file("src/lib.rs", ""); let p = p.build(); p.cargo("new --lib bar").with_stderr_data(str![[r#" [CREATING] library `bar` package [ADDING] `bar` as member of workspace at `[ROOT]/foo` [NOTE] see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html "#]]).run(); } #[cargo_test] fn new_warning_with_corrupt_ws() { let p = project().file("Cargo.toml", "asdf").build(); p.cargo("new bar").with_stderr_data(str![[r#" [CREATING] binary (application) `bar` package [ERROR] key with no value, expected `=` --> Cargo.toml:1:5 | 1 | asdf | ^ | [WARNING] compiling this new package may not work due to invalid workspace configuration [NOTE] see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html "#]]).run(); } #[cargo_test] fn lock_doesnt_change_depending_on_crate() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [workspace] members = ['baz'] [dependencies] foo = "*" "#, ) .file("src/lib.rs", "") .file( "baz/Cargo.toml", r#" [package] name = "baz" version = "0.1.0" edition = "2015" authors = [] [dependencies] bar = "*" "#, ) .file("baz/src/lib.rs", ""); let p = p.build(); Package::new("foo", "1.0.0").publish(); Package::new("bar", "1.0.0").publish(); p.cargo("check").run(); let lockfile = p.read_lockfile(); p.cargo("check").cwd("baz").run(); let lockfile2 = p.read_lockfile(); assert_eq!(lockfile, lockfile2); } #[cargo_test] fn rebuild_please() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ['lib', 'bin'] "#, ) .file("lib/Cargo.toml", &basic_manifest("lib", "0.1.0")) .file( "lib/src/lib.rs", r#" pub fn foo() -> u32 { 0 } "#, ) .file( "bin/Cargo.toml", r#" [package] name = "bin" version = "0.1.0" edition = "2015" [dependencies] lib = { path = "../lib" } "#, ) .file( "bin/src/main.rs", r#" extern crate lib; fn main() { assert_eq!(lib::foo(), 0); } "#, ); let p = p.build(); p.cargo("run").cwd("bin").run(); sleep_ms(1000); p.change_file("lib/src/lib.rs", "pub fn foo() -> u32 { 1 }"); p.cargo("build").cwd("lib").run(); p.cargo("run") .cwd("bin") .with_status(101) .with_stderr_data(str![[r#" ... assertion[..] ... "#]]) .run(); } #[cargo_test] fn workspace_in_git() { let git_project = git::new("dep1", |project| { project .file( "Cargo.toml", r#" [workspace] members = ["foo"] "#, ) .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("foo/src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "lib" version = "0.1.0" edition = "2015" [dependencies.foo] git = '{}' "#, git_project.url() ), ) .file( "src/lib.rs", r#" pub fn foo() -> u32 { 0 } "#, ); let p = p.build(); p.cargo("check").run(); } #[cargo_test] fn lockfile_can_specify_nonexistent_members() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a"] "#, ) .file("a/Cargo.toml", &basic_manifest("a", "0.1.0")) .file("a/src/main.rs", "fn main() {}") .file( "Cargo.lock", r#" [[package]] name = "a" version = "0.1.0" [[package]] name = "b" version = "0.1.0" "#, ); let p = p.build(); p.cargo("check").cwd("a").run(); } #[cargo_test] fn you_cannot_generate_lockfile_for_empty_workspaces() { let p = project() .file( "Cargo.toml", r#" [workspace] "#, ) .file("bar/Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("bar/src/main.rs", "fn main() {}"); let p = p.build(); p.cargo("update") .with_status(101) .with_stderr_data(str![[r#" [ERROR] you can't generate a lockfile for an empty workspace. "#]]) .run(); } #[cargo_test] fn workspace_with_transitive_dev_deps() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.5.0" edition = "2015" authors = ["mbrubeck@example.com"] [dependencies.bar] path = "bar" [workspace] "#, ) .file("src/main.rs", r#"fn main() {}"#) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.5.0" edition = "2015" authors = ["mbrubeck@example.com"] [dev-dependencies.baz] path = "../baz" "#, ) .file( "bar/src/lib.rs", r#" pub fn init() {} #[cfg(test)] #[test] fn test() { extern crate baz; baz::do_stuff(); } "#, ) .file("baz/Cargo.toml", &basic_manifest("baz", "0.5.0")) .file("baz/src/lib.rs", r#"pub fn do_stuff() {}"#); let p = p.build(); p.cargo("test -p bar").run(); } #[cargo_test] fn error_if_parent_cargo_toml_is_invalid() { let p = project() .file("Cargo.toml", "Totally not a TOML file") .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/main.rs", "fn main() {}"); let p = p.build(); p.cargo("check") .cwd("bar") .with_status(101) .with_stderr_data(str![[r#" [ERROR] key with no value, expected `=` --> ../Cargo.toml:1:9 | 1 | Totally not a TOML file | ^ | "#]]) .run(); } #[cargo_test] fn relative_path_for_member_works() { let p = project() .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [workspace] members = ["../bar"] "#, ) .file("foo/src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] workspace = "../foo" "#, ) .file("bar/src/main.rs", "fn main() {}"); let p = p.build(); p.cargo("check").cwd("foo").run(); p.cargo("check").cwd("bar").run(); } #[cargo_test] fn relative_path_for_root_works() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [workspace] [dependencies] subproj = { path = "./subproj" } "#, ) .file("src/main.rs", "fn main() {}") .file("subproj/Cargo.toml", &basic_manifest("subproj", "0.1.0")) .file("subproj/src/main.rs", "fn main() {}"); let p = p.build(); p.cargo("check --manifest-path ./Cargo.toml").run(); p.cargo("check --manifest-path ../Cargo.toml") .cwd("subproj") .run(); } #[cargo_test] fn path_dep_outside_workspace_is_not_member() { let p = project() .no_manifest() .file( "ws/Cargo.toml", r#" [package] name = "ws" version = "0.1.0" edition = "2015" authors = [] [dependencies] foo = { path = "../foo" } [workspace] "#, ) .file("ws/src/lib.rs", "extern crate foo;") .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("foo/src/lib.rs", ""); let p = p.build(); p.cargo("check").cwd("ws").run(); } #[cargo_test] fn test_in_and_out_of_workspace() { let p = project() .no_manifest() .file( "ws/Cargo.toml", r#" [package] name = "ws" version = "0.1.0" edition = "2015" authors = [] [dependencies] foo = { path = "../foo" } [workspace] members = [ "../bar" ] "#, ) .file("ws/src/lib.rs", "extern crate foo; pub fn f() { foo::f() }") .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [dependencies] bar = { path = "../bar" } "#, ) .file( "foo/src/lib.rs", "extern crate bar; pub fn f() { bar::f() }", ) .file( "bar/Cargo.toml", r#" [package] workspace = "../ws" name = "bar" version = "0.1.0" edition = "2015" authors = [] "#, ) .file("bar/src/lib.rs", "pub fn f() { }"); let p = p.build(); p.cargo("check").cwd("ws").run(); assert!(p.root().join("ws/Cargo.lock").is_file()); assert!(p.root().join("ws/target").is_dir()); assert!(!p.root().join("foo/Cargo.lock").is_file()); assert!(!p.root().join("foo/target").is_dir()); assert!(!p.root().join("bar/Cargo.lock").is_file()); assert!(!p.root().join("bar/target").is_dir()); p.cargo("check").cwd("foo").run(); assert!(p.root().join("foo/Cargo.lock").is_file()); assert!(p.root().join("foo/target").is_dir()); assert!(!p.root().join("bar/Cargo.lock").is_file()); assert!(!p.root().join("bar/target").is_dir()); } #[cargo_test] fn test_path_dependency_under_member() { let p = project() .file( "ws/Cargo.toml", r#" [package] name = "ws" version = "0.1.0" edition = "2015" authors = [] [dependencies] foo = { path = "../foo" } [workspace] "#, ) .file("ws/src/lib.rs", "extern crate foo; pub fn f() { foo::f() }") .file( "foo/Cargo.toml", r#" [package] workspace = "../ws" name = "foo" version = "0.1.0" edition = "2015" authors = [] [dependencies] bar = { path = "./bar" } "#, ) .file( "foo/src/lib.rs", "extern crate bar; pub fn f() { bar::f() }", ) .file("foo/bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("foo/bar/src/lib.rs", "pub fn f() { }"); let p = p.build(); p.cargo("check").cwd("ws").run(); assert!(!p.root().join("foo/bar/Cargo.lock").is_file()); assert!(!p.root().join("foo/bar/target").is_dir()); p.cargo("check").cwd("foo/bar").run(); assert!(!p.root().join("foo/bar/Cargo.lock").is_file()); assert!(!p.root().join("foo/bar/target").is_dir()); } #[cargo_test] fn excluded_simple() { let p = project() .file( "Cargo.toml", r#" [package] name = "ws" version = "0.1.0" edition = "2015" authors = [] [workspace] exclude = ["foo"] "#, ) .file("src/lib.rs", "") .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("foo/src/lib.rs", ""); let p = p.build(); p.cargo("check").run(); assert!(p.root().join("target").is_dir()); p.cargo("check").cwd("foo").run(); assert!(p.root().join("foo/target").is_dir()); } #[cargo_test] fn exclude_members_preferred() { let p = project() .file( "Cargo.toml", r#" [package] name = "ws" version = "0.1.0" edition = "2015" authors = [] [workspace] members = ["foo/bar"] exclude = ["foo"] "#, ) .file("src/lib.rs", "") .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("foo/src/lib.rs", "") .file("foo/bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("foo/bar/src/lib.rs", ""); let p = p.build(); p.cargo("check").run(); assert!(p.root().join("target").is_dir()); p.cargo("check").cwd("foo").run(); assert!(p.root().join("foo/target").is_dir()); p.cargo("check").cwd("foo/bar").run(); assert!(!p.root().join("foo/bar/target").is_dir()); } #[cargo_test] fn exclude_but_also_depend() { let p = project() .file( "Cargo.toml", r#" [package] name = "ws" version = "0.1.0" edition = "2015" authors = [] [dependencies] bar = { path = "foo/bar" } [workspace] exclude = ["foo"] "#, ) .file("src/lib.rs", "") .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("foo/src/lib.rs", "") .file("foo/bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("foo/bar/src/lib.rs", ""); let p = p.build(); p.cargo("check").run(); assert!(p.root().join("target").is_dir()); p.cargo("check").cwd("foo").run(); assert!(p.root().join("foo/target").is_dir()); p.cargo("check").cwd("foo/bar").run(); assert!(p.root().join("foo/bar/target").is_dir()); } #[cargo_test] fn excluded_default_members_still_must_be_members() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo"] default-members = ["foo", "bar"] exclude = ["bar"] "#, ) .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("foo/src/lib.rs", "") .file("bar/something.txt", ""); let p = p.build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package `[ROOT]/foo/bar` is listed in default-members but is not a member for workspace at `[ROOT]/foo/Cargo.toml`. "#]]) .run(); } #[cargo_test] fn excluded_default_members_crate_glob() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo", "bar/*"] default-members = ["bar/*"] exclude = ["bar/quux"] "#, ) .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("foo/src/main.rs", "fn main() {}") .file("bar/baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("bar/baz/src/main.rs", "fn main() {}") .file("bar/quux/Cargo.toml", &basic_manifest("quux", "0.1.0")) .file("bar/quux/src/main.rs", "fn main() {}"); let p = p.build(); p.cargo("build").run(); assert!(p.root().join("target").is_dir()); assert!(!p.bin("foo").is_file()); assert!(p.bin("baz").is_file()); assert!(!p.bin("quux").exists()); p.cargo("build --workspace").run(); assert!(p.root().join("target").is_dir()); assert!(p.bin("foo").is_file()); assert!(!p.bin("quux").exists()); p.cargo("build").cwd("bar/quux").run(); assert!(p.root().join("bar/quux/target").is_dir()); } #[cargo_test] fn excluded_default_members_not_crate_glob() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo", "bar/*"] default-members = ["bar/*"] exclude = ["bar/docs"] "#, ) .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("foo/src/main.rs", "fn main() {}") .file("bar/baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("bar/baz/src/main.rs", "fn main() {}") .file("bar/docs/readme.txt", "This folder is not a crate!"); let p = p.build(); p.cargo("build").run(); assert!(!p.bin("foo").is_file()); assert!(p.bin("baz").is_file()); p.cargo("build --workspace").run(); assert!(p.bin("foo").is_file()); } #[cargo_test] fn glob_syntax() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [workspace] members = ["crates/*"] exclude = ["crates/qux"] "#, ) .file("src/main.rs", "fn main() {}") .file( "crates/bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] workspace = "../.." "#, ) .file("crates/bar/src/main.rs", "fn main() {}") .file( "crates/baz/Cargo.toml", r#" [package] name = "baz" version = "0.1.0" edition = "2015" authors = [] workspace = "../.." "#, ) .file("crates/baz/src/main.rs", "fn main() {}") .file( "crates/qux/Cargo.toml", r#" [package] name = "qux" version = "0.1.0" edition = "2015" authors = [] "#, ) .file("crates/qux/src/main.rs", "fn main() {}"); let p = p.build(); p.cargo("build").run(); assert!(p.bin("foo").is_file()); assert!(!p.bin("bar").is_file()); assert!(!p.bin("baz").is_file()); p.cargo("build").cwd("crates/bar").run(); assert!(p.bin("foo").is_file()); assert!(p.bin("bar").is_file()); p.cargo("build").cwd("crates/baz").run(); assert!(p.bin("foo").is_file()); assert!(p.bin("baz").is_file()); p.cargo("build").cwd("crates/qux").run(); assert!(!p.bin("qux").is_file()); assert!(p.root().join("Cargo.lock").is_file()); assert!(!p.root().join("crates/bar/Cargo.lock").is_file()); assert!(!p.root().join("crates/baz/Cargo.lock").is_file()); assert!(p.root().join("crates/qux/Cargo.lock").is_file()); } /*FIXME: This fails because of how workspace.exclude and workspace.members are working. #[cargo_test] fn glob_syntax_2() { let p = project() .file("Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [workspace] members = ["crates/b*"] exclude = ["crates/q*"] "#) .file("src/main.rs", "fn main() {}") .file("crates/bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] workspace = "../.." "#) .file("crates/bar/src/main.rs", "fn main() {}") .file("crates/baz/Cargo.toml", r#" [package] name = "baz" version = "0.1.0" edition = "2015" authors = [] workspace = "../.." "#) .file("crates/baz/src/main.rs", "fn main() {}") .file("crates/qux/Cargo.toml", r#" [package] name = "qux" version = "0.1.0" edition = "2015" authors = [] "#) .file("crates/qux/src/main.rs", "fn main() {}"); p.build(); p.cargo("build").run(); assert!(p.bin("foo").is_file()); assert!(!p.bin("bar").is_file()); assert!(!p.bin("baz").is_file()); p.cargo("build").cwd("crates/bar").run(); assert!(p.bin("foo").is_file()); assert!(p.bin("bar").is_file()); p.cargo("build").cwd("crates/baz").run(); assert!(p.bin("foo").is_file()); assert!(p.bin("baz").is_file()); p.cargo("build").cwd("crates/qux").run(); assert!(!p.bin("qux").is_file()); assert!(p.root().join("Cargo.lock").is_file()); assert!(!p.root().join("crates/bar/Cargo.lock").is_file()); assert!(!p.root().join("crates/baz/Cargo.lock").is_file()); assert!(p.root().join("crates/qux/Cargo.lock").is_file()); } */ #[cargo_test] fn glob_syntax_invalid_members() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [workspace] members = ["crates/*"] "#, ) .file("src/main.rs", "fn main() {}") .file("crates/bar/src/main.rs", "fn main() {}"); let p = p.build(); p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to load manifest for workspace member `[ROOT]/foo/crates/bar` referenced via `crates/*` by workspace at `[ROOT]/foo/Cargo.toml` Caused by: failed to read `[ROOT]/foo/crates/bar/Cargo.toml` Caused by: [NOT_FOUND] "#]]) .run(); } /// This is a freshness test for feature use with workspaces. /// /// `feat_lib` is used by `caller1` and `caller2`, but with different features enabled. /// This test ensures that alternating building `caller1`, `caller2` doesn't force /// recompile of `feat_lib`. /// /// Ideally, once we solve rust-lang/cargo#3620, then a single Cargo build at the top level /// will be enough. #[cargo_test] fn dep_used_with_separate_features() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["feat_lib", "caller1", "caller2"] "#, ) .file( "feat_lib/Cargo.toml", r#" [package] name = "feat_lib" version = "0.1.0" edition = "2015" authors = [] [features] myfeature = [] "#, ) .file("feat_lib/src/lib.rs", "") .file( "caller1/Cargo.toml", r#" [package] name = "caller1" version = "0.1.0" edition = "2015" authors = [] [dependencies] feat_lib = { path = "../feat_lib" } "#, ) .file("caller1/src/main.rs", "fn main() {}") .file("caller1/src/lib.rs", "") .file( "caller2/Cargo.toml", r#" [package] name = "caller2" version = "0.1.0" edition = "2015" authors = [] [dependencies] feat_lib = { path = "../feat_lib", features = ["myfeature"] } caller1 = { path = "../caller1" } "#, ) .file("caller2/src/main.rs", "fn main() {}") .file("caller2/src/lib.rs", ""); let p = p.build(); // Build the entire workspace. p.cargo("build --workspace") .with_stderr_data(str![[r#" [COMPILING] feat_lib v0.1.0 ([ROOT]/foo/feat_lib) [COMPILING] caller1 v0.1.0 ([ROOT]/foo/caller1) [COMPILING] caller2 v0.1.0 ([ROOT]/foo/caller2) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert!(p.bin("caller1").is_file()); assert!(p.bin("caller2").is_file()); // Build `caller1`. Should build the dep library. Because the features // are different than the full workspace, it rebuilds. // Ideally once we solve rust-lang/cargo#3620, then a single Cargo build at the top level // will be enough. p.cargo("build") .cwd("caller1") .with_stderr_data(str![[r#" [COMPILING] feat_lib v0.1.0 ([ROOT]/foo/feat_lib) [COMPILING] caller1 v0.1.0 ([ROOT]/foo/caller1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // Alternate building `caller2`/`caller1` a few times, just to make sure // features are being built separately. Should not rebuild anything. p.cargo("build") .cwd("caller2") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build") .cwd("caller1") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); p.cargo("build") .cwd("caller2") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn dont_recurse_out_of_cargo_home() { let git_project = git::new("dep", |project| { project .file("Cargo.toml", &basic_manifest("dep", "0.1.0")) .file("src/lib.rs", "") .file( "build.rs", r#" use std::env; use std::path::Path; use std::process::{self, Command}; fn main() { let cargo = env::var_os("CARGO").unwrap(); let cargo_manifest_dir = env::var_os("CARGO_MANIFEST_DIR").unwrap(); let output = Command::new(cargo) .args(&["metadata", "--format-version", "1", "--manifest-path"]) .arg(&Path::new(&cargo_manifest_dir).join("Cargo.toml")) .output() .unwrap(); if !output.status.success() { eprintln!("{}", String::from_utf8(output.stderr).unwrap()); process::exit(1); } } "#, ) }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies.dep] git = "{}" [workspace] "#, git_project.url() ), ) .file("src/lib.rs", ""); let p = p.build(); p.cargo("check") .env("CARGO_HOME", p.root().join(".cargo")) .run(); } // FIXME: this fails because of how workspace.exclude and workspace.members are working. /* #[cargo_test] fn include_and_exclude() { let p = project() .file("Cargo.toml", r#" [workspace] members = ["foo"] exclude = ["foo/bar"] "#) .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("foo/src/lib.rs", "") .file("foo/bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("foo/bar/src/lib.rs", ""); p.build(); p.cargo("build").cwd("foo").run(); assert!(p.root().join("target").is_dir()); assert!(!p.root().join("foo/target").is_dir()); p.cargo("build").cwd("foo/bar").run(); assert!(p.root().join("foo/bar/target").is_dir()); } */ #[cargo_test] fn cargo_home_at_root_works() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [workspace] members = ["a"] "#, ) .file("src/lib.rs", "") .file("a/Cargo.toml", &basic_manifest("a", "0.1.0")) .file("a/src/lib.rs", ""); let p = p.build(); p.cargo("check").run(); p.cargo("check --frozen").env("CARGO_HOME", p.root()).run(); } #[cargo_test] fn relative_rustc() { let p = project() .file( "src/main.rs", r#" use std::process::Command; use std::env; fn main() { let mut cmd = Command::new("rustc"); for arg in env::args_os().skip(1) { cmd.arg(arg); } std::process::exit(cmd.status().unwrap().code().unwrap()); } "#, ) .build(); p.cargo("build").run(); let src = p .root() .join("target/debug/foo") .with_extension(env::consts::EXE_EXTENSION); Package::new("a", "0.1.0").publish(); let p = project() .at("lib") .file( "Cargo.toml", r#" [package] name = "lib" version = "0.1.0" edition = "2015" [dependencies] a = "0.1" "#, ) .file("src/lib.rs", "") .build(); fs::copy(&src, p.root().join(src.file_name().unwrap())).unwrap(); let file = format!("./foo{}", env::consts::EXE_SUFFIX); p.cargo("build").env("RUSTC", &file).run(); } #[cargo_test] fn ws_rustc_err() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a"] "#, ) .file("a/Cargo.toml", &basic_lib_manifest("a")) .file("a/src/lib.rs", "") .build(); p.cargo("rustc") .with_status(101) .with_stderr_data(str![[r#" [ERROR] manifest path `[ROOT]/foo/Cargo.toml` is a virtual manifest, but this command requires running against an actual package in this workspace "#]]) .run(); p.cargo("rustdoc") .with_status(101) .with_stderr_data(str![[r#" [ERROR] manifest path `[ROOT]/foo/Cargo.toml` is a virtual manifest, but this command requires running against an actual package in this workspace "#]]) .run(); } #[cargo_test] fn ws_err_unused() { for table in &[ "[lib]", "[[bin]]", "[[example]]", "[[test]]", "[[bench]]", "[dependencies]", "[dev-dependencies]", "[build-dependencies]", "[features]", "[target]", "[badges]", "[lints]", ] { let key = table.trim_start_matches('[').trim_end_matches(']'); let p = project() .file( "Cargo.toml", &format!( r#" [workspace] members = ["a"] {table} "#, ), ) .file("a/Cargo.toml", &basic_lib_manifest("a")) .file("a/src/lib.rs", "") .build(); p.cargo("check") .with_status(101) .with_stderr_data(&format!( "\ [ERROR] failed to parse manifest at `[..]/foo/Cargo.toml` Caused by: this virtual manifest specifies a `{key}` section, which is not allowed ", )) .run(); } } #[cargo_test] fn ws_warn_unused() { for (key, name) in &[ ("[profile.dev]\nopt-level = 1", "profiles"), ("[replace]\n\"bar:0.1.0\" = { path = \"bar\" }", "replace"), ("[patch.crates-io]\nbar = { path = \"bar\" }", "patch"), ] { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a"] "#, ) .file( "a/Cargo.toml", &format!( r#" [package] name = "a" version = "0.1.0" edition = "2015" {} "#, key ), ) .file("a/src/lib.rs", "") .build(); p.cargo("check") .with_stderr_data(&format!( "\ [WARNING] {} for the non root package will be ignored, specify {} at the workspace root: package: [ROOT]/foo/a/Cargo.toml workspace: [ROOT]/foo/Cargo.toml [CHECKING] a v0.1.0 ([ROOT]/foo/a) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s ", name, name )) .run(); } } #[cargo_test] fn ws_warn_path() { // Warnings include path to manifest. let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a"] "#, ) .file( "a/Cargo.toml", r#" cargo-features = ["edition"] [package] name = "foo" version = "0.1.0" edition = "2015" "#, ) .file("a/src/lib.rs", "") .build(); p.cargo("check").with_stderr_data(str![[r#" [WARNING] [ROOT]/foo/a/Cargo.toml: the cargo feature `edition` has been stabilized in the 1.31 release and is no longer necessary to be listed in the manifest See https://doc.rust-lang.org/cargo/reference/manifest.html#the-edition-field for more information about using this feature. [CHECKING] foo v0.1.0 ([ROOT]/foo/a) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn invalid_missing() { // Make sure errors are not suppressed with -q. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] x = { path = 'x' } "#, ) .file("src/lib.rs", "") .build(); p.cargo("check -q") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to get `x` as a dependency of package `foo v0.1.0 ([ROOT]/foo)` Caused by: failed to load source for dependency `x` Caused by: Unable to update [ROOT]/foo/x Caused by: failed to read `[ROOT]/foo/x/Cargo.toml` Caused by: [NOT_FOUND] "#]]) .run(); } #[cargo_test] fn member_dep_missing() { // Make sure errors are not suppressed with -q. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [workspace] members = ["bar"] "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" [dependencies] baz = { path = "baz" } "#, ) .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("check -q") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to load manifest for workspace member `[ROOT]/foo/bar` referenced by workspace at `[ROOT]/foo/Cargo.toml` Caused by: failed to load manifest for dependency `baz` Caused by: failed to read `[ROOT]/foo/bar/baz/Cargo.toml` Caused by: [NOT_FOUND] "#]]) .run(); } #[cargo_test] fn simple_primary_package_env_var() { let is_primary_package = r#" #[test] fn verify_primary_package() {{ assert!(option_env!("CARGO_PRIMARY_PACKAGE").is_some()); }} "#; let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [workspace] members = ["bar"] "#, ) .file("src/lib.rs", is_primary_package) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] workspace = ".." "#, ) .file("bar/src/lib.rs", is_primary_package); let p = p.build(); p.cargo("test").run(); // Again, this time selecting a specific crate p.cargo("clean").run(); p.cargo("test -p bar").run(); // Again, this time selecting all crates p.cargo("clean").run(); p.cargo("test --all").run(); } #[cargo_test] fn virtual_primary_package_env_var() { let is_primary_package = r#" #[test] fn verify_primary_package() {{ assert!(option_env!("CARGO_PRIMARY_PACKAGE").is_some()); }} "#; let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo", "bar"] "#, ) .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) .file("foo/src/lib.rs", is_primary_package) .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) .file("bar/src/lib.rs", is_primary_package); let p = p.build(); p.cargo("test").run(); // Again, this time selecting a specific crate p.cargo("clean").run(); p.cargo("test -p foo").run(); } #[cargo_test] fn ensure_correct_workspace_when_nested() { let p = project() .file( "Cargo.toml", r#" [workspace] [package] name = "bar" version = "0.1.0" edition = "2015" authors = [] "#, ) .file("src/lib.rs", "") .file( "sub/Cargo.toml", r#" [workspace] members = ["foo"] "#, ) .file( "sub/foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" authors = [] [dependencies] bar = { path = "../.."} "#, ) .file("sub/foo/src/main.rs", "fn main() {}"); let p = p.build(); p.cargo("tree") .cwd("sub/foo") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo/sub/foo) └── bar v0.1.0 ([ROOT]/foo) "#]]) .run(); } #[cargo_test] fn nonexistence_package_together_with_workspace() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" authors = [] edition = "2021" [workspace] members = ["baz"] "#, ) .file("src/lib.rs", "") .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) .file("baz/src/lib.rs", ""); let p = p.build(); p.cargo("check --package nonexistence --workspace") .with_status(101) .with_stderr_data( str![[r#" [ERROR] package(s) `nonexistence` not found in workspace `[ROOT]/foo` "#]] .unordered(), ) .run(); // With pattern * p.cargo("check --package nonpattern* --workspace") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package pattern(s) `nonpattern*` not found in workspace `[ROOT]/foo` "#]]) .run(); p.cargo("package --package nonexistence --workspace") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package(s) `nonexistence` not found in workspace `[ROOT]/foo` "#]]) .run(); // With pattern * p.cargo("package --package nonpattern* --workspace") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package pattern(s) `nonpattern*` not found in workspace `[ROOT]/foo` "#]]) .run(); p.cargo("publish --dry-run --package nonexistence --workspace") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package(s) `nonexistence` not found in workspace `[ROOT]/foo` "#]]) .run(); // With pattern * p.cargo("publish --dry-run --package nonpattern* --workspace") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package pattern(s) `nonpattern*` not found in workspace `[ROOT]/foo` "#]]) .run(); p.cargo("tree --package nonexistence --workspace") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package(s) `nonexistence` not found in workspace `[ROOT]/foo` "#]]) .run(); // With pattern * p.cargo("tree --package nonpattern* --workspace") .with_status(101) .with_stderr_data(str![[r#" [ERROR] package pattern(s) `nonpattern*` not found in workspace `[ROOT]/foo` "#]]) .run(); } // A failing case from #[cargo_test] fn fix_only_check_manifest_path_member() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo", "bar"] resolver = "3" "#, ) .file( "foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2021" "#, ) .file("foo/src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" edition = "2021" "#, ) .file("bar/src/main.rs", "fn main() {}") .build(); p.cargo("fix --manifest-path foo/Cargo.toml --allow-no-vcs") .with_stderr_data(str![[r#" [CHECKING] foo v0.1.0 ([ROOT]/foo/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } cargo-0.91.0/tests/testsuite/yank.rs000064400000000000000000000150001046102023000155050ustar 00000000000000//! Tests for the `cargo yank` command. use std::fs; use crate::prelude::*; use cargo_test_support::project; use cargo_test_support::registry; use cargo_test_support::str; fn setup(name: &str, version: &str) { let dir = registry::api_path().join(format!("api/v1/crates/{}/{}", name, version)); dir.mkdir_p(); fs::write(dir.join("yank"), r#"{"ok": true}"#).unwrap(); } #[cargo_test] fn explicit_version() { let registry = registry::init(); setup("foo", "0.0.1"); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] license = "MIT" description = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("yank --version 0.0.1") .replace_crates_io(registry.index_url()) .run(); p.cargo("yank --undo --version 0.0.1") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] crates.io index Unyank foo@0.0.1 [ERROR] failed to undo a yank from the registry at [ROOTURL]/api Caused by: EOF while parsing a value at line 1 column 0 "#]]) .run(); } #[cargo_test] fn explicit_version_with_asymmetric() { let registry = registry::RegistryBuilder::new() .http_api() .token(cargo_test_support::registry::Token::rfc_key()) .build(); setup("foo", "0.0.1"); let p = project() .file( "Cargo.toml", r#" [project] name = "foo" version = "0.0.1" authors = [] license = "MIT" description = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build(); // The http_api server will check that the authorization is correct. // If the authorization was not sent then we would get an unauthorized error. p.cargo("yank --version 0.0.1") .arg("-Zasymmetric-token") .masquerade_as_nightly_cargo(&["asymmetric-token"]) .replace_crates_io(registry.index_url()) .run(); p.cargo("yank --undo --version 0.0.1") .arg("-Zasymmetric-token") .masquerade_as_nightly_cargo(&["asymmetric-token"]) .replace_crates_io(registry.index_url()) .run(); } #[cargo_test] fn inline_version() { let registry = registry::init(); setup("foo", "0.0.1"); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] license = "MIT" description = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("yank foo@0.0.1") .replace_crates_io(registry.index_url()) .run(); p.cargo("yank --undo foo@0.0.1") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" [UPDATING] crates.io index Unyank foo@0.0.1 [ERROR] failed to undo a yank from the registry at [ROOTURL]/api Caused by: EOF while parsing a value at line 1 column 0 "#]]) .run(); } #[cargo_test] fn version_required() { setup("foo", "0.0.1"); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] license = "MIT" description = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("yank foo") .with_status(101) .with_stderr_data(str![[r#" [ERROR] `--version` is required "#]]) .run(); } #[cargo_test] fn inline_version_without_name() { setup("foo", "0.0.1"); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] license = "MIT" description = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("yank @0.0.1") .with_status(101) .with_stderr_data(str![[r#" [ERROR] missing crate name for `@0.0.1` "#]]) .run(); } #[cargo_test] fn inline_and_explicit_version() { setup("foo", "0.0.1"); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] license = "MIT" description = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("yank foo@0.0.1 --version 0.0.1") .with_status(101) .with_stderr_data(str![[r#" [ERROR] cannot specify both `@0.0.1` and `--version` "#]]) .run(); } #[cargo_test] fn bad_version() { let registry = registry::init(); setup("foo", "0.0.1"); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] license = "MIT" description = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("yank foo@bar") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid version `bar` Caused by: unexpected character 'b' while parsing major version number "#]]) .run(); } #[cargo_test] fn prefixed_v_in_version() { let registry = registry::init(); setup("foo", "0.0.1"); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] license = "MIT" description = "foo" "#, ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("yank bar@v0.0.1") .replace_crates_io(registry.index_url()) .with_status(101) .with_stderr_data(str![[r#" [ERROR] the version provided, `v0.0.1` is not a valid SemVer version [HELP] try changing the version to `0.0.1` Caused by: unexpected character 'v' while parsing major version number "#]]) .run(); } cargo-0.91.0/triagebot.toml000064400000000000000000000236561046102023000136770ustar 00000000000000[relabel] allow-unauthenticated = [ "A-*", "C-*", "Command-*", "E-*", "I-*", "O-*", "S-*", "Z-*", "beta-nominated", "regression-*", "relnotes", ] [ping.windows] message = """\ Hey Windows Group! This bug has been identified as a good "Windows candidate". In case it's useful, here are some [instructions] for tackling these sorts of bugs. Maybe take a look? Thanks! <3 [instructions]: https://rustc-dev-guide.rust-lang.org/notification-groups/windows.html """ label = "O-windows" [shortcut] # Enable issue transfers within the org # Documentation at: https://forge.rust-lang.org/triagebot/transfer.html [transfer] # Prevents mentions in commits to avoid users being spammed [no-mentions] # Enable `@rustbot note` functionality # Documentation at: https://forge.rust-lang.org/triagebot/note.html [note] [merge-conflicts] remove = [] add = ["S-waiting-on-author"] unless = ["S-blocked", "S-waiting-on-review"] [autolabel."S-waiting-on-review"] new_pr = true [assign] contributing_url = "https://rust-lang.github.io/cargo/contrib/" warn_non_default_branch = true [assign.owners] "*" = ["@ehuss", "@epage", "@weihanglo"] [review-submitted] reviewed_label = "S-waiting-on-author" review_labels = ["S-waiting-on-review"] [review-requested] remove_labels = ["S-waiting-on-author"] add_labels = ["S-waiting-on-review"] [autolabel."A-build-execution"] trigger_files = [ "src/cargo/core/compiler/compilation.rs", "src/cargo/core/compiler/job_queue/", "src/cargo/core/compiler/mod.rs", ] [autolabel."A-build-scripts"] trigger_files = [ "crates/build-rs-test-lib/", "crates/build-rs/", "src/cargo/core/compiler/custom_build.rs", ] [autolabel."A-cache-messages"] trigger_files = ["src/cargo/util/rustc.rs"] [autolabel."A-cargo-targets"] trigger_files = [ "src/cargo/ops/cargo_compile/compile_filter.rs", "src/cargo/ops/cargo_compile/unit_generator.rs", ] [autolabel."A-cfg-expr"] trigger_files = [ "crates/cargo-platform/", "src/cargo/core/compiler/build_context/target_info.rs", ] [autolabel."A-cli"] trigger_files = ["src/bin/", "src/cargo/util/command_prelude.rs"] [autolabel."A-cli-help"] trigger_files = ["crates/mdman/", "src/etc/man/"] [autolabel."A-completions"] trigger_files = ["src/etc/_cargo", "src/etc/cargo.bashcomp.sh"] [autolabel."A-configuration"] trigger_files = ["src/cargo/util/context/mod.rs"] [autolabel."A-console-output"] trigger_files = [ "src/cargo/core/shell.rs", "src/cargo/util/machine_message.rs", "src/cargo/util/progress.rs", ] [autolabel."A-crate-dependencies"] trigger_files = ["src/cargo/core/dependency.rs"] [autolabel."A-crate-types"] trigger_files = ["src/cargo/core/compiler/crate_type.rs"] [autolabel."A-credential-provider"] trigger_files = ["credential/"] [autolabel."A-dep-info"] trigger_files = ["src/cargo/core/compiler/output_depinfo.rs"] [autolabel."A-dependency-resolution"] trigger_files = [ "benches/benchsuite/benches/resolve.rs", "crates/resolver-tests/", "src/cargo/core/resolver/", ] [autolabel."A-directory-source"] trigger_files = ["src/cargo/sources/directory.rs"] [autolabel."A-documenting-cargo-itself"] trigger_files = ["src/doc/"] [autolabel."A-environment-variables"] trigger_files = [ "crates/home/", "src/cargo/util/context/environment.rs", ] [autolabel."A-features2"] trigger_files = ["src/cargo/core/resolver/features.rs"] [autolabel."A-filesystem"] trigger_files = ["src/cargo/util/flock.rs", "src/cargo/util/important_paths.rs"] [autolabel."A-future-incompat"] trigger_files = ["src/cargo/core/compiler/future_incompat.rs"] [autolabel."A-git"] trigger_files = ["src/cargo/sources/git/", "src/cargo/ops/cargo_package/vcs.rs"] [autolabel."A-home"] trigger_files = ["crates/home/"] [autolabel."A-infrastructure"] trigger_files = [ ".cargo/", ".github/", "build.rs", "ci/", "clippy.toml", "crates/xtask-", "deny.toml", "publish.py", "triagebot.toml", ] [autolabel."A-interacts-with-crates.io"] trigger_files = ["crates/crates-io/", "src/cargo/ops/registry/"] [autolabel."A-layout"] trigger_files = [ "src/cargo/core/compiler/build_runner/compilation_files.rs", "src/cargo/core/compiler/layout.rs", ] [autolabel."A-links"] trigger_files = ["src/cargo/core/compiler/links.rs"] [autolabel."A-local-registry-source"] trigger_files = ["src/cargo/sources/registry/local.rs"] [autolabel."A-lockfile"] trigger_files = ["src/cargo/ops/lockfile.rs", "src/cargo/core/resolver/encode.rs"] [autolabel."A-lto"] trigger_files = ["src/cargo/core/compiler/lto.rs"] [autolabel."A-manifest"] trigger_files = [ "crates/cargo-util-schemas/src/manifest/", "src/cargo/core/manifest.rs", "src/cargo/util/toml/mod.rs", "src/cargo/util/toml_mut/", ] [autolabel."A-networking"] trigger_files = ["src/cargo/util/network/"] [autolabel."A-overrides"] trigger_files = ["src/cargo/sources/replaced.rs"] [autolabel."A-profiles"] trigger_files = ["src/cargo/core/profiles.rs"] [autolabel."A-rebuild-detection"] trigger_files = ["src/cargo/core/compiler/fingerprint/"] [autolabel."A-registries"] trigger_files = ["src/cargo/sources/registry/", "src/cargo/core/registry.rs"] [autolabel."A-registry-authentication"] trigger_files = ["src/cargo/util/auth/"] [autolabel."A-semver"] trigger_files = [ "crates/semver-check", "src/cargo/util/semver_ext.rs", ] [autolabel."A-source-replacement"] trigger_files = ["src/cargo/sources/replaced.rs"] [autolabel."A-sparse-registry"] trigger_files = ["src/cargo/sources/registry/http_remote.rs"] [autolabel."A-testing-cargo-itself"] trigger_files = [ "benches/", "crates/cargo-test-macro/", "crates/cargo-test-support/", ] [autolabel."A-timings"] trigger_files = [ "src/cargo/core/compiler/timings.js", "src/cargo/core/compiler/timings.rs", "src/cargo/util/cpu.rs", ] [autolabel."A-unstable"] trigger_files = ["src/cargo/core/features.rs"] [autolabel."A-vcs"] trigger_files = ["src/cargo/util/vcs.rs"] [autolabel."A-workspaces"] trigger_files = [ "benches/benchsuite/benches/workspace_initialization.rs", "src/cargo/core/workspace.rs", "src/cargo/util/workspace.rs" ] [autolabel."Command-add"] trigger_files = ["src/bin/cargo/commands/add.rs", "src/cargo/ops/cargo_add/"] [autolabel."Command-bench"] trigger_files = ["src/bin/cargo/commands/bench.rs"] [autolabel."Command-build"] trigger_files = ["src/bin/cargo/commands/build.rs"] [autolabel."Command-check"] trigger_files = ["src/bin/cargo/commands/check.rs"] [autolabel."Command-clean"] trigger_files = ["src/bin/cargo/commands/clean.rs", "src/cargo/ops/cargo_clean.rs"] [autolabel."Command-doc"] trigger_files = ["src/bin/cargo/commands/doc.rs", "src/cargo/ops/cargo_doc.rs"] [autolabel."Command-fetch"] trigger_files = ["src/bin/cargo/commands/fetch.rs", "src/cargo/ops/cargo_fetch.rs"] [autolabel."Command-fix"] trigger_files = [ "crates/rustfix/", "src/bin/cargo/commands/fix.rs", "src/cargo/ops/fix/", "src/cargo/util/diagnostic_server.rs", "src/cargo/util/lockserver.rs", ] [autolabel."Command-generate-lockfile"] trigger_files = ["src/bin/cargo/commands/generate_lockfile.rs"] [autolabel."Command-git-checkout"] trigger_files = ["src/bin/cargo/commands/git_checkout.rs"] [autolabel."Command-info"] trigger_files = ["src/bin/cargo/commands/info.rs", "src/cargo/ops/registry/info/"] [autolabel."Command-init"] trigger_files = ["src/bin/cargo/commands/init.rs"] [autolabel."Command-install"] trigger_files = ["src/bin/cargo/commands/install.rs", "src/cargo/ops/cargo_install.rs"] [autolabel."Command-locate-project"] trigger_files = ["src/bin/cargo/commands/locate_project.rs"] [autolabel."Command-login"] trigger_files = ["src/bin/cargo/commands/login.rs", "src/cargo/ops/registry/login.rs"] [autolabel."Command-logout"] trigger_files = ["src/bin/cargo/commands/logout.rs", "src/cargo/ops/registry/logout.rs"] [autolabel."Command-metadata"] trigger_files = ["src/bin/cargo/commands/metadata.rs", "src/cargo/ops/cargo_output_metadata.rs"] [autolabel."Command-new"] trigger_files = ["src/bin/cargo/commands/new.rs", "src/cargo/ops/cargo_new.rs"] [autolabel."Command-owner"] trigger_files = ["src/bin/cargo/commands/owner.rs", "src/cargo/ops/registry/owner.rs"] [autolabel."Command-package"] trigger_files = ["src/bin/cargo/commands/package.rs", "src/cargo/ops/cargo_package/"] [autolabel."Command-pkgid"] trigger_files = ["src/bin/cargo/commands/pkgid.rs", "src/cargo/ops/cargo_pkgid.rs"] [autolabel."Command-publish"] trigger_files = ["src/bin/cargo/commands/publish.rs", "src/cargo/ops/registry/publish.rs"] [autolabel."Command-read-manifest"] trigger_files = ["src/bin/cargo/commands/read_manifest.rs", "src/cargo/ops/cargo_read_manifest.rs"] [autolabel."Command-remove"] trigger_files = ["src/bin/cargo/commands/remove.rs", "src/cargo/ops/cargo_remove.rs"] [autolabel."Command-report"] trigger_files = ["src/bin/cargo/commands/report.rs"] [autolabel."Command-run"] trigger_files = ["src/bin/cargo/commands/run.rs", "src/cargo/ops/cargo_run.rs"] [autolabel."Command-rustc"] trigger_files = ["src/bin/cargo/commands/rustc.rs"] [autolabel."Command-rustdoc"] trigger_files = ["src/bin/cargo/commands/rustdoc.rs"] [autolabel."Command-search"] trigger_files = ["src/bin/cargo/commands/search.rs", "src/cargo/ops/registry/search.rs"] [autolabel."Command-test"] trigger_files = ["src/bin/cargo/commands/test.rs", "src/cargo/ops/cargo_test.rs"] [autolabel."Command-tree"] trigger_files = ["src/bin/cargo/commands/tree.rs", "src/cargo/ops/tree/"] [autolabel."Command-uninstall"] trigger_files = ["src/bin/cargo/commands/uninstall.rs", "src/cargo/ops/cargo_uninstall.rs"] [autolabel."Command-update"] trigger_files = ["src/bin/cargo/commands/update.rs", "src/cargo/ops/cargo_update.rs"] [autolabel."Command-vendor"] trigger_files = ["src/bin/cargo/commands/vendor.rs", "src/cargo/ops/vendor.rs"] [autolabel."Command-verify-project"] trigger_files = ["src/bin/cargo/commands/verify_project.rs"] [autolabel."Command-version"] trigger_files = ["src/bin/cargo/commands/version.rs"] [autolabel."Command-yank"] trigger_files = ["src/bin/cargo/commands/yank.rs", "src/cargo/ops/registry/yank.rs"] cargo-0.91.0/windows.manifest.xml000064400000000000000000000026111046102023000150270ustar 00000000000000 UTF-8 true