diff --git a/.ci/checksum.ps1 b/.ci/checksum.ps1 deleted file mode 100644 index a21dbe90..00000000 --- a/.ci/checksum.ps1 +++ /dev/null @@ -1,7 +0,0 @@ -param ( - [string]$filename = $(throw "-filename is required.") -) - -$ErrorActionPreference="Stop" - -(Get-FileHash "${filename}").Hash | Out-File -Encoding ASCII -NoNewline "${filename}.sha256" \ No newline at end of file diff --git a/.ci/install_musl.sh b/.ci/install_musl.sh deleted file mode 100755 index e20dd6fd..00000000 --- a/.ci/install_musl.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash -set -e - -travis_fold start "apt-get.musl" - travis_time_start - sudo apt-get update && sudo -E apt-get -yq --no-install-suggests --no-install-recommends install musl-tools - travis_time_finish -travis_fold end "apt-get.musl" - -travis_fold start "rustup.target.musl" - travis_time_start - rustup target add x86_64-unknown-linux-musl - travis_time_finish -travis_fold end "rustup.target.musl" diff --git a/.ci/lint.sh b/.ci/lint.sh deleted file mode 100755 index fb75fded..00000000 --- a/.ci/lint.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash -set -e - -travis_fold start "rustup.component.install" - travis_time_start - rustup component add rustfmt clippy - travis_time_finish -travis_fold end "rustup.component.install" - -# Ensure everything has been rustfmt'ed -travis_fold start "rustfmt" - travis_time_start - cargo fmt -- --check - travis_time_finish -travis_fold end "rustfmt" - -# Download in a separate step to separate -# building from fetching dependencies -travis_fold start "cargo.fetch" - travis_time_start - cargo fetch - travis_time_finish -travis_fold end "cargo.fetch" - -# Because rust isn't brutal enough itself -travis_fold start "clippy" - travis_time_start - cargo clippy -- -D warnings - travis_time_finish -travis_fold end "clippy" diff --git a/.ci/prep_deploy.sh b/.ci/prep_deploy.sh deleted file mode 100755 index cd26d902..00000000 --- a/.ci/prep_deploy.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/bash -set -e - -# Fetch dependencies in a different step to clearly -# delineate between downloading and building -travis_fold start "cargo.fetch" - travis_time_start - cargo fetch --target "$TARGET" - travis_time_finish -travis_fold end "cargo.fetch" - -# Build without running to clearly delineate between -# building and packaging -travis_fold start "cargo.build" - travis_time_start - cargo build --release --target "$TARGET" - travis_time_finish -travis_fold end "cargo.build" - -travis_fold start "package.release" - travis_time_start - name="cargo-deny" - release_name="$name-$TRAVIS_TAG-$TARGET" - mkdir "$release_name" - - if [ "$TARGET" == "x86_64-pc-windows-msvc" ]; then - # We don't use name again, so just add the exe extension - # to it and call it a day - name="$name.exe" - else - # If we're not on windows, strip the binary to remove - # debug symbols and minimize the resulting release - # size without much effort - strip "target/$TARGET/release/$name" - fi - - cp "target/$TARGET/release/$name" "$release_name/" - cp README.md LICENSE-APACHE LICENSE-MIT "$release_name/" - tar czvf "$release_name.tar.gz" "$release_name" - - rm -r "$release_name" - - stat "$release_name.tar.gz" - - # Get the sha-256 checksum w/o filename and newline, on windows we use - # powershell because git bash only seems to include md5/sha1sum - if [ "$TARGET" == "x86_64-pc-windows-msvc" ]; then - powershell -NoLogo -ExecutionPolicy Bypass -File .ci/checksum.ps1 "$release_name.tar.gz" - else - echo -n "$(shasum -ba 256 "$release_name.tar.gz" | cut -d " " -f 1)" > "$release_name.tar.gz.sha256" - fi - - stat "$release_name.tar.gz.sha256" - travis_time_finish -travis_fold end "package.release" diff --git a/.ci/test.sh b/.ci/test.sh deleted file mode 100755 index d1c13df3..00000000 --- a/.ci/test.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash -set -e - -HOST_OS_NAME=$(uname -s) -TARGET="" - -if [ "$(uname)" == "Darwin" ]; then - TARGET="apple-darwin" -elif [ "${HOST_OS_NAME::5}" == "Linux" ]; then - # If we're on linux, always build for musl - TARGET="unknown-linux-musl" - .ci/install_musl.sh -elif [ "${HOST_OS_NAME::10}" == "MINGW64_NT" ]; then - TARGET="pc-windows-msvc" -elif [ "${HOST_OS_NAME::7}" == "MSYS_NT" ]; then # travis uses msys - TARGET="pc-windows-msvc" -elif [ "${HOST_OS_NAME::10}" == "MINGW32_NT" ]; then - echo "Why are you on a 32-bit machine?" - exit 1 -else - echo "Unknown host platform! '$HOST_OS_NAME'" - exit 1 -fi - -TARGET="x86_64-$TARGET" - -# Fetch dependencies in a different step to clearly -# delineate between downloading and building -travis_fold start "cargo.fetch" - travis_time_start - cargo fetch - travis_time_finish -travis_fold end "cargo.fetch" - -# Build without running to clearly delineate between -# building and running the tests -travis_fold start "cargo.build" - travis_time_start - cargo test --no-run --target $TARGET - travis_time_finish -travis_fold end "cargo.build" - -travis_fold start "cargo.test" - travis_time_start - cargo test --target $TARGET - travis_time_finish -travis_fold end "cargo.test" diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..c40500aa --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,32 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Device:** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..11fc491e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml new file mode 100644 index 00000000..5d825bbe --- /dev/null +++ b/.github/workflows/automerge.yml @@ -0,0 +1,26 @@ +name: automerge +on: + pull_request: + types: + - labeled + - unlabeled + - synchronize + - opened + - edited + - ready_for_review + - reopened + - unlocked + pull_request_review: + types: + - submitted + status: {} +jobs: + automerge: + runs-on: ubuntu-latest + steps: + - name: automerge + uses: "pascalgn/automerge-action@v0.3.2" + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + MERGE_METHOD: "squash" + LABELS: "!work-in-progress" diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 00000000..bcb1ed67 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,203 @@ +on: [push, pull_request] +name: CI +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + + # make sure all code has been formatted with rustfmt + - run: rustup component add rustfmt + - name: check rustfmt + uses: actions-rs/cargo@v1 + with: + command: fmt + args: -- --check --color always + + # run clippy to verify we have no warnings + - run: rustup component add clippy + - name: cargo fetch + uses: actions-rs/cargo@v1 + with: + command: fetch + - name: cargo clippy + uses: actions-rs/cargo@v1 + with: + command: clippy + args: --lib --tests -- -D warnings + + test: + name: Test + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macOS-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + - name: cargo fetch + uses: actions-rs/cargo@v1 + with: + command: fetch + - name: cargo test build + uses: actions-rs/cargo@v1 + with: + command: build + args: --tests + - name: cargo test + uses: actions-rs/cargo@v1 + with: + command: test + + self: + name: Self Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + - name: cargo fetch + uses: actions-rs/cargo@v1 + with: + command: fetch + - name: cargo install + uses: actions-rs/cargo@v1 + with: + command: install + # Install in debug mode since this part is sloooooow and + # release doesn't really matter much for runtime + args: --path . --debug + - name: self check + uses: actions-rs/cargo@v1 + with: + command: deny + args: -L debug check + + publish-check: + name: Publish Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + - name: cargo fetch + uses: actions-rs/cargo@v1 + with: + command: fetch + - name: cargo publish + uses: actions-rs/cargo@v1 + with: + command: publish + args: --dry-run + + publish: + name: Publish + needs: [test, self, publish-check] + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/') + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + - name: cargo fetch + uses: actions-rs/cargo@v1 + with: + command: fetch + - name: cargo publish + uses: actions-rs/cargo@v1 + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_IO_TOKEN }} + with: + command: publish + + release: + name: Release + needs: [test, self] + if: startsWith(github.ref, 'refs/tags/') + strategy: + matrix: + os: [ubuntu-latest, macOS-latest, windows-latest] + include: + - os: ubuntu-latest + rust: stable + target: x86_64-unknown-linux-musl + bin: cargo-deny + - os: windows-latest + rust: stable + target: x86_64-pc-windows-msvc + bin: cargo-deny.exe + - os: macOS-latest + rust: stable + target: x86_64-apple-darwin + bin: cargo-deny + runs-on: ${{ matrix.os }} + steps: + - name: Install stable toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + override: true + target: ${{ matrix.target }} + - name: Install musl tools + if: matrix.os == 'ubuntu-latest' + run: | + sudo apt-get install -y musl-tools + - name: Checkout + uses: actions/checkout@v1 + - name: cargo fetch + uses: actions-rs/cargo@v1 + with: + command: fetch + args: --target ${{ matrix.target }} + - name: Release build + uses: actions-rs/cargo@v1 + with: + command: build + args: --release --target ${{ matrix.target }} + - name: Package + shell: bash + run: | + name=cargo-deny + tag=$(git describe --tags --abbrev=0) + release_name="$name-$tag-${{ matrix.target }}" + release_tar="${release_name}.tar.gz" + mkdir "$release_name" + + if [ "${{ matrix.target }}" != "x86_64-pc-windows-msvc" ]; then + strip "target/${{ matrix.target }}/release/${{ matrix.bin }}" + fi + + cp "target/${{ matrix.target }}/release/${{ matrix.bin }}" "$release_name/" + cp README.md LICENSE-APACHE LICENSE-MIT "$release_name/" + tar czvf "$release_tar" "$release_name" + + rm -r "$release_name" + + # Windows environments in github actions don't have the gnu coreutils installed, + # which includes the shasum exe, so we just use powershell instead + if [ "${{ matrix.os }}" == "windows-latest" ]; then + echo "(Get-FileHash \"${release_tar}\" -Algorithm SHA256).Hash | Out-File -Encoding ASCII -NoNewline \"${release_tar}.sha256\"" | pwsh -c - + else + echo -n "$(shasum -ba 256 "${release_tar}" | cut -d " " -f 1)" > "${release_tar}.sha256" + fi + - name: Publish + uses: softprops/action-gh-release@v1 + with: + draft: true + files: 'cargo-deny*' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index d6df87bf..00000000 --- a/.travis.yml +++ /dev/null @@ -1,151 +0,0 @@ -language: rust - -stages: -- test -- name: deploy - if: tag =~ /^\d+\.\d+\.\d+.*$/ - -env: - global: - - RUST_BACKTRACE=1 - -# See http://www.garbers.co.za/2017/11/01/code-folding-and-timing-in-travis-ci/ -before_script: -- export -f travis_nanoseconds -- export -f travis_fold -- export -f travis_time_start -- export -f travis_time_finish - -matrix: - allow_failures: - - env: NIGHTLY=1 - fast_finish: true - include: - # _ _ _ - # | | (_) | | - # | | _ _ __ | |_ - # | | | | '_ \| __| - # | |____| | | | | |_ - # |______|_|_| |_|\__| - - name: "stable lint" - stage: test - rust: stable - os: linux - script: .ci/lint.sh - - - name: "beta lint" - stage: test - rust: beta - os: linux - script: .ci/lint.sh - - # _______ _ - # |__ __| | | - # | | ___ ___| |_ - # | |/ _ \/ __| __| - # | | __/\__ \ |_ - # |_|\___||___/\__| - - name: "stable test linux" - stage: test - rust: stable - os: linux - script: .ci/test.sh - - - name: "beta test linux" - stage: test - rust: beta - os: linux - script: .ci/test.sh - - - name: "nightly test linux" - stage: test - rust: nightly - script: .ci/test.sh - env: - - NIGHTLY=1 - - - name: "stable test osx" - rust: stable - os: osx - script: .ci/test.sh - - - name: "stable test windows" - rust: stable - os: windows - script: .ci/test.sh - - - name: "self check" - stage: test - rust: stable - os: linux - env: - - TARGET=x86_64-unknown-linux-musl - script: - - .ci/install_musl.sh - - cargo fetch - - cargo install --target $TARGET --path . - - cargo-deny check - - # _____ _ _ _ _ - # | __ \ | | | (_) | | - # | |__) | _| |__ | |_ ___| |__ - # | ___/ | | | '_ \| | / __| '_ \ - # | | | |_| | |_) | | \__ \ | | | - # |_| \__,_|_.__/|_|_|___/_| |_| - - name: "publish x86_64-unknown-linux-musl" - stage: deploy - rust: stable - os: linux - env: - - DEPLOY=1 - - TARGET=x86_64-unknown-linux-musl - script: - - .ci/install_musl.sh - - .ci/prep_deploy.sh - - - name: "publish x86_64-pc-windows-msvc" - stage: deploy - os: windows - env: - - DEPLOY=1 - - TARGET=x86_64-pc-windows-msvc - # Override Travis' rust version, we don't want the gnu default - # caused by running in bash - - TRAVIS_RUST_VERSION=stable-x86_64-pc-windows-msvc - script: - - .ci/prep_deploy.sh - - - name: "publish x86_64-apple-darwin" - stage: deploy - rust: stable - os: osx - env: - - DEPLOY=1 - - TARGET=x86_64-apple-darwin - script: - - .ci/prep_deploy.sh - - - stage: deploy - rust: stable - os: linux - script: echo "deploying $TRAVIS_TAG to crates.io" - deploy: - provider: cargo - token: - secure: EgipoJ4TiLN92aDEbZ7as1gMOaXAVCsIar3YVIAQW/KvY6qjfYwjl7Q+kuocIRD910HUb71kZ1AduyI9GH2JH0r91VsL+hK22b8CU6AVnyKDsuS7B2FbivQukIdP/bjGo1XT10NSFx/Qpy7UrckeMCyuAdpN6yPnuqHaRam9JyM2aOTKeOoqIWB6E54y7rlKGSzUHfsNx741J2qBtevwJF2zO7ZRPe+I4uVzllISnsNw+ldmL8RleVLGPCSg25HYvJNWaclu78zi3k7q5YQVhcb9JA3YWeGY+yxCNJqzEvh8KPl3THDhzAgWJXG9RnktWblOgXsin4tLzH12AbVcZvjvJB2X1PcLi4xhHpRoIXLOxaoRUoCqrNIrntuxdZYR/+9O8UQHaZ/Cq3KlalsiBO4/KIQ+Tu2gsz0gWo2YTCksAHCr6g6p5nS3qWDz6yX6QxHK9xB1z7ohDC9lRvjdw4gYY6frvmq3cKT0kFgqANER9P6dlHIRf42t6sJzms/i16w6ZnQdI6jAekdJYqdEXY7I1J/+ZKE5DIcW6hObIuIp2rIHKm2PuSJaI456+VACg9dx/FUrgbpVkJLGSNuMC3zPdEI+nNvsRHR6oUfvNE9oh0TxlY+tRhAGl0YT3syoxnKdGJKq7XFSkew37MQFb4L0GI/Hs/OmGitKxc82OeQ= - on: - repo: EmbarkStudios/cargo-deny - tags: true - -deploy: -- provider: releases - api_key: - secure: OmW9w6uglrd5LxuTCOlIWXW409Cc/ONjRfgQhhXVLqgxgtx64SlabrcASnA7q/V8vPAjBoNBky7Uz/KMIvnC7bGpXDAu9cHddJ+xaXCUgQ4yDJDsYJo4z1Qap5k+EoVXYbUSVaMpK087wlF5vjOMdUXKB56JkvQf8kFpTv6cvbN6Vn42Sq+pq59w/puDaXZJVqnnOjzgQK4YETh8pe2xoWBrlHIbb0u8281LV4KOqXeY3h4Nve/rCIk2+qo04NJF/GxL6SMLVRPFrZkvJhc2OSdnDwhwv8fC9ssyKBKEtfjRsJpn3PG9GoEH6KPNvb0HcmAoNHhmjuQPwlO3HAQ+bbkG0CC9MrbA93Sa9SJYzT28FAhCu7iSUeePEaLKGy9XNGW3qhlH26Ycqitb2kQ2R+RLW84u/xEjWs4kLoDfbmxVPzmJfwdCjJTT2b0BklzRFRvQdfS7f3aIvaIguenO0YlMX2IzBmsPqJGMjG+e/Dwi+3Sso+bGE/K757cmBrgsUjqNyTY0jIdPZ2xaMDZ58M5G4tlG6VWc6WIEH4hfFqD/8EUl7ZeInx629xm4KSh75YMlL8Id46on2L0gOecXJZYSglnxA78DWOMj9yZDuhcW0f/kpTPiFtbN/RkPE9eEW9Mk2EetxgRhTOFt3AAqoK5OzeVt1Ub/iQCFkLEspIM= - file: - - cargo-deny-$TRAVIS_TAG-$TARGET.tar.gz - - cargo-deny-$TRAVIS_TAG-$TARGET.tar.gz.sha256 - skip_cleanup: true - on: - condition: $DEPLOY = 1 - repo: EmbarkStudios/cargo-deny - tags: true diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..55722967 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,69 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [0.3.0-beta] - 2019-10-07 +### Added +- Output that pertains to a particular crate now outputs the inclusion graph for that crate, similarly to how [cargo tree](https://github.com/sfackler/cargo-tree) shows the inverse dependency graph. This can be turned off with the `--hide-inclusion-graphs` flag on the `check` subcommand. +- All configuration problems that aren't directly related to actual toml parsing now pretty print the location and cause(s) +of the problem so that you can more easily fix the issue. +- Added the ``[licenses.clarify]]` key to the configuration, which allows users to specify the license expression +for a crate that will be used as long as the version requirements are met, and the hash of the license file(s) are the same +- Added the `licenses.allow-osi-fsf-free` key, which can be used to specify blanket allowance of licenses based on whether they are [OSI Approved](https://opensource.org/licenses) or [FSF/Free Libre](https://www.gnu.org/licenses/license-list.en.html). It defaults to `neither`. + +### Changed +- The output of the tool as a whole is dramatically different. Previously, all logging was done via `slog`, which is +great for structured logging of high volume output, but wasn't really appropriate for a user facing tool. Some normal log output still exists, but almost all output is now done with the excellent [codespan](https://github.com/brendanzab/codespan) crate to give more user-friendly output. +- All configuration keys are now `kebab-case` instead of `snake_case` +- Improved the checking of [SPDX license expressions](https://spdx.org/spdx-specification-21-web-version#h.jxpfx0ykyb60), +previously the expression was just lexed and all the requirements that could be understood were required, but now the operators in the expression are actually respected. +- Added proper support for license exceptions, you must now allow or deny licenses including their exception, which treated as a different case than the same license without the exception. eg `allow = [ "Apache-2.0 WITH LLVM-exception" ]` will not also allow `Apache-2.0` without the exception. +- The usage of `+` is now properly supported, eg. `Apache-2.0+` will now match `Apache-2.0` or a hypothetical `Apache-3.0` in the future. +- The `list` subcommand now treats licenses with exceptions as unique licenses. +- When `bans.multiple-versions` is either `deny` or `warn`, duplicates are printed out, including their particular +inclusion graphs, in addition to optionally writing a dotgraph to a file on disk for more thorough inspection. +- LICENSE(-*)? files are no longer eagerly evaluated, rather the crate's license expression is retrieved via + 1. `license` + 2. Matching user override as specified via `licenses.clarify` + 3. Compounding all licenses together via `AND` only if **all** detected LICENSE files can be scored with confidence + +### Fixed +- Previously, just having an empty `licenses.deny` and `licenses.allow` meant that **every** license would be accepted. +Now each license has to be explicitly approved, either by listing them in `licenses.allow` or `licenses.allow-osi-fsf-free`. + +### Removed +- Removed the `licenses.ignore` key from the configuration, as this was [confusing](https://github.com/EmbarkStudios/cargo-deny/issues/16) to [users](https://github.com/EmbarkStudios/cargo-deny/issues/24). Supplanted by `licenses.clarify`. +- Removed the `licenses.skip` key from the configuration, supplanted by `licenses.clarify`. +- Removed the `licenses.unknown` key from the configuration, if a license cannot be inferred from a file, the path, score, and hash are now shown to the user as additional info for why a crate is considered "unlicensed". + +## [0.2.5] - 2019-07-01 +### Fixed +- Removed duplicate version of `rand` + +## [0.2.4] - 2019-07-01 +### Fixed +- Fixed banning specific crates via `bans.deny` + +## [0.2.3] - 2019-07-01 +### Changed +- Fixed up README in published crate + +## [0.2.2] - 2019-06-28 +### Added +- Added more badges to published crate + +## [0.2.1] - 2019-06-28 +### Added +- Initial implementation release + +[Unreleased]: https://github.com/EmbarkStudios/cargo-deny/compare/0.3.0-beta...HEAD +[0.3.0-beta]: https://github.com/EmbarkStudios/cargo-deny/compare/0.2.5...0.3.0-beta +[0.2.5]: https://github.com/EmbarkStudios/cargo-deny/compare/0.2.4...0.2.5 +[0.2.4]: https://github.com/EmbarkStudios/cargo-deny/compare/0.2.3...0.2.4 +[0.2.3]: https://github.com/EmbarkStudios/cargo-deny/compare/0.2.2...0.2.3 +[0.2.2]: https://github.com/EmbarkStudios/cargo-deny/compare/0.2.1...0.2.2 +[0.2.1]: https://github.com/EmbarkStudios/cargo-deny/releases/tag/0.2.1 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..7d03b675 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at opensource@embark-studios.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..7a537ca3 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,55 @@ +# Embark Contributor Guidelines + +Welcome! This project is created by the team at [Embark Studios](https://embark.games). We're glad you're interested in contributing! We welcome contributions from people of all backgrounds who are interested in making great software with us. + +At Embark, we aspire to empower everyone to create interactive experiences. To do this, we're exploring and pushing the boundaries of new technologies, and sharing our learnings with the open source community. + +If you have ideas for collaboration, email us at opensource@embark-studios.com. + +We're also hiring full-time engineers to work with us in Stockholm! Check out our current job postings [here](https://embark.games/careers). + +## Issues + +### Feature Requests + +If you have ideas or how to improve our projects, you can suggest features by opening a GitHub issue. Make sure to include details about the feature or change, and describe any uses cases it would enable. + +Feature requests will be tagged as `enhancement` and their status will be updated in the comments of the issue. + +### Bugs + +When reporting a bug or unexpected behaviour in a project, make sure your issue descibes steps to reproduce the behaviour, including the platform you were using, what steps you took, and any error messages. + +Reproducible bugs will be tagged as `bug` and their status will be updated in the comments of the issue. + +### Wontfix + +Issues will be closed and tagged as `wontfix` if we decide that we do not wish to implement it, usually due to being misaligned with the project vision or out of scope. We will comment on the issue with more detailed reasoning. + +## Contribution Workflow + +### Open Issues + +If you're ready to contribute, start by looking at our open issues tagged as [`help wanted`](../../issues?q=is%3Aopen+is%3Aissue+label%3A"help+wanted") or [`good first issue`](../../issues?q=is%3Aopen+is%3Aissue+label%3A"good+first+issue"). + +You can comment on the issue to let others know you're interested in working on it or to ask questions. + +### Making Changes + +1. Fork the repository. + +2. Create a new feature branch. + +3. Make your changes. Ensure that there are no build errors by running the project with your changes locally. + +4. Open a pull request with a name and description of what you did. You can read more about working with pull requests on GitHub [here](https://help.github.com/en/articles/creating-a-pull-request-from-a-fork). + +5. A maintainer will review your pull request and may ask you to make changes. + +## Licensing + +Unless otherwise specified, all Embark open source projects are licensed under a dual MIT OR Apache-2.0 license, allowing licensees to chose either at their option. You can read more in each project's respective README. + +## Code of Conduct + +Please note that our projects are released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md) to ensure that they are welcoming places for everyone to contribute. By participating in any Embark open source project, you agree to abide by these terms. diff --git a/Cargo.lock b/Cargo.lock index db310c07..c8049cec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,7 +2,7 @@ # It is not intended for manual editing. [[package]] name = "adler32" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -10,15 +10,15 @@ name = "aho-corasick" version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "aho-corasick" -version = "0.7.3" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -26,21 +26,12 @@ name = "ansi_term" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "argon2rs" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "arrayvec" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -52,70 +43,55 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "flate2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "rmp-serde 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "atty" -version = "0.2.11" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "termion 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "autocfg" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "backtrace" -version = "0.3.32" +version = "0.3.38" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace-sys 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "backtrace-sys" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bitflags" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "blake2-rfc" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "build_const" -version = "0.2.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -128,71 +104,71 @@ name = "c2-chacha" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cargo-deny" -version = "0.2.5" +version = "0.3.0-beta" dependencies = [ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "askalono 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "cargo_metadata 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "cargo_metadata 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", + "codespan 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "codespan-reporting 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "erased-serde 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "fern 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", - "slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "slog-async 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slog-json 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slog-perf 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slog-term 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slog_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "spdx 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "spdx 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "twox-hash 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "twox-hash 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cargo_metadata" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cc" -version = "1.0.37" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cfg-if" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "chrono" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", @@ -204,11 +180,11 @@ version = "2.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -217,20 +193,25 @@ name = "cloudabi" version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "constant_time_eq" -version = "0.1.3" +name = "codespan" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] -name = "crc" -version = "1.8.1" +name = "codespan-reporting" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "codespan 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -238,29 +219,50 @@ name = "crc32fast" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-channel" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-deque" -version = "0.6.3" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-epoch" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -268,16 +270,16 @@ name = "crossbeam-queue" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-utils" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -285,19 +287,9 @@ name = "difference" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "dirs" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_users 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "either" -version = "1.5.2" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -305,7 +297,7 @@ name = "erased-serde" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -313,7 +305,7 @@ name = "failure" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -323,11 +315,19 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "fern" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "fixedbitset" version = "0.1.9" @@ -335,27 +335,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "flate2" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "miniz-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz_oxide_c_api 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "getrandom" -version = "0.1.6" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -366,17 +362,6 @@ dependencies = [ "unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "isatty" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "itoa" version = "0.4.4" @@ -384,61 +369,58 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "lazy_static" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "libc" -version = "0.2.58" +version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "lock_api" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "log" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "memchr" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "memoffset" -version = "0.2.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "miniz-sys" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "miniz_oxide" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "miniz_oxide_c_api" -version = "0.2.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", - "crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz_oxide 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -451,15 +433,7 @@ name = "num-integer" version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-traits" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -468,7 +442,7 @@ name = "num-traits" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -476,18 +450,37 @@ name = "num_cpus" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "numtoa" -version = "0.1.0" +name = "ordermap" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "ordermap" -version = "0.3.5" +name = "parking_lot" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot_core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "petgraph" @@ -503,6 +496,16 @@ name = "ppv-lite86" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "proc-macro-error" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "proc-macro2" version = "0.4.30" @@ -512,54 +515,56 @@ dependencies = [ ] [[package]] -name = "quote" -version = "0.6.12" +name = "proc-macro2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "rand" -version = "0.7.0" +name = "quote" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "getrandom 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "rand_chacha" -version = "0.2.0" +name = "quote" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "rand_core" -version = "0.3.1" +name = "rand" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "rand_core" -version = "0.4.0" +name = "rand_chacha" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "rand_core" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "getrandom 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -567,75 +572,35 @@ name = "rand_hc" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_os" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rayon" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon-core 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rayon-core" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "redox_syscall" -version = "0.1.54" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "redox_termios" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "redox_users" -version = "0.3.0" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "regex" @@ -643,22 +608,21 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "aho-corasick 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "utf8-ranges 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "utf8-ranges 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex" -version = "1.1.7" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "utf8-ranges 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -666,32 +630,29 @@ name = "regex-syntax" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ucd-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex-syntax" -version = "0.6.7" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "remove_dir_all" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rmp" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -700,28 +661,31 @@ version = "0.13.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rmp 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "rmp 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rustc-demangle" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "ryu" -version = "1.0.0" +name = "rustc_version" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] -name = "scoped_threadpool" -version = "0.1.9" +name = "ryu" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "scopeguard" -version = "0.3.3" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -730,7 +694,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -740,20 +704,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.94" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive" -version = "1.0.94" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -763,63 +727,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "slog" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "slog-async" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "slog-json" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", - "slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "slog-perf" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "slog-term" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "isatty 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "slog_derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -829,13 +737,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "spdx" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "spin" -version = "0.5.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "strsim" @@ -844,82 +752,75 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "structopt" -version = "0.2.18" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", - "structopt-derive 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt-derive 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "structopt-derive" -version = "0.2.18" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-error 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "syn" -version = "0.15.39" +version = "0.15.44" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "syn" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "synstructure" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "take_mut" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "tempfile" version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "term" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "termion" -version = "1.5.3" +name = "termcolor" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -927,7 +828,7 @@ name = "textwrap" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -935,7 +836,7 @@ name = "thread_local" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -943,27 +844,27 @@ name = "time" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "toml" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "twox-hash" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "ucd-util" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -981,7 +882,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "unicode-width" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -989,9 +890,14 @@ name = "unicode-xid" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "utf8-ranges" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -999,9 +905,14 @@ name = "vec_map" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "wasi" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "winapi" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1013,132 +924,139 @@ name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "winapi-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "wincolor" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [metadata] -"checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" +"checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" "checksum aho-corasick 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "81ce3d38065e618af2d7b77e10c5ad9a069859b4be3c2250f674af3840d9c8a5" -"checksum aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e6f484ae0c99fec2e858eb6134949117399f222608d84cadb3f58c1f97c2364c" +"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -"checksum argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f67b0b6a86dae6e67ff4ca2b6201396074996379fba2b92ff649126f37cb392" -"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" +"checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" "checksum askalono 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e62de03dfcb3e55b6c05fbcc2fd51d712be3904f3981ee2ae6252087afc3f1e1" -"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" -"checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf" -"checksum backtrace 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)" = "18b50f5258d1a9ad8396d2d345827875de4261b158124d4c819d9b351454fae5" -"checksum backtrace-sys 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)" = "5b3a000b9c543553af61bc01cbfc403b04b5caa9e421033866f2e98061eb3e61" -"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" -"checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" -"checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" +"checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" +"checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875" +"checksum backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)" = "690a62be8920ccf773ee00ef0968649b0e724cda8bd5b12286302b4ae955fdf5" +"checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b" +"checksum bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a606a02debe2813760609f57a64a2ffd27d9fdf5b2f133eaca0b248dd92cdd2" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" "checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101" -"checksum cargo_metadata 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "929766d993a2fde7a0ae962ee82429069cd7b68839cd9375b98efd719df65d3a" -"checksum cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "39f75544d7bbaf57560d2168f28fd649ff9c76153874db88bdbdfd839b1a7e7d" -"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" -"checksum chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "77d81f58b7301084de3b958691458a53c3f7e0b1d702f77e550b6a88e3a88abe" +"checksum cargo_metadata 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "700b3731fd7d357223d0000f4dbf1808401b694609035c3c411fbc0cd375c426" +"checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be" +"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +"checksum chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e8493056968583b0193c1bb04d6f7684586f3726992d6c573261941a895dbd68" "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" -"checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" +"checksum codespan 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "51c1d25c09c2f9e0d86d834345d04c8765de69d90b9592d6f1e4ef529d92dfde" +"checksum codespan-reporting 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bdba3142d67d3a2b1b67852043f98f9318d3a8d6d7b8d24ae4a3b3909399c1a7" "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" -"checksum crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "05e44b8cf3e1a625844d1750e1f7820da46044ff6d28f4d43e455ba3e5bb2c13" -"checksum crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "04c9e3102cc2d69cd681412141b390abd55a362afc1540965dad0ad4d34280b4" +"checksum crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2d818a4990769aac0c7ff1360e233ef3a41adcb009ebb2036bf6915eb0f6b23c" +"checksum crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c8ec7fcd21571dc78f96cc96243cab8d8f035247c3efd16c687be154c3fa9efa" +"checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" +"checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" "checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" -"checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c" +"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" "checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" -"checksum dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" -"checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b" +"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" "checksum erased-serde 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3beee4bc16478a1b26f2e80ad819a52d24745e292f521a63c16eea5f74b7eb60" "checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" +"checksum fern 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "29d26fa0f4d433d1956746e66ec10d6bf4d6c8b93cd39965cceea7f7cc78c7dd" "checksum fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" -"checksum flate2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "550934ad4808d5d39365e5d61727309bf18b3b02c6c56b729cb92e7dd84bc3d8" -"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" -"checksum getrandom 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e65cce4e5084b14874c4e7097f38cab54f47ee554f9194673456ea379dcc4c55" +"checksum flate2 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "2adaffba6388640136149e18ed080b77a78611c1e1d6de75aedcdf78df5d4682" +"checksum getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "473a1265acc8ff1e808cd0a1af8cee3c2ee5200916058a2ca113c29f2d903571" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" -"checksum isatty 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e31a8281fc93ec9693494da65fbf28c0c2aa60a2eaec25dc58e2f31952e95edc" "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" -"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" -"checksum libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "6281b86796ba5e4366000be6e9e18bf35580adf9e63fbe2294aadb587613a319" -"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" -"checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39" -"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" +"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" +"checksum lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8912e782533a93a167888781b836336a6ca5da6175c05944c86cf28c31104dc" +"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" +"checksum memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f" "checksum miniz-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9e3ae51cea1576ceba0dde3d484d30e6e5b86dee0b2d412fe3a16a15c98202" -"checksum miniz_oxide 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c468f2369f07d651a5d0bb2c9079f8488a66d5466efe42d0c5c6466edcb7f71e" -"checksum miniz_oxide_c_api 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7fe927a42e3807ef71defb191dc87d4e24479b221e67015fe38ae2b7b447bab" +"checksum miniz_oxide 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7108aff85b876d06f22503dcce091e29f76733b2bfdd91eebce81f5e68203a10" "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" "checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" -"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" "checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" -"checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" "checksum ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063" +"checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" +"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" "checksum petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f" "checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" +"checksum proc-macro-error 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aeccfe4d5d8ea175d5f0e4a2ad0637e0f4121d63bd99d356fb1f39ab2e7c6097" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -"checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" -"checksum rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c" -"checksum rand_chacha 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e193067942ef6f485a349a113329140d0ab9e2168ce92274499bb0e9a4190d9d" -"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -"checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" -"checksum rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "615e683324e75af5d43d8f7a39ffe3ee4a9dc42c5c701167a71dc59c3a493aca" +"checksum proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afdc77cc74ec70ed262262942ebb7dac3d479e9e5cfa2da1841c0806f6cdabcc" +"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" +"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" +"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" "checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -"checksum rayon 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a4b0186e22767d5b9738a05eab7c6ac90b15db17e5b5f9bd87976dd7d89a10a4" -"checksum rayon-core 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbe0df8435ac0c397d467b6cad6d25543d06e8a019ef3f6af3c384597515bd2" -"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -"checksum redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)" = "12229c14a0f65c4f1cb046a3b52047cdd9da1f4b30f8a39c5063c8bae515e252" -"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" -"checksum redox_users 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe5204c3a17e97dde73f285d49be585df59ed84b50a872baf416e73b62c3828" +"checksum rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83a27732a533a1be0a0035a111fe76db89ad312f6f0347004c220c57f209a123" +"checksum rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "98dcf634205083b17d0861252431eb2acbfb698ab7478a2d20de07954f47ec7b" +"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" "checksum regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384" -"checksum regex 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0b2f0808e7d7e4fb1cb07feb6ff2f4bc827938f24f8c2e6a3beb7370af544bdd" +"checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd" "checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7" -"checksum regex-syntax 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d76410686f9e3a17f06128962e0ecc5755870bb890c34820c7af7f1db2e1d48" +"checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" -"checksum rmp 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a3d45d7afc9b132b34a2479648863aa95c5c88e98b32285326a6ebadc80ec5c9" +"checksum rmp 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f594cb7ff8f1c5a7907f6be91f15795c8301e0d5718eb007fb5832723dd716e" "checksum rmp-serde 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)" = "011e1d58446e9fa3af7cdc1fb91295b10621d3ac4cb3a85cc86385ee9ca50cd3" -"checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af" +"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" +"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" -"checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" -"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" +"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)" = "076a696fdea89c19d3baed462576b8f6d663064414b5c793642da8dfeb99475b" -"checksum serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)" = "ef45eb79d6463b22f5f9e16d283798b7c0175ba6050bc25c1a946c122727fe7b" +"checksum serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "9796c9b7ba2ffe7a9ce53c2287dfc48080f4b2b362fcc245a259b3a7201119dd" +"checksum serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "4b133a43a1ecd55d4086bd5b4dc6c1751c68b1bfbeba7a5040442022c7e7c02e" "checksum serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "051c49229f282f7c6f3813f8286cc1e3323e8051823fce42c7ea80fe13521704" -"checksum slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1e1a2eec401952cd7b12a84ea120e2d57281329940c3f93c2bf04f462539508e" -"checksum slog-async 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e544d16c6b230d84c866662fe55e31aacfca6ae71e6fc49ae9a311cb379bfc2f" -"checksum slog-json 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ddc0d2aff1f8f325ef660d9a0eb6e6dcd20b30b3f581a5897f58bf42d061c37a" -"checksum slog-perf 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cca2f6919a4fa9b1746df25aa5a046fbc520629b9ff068a3b980c9e19884a812" -"checksum slog-term 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5951a808c40f419922ee014c15b6ae1cd34d963538b57d8a4778b9ca3fff1e0b" -"checksum slog_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9eff3b513cf2e0d1a60e1aba152dc72bedc5b05585722bb3cebd7bcb1e31b98f" "checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" -"checksum spdx 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5c33f8a5efba64068cb053c3196e92f4cf0013c002bc298dc84c0b05c6feeff5" -"checksum spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44363f6f51401c34e7be73db0db371c04705d35efbe9f7d6082e03a921a32c55" +"checksum spdx 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea9c247b776cc364282c33ca50a292d5151c8af5e7d27f666202afc1476c4a82" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -"checksum structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "16c2cdbf9cc375f15d1b4141bc48aeef444806655cd0e904207edc8d68d86ed7" -"checksum structopt-derive 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "53010261a84b37689f9ed7d395165029f9cc7abb9f56bbfe86bee2597ed25107" -"checksum syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d960b829a55e56db167e861ddb43602c003c7be0bee1d345021703fac2fb7c" +"checksum structopt 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe8d3289b63ef2f196d89e7701f986583c0895e764b78f052a55b9b5d34d84a" +"checksum structopt-derive 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f3add731f5b4fb85931d362a3c92deb1ad7113649a8d51701fb257673705f122" +"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" -"checksum take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" -"checksum term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" -"checksum termion 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a8fb22f7cde82c8220e5aeacb3258ed7ce996142c77cba193f203515e26c330" +"checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" -"checksum toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b8c96d7873fa7ef8bdeb3a9cda3ac48389b4154f32b9803b4bc26220b677b039" -"checksum twox-hash 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "51609966a9ed0864eafb5fc6a679733401f4a47b53c51314dbc38b9825dea966" -"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" +"checksum toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7aabe75941d914b72bf3e5d3932ed92ce0664d49d8432305a8b547c37227724" +"checksum twox-hash 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bfd5b7557925ce778ff9b9ef90e3ade34c524b5ff10e239c69a42d546d2af56" +"checksum ucd-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa9b3b49edd3468c0e6565d85783f51af95212b6fa3986a5500954f00b460874" "checksum unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "141339a08b982d942be2ca06ff8b076563cbe223d1befd5450716790d44e2426" "checksum unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1967f4cdfc355b37fd76d2a954fb2ed3871034eb4f26d60537d88795cfc332a9" -"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" +"checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -"checksum utf8-ranges 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9d50aa7650df78abf942826607c62468ce18d9019673d4a2ebe1865dbb96ffde" +"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +"checksum utf8-ranges 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" -"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" +"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" +"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "96f5016b18804d24db43cebf3c77269e7569b8954a8464501c216cc5e070eaa9" diff --git a/Cargo.toml b/Cargo.toml index 1026c72e..2cba1829 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "cargo-deny" description = "Cargo plugin to help you manage large dependency graphs" repository = "https://github.com/EmbarkStudios/cargo-deny" -version = "0.2.5" +version = "0.3.0-beta" authors = ["Embark ", "Jake Shadle "] edition = "2018" license = "MIT OR Apache-2.0" @@ -15,10 +15,11 @@ exclude = [ # Exclude the zstd cache for now, as we are using the vanilla # askalono with support for gzip "spdx_cache.bin.zstd", + "examples/", + ".github/", ] [badges] -travis-ci = { repository = "EmbarkStudios/cargo-deny", branch = "master" } maintenance = { status = "actively-developed" } [[bin]] @@ -27,28 +28,29 @@ path = "src/cargo-deny/main.rs" [dependencies] ansi_term = "0.11.0" -atty = "0.2.11" -cargo_metadata = "0.8.0" +atty = "0.2.13" +cargo_metadata = "0.8.2" +chrono = "0.4.9" clap = "2.33.0" +codespan = "0.4.1" +codespan-reporting = "0.4.1" +crossbeam = "0.7.2" erased-serde = "0.3.9" failure = "0.1.5" -log = "0.4.6" +fern = "0.5.8" +log = "0.4.8" +parking_lot = "0.9.0" petgraph = "0.4.13" -rayon = "1.1.0" -regex = "1.1.7" +rayon = "1.2.0" +regex = { version = "1.3.1", default-features = true } semver = "0.9.0" -serde = { version = "1.0.92", features = ["derive"] } -serde_json = "1.0.39" -slog = { version = "2.4.1", features = ["max_level_trace", "release_max_level_debug"] } -slog-async = "2.3.0" -slog_derive = "0.1.1" -slog-json = "2.3.0" -slog-perf = "0.2.0" -slog-term = "2.4.0" -spdx = "0.1.0" -structopt = "0.2.16" -toml = "0.5.1" -twox-hash = { version = "1.4.1", default-features = false } +serde = { version = "1.0.101", features = ["derive"] } +serde_json = "1.0.40" +smallvec = "0.6.10" +spdx = "0.2.0" +structopt = "0.3.2" +toml = "0.5.3" +twox-hash = { version = "1.5.0", default-features = false } [dependencies.askalono] version = "0.3.0" @@ -57,7 +59,8 @@ version = "0.3.0" # We use this for pretty printing errors difference = "2.0.0" # Avoid loading license check many times -lazy_static = "1.3.0" +lazy_static = "1.4.0" # We use this for creating fake crate directories for # crawling license files on disk -tempfile = "3.0.8" +tempfile = "3.1.0" + diff --git a/README.md b/README.md index f082b341..2e5e42f6 100644 --- a/README.md +++ b/README.md @@ -1,174 +1,214 @@ # ❌ cargo-deny -[![Build Status](https://travis-ci.com/EmbarkStudios/cargo-deny.svg?branch=master)](https://travis-ci.com/EmbarkStudios/cargo-deny) +[![Build Status](https://github.com/EmbarkStudios/cargo-deny/workflows/CI/badge.svg)](https://github.com/EmbarkStudios/cargo-deny/actions?workflow=CI) [![Latest version](https://img.shields.io/crates/v/cargo-deny.svg)](https://crates.io/crates/cargo-deny) [![Docs](https://docs.rs/cargo-deny/badge.svg)](https://docs.rs/cargo-deny) +[![Contributor Covenant](https://img.shields.io/badge/contributor%20covenant-v1.4%20adopted-ff69b4.svg)](CODE_OF_CONDUCT.md) +[![Embark](https://img.shields.io/badge/embark-open%20source-blueviolet.svg)](http://embark.games) One of the key selling points of Rust is the ever growing and improving ecosystem of crates available that can be easily added to your project incredibly easily via `cargo`. This is great! However, the larger the project is and the more dependencies you have, the harder it is to keep -track of certain things, especially as a project evolves over time, which is what `cargo-deny` tries to help -you with. +track of certain things, especially as a project evolves over time, which is what `cargo-deny` tries to help you with. -* [Licenses](#licenses) - Configure which licenses are allowed -* [Bans](#crate-bans) - Configure whether certain crates are allowed to be in your dependency graph +* [Licenses](#licenses---cargo-deny-check-license) - Configure which license terms you accept +* [Bans](#crate-bans---cargo-deny-check-ban) - Configure whether particular crates are allowed in your dependency graph ## tl;dr -* `cargo-deny check ` - verify licenses for a crate graph -* `cargo-deny check ` - verify crate graph doesn't contain certain crates -* `cargo-deny list` - list all of the licenses in a crate graph +* `cargo deny check ` - verify crate graph only contains acceptable license requirements +* `cargo deny check ` - verify crate graph doesn't contain certain crates +* `cargo deny list` - list all of the licenses for all crates in a project -## Licenses - `cargo-deny check license` +## Licenses - `cargo deny check license` -One important aspect that one must always keep in mind when using code from other people is what the licensing -of that code is and whether it fits the requirements of your project. Luckily, most of the crates in the Rust -ecosystem tend to follow the example set forth by Rust itself, namely dual-license `MIT OR Apache-2.0`, but of -course, that is not always the case. +One important aspect that one must always keep in mind when using code from other people is what the licensing of that code is and whether it fits the requirements of your project. Luckily, most of the crates in the Rust ecosystem tend to follow the example set forth by Rust itself, namely dual-license `MIT OR Apache-2.0`, but of course, that is not always the case. -So `cargo-deny` allows you to ensure that all of your dependencies meet the requirements you want. +So `cargo-deny` allows you to ensure that all of your dependencies have license requirements that align with your configuration. -1. What happens when a crate is unlicensed? `allow` / `deny` / `warn` -1. What happens when a crate's license can't be determined? `allow` / `deny` / `warn` -1. Explicitly allow or deny 1 or more licenses. -1. Skip checking certain crates as long as they still have the same license information. -1. Ignore specific `LICENSE*` files. `cargo-deny` uses the [askalono](https://github.com/amzn/askalono) crate -to parse and score license text as being a certain license, but some license text can be modified to such -an extent that it makes it difficult to automatically determine it. +### The `[licenses]` section + +Contains all of the configuration for `cargo deny check license` + +#### The `unlicensed` field + +Determines what happens when a crate has not explicitly specified its license terms, and no license +information could be easily detected via `LICENSE*` files in the crate's source. + +* `deny` (default) - All unlicensed crates will emit an error and fail the license check +* `allow` - All unlicensed crates will be allowed with no feedback +* `warn` - All unlicensed crates will show a warning, but will not fail the license check + +#### The `allow` and `deny` fields + +The licenses that should be allowed or denied. The license must be a valid SPDX v2.1 identifier, which must either be in version 3.6 of the [SPDX License List](https://spdx.org/licenses/), with an optional [exception](https://spdx.org/licenses/exceptions-index.html) specified by `WITH `, or else a user defined license reference denoted by `LicenseRef-` for a license not on the SPDX License List. + +The same license cannot appear in both the `allow` and `deny` lists. + +#### The `allow-osi-fsf-free` field + +Determines what happens when licenses aren't explicitly allowed or denied, but are marked as [OSI Approved](https://opensource.org/licenses) or [FSF Free/Libre](https://www.gnu.org/licenses/license-list.en.html) in version 3.6 of the [SPDX License List](https://spdx.org/licenses/). + +* `both` - The license is accepted if it is both OSI approved and FSF Free +* `either` - The license is accepted if it is either OSI approved or FSF Free +* `osi-only` - The license is accepted if it is OSI approved and not FSF Free +* `fsf-only` - The license is accepted if it is FSF Free and not OSI approved +* `neither` (default) - No special consideration is given the license + +#### The `confidence-threshold` field + +`cargo-deny` uses [askalono](https://github.com/amzn/askalono) to determine the license of a license file, the confidence threshold value determines if askalono's determination meets your +minimum requirements. The higher the value, the more closely the license text must be to the canonical license text of a valid SPDX license file. + +`0.0` - `1.0` (default `0.8`) + +#### The `clarify` field + +In some exceptional cases, the crate does not have easily machine readable license information, and would by default be considered "unlicensed" by `cargo-deny`. As a (hopefully) temporary patch for using the crate, you can specify a clarification for a crate where you can specify the license expression based on your understanding of the requirements as described by the license holder. + +##### The `name` field + +The name of the crate that you are clarifying + +##### The `version` field + +An optional version constraint specifying the range of crate versions you are clarifying. Defaults to all versions (`*`). + +##### The `expression` field + +The [SPDX license expression](https://spdx.org/spdx-specification-21-web-version#h.jxpfx0ykyb60) you are specifying as the license requirements for the crate in question. + +##### The `license-files` field + +Contains one or more files that will be checked to ensure the license expression still applies to a version of the crate. Each file is a `path` to the file relative to the crate route, and a `hash` of the contents to detect changes between versions. This hash is printed out when license files cannot have their license determined with high confidence. ### Example config ```toml [licenses] -# If a crate doesn't have a license, error unlicensed = "deny" -# If a crate has a LICENSE* file, but it can't be determined, error -unknown = "deny" -# We want really high confidence when inferring licenses from text -confidence_threshold = 0.92 -# The only licenses we allow. These must be valid SPDX identifiers, at least syntactically, -# but nothing stops you from using your own license identifier for your private crates +allow-osi-fsf-free = "either" +confidence-threshold = 0.92 +deny = [ + "GPL-3.0-or-later", +] allow = [ - "Embark-Proprietary", "Apache-2.0", - "BSD-2-Clause", - "BSD-2-Clause-FreeBSD", + "Apache-2.0 WITH LLVM-exception", "BSD-3-Clause", - "BSL-1.0", - "CC0-1.0", - "FTL", - "ISC", - "LLVM-exception", "MIT", - "MPL-2.0", - "Unicode-DFS-2016", - "Unlicense", "Zlib", ] -skip = [ - # ring has a rather complicated LICENSE file due to reasons spelled out - # in said LICENSE file, but is basically OpenSSL for older parts, and ISC - # for newer parts - { name = "ring", licenses = [] }, - # webpki uses an ISC license but it only has a 0.83 confidence level - { name = "webpki", licenses = [] }, -] -[[licenses.ignore]] -name = "rustls" -license_files = [ - # This is a top-level LICENSE that just spells out the *actual* 3 - # licenses that can be used with the crate, which askalono is unable - # to score - { path = "LICENSE", hash = 0xe567c411 }, +# ring has a rather complicated license file, and unfortunately does not +# provide an SPDX expression in the `license` toml +[[licenses.clarify]] +name = "ring" +# SPDX considers OpenSSL to encompass both the OpenSSL and SSLeay licenses +# https://spdx.org/licenses/OpenSSL.html +# ISC - Both BoringSSL and ring use this for their new files +# MIT - "Files in third_party/ have their own licenses, as described therein. The MIT +# license, for third_party/fiat, which, unlike other third_party directories, is +# compiled into non-test libraries, is included below." +# OpenSSL - Obviously +expression = "ISC AND MIT AND OpenSSL" +license-files = [ + { path = "LICENSE", hash = 0xbd0eed23 }, ] ``` -## Crate bans - `cargo-deny check ban` +## Crate bans - `cargo deny check ban` ### Use Case - Keeping certain crates out of your dependency graph Sometimes, certain crates just don't fit in your project, so you have to remove them. However, nothing really stops them from sneaking back in due to small changes, like updating a crate to -a new version that happens to add it as a dependency, or an existing dependency just changing +a new version that happens to add it as a dependency, or an existing dependency just changing what crates are included in the default feature set. For example, we previously depended on OpenSSL as it is the "default" for many crates that deal -with HTTP traffic. This was extremely annoying as it required us to have OpenSSL development libraries -installed on Windows, for both individuals and CI. We moved all of our dependencies to use the -much more streamlined `native-tls` and `ring` crates instead, and now we can make sure that OpenSSL -doesn't return from the grave by being pulled in as a default feature of some future HTTP crate -we might use. - -1. Dis/allow certain crates in your dependency graph. +with HTTP traffic. This was extremely annoying as it required us to have OpenSSL development libraries installed on Windows, for both individuals and CI. We moved all of our dependencies to use the much more streamlined `native-tls` and `ring` crates instead, and now we can make sure that OpenSSL doesn't return from the grave by being pulled in as a default feature of some future HTTP crate we might use. ### Use Case - Get a handle on duplicate versions One thing that is part of the tradeoff of being able to use so many crates, is that they all won't -necessarily agree on what versions of a dependency they want to use, and cargo and rust will happily -chug along compiling all of them. This is great when just trying out a new dependency as quickly as -possible, but it does come with some long term costs. Crate fetch times (and disk space) are increased, -but in particular, **compile times**, and ultimately your binary sizes, also increase. If you are made aware -that you depend on multiple versions of the same crate, you at least have an opportunity to decide -how you want to handle them. - -1. What happens when multiple versions of a crate are used? `allow` / `deny` / `warn` -1. Skip certain versions of crates, sometimes you just need to wait for a crate -to get a new release, or sometimes a little duplication is ok and not worth the effort -to "fix", but you are at least aware of it and explicitly allowing it, rather than suffering in -ignorance. -1. The `-g ` cmd line option on the `check` subcommand instructs `cargo-deny` to create -a [dotgraph](https://www.graphviz.org/) if multiple versions of a crate are detected and that -isn't allowed. A single graph will be created for each crate, with each version as a terminating -node in the graph with the full graph of crates that reference each version to more easily -show you why a particular version is included. It also highlights the lowest version's path -in ![red](https://placehold.it/15/ff0000/000000?text=+), and, if it differs from the lowest version, -the "simplest" path is highlighted in ![blue](https://placehold.it/15/0000FF/000000?text=+). +necessarily agree on what versions of a dependency they want to use, and cargo and rust will happily chug along compiling all of them. This is great when just trying out a new dependency as quickly as possible, but it does come with some long term costs. Crate fetch times (and disk space) are increased, but in particular, **compile times**, and ultimately your binary sizes, also increase. If you are made aware that you depend on multiple versions of the same crate, you at least have an opportunity to decide how you want to handle them. + +### The `[bans]` section + +Contains all of the configuration for `cargo deny check ban` + +#### The `multiple-versions` field + +Determines what happens when multiple versions of the same crate are encountered. + +* `deny` - Will emit an error for each crate with duplicates and fail the check. +* `warn` (default) - Prints a warning for each crate with duplicates, but does not fail the check. +* `allow` - Ignores duplicate versions of the same crate. + +#### The `highlight` field + +When multiple versions of the same crate are encountered and the `multiple-versions` is set to `warn` or `deny`, using the `-g ` option will print out a [dotgraph](https://www.graphviz.org/) of each of the versions and how they were included into the graph. This field determines how the graph is colored to help you quickly spot good candidates for removal or updating. + +* `lowest-version` - Highlights the path to the lowest duplicate version. Highlighted in ![red](https://placehold.it/15/ff0000/000000?text=+) +* `simplest-path` - Highlights the path to the duplicate version with the fewest number of total +edges to the root of the graph, which will often be the best candidate for removal and/or upgrading. Highlighted in ![blue](https://placehold.it/15/0000FF/000000?text=+). +* `all` - Highlights both the `lowest-version` and `simplest-path`. If they are the same, they are only highlighted in ![red](https://placehold.it/15/ff0000/000000?text=+). ![Imgur](https://i.imgur.com/xtarzeU.png) +#### The `allow` and `deny` fields + +As with `licenses`, these determine which specificy crates and version ranges are actually allowed or denied. + +#### The `skip` field + +When denying duplicate versions, it sometimes takes time to update versions in transitive dependencies, or big changes in core often used crates such as winapi and others to ripple through the rest of the ecosystem. In such cases, it can be ok to remove certain versions from consideration so that they won't trigger failures due to multiple versions, and can eventually be removed once all crates have update to the later version(s). + +Note entries in the `skip` field that never match a crate in your graph will have a warning printed that they never matched, allowing you to clean up your configuration as your crate graph changes over time. + +#### Crate specifier + +The `allow`, `deny`, and `skip` fields all use a crate identifier to specify what crate(s) they want to match against. + +##### The `name` field + +The name of the crate. + +##### The `version` field + +An optional version constraint specifying the range of crate versions that will match. Defaults to all versions (`*`). + ### Example Config ```toml [bans] -# Emit an error if we detect multiple versions of the same crate -multiple_versions = "deny" +multiple-versions = "deny" deny = [ - # OpenSSL = Just Say No. + # You can never be too sure { name = "openssl" }, ] skip = [ - # The issue where mime_guess is using a really old version of - # unicase has been fixed, it just needs to be released - # https://github.com/sfackler/rust-phf/issues/143 - { name = "unicase", version = "=1.4.2" }, - # rayon/rayon-core use very old versions of crossbeam crates, - # so skip them for now until rayon updates them - { name = "crossbeam-deque", version = "=0.2.0" }, - { name = "crossbeam-epoch", version = "=0.3.1" }, - { name = "crossbeam-utils", version = "=0.2.2" }, - # tokio-reactor, wasmer, and winit all use an older version - # of parking_lot - { name = "parking_lot", version = "=0.7.1" }, - { name = "parking_lot_core", version = "=0.4.0" }, - { name = "lock_api", version = "=0.1.5" }, - # rand_core depends on a newever version of itself... - { name = "rand_core", version = "=0.3.1" }, - # lots of transitive dependencies use the pre-1.0 version - # of scopeguard - { name = "scopeguard", version = "=0.3.3" }, - # tons of transitive dependencies use this older winapi version - { name = "winapi", version = "=0.2.8" }, + # askalono 0.3.0 uses an ancient regex version which pulls + # in other duplicates + { name = "regex", version = "=0.2.11" }, + { name = "regex-syntax", version = "=0.5.6" }, + { name = "aho-corasick", version = "=0.6.10" }, + + # some macro crates use the pre 1.0 syn dependencies + { name = "syn", version = "<=0.15" }, + { name = "proc-macro2", version = "<=0.4" }, + { name = "quote", version = "<=0.6" }, + { name = "unicode-xid", version = "=0.1" }, ] ``` ## CI Usage `cargo-deny` is primarily meant to be used in your CI so it can do automatic verification for all -your changes, for an example of this, you can look at the [self check](https://github.com/EmbarkStudios/cargo-deny/blob/master/.travis.yml#L77-L87) job for this repository, which just checks `cargo-deny` itself using -the [deny.toml](deny.toml) config. +your changes, for an example of this, you can look at the [self check](https://github.com/EmbarkStudios/cargo-deny/blob/master/.travis.yml#L77-L87) job for this repository, which just checks `cargo-deny` itself using the [deny.toml](deny.toml) config. -## List - `cargo-deny list` +## List - `cargo deny list` Similarly to [cargo-license](https://github.com/onur/cargo-license), print out the licenses and crates that use them. @@ -189,6 +229,12 @@ that use them. ![Imgur](https://i.imgur.com/14l8a5K.png) +## Contributing + +We welcome community contributions to this project. + +Please read our [Contributor Guide](CONTRIBUTING.md) for more information on how to get started. + ## License Licensed under either of diff --git a/deny.toml b/deny.toml index a9e76312..3b3ac2b9 100644 --- a/deny.toml +++ b/deny.toml @@ -1,62 +1,32 @@ [bans] -multiple_versions = "deny" +multiple-versions = "deny" deny = [ # You can never be too sure { name = "openssl" }, ] skip = [ - # rand depends on a multiple versions of rand_core... - { name = "rand_core", version = "=0.3.1" }, - { name = "rand_core", version = "=0.4.0" }, - # rmp depends on an old version of num-traits, just ignore - # for now - { name = "num-traits", version = "=0.1.43" }, # askalono 0.3.0 uses an ancient regex version which pulls # in other duplicates { name = "regex", version = "=0.2.11" }, { name = "regex-syntax", version = "=0.5.6" }, { name = "aho-corasick", version = "=0.6.10" }, + + # some macro crates use the pre 1.0 syn dependencies + { name = "syn", version = "<=0.15" }, + { name = "proc-macro2", version = "<=0.4" }, + { name = "quote", version = "<=0.6" }, + { name = "unicode-xid", version = "=0.1" }, ] [licenses] unlicensed = "deny" unknown = "deny" +allow-osi-fsf-free = "neither" # We want really high confidence when inferring licenses from text -confidence_threshold = 0.93 +confidence-threshold = 0.93 allow = [ "Apache-2.0", "BSD-2-Clause", - "BSD-2-Clause-FreeBSD", - "BSD-3-Clause", - "BSL-1.0", - "CC0-1.0", - "ISC", "MIT", - "MPL-2.0", - "Unicode-DFS-2016", - "Unlicense", "Zlib", ] -skip = [ - # For illustration purposes: - - # ring has a rather complicated LICENSE file due to reasons spelled out - # in said LICENSE file, but is basically OpenSSL for older parts, and ISC - # for newer parts - #{ name = "ring", licenses = [] }, -] - -[[licenses.ignore]] -name = "crossbeam-queue" -license_files = [ - # A BSD-2-Clause-FreeBSD license, but it has the lowest confidence score - # of all the current licenses, so ignore it so we can raise the threshold - { path = "LICENSE-THIRD-PARTY", hash = 0x7e40bc60 }, -] - -[[licenses.ignore]] -name = "adler32" -license_files = [ - # Zlib with a slightly lower score (at least with vanilla askalono 0.3.0) - { path = "LICENSE-ZLIB", hash = 0x35858a2e }, -] \ No newline at end of file diff --git a/examples/01_allow_license/Cargo.lock b/examples/01_allow_license/Cargo.lock new file mode 100644 index 00000000..be04a5d9 --- /dev/null +++ b/examples/01_allow_license/Cargo.lock @@ -0,0 +1,6 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "allow-license" +version = "0.1.0" + diff --git a/examples/01_allow_license/Cargo.toml b/examples/01_allow_license/Cargo.toml new file mode 100644 index 00000000..fcdc615d --- /dev/null +++ b/examples/01_allow_license/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "allow-license" +version = "0.1.0" +authors = ["Jake Shadle "] +license = "MIT OR Apache-2.0" +edition = "2018" + +[[bin]] +name = "allow-license" +path = "main.rs" diff --git a/examples/01_allow_license/README.md b/examples/01_allow_license/README.md new file mode 100644 index 00000000..f61d324f --- /dev/null +++ b/examples/01_allow_license/README.md @@ -0,0 +1,23 @@ +# 01_allow_license + +This example shows how to selectively allow certain licenses that will be checked against +the license requirements of every crate in your dependency graph. + +## Requirement + +```toml +license = "MIT OR Apache-2.0" +``` + +## Config + +```toml +[license] +allow = [ "MIT" ] +``` + +## Description + +The example crate uses the same dual-licensing of the actual cargo-deny project, namely `MIT OR Apache-2.0`, +and this expression is checked against our configured allow list, which is simply `MIT`. This license check +passes because the license expression allows you to license it under either `MIT` or `Apache-2.0` at your option. diff --git a/examples/01_allow_license/deny.toml b/examples/01_allow_license/deny.toml new file mode 100644 index 00000000..e2308e96 --- /dev/null +++ b/examples/01_allow_license/deny.toml @@ -0,0 +1,2 @@ +[licenses] +allow = [ "MIT" ] diff --git a/examples/01_allow_license/main.rs b/examples/01_allow_license/main.rs new file mode 100644 index 00000000..f328e4d9 --- /dev/null +++ b/examples/01_allow_license/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/examples/02_deny_license/Cargo.lock b/examples/02_deny_license/Cargo.lock new file mode 100644 index 00000000..cc4c9a80 --- /dev/null +++ b/examples/02_deny_license/Cargo.lock @@ -0,0 +1,6 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "deny-license" +version = "0.1.0" + diff --git a/examples/02_deny_license/Cargo.toml b/examples/02_deny_license/Cargo.toml new file mode 100644 index 00000000..11539dda --- /dev/null +++ b/examples/02_deny_license/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "deny-license" +version = "0.1.0" +authors = ["Jake Shadle "] +license = "MIT AND Apache-2.0" +edition = "2018" + +[[bin]] +name = "allow-license" +path = "main.rs" diff --git a/examples/02_deny_license/README.md b/examples/02_deny_license/README.md new file mode 100644 index 00000000..88c9af64 --- /dev/null +++ b/examples/02_deny_license/README.md @@ -0,0 +1,25 @@ +# 02_deny_license + +This example shows how to explicitly deny certain licenses that will be checked against +the license requirements of every crate in your dependency graph. + +## Requirement + +```toml +license = "MIT AND Apache-2.0" +``` + +## Config + +```toml +[licenses] +allow = [ "MIT" ] +deny = [ "Apache-2.0" ] +``` + +## Description + +Just as we can allow specific licenses, we can deny specific ones via `[licenses.deny]`. Note that the license requirement +has changed to use the operator `AND` instead of `OR` which means that the user is required to license the crate under +both of the licenses, so even though we still allow `MIT`, our denial of `Apache-2.0` causes the expression to fail and +cargo-deny to emit an error that we did not accede to the license requirements of the crate. diff --git a/examples/02_deny_license/deny.toml b/examples/02_deny_license/deny.toml new file mode 100644 index 00000000..eed551b0 --- /dev/null +++ b/examples/02_deny_license/deny.toml @@ -0,0 +1,3 @@ +[licenses] +allow = [ "MIT" ] +deny = [ "Apache-2.0" ] diff --git a/examples/02_deny_license/main.rs b/examples/02_deny_license/main.rs new file mode 100644 index 00000000..f328e4d9 --- /dev/null +++ b/examples/02_deny_license/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/src/ban.rs b/src/ban.rs index f370bb92..5d13cddd 100644 --- a/src/ban.rs +++ b/src/ban.rs @@ -1,11 +1,12 @@ use crate::LintLevel; +use codespan_reporting::diagnostic::Diagnostic; use failure::Error; use rayon::prelude::*; use semver::{Version, VersionReq}; use serde::Deserialize; use std::{cmp, collections::HashMap, fmt}; -#[derive(Deserialize, Debug, PartialOrd, PartialEq, Ord, Eq)] +#[derive(Deserialize)] pub struct CrateId { // The name of the crate pub name: String, @@ -26,8 +27,8 @@ const fn highlight() -> GraphHighlight { GraphHighlight::All } -#[derive(Deserialize, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] +#[derive(Deserialize, PartialEq, Eq, Copy, Clone)] +#[serde(rename_all = "kebab-case")] pub enum GraphHighlight { /// Highlights the path to a duplicate dependency with the fewest number /// of total edges, which tends to make it the best candidate for removing @@ -40,17 +41,18 @@ pub enum GraphHighlight { impl GraphHighlight { #[inline] - fn simplest(&self) -> bool { - *self == GraphHighlight::SimplestPath || *self == GraphHighlight::All + fn simplest(self) -> bool { + self == Self::SimplestPath || self == Self::All } #[inline] - fn lowest_version(&self) -> bool { - *self == GraphHighlight::LowestVersion || *self == GraphHighlight::All + fn lowest_version(self) -> bool { + self == Self::LowestVersion || self == Self::All } } #[derive(Deserialize)] +#[serde(rename_all = "kebab-case")] pub struct Config { /// Disallow multiple versions of the same crate #[serde(default = "lint_warn")] @@ -60,27 +62,151 @@ pub struct Config { pub highlight: GraphHighlight, /// The crates that will cause us to emit failures #[serde(default)] - pub deny: Vec, + pub deny: Vec>, /// If specified, means only the listed crates are allowed #[serde(default)] - pub allow: Vec, + pub allow: Vec>, /// If specified, disregards the crate completely #[serde(default)] - pub skip: Vec, + pub skip: Vec>, } impl Config { - pub fn sort(&mut self) { - self.deny.par_sort(); - self.allow.par_sort(); - self.skip.par_sort(); + pub fn validate( + self, + cfg_file: codespan::FileId, + _contents: &str, + ) -> Result> { + use codespan_reporting::diagnostic::Label; + + let from = |s: toml::Spanned| { + let span = s.start() as u32..s.end() as u32; + let inner = s.into_inner(); + KrateId { + name: inner.name, + version: inner.version, + span, + } + }; + + let mut diagnostics = Vec::new(); + + let mut denied: Vec<_> = self.deny.into_iter().map(from).collect(); + denied.par_sort(); + + let mut allowed: Vec<_> = self.allow.into_iter().map(from).collect(); + allowed.par_sort(); + + let mut skipped: Vec<_> = self.skip.into_iter().map(from).collect(); + skipped.par_sort(); + + let mut add_diag = |first: (&KrateId, &str), second: (&KrateId, &str)| { + let flabel = Label::new( + cfg_file, + first.0.span.clone(), + format!("marked as `{}`", first.1), + ); + let slabel = Label::new( + cfg_file, + second.0.span.clone(), + format!("marked as `{}`", second.1), + ); + + // Put the one that occurs last as the primary label to make it clear + // that the first one was "ok" until we noticed this other one + let diag = if flabel.span.start() > slabel.span.start() { + Diagnostic::new_error( + format!( + "a license id was specified in both `{}` and `{}`", + first.1, second.1 + ), + flabel, + ) + .with_secondary_labels(std::iter::once(slabel)) + } else { + Diagnostic::new_error( + format!( + "a license id was specified in both `{}` and `{}`", + second.1, first.1 + ), + slabel, + ) + .with_secondary_labels(std::iter::once(flabel)) + }; + + diagnostics.push(diag); + }; + + for d in &denied { + if let Ok(ai) = allowed.binary_search(&d) { + add_diag((d, "deny"), (&allowed[ai], "allow")); + } + if let Ok(si) = skipped.binary_search(&d) { + add_diag((d, "deny"), (&skipped[si], "skip")); + } + } + + for a in &allowed { + if let Ok(si) = skipped.binary_search(&a) { + add_diag((a, "allow"), (&skipped[si], "skip")); + } + } + if !diagnostics.is_empty() { + Err(diagnostics) + } else { + Ok(ValidConfig { + file_id: cfg_file, + multiple_versions: self.multiple_versions, + highlight: self.highlight, + denied, + allowed, + skipped, + }) + } + } +} + +#[derive(Eq)] +struct KrateId { + name: String, + version: VersionReq, + span: std::ops::Range, +} + +impl Ord for KrateId { + fn cmp(&self, o: &Self) -> cmp::Ordering { + match self.name.cmp(&o.name) { + cmp::Ordering::Equal => self.version.cmp(&o.version), + o => o, + } + } +} + +impl PartialOrd for KrateId { + fn partial_cmp(&self, o: &Self) -> Option { + Some(self.cmp(o)) + } +} + +impl PartialEq for KrateId { + fn eq(&self, o: &Self) -> bool { + self.cmp(o) == cmp::Ordering::Equal } } +pub struct ValidConfig { + pub file_id: codespan::FileId, + pub multiple_versions: LintLevel, + pub highlight: GraphHighlight, + denied: Vec, + allowed: Vec, + skipped: Vec, +} + fn binary_search<'a>( - arr: &'a [CrateId], - details: &crate::CrateDetails, -) -> Result<(usize, &'a CrateId), usize> { + arr: &'a [KrateId], + details: &crate::KrateDetails, +) -> Result<(usize, &'a KrateId), usize> { let lowest = VersionReq::exact(&Version::new(0, 0, 0)); match arr.binary_search_by(|i| match i.name.cmp(&details.name) { @@ -89,15 +215,24 @@ fn binary_search<'a>( }) { Ok(i) => Ok((i, &arr[i])), Err(i) => { - for (j, crate_) in arr[i..].iter().enumerate() { - if crate_.name != details.name { + // Backtrack 1 if the crate name matches, as, for instance, wildcards will be sorted + // before the 0.0.0 version + let begin = if i > 0 && arr[i - 1].name == details.name { + i - 1 + } else { + i + }; + + for (j, krate) in arr[begin..].iter().enumerate() { + if krate.name != details.name { break; } - if crate_.version.matches(&details.version) { - return Ok((i + j, crate_)); + if krate.version.matches(&details.version) { + return Ok((begin + j, krate)); } } + Err(i) } } @@ -111,8 +246,8 @@ struct Node<'a> { version: &'a Version, } -impl<'a, 'b: 'a> From<&'b crate::CrateDetails> for Node<'a> { - fn from(d: &'b crate::CrateDetails) -> Self { +impl<'a, 'b: 'a> From<&'b crate::KrateDetails> for Node<'a> { + fn from(d: &'b crate::KrateDetails) -> Self { Self { name: &d.name, version: &d.version, @@ -135,12 +270,12 @@ impl<'a> fmt::Display for Node<'a> { type Id = petgraph::graph::NodeIndex; fn append_dependency_chain<'a>( - crates: &'a crate::Crates, + crates: &'a crate::Krates, start: usize, graph: &mut Graph, &'a str>, node_map: &mut HashMap, Id>, ) { - let cd = &crates.crates[start]; + let cd = &crates.krates[start]; let root_node = Node::from(cd); let root_id = graph.add_node(root_node); @@ -320,6 +455,131 @@ struct EdgeAttributes { const INDENT: &str = " "; +fn create_graph( + dup_name: &str, + highlight: GraphHighlight, + krates: &crate::Krates, + dup_range: std::ops::Range, +) -> Result { + use petgraph::visit::{EdgeRef, NodeRef}; + + let mut graph = Graph::new(); + let mut node_map = HashMap::new(); + + for duplicate in dup_range.clone() { + append_dependency_chain(krates, duplicate, &mut graph, &mut node_map); + } + + let mut edges = Vec::with_capacity(dup_range.end - dup_range.start); + let mut dupes: HashMap<&str, Vec<_>> = HashMap::new(); + + for duplicate in dup_range { + let dest = Node::from(&krates.krates[duplicate]); + + if let Some(id) = node_map.get(&dest) { + let mut nodes = vec![*id]; + let mut set = std::collections::HashSet::new(); + while let Some(id) = nodes.pop() { + let node = graph.node_weight(id).unwrap(); + dupes + .entry(node.name) + .and_modify(|v| { + if !v.contains(&node.version) { + v.push(node.version); + } + }) + .or_insert_with(|| vec![node.version]); + + for node in graph.neighbors_directed(id, petgraph::Direction::Incoming) { + set.insert(graph.find_edge(node, id).unwrap()); + nodes.push(node); + } + } + + edges.push(set); + } + } + + dupes.retain(|_, v| { + v.sort(); + v.len() > 1 + }); + + // Find the version with the least number of total edges to the least common ancestor, + // this will presumably be the easiest version to "fix" + // This just returns the first lowest one, there can be multiple with + // same number of edges + let smollest = edges + .iter() + .min_by(|a, b| a.len().cmp(&b.len())) + .ok_or_else(|| failure::format_err!("expected shortest edge path"))?; + let lowest = &edges[0]; + + print_graph( + &graph, + |node| { + let node_weight = node.weight(); + + if node_weight.name == dup_name || dupes.contains_key(node_weight.name) { + NodeAttributes { + label: Some(node_weight.version), + shape: Some(Shape::r#box), + color: Some("red"), + style: Some(Style::rounded), + ..Default::default() + } + } else { + NodeAttributes { + label: Some(node.1), + shape: Some(Shape::r#box), + style: Some(Style::rounded), + ..Default::default() + } + } + }, + |edge| { + if highlight.simplest() && smollest.contains(&edge.id()) { + EdgeAttributes { color: Some("red") } + } else if highlight.lowest_version() && lowest.contains(&edge.id()) { + EdgeAttributes { + color: Some("blue"), + } + } else { + EdgeAttributes { color: None } + } + }, + |output| { + use std::fmt::Write; + + for (i, (name, versions)) in dupes.iter().enumerate() { + writeln!(output, "{}subgraph cluster_{} {{", INDENT, i)?; + + write!(output, "{}{}{{rank=same ", INDENT, INDENT)?; + + for version in versions { + if let Some(id) = node_map.get(&Node { name, version }) { + write!(output, "{} ", id.index())?; + } + } + + writeln!( + output, + "}}\n{}{}style=\"rounded{}\";\n{}{}label=\"{}\"\n{}}}", + INDENT, + INDENT, + if name == &dup_name { ",filled" } else { "" }, + INDENT, + INDENT, + name, + INDENT + )?; + } + + Ok(()) + }, + ) +} + fn print_graph<'a: 'b, 'b, NP, EP, SG>( graph: &'a Graph, &'a str>, node_print: NP, @@ -423,218 +683,230 @@ pub struct DupGraph { } pub fn check_bans( - log: slog::Logger, - crates: &crate::Crates, - cfg: &Config, + krates: &crate::Krates, + cfg: ValidConfig, + (lock_id, lock_contents): (codespan::FileId, &str), output_graph: Option, + sender: crossbeam::channel::Sender, ) -> Result<(), Error> where OG: Fn(DupGraph) -> Result<(), Error>, { - use petgraph::visit::{EdgeRef, NodeRef}; - use slog::{debug, error, warn}; - let mut multi_detector = (&crates.as_ref()[0].name, 0, 0); - let mut errors = 0; - let mut warnings = 0; + use codespan_reporting::diagnostic::{Label, Severity}; - // Keep track of all the crates we skip, and emit a warning if - // we encounter a skip that didn't actually match any crate version - // so that people can clean up their config files - let mut skip_hit = vec![0; cfg.skip.len()]; + // Get the offset of the beginning of the metadata section + let metadata_start = lock_contents + .rfind("[metadata]") + .ok_or_else(|| failure::format_err!("unable to find metadata section in Cargo.lock"))? + + 10; - for (i, crate_) in crates.iter().enumerate() { - if let Ok((index, skip)) = binary_search(&cfg.skip, crate_) { - debug!(log, "skipping crate"; "crate" => format!("{}@{}", crate_.name, crate_.version), "version_req" => format!("{}", skip.version)); - skip_hit[index] += 1; + let mut krate_spans: Vec>> = vec![None; krates.krates.len()]; + + let mut cur_offset = metadata_start; + + for (i, krate) in krates.iter().enumerate() { + // Local crates don't have metadata entries, and it would also be kind of weird to + // ban your own local crates... + if krate.source.is_none() { continue; } - if multi_detector.0 == &crate_.name { - multi_detector.1 += 1; - } else { - if multi_detector.1 > 1 && cfg.multiple_versions != LintLevel::Allow { - match cfg.multiple_versions { - LintLevel::Warn => { - warn!(log, "detected multiple versions of crate"; "crate" => multi_detector.0, "count" => multi_detector.1); - warnings += 1; - } - LintLevel::Deny => { - error!(log, "detected multiple versions of crate"; "crate" => multi_detector.0, "count" => multi_detector.1); - errors += 1; - } - LintLevel::Allow => unreachable!(), - } + let krate_start = lock_contents[cur_offset..] + .find("\"checksum ") + .ok_or_else(|| { + failure::format_err!("unable to find metadata entry for krate {}", krate.id) + })?; + + let id_end = lock_contents[cur_offset + krate_start..] + .find("\" = \"") + .ok_or_else(|| failure::format_err!("invalid metadata format"))?; + + let lock_id = + &lock_contents[cur_offset + krate_start + 10..cur_offset + krate_start + id_end - 1]; + + // Git ids will can differ, but they have to start the same + if &krate.id.repr[..lock_id.len()] != lock_id { + failure::bail!( + "invalid metadata for package '{}' != '{}'", + krate.id, + lock_id + ); + } - if let Some(ref og) = output_graph { - let mut graph = Graph::new(); - let mut node_map = HashMap::new(); + let krate_end = lock_contents[cur_offset + krate_start..] + .find('\n') + .ok_or_else(|| failure::format_err!("unable to find end for krate {}", krate.id))?; - for duplicate in multi_detector.2..i { - append_dependency_chain(crates, duplicate, &mut graph, &mut node_map); - } + krate_spans[i] = + Some((cur_offset + krate_start) as u32..(cur_offset + krate_start + krate_end) as u32); + cur_offset = cur_offset + krate_start + krate_end; + } - let mut edges = Vec::with_capacity(i - multi_detector.2); - let mut dupes: HashMap<&str, Vec<_>> = HashMap::new(); - - for duplicate in multi_detector.2..i { - let dest = Node::from(&crates.crates[duplicate]); - - if let Some(id) = node_map.get(&dest) { - let mut nodes = vec![*id]; - let mut set = std::collections::HashSet::new(); - while let Some(id) = nodes.pop() { - let node = graph.node_weight(id).unwrap(); - dupes - .entry(node.name) - .and_modify(|v| { - if !v.contains(&node.version) { - v.push(node.version); - } - }) - .or_insert_with(|| vec![node.version]); - - for node in - graph.neighbors_directed(id, petgraph::Direction::Incoming) - { - set.insert(graph.find_edge(node, id).unwrap()); - nodes.push(node); - } + // Keep track of all the crates we skip, and emit a warning if + // we encounter a skip that didn't actually match any crate version + // so that people can clean up their config files + let mut skip_hit = vec![0; cfg.skipped.len()]; + let mut multi_detector = (&krates.as_ref()[0].name, 0); + + for (i, krate) in krates.iter().enumerate() { + let mut diagnostics = Vec::new(); + + if let Ok((index, skip)) = binary_search(&cfg.skipped, krate) { + diagnostics.push(Diagnostic::new( + Severity::Help, + format!("skipping crate {} = {}", krate.name, krate.version), + Label::new(cfg.file_id, skip.span.clone(), "matched filter"), + )); + + // Keep a count of the number of times each skip filter is hit + // so that we can report unused filters to the user so that they + // can cleanup their configs as their dependency graph changes over time + skip_hit[index] += 1; + } else { + if multi_detector.0 == &krate.name { + multi_detector.1 += 1; + } else { + if multi_detector.1 > 1 && cfg.multiple_versions != LintLevel::Allow { + let severity = match cfg.multiple_versions { + LintLevel::Warn => Severity::Warning, + LintLevel::Deny => Severity::Error, + LintLevel::Allow => unreachable!(), + }; + + let mut all_start = std::u32::MAX; + let mut all_end = 0; + + let mut dupes = Vec::with_capacity(multi_detector.1); + + #[allow(clippy::needless_range_loop)] + for dup in i - multi_detector.1..i { + if let Some(ref span) = krate_spans[dup] { + if span.start < all_start { + all_start = span.start + } + + if span.end > all_end { + all_end = span.end } - edges.push(set); - } else { - failure::bail!("failed to find root node for duplicate crate"); + let krate = &krates.krates[dup]; + + dupes.push(crate::DiagPack { + krate_id: Some(krate.id.clone()), + diagnostics: vec![Diagnostic::new( + severity, + format!( + "duplicate #{} {} = {}", + dupes.len() + 1, + krate.name, + krate.version + ), + Label::new(lock_id, span.clone(), "lock entry"), + )], + }); } } - dupes.retain(|_, v| { - v.sort(); - v.len() > 1 - }); - - // Find the least common ancestor between all of our duplicates, the nodes that have an incoming edge - // from that least common ancestor are the root cause packages, mark them as such - // for duplicate in multi_detector.2..i { - // let dest = Node::from(&crates.crates[duplicate]); - // } - - // Find the version with the least number of total edges to the least common ancestor, - // this will presumably be the easiest version to "fix" - // This just returns the first lowest one, there can be multiple with - // same number of edges - let smollest = edges - .iter() - .min_by(|a, b| a.len().cmp(&b.len())) - .ok_or_else(|| failure::format_err!("expected shortest edge path"))?; - let lowest = &edges[0]; - - let graph = print_graph( - &graph, - |node| { - let node_weight = node.weight(); - - if node_weight.name == multi_detector.0 - || dupes.contains_key(node_weight.name) - { - NodeAttributes { - label: Some(node_weight.version), - shape: Some(Shape::r#box), - color: Some("red"), - style: Some(Style::rounded), - ..Default::default() - } - } else { - NodeAttributes { - label: Some(node.1), - shape: Some(Shape::r#box), - style: Some(Style::rounded), - ..Default::default() - } - } - }, - |edge| { - if cfg.highlight.simplest() && smollest.contains(&edge.id()) { - EdgeAttributes { color: Some("red") } - } else if cfg.highlight.lowest_version() && lowest.contains(&edge.id()) - { - EdgeAttributes { - color: Some("blue"), - } - } else { - EdgeAttributes { color: None } - } - }, - |output| { - use std::fmt::Write; - - for (i, (name, versions)) in dupes.iter().enumerate() { - writeln!(output, "{}subgraph cluster_{} {{", INDENT, i)?; + sender + .send(crate::DiagPack { + krate_id: None, + diagnostics: vec![Diagnostic::new( + severity, + format!( + "found {} duplicate entries for crate '{}'", + dupes.len(), + multi_detector.0 + ), + Label::new(lock_id, all_start..all_end, "lock entries"), + )], + }) + .unwrap(); + + for dup in dupes { + sender.send(dup).unwrap(); + } - write!(output, "{}{}{{rank=same ", INDENT, INDENT)?; + if let Some(ref og) = output_graph { + let graph = create_graph( + multi_detector.0, + cfg.highlight, + krates, + i - multi_detector.1..i, + )?; + + og(DupGraph { + duplicate: multi_detector.0.to_owned(), + graph, + })?; + } + } - for version in versions { - if let Some(id) = node_map.get(&Node { name, version }) { - write!(output, "{} ", id.index())?; - } - } + multi_detector.0 = &krate.name; + multi_detector.1 = 1; + } - writeln!( - output, - "}}\n{}{}style=\"rounded{}\";\n{}{}label=\"{}\"\n{}}}", - INDENT, - INDENT, - if name == multi_detector.0 { - ",filled" - } else { - "" - }, - INDENT, - INDENT, - name, - INDENT - )?; - } + if let Ok((_, ban)) = binary_search(&cfg.denied, krate) { + diagnostics.push(Diagnostic::new( + Severity::Error, + format!("detected banned crate {} = {}", krate.name, krate.version), + Label::new(cfg.file_id, ban.span.clone(), "matching ban entry"), + )); + } - Ok(()) - }, - )?; + if !cfg.allowed.is_empty() { + // Since only allowing specific crates is pretty draconian, + // also emit which allow filters actually passed each crate + match binary_search(&cfg.allowed, krate) { + Ok((_, allow)) => { + diagnostics.push(Diagnostic::new( + Severity::Note, + format!("allowed {} = {}", krate.name, krate.version), + Label::new(cfg.file_id, allow.span.clone(), "matching allow entry"), + )); + } + Err(mut ind) => { + if ind >= cfg.allowed.len() { + ind = cfg.allowed.len() - 1; + } - og(DupGraph { - duplicate: multi_detector.0.to_owned(), - graph, - })?; + diagnostics.push(Diagnostic::new( + Severity::Error, + format!( + "detected crate not specifically allowed {} = {}", + krate.name, krate.version + ), + Label::new(cfg.file_id, cfg.allowed[ind].span.clone(), "closest match"), + )); + } } } - - multi_detector.0 = &crate_.name; - multi_detector.1 = 1; - multi_detector.2 = i; } - if let Ok((_, ban)) = binary_search(&cfg.deny, crate_) { - error!(log, "detected a banned crate"; "crate" => format!("{}@{}", crate_.name, crate_.version), "ban" => format!("{} = {}", ban.name, ban.version)) - } else if !cfg.allow.is_empty() && binary_search(&cfg.allow, crate_).is_ok() { - error!(log, "detected a crate not explicitly allowed"; "crate" => format!("{}@{}", crate_.name, crate_.version)); - errors += 1; + if !diagnostics.is_empty() { + sender + .send(crate::DiagPack { + krate_id: Some(krate.id.clone()), + diagnostics, + }) + .unwrap(); } } - for (count, skip) in skip_hit.into_iter().zip(cfg.skip.iter()) { + for (count, skip) in skip_hit.into_iter().zip(cfg.skipped.into_iter()) { if count == 0 { - warn!(log, "skipped crate not encountered"; "crate" => skip.name.to_owned(), "version_req" => skip.version.to_string()); - warnings += 1; + sender + .send(crate::DiagPack { + krate_id: None, + diagnostics: vec![Diagnostic::new( + Severity::Warning, + "skipped crate was not encountered", + Label::new(cfg.file_id, skip.span, "no crate matches these criteria"), + )], + }) + .unwrap(); } } - if warnings > 0 { - warn!(log, "encountered {} ban warnings", warnings); - } - - if errors > 0 { - error!(log, "encountered {} ban errors", errors); - failure::bail!("failed ban check"); - } - Ok(()) } @@ -644,7 +916,7 @@ mod test { #[test] fn binary_search_() { - let mut versions = vec![ + let versions = [ CrateId { name: "unicase".to_owned(), version: VersionReq::parse("=1.4.2").unwrap(), @@ -689,26 +961,59 @@ mod test { name: "rand_pcg".to_owned(), version: VersionReq::parse("=0.1.2").unwrap(), }, + CrateId { + name: "winapi".to_owned(), + version: VersionReq::parse("<0.3").unwrap(), + }, + CrateId { + name: "serde".to_owned(), + version: VersionReq::any(), + }, CrateId { name: "scopeguard".to_owned(), version: VersionReq::parse("=0.3.3").unwrap(), }, CrateId { - name: "winapi".to_owned(), - version: VersionReq::parse("=0.2.8").unwrap(), + name: "num-traits".to_owned(), + version: VersionReq::parse("=0.1.43").unwrap(), }, CrateId { name: "num-traits".to_owned(), - version: VersionReq::parse("=0.1.43").unwrap(), + version: VersionReq::parse("<0.1").unwrap(), + }, + CrateId { + name: "num-traits".to_owned(), + version: VersionReq::parse("<0.2").unwrap(), + }, + CrateId { + name: "num-traits".to_owned(), + version: VersionReq::parse("0.1.*").unwrap(), + }, + CrateId { + name: "num-traits".to_owned(), + version: VersionReq::parse("<0.1.42").unwrap(), + }, + CrateId { + name: "num-traits".to_owned(), + version: VersionReq::parse(">0.1.43").unwrap(), }, ]; + let mut versions: Vec<_> = versions + .iter() + .map(|v| super::KrateId { + name: v.name.clone(), + version: v.version.clone(), + span: 0..0, + }) + .collect(); + versions.sort(); assert_eq!( binary_search( &versions, - &crate::CrateDetails { + &crate::KrateDetails { name: "rand_core".to_owned(), version: Version::parse("0.3.1").unwrap(), ..Default::default() @@ -718,5 +1023,109 @@ mod test { .unwrap(), &(VersionReq::parse("=0.3.1").unwrap()) ); + + assert_eq!( + binary_search( + &versions, + &crate::KrateDetails { + name: "serde".to_owned(), + version: Version::parse("1.0.94").unwrap(), + ..Default::default() + } + ) + .map(|(_, s)| &s.version) + .unwrap(), + &(VersionReq::any()) + ); + + assert!(binary_search( + &versions, + &crate::KrateDetails { + name: "nope".to_owned(), + version: Version::parse("1.0.0").unwrap(), + ..Default::default() + } + ) + .is_err()); + + assert_eq!( + binary_search( + &versions, + &crate::KrateDetails { + name: "num-traits".to_owned(), + version: Version::parse("0.1.43").unwrap(), + ..Default::default() + } + ) + .map(|(_, s)| &s.version) + .unwrap(), + &(VersionReq::parse("=0.1.43").unwrap()) + ); + + assert_eq!( + binary_search( + &versions, + &crate::KrateDetails { + name: "num-traits".to_owned(), + version: Version::parse("0.1.2").unwrap(), + ..Default::default() + } + ) + .map(|(_, s)| &s.version) + .unwrap(), + &(VersionReq::parse("<0.1.42").unwrap()) + ); + + assert_eq!( + binary_search( + &versions, + &crate::KrateDetails { + name: "num-traits".to_owned(), + version: Version::parse("0.2.0").unwrap(), + ..Default::default() + } + ) + .map(|(_, s)| &s.version) + .unwrap(), + &(VersionReq::parse(">0.1.43").unwrap()) + ); + + assert_eq!( + binary_search( + &versions, + &crate::KrateDetails { + name: "num-traits".to_owned(), + version: Version::parse("0.0.99").unwrap(), + ..Default::default() + } + ) + .map(|(_, s)| &s.version) + .unwrap(), + &(VersionReq::parse("<0.1").unwrap()) + ); + + assert_eq!( + binary_search( + &versions, + &crate::KrateDetails { + name: "winapi".to_owned(), + version: Version::parse("0.2.8").unwrap(), + ..Default::default() + } + ) + .map(|(_, s)| &s.version) + .unwrap(), + &(VersionReq::parse("<0.3").unwrap()) + ); + + assert!(binary_search( + &versions, + &crate::KrateDetails { + name: "winapi".to_owned(), + version: Version::parse("0.3.8").unwrap(), + ..Default::default() + } + ) + .is_err()); } } diff --git a/src/cargo-deny/check.rs b/src/cargo-deny/check.rs index 151d4bd2..7feb571b 100644 --- a/src/cargo-deny/check.rs +++ b/src/cargo-deny/check.rs @@ -1,10 +1,9 @@ -use ansi_term::Color; use cargo_deny::{ban, licenses}; use clap::arg_enum; +use codespan_reporting::diagnostic::Diagnostic; use failure::{format_err, Error}; use serde::Deserialize; -use slog::info; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use structopt::StructOpt; arg_enum! { @@ -27,10 +26,14 @@ pub struct Args { /// The /graph_output/* is deleted and recreated each run. #[structopt(short, long, parse(from_os_str))] graph: Option, + /// Hides the inclusion graph when printing out info for a crate + #[structopt(short, long)] + hide_inclusion_graph: bool, /// The check(s) to perform #[structopt( default_value = "all", - raw(possible_values = "&WhichCheck::variants()", case_insensitive = "true") + possible_values = &WhichCheck::variants(), + case_insensitive = true, )] which: WhichCheck, } @@ -47,26 +50,42 @@ struct Config { bans: Option, } +struct ValidatedConfig { + licenses: Option, + bans: Option, +} + impl Config { - fn sort(&mut self) { - if let Some(lcfg) = self.licenses.as_mut() { - lcfg.sort(); - } + fn validate( + self, + files: &mut codespan::Files, + path: &Path, + contents: String, + ) -> Result> { + let id = files.add(path.to_string_lossy(), contents.clone()); - if let Some(bcfg) = self.bans.as_mut() { - bcfg.sort(); - } + let licenses = match self.licenses { + Some(lc) => Some(lc.validate(id)?), + None => None, + }; + + let bans = match self.bans { + Some(b) => Some(b.validate(id, &contents)?), + None => None, + }; + + Ok(ValidatedConfig { licenses, bans }) } } pub fn cmd( - log: slog::Logger, + log_level: log::LevelFilter, context_dir: PathBuf, args: Args, - crates: cargo_deny::Crates, + krates: cargo_deny::Krates, store: Option, ) -> Result<(), Error> { - let config = args + let cfg_path = args .config .or_else(|| Some("deny.toml".to_owned().into())) .map(|p| { @@ -78,95 +97,177 @@ pub fn cmd( }) .ok_or_else(|| format_err!("unable to determine config path"))?; - let mut cfg = { - let cfg_contents = std::fs::read_to_string(&config) - .map_err(|e| format_err!("failed to read config from {}: {}", config.display(), e))?; + let mut files = codespan::Files::new(); - let mut cfg: Config = toml::from_str(&cfg_contents).map_err(|e| { + let cfg = { + let cfg_contents = std::fs::read_to_string(&cfg_path) + .map_err(|e| format_err!("failed to read config from {}: {}", cfg_path.display(), e))?; + + let cfg: Config = toml::from_str(&cfg_contents).map_err(|e| { format_err!( "failed to deserialize config from {}: {}", - config.display(), + cfg_path.display(), e ) })?; - cfg.sort(); + match cfg.validate(&mut files, &cfg_path, cfg_contents) { + Ok(vcfg) => vcfg, + Err(diags) => { + use codespan_reporting::term; + + let writer = + term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Auto); + let config = term::Config::default(); + for diag in &diags { + term::emit(&mut writer.lock(), &config, &files, &diag).unwrap(); + } - cfg + return Err(format_err!( + "failed to validate configuration file {}", + cfg_path.display() + )); + } + } }; - info!(log, "checking crates"; "count" => crates.as_ref().len()); + let lic_cfg = if args.which == WhichCheck::All || args.which == WhichCheck::License { + if let Some(licenses) = cfg.licenses { + let gatherer = licenses::Gatherer::default() + .with_store(std::sync::Arc::new( + store.expect("we should have a license store"), + )) + .with_confidence_threshold(licenses.confidence_threshold); - if args.which == WhichCheck::All || args.which == WhichCheck::License { - if let Some(ref mut licenses) = cfg.licenses { - let ignored = licenses.get_ignore_licenses(); + Some(( + gatherer.gather(krates.as_ref(), &mut files, Some(&licenses)), + licenses, + )) + } else { + None + } + } else { + None + }; - { - let mut timer = slog_perf::TimeReporter::new_with_level( - "check-licenses", - log.clone(), - slog::Level::Debug, - ); + let (ban_cfg, lock_id, lock_contents) = + if args.which == WhichCheck::All || args.which == WhichCheck::Ban { + let lock_contents = std::fs::read_to_string(&krates.lock_file)?; + let lock_id = files.add(krates.lock_file.to_string_lossy(), lock_contents.clone()); - let gatherer = - licenses::Gatherer::new(log.new(slog::o!("stage" => "license_gather"))) - .with_store(std::sync::Arc::new( - store.expect("we should have a license store"), - )) - .with_confidence_threshold(licenses.confidence_threshold); - - let summary = - timer.start_with("gather", || gatherer.gather(crates.as_ref(), ignored)); - - timer.start_with("check", || { - licenses::check_licenses( - log.new(slog::o!("stage" => "license_check")), - summary, - licenses, - ) - })?; - } + (cfg.bans, Some(lock_id), Some(lock_contents)) + } else { + (None, None, None) + }; - info!(log, "{}", Color::Green.paint("license check succeeded!")); - } - } + let graph_out_dir = args.graph; - if args.which == WhichCheck::All || args.which == WhichCheck::Ban { - if let Some(ref bans) = cfg.bans { - let mut timer = slog_perf::TimeReporter::new_with_level( - "check-bans", - log.clone(), - slog::Level::Debug, - ); + let (send, recv) = crossbeam::channel::unbounded(); - let output_graph = args.graph.map(|pb| { - let output_dir = pb.join("graph_output"); - let _ = std::fs::remove_dir_all(&output_dir); + let krates = &krates; + let mut inc_grapher = if args.hide_inclusion_graph { + None + } else { + Some(cargo_deny::inclusion_graph::Grapher::new(krates)) + }; - std::fs::create_dir_all(&output_dir).unwrap(); + use codespan_reporting::diagnostic::Severity; - move |dup_graph: ban::DupGraph| { - std::fs::write( - output_dir.join(format!("{}.dot", dup_graph.duplicate)), - dup_graph.graph.as_bytes(), - )?; + let max_severity = match log_level { + log::LevelFilter::Off => None, + log::LevelFilter::Debug => Some(Severity::Help), + log::LevelFilter::Error => Some(Severity::Error), + log::LevelFilter::Info => Some(Severity::Note), + log::LevelFilter::Trace => Some(Severity::Help), + log::LevelFilter::Warn => Some(Severity::Warning), + }; - Ok(()) - } - }); + let (check_error, error) = rayon::join( + move || { + if let Some((summary, lic_cfg)) = lic_cfg { + log::info!("checking licenses..."); + licenses::check_licenses(summary, max_severity, &lic_cfg, send.clone()); + } - timer.start_with("check", || { - ban::check_bans( - log.new(slog::o!("stage" => "ban_check")), - &crates, + if let Some(bans) = ban_cfg { + let output_graph = graph_out_dir.map(|pb| { + let output_dir = pb.join("graph_output"); + let _ = std::fs::remove_dir_all(&output_dir); + + std::fs::create_dir_all(&output_dir).unwrap(); + + move |dup_graph: ban::DupGraph| { + std::fs::write( + output_dir.join(format!("{}.dot", dup_graph.duplicate)), + dup_graph.graph.as_bytes(), + )?; + + Ok(()) + } + }); + + log::info!("checking bans..."); + return ban::check_bans( + krates, bans, + (lock_id.unwrap(), &lock_contents.unwrap()), output_graph, - ) - })?; + send.clone(), + ); + } - info!(log, "{}", Color::Green.paint("ban check succeeded!")); - } - } + Ok(()) + }, + move || { + use codespan_reporting::term; + + let writer = + term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Auto); + let config = term::Config::default(); + + let mut error_count = 0; + + for pack in recv { + let mut note = pack + .krate_id + .and_then(|pid| inc_grapher.as_mut().map(|ig| (pid, ig))) + .map(|(pid, ig)| ig.write_graph(&pid).unwrap()); - Ok(()) + for mut diag in pack.diagnostics.into_iter() { + if diag.severity >= codespan_reporting::diagnostic::Severity::Error { + error_count += 1; + } + + match max_severity { + Some(max) => { + if diag.severity < max { + continue; + } + } + None => continue, + } + + if note.is_some() { + diag = diag.with_notes(vec![note.take().unwrap()]); + } + + term::emit(&mut writer.lock(), &config, &files, &diag).unwrap(); + } + } + + if error_count > 0 { + Some(failure::format_err!("encountered {} errors", error_count)) + } else { + None + } + }, + ); + + if let Some(err) = error { + Err(err) + } else if let Err(err) = check_error { + Err(err) + } else { + Ok(()) + } } diff --git a/src/cargo-deny/list.rs b/src/cargo-deny/list.rs index b390ebc9..b16a5271 100644 --- a/src/cargo-deny/list.rs +++ b/src/cargo-deny/list.rs @@ -3,7 +3,6 @@ use cargo_deny::licenses; use clap::arg_enum; use failure::Error; use serde::Serialize; -use slog::warn; use structopt::StructOpt; arg_enum! { @@ -43,17 +42,16 @@ pub struct Args { short, long, default_value = "human", - raw( - possible_values = "&OutputFormat::variants()", - case_insensitive = "true" - ) + possible_values = &OutputFormat::variants(), + case_insensitive = true, )] format: OutputFormat, /// Output coloring, only applies to 'human' format #[structopt( long, default_value = "auto", - raw(possible_values = "&ColorWhen::variants()", case_insensitive = "true") + possible_values = &ColorWhen::variants(), + case_insensitive = true, )] color: ColorWhen, /// This just determines if log messages are emitted, the log level specified @@ -65,76 +63,50 @@ pub struct Args { short, long, default_value = "license", - raw(possible_values = "&Layout::variants()", case_insensitive = "true") + possible_values = &Layout::variants(), + case_insensitive = true, )] layout: Layout, } #[allow(clippy::cognitive_complexity)] pub fn cmd( - log: slog::Logger, args: Args, - crates: cargo_deny::Crates, + crates: cargo_deny::Krates, store: Option, ) -> Result<(), Error> { - use licenses::{Note, Summary}; - use std::{ - collections::{BTreeMap, HashMap}, - fmt::Write, - }; + use licenses::LicenseInfo; + type Pid = cargo_metadata::PackageId; + + use std::{collections::BTreeMap, fmt::Write}; - let gatherer = licenses::Gatherer::new(log.new(slog::o!("stage" => "license_gather"))) + let gatherer = licenses::Gatherer::default() .with_store(std::sync::Arc::new( store.expect("we should have a license store"), )) .with_confidence_threshold(args.threshold); - let summary = gatherer.gather(crates.as_ref(), HashMap::new()); - - #[derive(Serialize)] - struct Crate<'a> { - licenses: Vec<&'a str>, - #[serde(skip_serializing_if = "Vec::is_empty")] - exceptions: Vec<&'a str>, - } + let mut files = codespan::Files::new(); - #[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] - struct CrateId<'a> { - name: &'a str, - version: &'a semver::Version, - } + let summary = gatherer.gather(crates.as_ref(), &mut files, None); - impl<'a> Serialize for CrateId<'a> { - fn serialize(&self, s: S) -> Result - where - S: serde::Serializer, - { - s.serialize_str(&format!("{}@{}", self.name, self.version)) - } - } - - #[inline] - fn bin_search(s: &[(T, Vec>)], query: &Q) -> Result - where - T: std::borrow::Borrow, - Q: Ord + ?Sized, - { - s.binary_search_by(|(i, _)| i.borrow().cmp(query)) + #[derive(Serialize)] + struct Crate { + licenses: Vec, } #[derive(Serialize)] struct LicenseLayout<'a> { - licenses: Vec<(String, Vec>)>, - exceptions: Vec<(&'a str, Vec>)>, - unlicensed: Vec>, + licenses: Vec<(String, Vec<&'a Pid>)>, + unlicensed: Vec<&'a Pid>, } - struct CrateLayout<'a> { - crates: BTreeMap, Crate<'a>>, + struct CrateLayout { + crates: BTreeMap, } - impl<'a> CrateLayout<'a> { - fn search(&self, id: &CrateId<'a>) -> &Crate<'a> { + impl CrateLayout { + fn search(&self, id: &Pid) -> &Crate { self.crates.get(id).expect("unable to find crate") } } @@ -145,125 +117,59 @@ pub fn cmd( let mut license_layout = LicenseLayout { licenses: Vec::with_capacity(20), - exceptions: Vec::with_capacity(4), unlicensed: Vec::new(), }; { let licenses = &mut license_layout.licenses; - let exceptions = &mut license_layout.exceptions; let unlicensed = &mut license_layout.unlicensed; - for crate_note in summary.notes() { - let id = CrateId { - name: crate_note.name, - version: &crate_note.version, - }; - + for krate_lic_nfo in summary.nfos { let mut cur = Crate { licenses: Vec::with_capacity(2), - exceptions: Vec::new(), }; - for note in &crate_note.notes { - match note { - Note::License { name, .. } => { - let license_name = Summary::resolve_id(*name); + match krate_lic_nfo.lic_info { + LicenseInfo::SPDXExpression { expr, .. } => { + for req in expr.requirements() { + let s = req.req.to_string(); - // The same license can (often) be present in both metadata - // and a license file, so don't count them twice - if cur.licenses.contains(&license_name) { + if cur.licenses.contains(&s) { continue; } - match bin_search(licenses, license_name) { - Ok(i) => { - licenses[i].1.push(id); - } - Err(i) => { - let mut v = Vec::with_capacity(20); - v.push(id); - licenses.insert(i, (license_name.to_owned(), v)); - } - }; - - cur.licenses.push(license_name); - } - Note::Unlicensed => {} - Note::Exception(exc) => { - match bin_search(exceptions, exc) { - Ok(i) => { - exceptions[i].1.push(id); - } + match licenses.binary_search_by(|(r, _)| r.cmp(&s)) { + Ok(i) => licenses[i].1.push(&krate_lic_nfo.krate.id), Err(i) => { let mut v = Vec::with_capacity(20); - v.push(id); - exceptions.insert(i, (exc, v)); + v.push(&krate_lic_nfo.krate.id); + licenses.insert(i, (s.clone(), v)); } - }; - - cur.exceptions.push(exc); - } - Note::Unknown { name, source } => { - if args.verbose { - warn!( - log, - "detected an unknown license"; - "crate" => crate_note, - "src" => source, - ); - } - - match bin_search(licenses, name) { - Ok(i) => { - licenses[i].1.push(id); - } - Err(i) => { - let mut v = Vec::with_capacity(20); - v.push(id); - licenses.insert(i, (name.clone(), v)); - } - }; - - cur.licenses.push(name); - } - Note::LowConfidence { score, source } => { - if args.verbose { - warn!( - log, - "unable to determine license with high confidence"; - "crate" => crate_note, - "score" => score, - "src" => source, - ); - } - } - Note::UnreadableLicense { path, err } => { - if args.verbose { - warn!( - log, - "license file is unreadable"; - "crate" => crate_note, - "path" => path.display(), - "err" => err.to_string(), // io::Error makes slog sad - ); } + cur.licenses.push(s); } - Note::Ignored(_) => unreachable!(), + } + LicenseInfo::Unlicensed => { + unlicensed.push(&krate_lic_nfo.krate.id); } } - // This can happen if a crate does have a license file, but it - // has a confidence score below the current threshold - if cur.licenses.is_empty() && cur.exceptions.is_empty() { - unlicensed.push(id); - } - - crate_layout.crates.insert(id, cur); + crate_layout + .crates + .insert(krate_lic_nfo.krate.id.clone(), cur); } + } - // Drop the stderr log so all of its output is written first - drop(log); + fn get_parts(pid: &Pid) -> (&str, &str) { + let mut it = pid.repr.split(' '); + + (it.next().unwrap(), it.next().unwrap()) + } + + fn write_pid(out: &mut String, pid: &Pid) -> Result<(), Error> { + let parts = get_parts(pid); + + Ok(write!(out, "{}@{}", parts.0, parts.1)?) } match args.format { @@ -289,70 +195,23 @@ pub fn cmd( write!(output, "{} ({}): ", license.0, license.1.len())?; } - for (i, crate_id) in license.1.iter().enumerate() { + for (i, krate_id) in license.1.iter().enumerate() { if i != 0 { write!(output, ", ")?; } if color { - let crate_ = crate_layout.search(crate_id); - let color = if crate_.licenses.len() > 1 { + let krate = crate_layout.search(krate_id); + let color = if krate.licenses.len() > 1 { Color::Yellow } else { Color::White }; - write!( - output, - "{}@{}", - color.paint(crate_id.name), - crate_id.version - )?; + let parts = get_parts(krate_id); + write!(output, "{}@{}", color.paint(parts.0), parts.1,)?; } else { - write!(output, "{}@{}", crate_id.name, crate_id.version)?; - } - } - - writeln!(output)?; - } - - for (i, except) in license_layout.exceptions.iter().enumerate() { - if i != 0 { - write!(output, ", ")?; - } - - if color { - write!( - output, - "{} ({}): ", - Color::Purple.paint(except.0), - Color::White.bold().paint(except.1.len().to_string()) - )?; - } else { - write!(output, "{} ({}): ", except.0, except.1.len())?; - } - - for (i, crate_id) in except.1.iter().enumerate() { - if i != 0 { - write!(output, ", ")?; - } - - if color { - let crate_ = crate_layout.search(crate_id); - let color = if crate_.licenses.len() > 1 { - Color::Yellow - } else { - Color::White - }; - - write!( - output, - "{}@{}", - color.paint(crate_id.name), - crate_id.version - )?; - } else { - write!(output, "{}@{}", crate_id.name, crate_id.version)?; + write_pid(&mut output, krate_id)?; } } @@ -373,69 +232,59 @@ pub fn cmd( write!(output, "Unlicensed ({}): ", license_layout.unlicensed.len())?; } - for (i, crate_) in license_layout.unlicensed.iter().enumerate() { + for (i, krate) in license_layout.unlicensed.iter().enumerate() { if i != 0 { write!(output, ", ")?; } - write!(output, "{}@{}", crate_.name, crate_.version)?; + write_pid(&mut output, krate)?; } writeln!(output)?; } } Layout::Crate => { - for (id, crate_) in crate_layout.crates { + for (id, krate) in crate_layout.crates { if color { - let color = if crate_.licenses.len() > 1 { + let color = if krate.licenses.len() > 1 { Color::Yellow - } else if crate_.licenses.len() == 1 { + } else if krate.licenses.len() == 1 { Color::White } else { Color::Red }; + let parts = get_parts(&id); write!( output, "{}@{} ({}): ", - color.paint(id.name), - id.version, - Color::White.bold().paint( - (crate_.licenses.len() + crate_.exceptions.len()).to_string() - ), + color.paint(parts.0), + parts.1, + Color::White.bold().paint(krate.licenses.len().to_string()), )?; } else { + let parts = get_parts(&id); write!( output, "{}@{} ({}): ", - id.name, - id.version, - crate_.licenses.len() + crate_.exceptions.len(), + parts.0, + parts.1, + krate.licenses.len(), )?; } - for (i, license) in crate_.licenses.iter().enumerate() { + for (i, license) in krate.licenses.iter().enumerate() { if i != 0 { write!(output, ", ")?; } if color { - write!(output, "{}", Color::Cyan.paint(*license))?; + write!(output, "{}", Color::Cyan.paint(license))?; } else { write!(output, "{}", license)?; } } - for exc in crate_.exceptions.iter() { - write!(output, ", ")?; - - if color { - write!(output, "{}", Color::Purple.paint(*exc))?; - } else { - write!(output, "{}", exc)?; - } - } - writeln!(output)?; } } @@ -461,10 +310,6 @@ pub fn cmd( write!(output, "\t{}", license.0)?; } - for exc in &license_layout.exceptions { - write!(output, "\t{}", exc.0)?; - } - if !license_layout.unlicensed.is_empty() { write!(output, "\tUnlicensed")?; } @@ -472,26 +317,18 @@ pub fn cmd( writeln!(output)?; } - for (id, crate_) in crate_layout.crates { - write!(output, "{}@{}", id.name, id.version)?; + for (id, krate) in crate_layout.crates { + write_pid(&mut output, &id)?; for lic in &license_layout.licenses { - if lic.1.binary_search(&id).is_ok() { - write!(output, "\tX")?; - } else { - write!(output, "\t")?; - } - } - - for exc in &license_layout.exceptions { - if exc.1.binary_search(&id).is_ok() { + if lic.1.binary_search(&&id).is_ok() { write!(output, "\tX")?; } else { write!(output, "\t")?; } } - if crate_.licenses.is_empty() && crate_.exceptions.is_empty() { + if krate.licenses.is_empty() { write!(output, "\tX")?; } diff --git a/src/cargo-deny/main.rs b/src/cargo-deny/main.rs index 9583dd46..da20f361 100644 --- a/src/cargo-deny/main.rs +++ b/src/cargo-deny/main.rs @@ -1,10 +1,8 @@ #![warn(clippy::all)] #![warn(rust_2018_idioms)] -use ansi_term::Color; use cargo_deny::licenses; use failure::{bail, format_err, Error}; -//use slog::{info, warn}; use std::path::PathBuf; use structopt::StructOpt; @@ -12,8 +10,6 @@ mod check; mod common; mod list; -use crate::common::MessageFormat; - #[derive(StructOpt, Debug)] enum Command { /// Outputs a listing of all licenses and the crates that use them @@ -24,8 +20,8 @@ enum Command { Check(check::Args), } -fn parse_level(s: &str) -> Result { - s.parse::() +fn parse_level(s: &str) -> Result { + s.parse::() .map_err(|_| format_err!("failed to parse level '{}'", s)) } @@ -36,23 +32,19 @@ struct Opts { #[structopt( short = "L", long = "log-level", - default_value = "info", - parse(try_from_str = "parse_level"), + default_value = "warn", + parse(try_from_str = parse_level), long_help = "The log level for messages, only log messages at or above the level will be emitted. Possible values: * off -* critical * error -* warning +* warn * info * debug * trace" )] - log_level: slog::FilterLevel, - /// The format for log messages: 'human' or 'json' - #[structopt(long = "message-format", default_value = "human")] - msg_format: MessageFormat, + log_level: log::LevelFilter, /// The directory used as the context for the deny, if not specified, /// the current working directory is used instead. Must contain a Cargo.toml file. #[structopt(long = "context", parse(from_os_str))] @@ -61,36 +53,44 @@ Possible values: cmd: Command, } -fn real_main() -> Result<(), Error> { - use slog::Drain; - let args = Opts::from_args(); - - let drain = match args.msg_format { - MessageFormat::Human => { - let decorator = slog_term::TermDecorator::new().stderr().build(); - - slog_async::Async::new(slog_term::CompactFormat::new(decorator).build().fuse()) - .build() - .fuse() - } - MessageFormat::Json => slog_async::Async::new( - slog_json::Json::new(std::io::stderr()) - .add_default_keys() - .set_newlines(true) - .build() - .fuse(), - ) - .build() - .fuse(), - }; - - let filter_level = args.log_level; +fn setup_logger(level: log::LevelFilter) -> Result<(), fern::InitError> { + use ansi_term::Color::*; + use log::Level::*; + + fern::Dispatch::new() + .level(level) + .format(move |out, message, record| { + out.finish(format_args!( + "{date} [{level}] {message}\x1B[0m", + date = chrono::Utc::now().format("%Y-%m-%d %H:%M:%S"), + level = match record.level() { + Error => Red.paint("ERROR"), + Warn => Yellow.paint("WARN"), + Info => Green.paint("INFO"), + Debug => Blue.paint("DEBUG"), + Trace => Purple.paint("TRACE"), + }, + message = message, + )); + }) + .chain(std::io::stderr()) + .apply()?; + Ok(()) +} - let drain = drain - .filter(move |r: &'_ slog::Record<'_>| r.level().as_usize() <= filter_level.as_usize()) - .fuse(); +fn real_main() -> Result<(), Error> { + let args = + Opts::from_iter({ + std::env::args().enumerate().filter_map(|(i, a)| { + if i == 1 && a == "deny" { + None + } else { + Some(a) + } + }) + }); - let root_logger = slog::Logger::root(drain, slog::o!()); + setup_logger(args.log_level)?; let context_dir = args .context @@ -108,15 +108,8 @@ fn real_main() -> Result<(), Error> { let (all_crates, store) = rayon::join( || { - let mut timer = slog_perf::TimeReporter::new_with_level( - "read-crates", - root_logger.clone(), - slog::Level::Debug, - ); - - timer.start_with("read", || { - cargo_deny::get_all_crates(&context_dir).expect("failed to acquire crates") - }) + log::info!("gathering crates for {}", context_dir.display()); + cargo_deny::get_all_crates(&context_dir) }, || { if let Command::Check(ref check) = args.cmd { @@ -125,21 +118,29 @@ fn real_main() -> Result<(), Error> { } } - let mut timer = slog_perf::TimeReporter::new_with_level( - "load-license-store", - root_logger.clone(), - slog::Level::Debug, - ); - - Some(timer.start_with("load", || { - licenses::LicenseStore::from_cache().expect("failed to load license store") - })) + log::info!("loading license store"); + Some(licenses::LicenseStore::from_cache()) }, ); + let all_crates = all_crates?; + + log::info!("gathered {} crates", all_crates.krates.len()); + + let license_store = match store { + Some(res) => Some(res?), + None => None, + }; + match args.cmd { - Command::List(list) => list::cmd(root_logger, list, all_crates, store), - Command::Check(check) => check::cmd(root_logger, context_dir, check, all_crates, store), + Command::List(list) => list::cmd(list, all_crates, license_store), + Command::Check(check) => check::cmd( + args.log_level, + context_dir, + check, + all_crates, + license_store, + ), } } @@ -147,7 +148,7 @@ fn main() { match real_main() { Ok(_) => {} Err(e) => { - eprintln!("{}", Color::Red.paint(format!("{}", e))); + log::error!("{}", e); std::process::exit(1); } } diff --git a/src/inclusion_graph.rs b/src/inclusion_graph.rs new file mode 100644 index 00000000..0cad64a8 --- /dev/null +++ b/src/inclusion_graph.rs @@ -0,0 +1,196 @@ +use crate::{KrateDetails, Krates}; +use failure::Error; +use petgraph::Graph; +use rayon::prelude::*; +use std::collections::{hash_map::Entry, HashMap, HashSet}; + +pub type Pid = cargo_metadata::PackageId; +type Nid = petgraph::graph::NodeIndex; + +struct Node<'a> { + metadata: &'a KrateDetails, +} + +/// Simplified copy of what cargo tree does to display dependency graphs. +/// In our case, we only care about the inverted form, ie, not what the +/// dependencies of a package are, but rather how a particular package +/// is actually pulled into the root project +pub struct Grapher<'a> { + graph: Graph, &'a str>, + node_map: HashMap, + krates: &'a Krates, +} + +const DWN: char = '│'; +const TEE: char = '├'; +const ELL: char = '└'; +const RGT: char = '─'; + +impl<'a> Grapher<'a> { + pub fn new(krates: &'a Krates) -> Self { + Self { + graph: Graph::new(), + node_map: HashMap::new(), + krates, + } + } + + fn build_graph_to_package(&mut self, id: &Pid) -> Result<(), Error> { + use smallvec::SmallVec; + + if self.node_map.contains_key(id) { + return Ok(()); + } + + let node = Node { + metadata: &self.krates.krates[self.krates.krate_map[id]], + }; + + let node_id = self.graph.add_node(node); + self.node_map.insert(id.clone(), node_id); + + let mut pending = SmallVec::<[Pid; 10]>::new(); + pending.push(id.clone()); + + while let Some(pkg_id) = pending.pop() { + let idx = self.node_map[&pkg_id]; + let pkg = &self.krates.krates[self.krates.krate_map[&pkg_id]]; + + // Obtain the crates that are directly referencing the current crate + let parents: Vec<_> = self + .krates + .resolved + .nodes + .par_iter() + .filter_map(|rnode| { + rnode + .dependencies + .binary_search(&pkg_id) + .ok() + .map(|_| &self.krates.krates[self.krates.krate_map[&rnode.id]]) + }) + .collect(); + + for parent in parents { + let parent_nid = match self.node_map.entry(parent.id.clone()) { + Entry::Occupied(e) => *e.get(), + Entry::Vacant(e) => { + pending.push(parent.id.clone()); + let parent_node = Node { metadata: &parent }; + + *e.insert(self.graph.add_node(parent_node)) + } + }; + + let kind = parent + .deps + .iter() + .find(|d| d.name == pkg.name) + .map(|dep| match dep.kind { + cargo_metadata::DependencyKind::Normal + | cargo_metadata::DependencyKind::Unknown => "", + cargo_metadata::DependencyKind::Development => "dev", + cargo_metadata::DependencyKind::Build => "build", + }) + .unwrap_or(""); + + self.graph.add_edge(idx, parent_nid, kind); + } + } + + Ok(()) + } + + pub fn write_graph(&mut self, id: &Pid) -> Result { + self.build_graph_to_package(id)?; + + let mut out = String::with_capacity(1024); + let mut levels = Vec::new(); + let mut visited = HashSet::new(); + + let node = &self.graph[self.node_map[id]]; + + self.write_parent(node, "", &mut out, &mut visited, &mut levels)?; + + Ok(out) + } + + fn write_parent( + &self, + krate: &Node<'a>, + kind: &'a str, + out: &mut String, + visited: &mut HashSet, + levels_continue: &mut Vec, + ) -> Result<(), Error> { + use petgraph::visit::EdgeRef; + use std::fmt::Write; + + let new = visited.insert(krate.metadata.id.clone()); + let star = if new { "" } else { " (*)" }; + + if let Some((&last_continues, rest)) = levels_continue.split_last() { + for &continues in rest { + let c = if continues { DWN } else { ' ' }; + write!(out, "{} ", c)?; + } + + let c = if last_continues { TEE } else { ELL }; + write!(out, "{0}{1}{1} ", c, RGT)?; + } + + match kind { + "" => writeln!( + out, + "{} v{}{}", + krate.metadata.name, krate.metadata.version, star + ), + k => writeln!( + out, + "({}) {} v{}{}", + k, krate.metadata.name, krate.metadata.version, star + ), + }?; + + if !new { + return Ok(()); + } + + let mut parents = smallvec::SmallVec::<[(&Node<'_>, &'_ str); 6]>::new(); + for edge in self.graph.edges_directed( + self.node_map[&krate.metadata.id], + petgraph::Direction::Outgoing, + ) { + let parent = &self.graph[edge.target()]; + parents.push((parent, *edge.weight())); + } + + if !parents.is_empty() { + self.write_parents(&mut parents, out, visited, levels_continue)?; + } + + Ok(()) + } + + fn write_parents( + &self, + parents: &mut [(&Node<'a>, &'a str)], + out: &mut String, + visited: &mut HashSet, + levels_continue: &mut Vec, + ) -> Result<(), Error> { + // Resolve uses Hash data types internally but we want consistent output ordering + parents.sort_by_key(|n| &n.0.metadata.id); + + let mut it = parents.iter().peekable(); + while let Some(parent) = it.next() { + levels_continue.push(it.peek().is_some()); + + self.write_parent(parent.0, parent.1, out, visited, levels_continue)?; + + levels_continue.pop(); + } + + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs index a1c9284f..7b985e17 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -217,11 +217,12 @@ use std::{ path::{Path, PathBuf}, }; +pub use codespan_reporting::diagnostic::Label; + pub mod ban; +pub mod inclusion_graph; pub mod licenses; -use licenses::{LicenseField, LicenseInfo}; - #[derive(serde::Deserialize, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub enum LintLevel { @@ -237,165 +238,121 @@ impl Default for LintLevel { } #[derive(Debug)] -pub struct CrateDetails { +pub struct KrateDetails { pub name: String, pub id: cargo_metadata::PackageId, pub version: Version, + pub source: Option, pub authors: Vec, pub repository: Option, pub description: Option, - pub root: Option, - pub license: LicenseField, + pub manifest_path: PathBuf, + pub license: Option, pub license_file: Option, pub deps: Vec, + pub features: HashMap>, + pub targets: Vec, } -impl Default for CrateDetails { +#[cfg(test)] +impl Default for KrateDetails { fn default() -> Self { Self { name: "".to_owned(), + version: Version::new(0, 1, 0), + authors: Vec::new(), id: cargo_metadata::PackageId { repr: "".to_owned(), }, - version: Version::new(0, 1, 0), - authors: Vec::new(), - repository: None, + source: None, description: None, - root: None, - license: LicenseField::default(), - license_file: None, deps: Vec::new(), + license: None, + license_file: None, + targets: Vec::new(), + features: HashMap::new(), + manifest_path: PathBuf::new(), + repository: None, } } } -impl PartialOrd for CrateDetails { +impl PartialOrd for KrateDetails { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl Ord for CrateDetails { +impl Ord for KrateDetails { fn cmp(&self, other: &Self) -> cmp::Ordering { - match self.name.cmp(&other.name) { - cmp::Ordering::Equal => self.version.cmp(&other.version), - o => o, - } + self.id.cmp(&other.id) } } -impl PartialEq for CrateDetails { +impl PartialEq for KrateDetails { fn eq(&self, other: &Self) -> bool { - self.name == other.name && self.version == other.version + self.id == other.id } } -impl Eq for CrateDetails {} +impl Eq for KrateDetails {} -impl CrateDetails { - pub fn new(package: cargo_metadata::Package) -> Self { +impl KrateDetails { + pub fn new(pkg: cargo_metadata::Package) -> Self { Self { - name: package.name, - id: package.id, - version: package.version, - authors: package.authors, - repository: package.repository, - license: package.license.map(LicenseField::new).unwrap_or_default(), - license_file: package.license_file, - description: package.description, - root: { - let mut mp = package.manifest_path; - mp.pop(); - Some(mp) - }, + name: pkg.name, + id: pkg.id, + version: pkg.version, + authors: pkg.authors, + repository: pkg.repository, + source: pkg.source, + targets: pkg.targets, + license: pkg.license.map(|lf| { + // cargo used to allow / in place of OR which is not valid + // in SPDX expression, we force correct it here + if lf.contains('/') { + lf.replace("/", " OR ") + } else { + lf + } + }), + license_file: pkg.license_file, + description: pkg.description, + manifest_path: pkg.manifest_path, deps: { - let mut deps = package.dependencies; + let mut deps = pkg.dependencies; deps.par_sort_by(|a, b| a.name.cmp(&b.name)); deps }, + features: pkg.features, } } - - pub fn licenses(&self) -> impl Iterator> { - let root = self.root.as_ref(); - let explicit = self - .license_file - .as_ref() - .and_then(|lf| root.map(|r| r.join(lf))); - - // metadata licenses + inferred licenses + explicit license - - self.license.iter().map(LicenseInfo::Metadata).chain( - find_license_files(root) - .into_iter() - .filter_map(move |found_path| { - // If the license is specified in Cargo.toml, just - // skip it to differentiate between what *might* be - // a license vs what the crate maintainer explicitly - // specified *is* a license - if let Some(ref specified) = explicit { - if *specified == found_path { - return None; - } - } - - Some(LicenseInfo::InferredLicenseFile(found_path)) - }) - .chain(self.license_file.iter().filter_map(move |elf| { - root.map(|r| LicenseInfo::ExplicitLicenseFile(r.join(elf))) - })), - ) - } -} - -fn find_license_files(dir: Option<&PathBuf>) -> Vec { - if let Some(dir) = dir { - if let Ok(entries) = std::fs::read_dir(dir) { - let mut license_files: Vec<_> = entries - .filter_map(|e| { - e.ok().and_then(|e| { - let p = e.path(); - let file_name = p.file_name().and_then(|n| n.to_str()).unwrap_or(""); - if p.is_file() && file_name.starts_with("LICENSE") { - Some(p) - } else { - None - } - }) - }) - .collect(); - - license_files.sort(); - return license_files; - } - } - - Vec::new() } -pub struct Crates { - pub crates: Vec, - pub crate_map: HashMap, +pub struct Krates { + pub krates: Vec, + pub krate_map: HashMap, pub resolved: cargo_metadata::Resolve, + pub lock_file: PathBuf, } -impl Crates { - pub fn crate_by_id(&self, id: &cargo_metadata::PackageId) -> Option<&CrateDetails> { - self.crate_map.get(id).map(|i| &self.crates[*i]) +impl Krates { + pub fn crate_by_id(&self, id: &cargo_metadata::PackageId) -> Option<&KrateDetails> { + self.krate_map.get(id).map(|i| &self.krates[*i]) } - pub fn iter(&self) -> impl Iterator { - self.crates.iter() + pub fn iter(&self) -> impl Iterator { + self.krates.iter() } } -impl AsRef<[CrateDetails]> for Crates { - fn as_ref(&self) -> &[CrateDetails] { - &self.crates[..] +impl AsRef<[KrateDetails]> for Krates { + fn as_ref(&self) -> &[KrateDetails] { + &self.krates[..] } } -pub fn get_all_crates>(root: P) -> Result { +pub fn get_all_crates>(root: P) -> Result { let cargo_toml = root.as_ref().join("Cargo.toml"); let metadata = cargo_metadata::MetadataCommand::new() .manifest_path(cargo_toml) @@ -406,7 +363,7 @@ pub fn get_all_crates>(root: P) -> Result { let mut crate_infos: Vec<_> = metadata .packages .into_iter() - .map(CrateDetails::new) + .map(KrateDetails::new) .collect(); crate_infos.par_sort(); @@ -425,10 +382,11 @@ pub fn get_all_crates>(root: P) -> Result { .par_iter_mut() .for_each(|nodes| nodes.dependencies.par_sort()); - Ok(Crates { - crates: crate_infos, - crate_map: map, + Ok(Krates { + krates: crate_infos, + krate_map: map, resolved, + lock_file: root.as_ref().join("Cargo.lock"), }) } @@ -463,13 +421,8 @@ pub fn hash(data: &[u8]) -> u32 { pub struct CrateVersion<'a>(pub &'a semver::Version); -impl<'a> slog::Value for CrateVersion<'a> { - fn serialize( - &self, - _record: &slog::Record<'_>, - key: slog::Key, - serializer: &mut dyn slog::Serializer, - ) -> slog::Result { - serializer.emit_arguments(key, &format_args!("{}", self.0)) - } +pub struct DiagPack { + // The particular package that the diagnostics pertain to + pub krate_id: Option, + pub diagnostics: Vec, } diff --git a/src/licenses.rs b/src/licenses.rs index 7c24a9d9..6173a281 100644 --- a/src/licenses.rs +++ b/src/licenses.rs @@ -1,194 +1,265 @@ -use crate::LintLevel; +use crate::{KrateDetails, LintLevel}; +use codespan_reporting::diagnostic::{Diagnostic, Label, Severity}; use failure::Error; use rayon::prelude::*; -use semver::{Version, VersionReq}; +use semver::VersionReq; use serde::Deserialize; -use slog::{debug, error, trace, warn}; -use std::{collections::HashMap, fmt, path::PathBuf, sync::Arc}; +use smallvec::SmallVec; +use spdx::Licensee; +use std::{ + cmp, fmt, + path::{Path, PathBuf}, + sync::Arc, +}; const LICENSE_CACHE: &[u8] = include_bytes!("../spdx_cache.bin.gz"); -const fn lint_warn() -> LintLevel { - LintLevel::Warn +const fn lint_deny() -> LintLevel { + LintLevel::Deny } const fn confidence_threshold() -> f32 { 0.8 } -#[derive(Debug)] -pub enum LicenseFieldItem<'a> { - License(&'a str), - Exception(&'a str), - UnknownLicense(&'a str), +#[derive(Deserialize)] +pub struct Clarification { + pub name: String, + pub version: Option, + pub expression: toml::Spanned, + #[serde(default, rename = "license-files")] + pub license_files: Vec, } -#[derive(Debug, Default)] -pub struct LicenseField { - data: String, +pub struct ValidClarification { + pub name: String, + pub version: VersionReq, + pub expr_offset: u32, + pub expression: spdx::Expression, + pub license_files: Vec, } -impl LicenseField { - pub fn new(lf: String) -> Self { - let license_expr = if lf.contains('/') { - lf.replace("/", " OR ") - } else { - lf - }; - - Self { data: license_expr } +impl Ord for ValidClarification { + fn cmp(&self, o: &Self) -> cmp::Ordering { + match self.name.cmp(&o.name) { + cmp::Ordering::Equal => self.version.cmp(&o.version), + o => o, + } } +} - pub fn iter(&self) -> impl Iterator> { - spdx::iter_expr(&self.data).filter_map(|item| { - Some(match item { - Ok(spdx::LicenseExpr::License(l)) => LicenseFieldItem::License(l), - Ok(spdx::LicenseExpr::Exception(e)) => LicenseFieldItem::Exception(e), - Err(spdx::ParseError::UnknownLicenseId(id)) => LicenseFieldItem::UnknownLicense(id), - _ => return None, - }) - }) +impl PartialOrd for ValidClarification { + fn partial_cmp(&self, o: &Self) -> Option { + Some(self.cmp(o)) } } -#[derive(Debug)] -pub enum LicenseInfo<'a> { - Metadata(LicenseFieldItem<'a>), - ExplicitLicenseFile(PathBuf), - InferredLicenseFile(PathBuf), +impl PartialEq for ValidClarification { + fn eq(&self, o: &Self) -> bool { + self.cmp(o) == cmp::Ordering::Equal + } } -#[derive(Deserialize)] -pub struct LicenseFile { - /// The crate relative path of the LICENSE file - pub path: PathBuf, - /// The hash of the LICENSE text, as outputted - /// when a license file hash mismatch occurs, - /// to detect when the text changes between versions - /// and needs to be verified again before using - /// the new hash - pub hash: u32, +impl Eq for ValidClarification {} + +#[inline] +fn iter_clarifications<'a>( + all: &'a [ValidClarification], + krate: &'a KrateDetails, +) -> impl Iterator { + all.iter().filter(move |vc| { + if vc.name == krate.name { + return vc.version.matches(&krate.version); + } + + false + }) } +/// Allows agreement of licensing terms based on whether the license is +/// [OSI Approved](https://opensource.org/licenses) or [considered free]( +/// https://www.gnu.org/licenses/license-list.en.html) by the FSF #[derive(Deserialize)] -pub struct IgnoreLicenses { - /// The name of the crate we are ignoring - pub name: String, - /// The version constraints of the crate we are ignoring - pub version: Option, - /// Ignores certain LICENSE* files when - /// analyzing the crate - pub license_files: Vec, +#[serde(rename_all = "kebab-case")] +pub enum BlanketAgreement { + /// The license must be both OSI Approved and FSF/Free Libre + Both, + /// The license can be be either OSI Approved or FSF/Free Libre + Either, + /// The license must be OSI Approved but not FSF/Free Libre + OsiOnly, + /// The license must be FSF/Free Libre but not OSI Approved + FsfOnly, + /// The license is not regarded specially + Neither, } -#[derive(Deserialize, Debug)] -pub struct SkipCrate { - /// The name of the crate we may skip - pub name: String, - /// The version constraints of the crate we may skip - pub version: Option, - /// The license configuration that are allowed to be ignored, - /// if this differs from the state of the crate being checked - /// it is treated as a warning and checked fully - #[serde(default)] - pub licenses: Vec, - /// The exceptions that are allowed to be ignored - #[serde(default)] - pub exceptions: Vec, +impl Default for BlanketAgreement { + fn default() -> Self { + BlanketAgreement::Neither + } } #[derive(Deserialize, Default)] +#[serde(rename_all = "kebab-case")] pub struct Config { - /// If true, will cause failures if the license is not specified - /// for a crate - #[serde(default = "lint_warn")] + /// Determines what happens when license information cannot be + /// determined for a crate + #[serde(default = "lint_deny")] pub unlicensed: LintLevel, - /// If true, will cause failures if some kind of license is specified - /// but it is not known, ie, is not an SDPX identifier - #[serde(default = "lint_warn")] - pub unknown: LintLevel, + /// Agrees to licenses based on whether they are OSI Approved + /// or FSF/Free Libre + #[serde(default)] + pub allow_osi_fsf_free: BlanketAgreement, /// The minimum confidence threshold we allow when determining the license /// in a text file, on a 0.0 (none) to 1.0 (maximum) scale #[serde(default = "confidence_threshold")] pub confidence_threshold: f32, - /// The licenses or exceptions that will cause us to emit failures + /// Licenses that will be rejected in a license expression #[serde(default)] - pub deny: Vec, - /// If specified, allows the following licenses or exceptions, if they are not - /// otherwise denied, including "unknown" licenses eg. proprietary ones that - /// aren't a known SPDX license + pub deny: Vec>, + /// Licenses that will be allowed in a license expression #[serde(default)] - pub allow: Vec, - /// If specified, allows crates to pass the license check, even - /// if they otherwise violate one of the other constraints, eg - /// you want to deny unlicensed by default, especially for new - /// crates, but you've already "manually" verified 1 or more crates - /// you already use that are unlicensed + pub allow: Vec>, + /// Overrides the license expression used for a particular crate as long as it + /// exactly matches the specified license files and hashes #[serde(default)] - pub skip: Vec, - /// If specified, ignores specific license files within a crate - #[serde(default)] - pub ignore: Vec, -} - -pub struct Ignored { - version_req: VersionReq, - licenses: Vec, + pub clarify: Vec, } impl Config { - pub fn sort(&mut self) { - self.deny.par_sort(); - self.allow.par_sort(); - self.skip.par_sort_by(|a, b| match a.name.cmp(&b.name) { - std::cmp::Ordering::Equal => a.version.cmp(&b.version), - o => o, - }); - } - - pub fn get_skipped(&self, crate_name: &str, version: &Version) -> Option<&SkipCrate> { - self.skip - .binary_search_by(|ic| ic.name.as_str().cmp(crate_name)) - .ok() - .and_then(|i| { - for sc in &self.skip[i..] { - if sc.name != crate_name { - break; - } + pub fn validate( + self, + cfg_file: codespan::FileId, + ) -> Result> { + let mut diagnostics = Vec::new(); + + let mut parse_license = + |l: &toml::Spanned, v: &mut Vec| match Licensee::parse(l.get_ref()) { + Ok(l) => v.push(l), + Err(pe) => { + let offset = (l.start() + 1) as u32; + let span = pe.span.start as u32 + offset..pe.span.end as u32 + offset; + let diag = Diagnostic::new_error( + "invalid licensee", + Label::new(cfg_file, span, format!("{}", pe.reason)), + ); - match sc.version { - Some(ref req) => { - if req.matches(version) { - return Some(sc); - } - } - None => return Some(sc), - } + diagnostics.push(diag); } + }; - None - }) - } + let mut denied = Vec::with_capacity(self.deny.len()); + for d in &self.deny { + parse_license(d, &mut denied); + } + + let mut allowed: Vec = Vec::with_capacity(self.allow.len()); + for a in &self.allow { + parse_license(a, &mut allowed); + } + + denied.par_sort(); + allowed.par_sort(); + + // Ensure the config doesn't contain the same exact license as + // both denied and allowed, that's confusing and probably + // not intended, so they should pick one + for (di, d) in denied.iter().enumerate() { + if let Ok(ai) = allowed.binary_search(&d) { + let dlabel = Label::new( + cfg_file, + self.deny[di].start() as u32..self.deny[di].end() as u32, + "marked as `deny`", + ); + let alabel = Label::new( + cfg_file, + self.allow[ai].start() as u32..self.allow[ai].end() as u32, + "marked as `allow`", + ); + + // Put the one that occurs last as the primary label to make it clear + // that the first one was "ok" until we noticed this other one + let diag = if dlabel.span.start() > alabel.span.start() { + Diagnostic::new_error( + "a license id was specified in both `allow` and `deny`", + dlabel, + ) + .with_secondary_labels(std::iter::once(alabel)) + } else { + Diagnostic::new_error( + "a license id was specified in both `allow` and `deny`", + alabel, + ) + .with_secondary_labels(std::iter::once(dlabel)) + }; + + diagnostics.push(diag); + } + } - pub fn get_ignore_licenses(&mut self) -> HashMap> { - let ignored = std::mem::replace(&mut self.ignore, Vec::new()); + let mut clarifications = Vec::with_capacity(self.clarify.len()); + for c in self.clarify { + let expr = match spdx::Expression::parse(c.expression.get_ref()) { + Ok(validated) => validated, + Err(err) => { + let offset = (c.expression.start() + 1) as u32; + let expr_span = offset + err.span.start as u32..offset + err.span.end as u32; - ignored.into_iter().fold(HashMap::new(), |mut hm, ic| { - let entry = hm.entry(ic.name).or_insert_with(Vec::new); + diagnostics.push(Diagnostic::new_error( + "unable to parse license expression", + Label::new(cfg_file, expr_span, format!("{}", err.reason)), + )); - entry.push(Ignored { - version_req: ic.version.unwrap_or_else(VersionReq::any), - licenses: ic.license_files, + continue; + } + }; + + let mut license_files = c.license_files; + license_files.sort_by(|a, b| a.path.cmp(&b.path)); + + clarifications.push(ValidClarification { + name: c.name, + version: c.version.unwrap_or_else(VersionReq::any), + expr_offset: (c.expression.start() + 1) as u32, + expression: expr, + license_files, }); + } - hm - }) + clarifications.par_sort(); + + if !diagnostics.is_empty() { + Err(diagnostics) + } else { + Ok(ValidConfig { + file_id: cfg_file, + unlicensed: self.unlicensed, + allow_osi_fsf_free: self.allow_osi_fsf_free, + confidence_threshold: self.confidence_threshold, + clarifications, + denied, + allowed, + }) + } } } -#[derive(PartialEq, Eq)] +pub struct ValidConfig { + pub file_id: codespan::FileId, + pub unlicensed: LintLevel, + pub allow_osi_fsf_free: BlanketAgreement, + pub confidence_threshold: f32, + pub denied: Vec, + pub allowed: Vec, + pub clarifications: Vec, +} + +#[derive(PartialEq, Eq, Deserialize)] pub struct FileSource { + /// The crate relative path of the LICENSE file pub path: PathBuf, + /// The hash of the LICENSE text pub hash: u32, } @@ -201,25 +272,36 @@ impl fmt::Debug for FileSource { } } -impl slog::Value for FileSource { - fn serialize( - &self, - _record: &slog::Record<'_>, - key: slog::Key, - serializer: &mut dyn slog::Serializer, - ) -> slog::Result { - serializer.emit_arguments(key, &format_args!("{:#?}", self)) - } +fn find_license_files(dir: &Path) -> Result, std::io::Error> { + let entries = std::fs::read_dir(dir)?; + Ok(entries + .filter_map(|e| { + e.ok().and_then(|e| { + let p = e.path(); + let file_name = p.file_name().and_then(|n| n.to_str()).unwrap_or(""); + if p.is_file() && file_name.starts_with("LICENSE") { + Some(p) + } else { + None + } + }) + }) + .collect()) } -pub fn get_file_source(path: PathBuf) -> Result<(String, FileSource), (PathBuf, std::io::Error)> { +fn get_file_source(path: PathBuf) -> PackFile { use std::io::BufRead; // Normalize on plain newlines to handle terrible Windows conventions let content = { let file = match std::fs::File::open(&path) { Ok(f) => f, - Err(e) => return Err((path, e)), + Err(e) => { + return PackFile { + path, + data: PackFileData::Bad(e), + } + } }; let mut s = @@ -243,164 +325,292 @@ pub fn get_file_source(path: PathBuf) -> Result<(String, FileSource), (PathBuf, s }; - let fs = FileSource { + let hash = crate::hash(content.as_bytes()); + PackFile { path, - hash: crate::hash(content.as_bytes()), - }; + data: PackFileData::Good(LicenseFile { hash, content }), + } +} - Ok((content, fs)) +struct LicenseFile { + hash: u32, + content: String, } -#[derive(Debug, PartialEq, Eq)] -pub enum LicenseSource { - /// An SPDX identifier in the Cargo.toml `license` field - Metadata, - /// The canonical text of the license. - Original(FileSource), - /// A license header. There may be more than one in a `Store`. - Header(FileSource), - /// An alternate form of a license. This is intended to be used for - /// alternate _formats_ of a license, not for variants where the text has - /// different meaning. Not currently used in askalono's SPDX dataset. - Alternate(FileSource), +enum PackFileData { + Good(LicenseFile), + Bad(std::io::Error), } -impl fmt::Display for LicenseSource { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - LicenseSource::Metadata => write!(f, "metadata"), - LicenseSource::Original(fs) => { - write!(f, "text={},hash={:#x}", fs.path.display(), fs.hash) +struct PackFile { + path: PathBuf, + data: PackFileData, +} + +struct LicensePack { + license_files: Vec, + err: Option, +} + +impl LicensePack { + fn read(krate: &KrateDetails) -> Self { + let root_path = krate.manifest_path.parent().unwrap(); + + let mut lic_paths = match find_license_files(root_path) { + Ok(paths) => paths, + Err(e) => { + return Self { + license_files: Vec::new(), + err: Some(e), + } } - LicenseSource::Header(fs) => { - write!(f, "header={},hash={:#x}", fs.path.display(), fs.hash) + }; + + // Add the explicitly specified license if it wasn't + // already found in the root directory + if let Some(ref lf) = krate.license_file { + if lic_paths.iter().find(|l| l.ends_with(lf)).is_none() { + lic_paths.push(lf.clone()); } - LicenseSource::Alternate(fs) => { - write!(f, "alt-text={},hash={:#x}", fs.path.display(), fs.hash) + } + + let mut license_files: Vec<_> = lic_paths.into_iter().map(get_file_source).collect(); + + license_files.sort_by(|a, b| a.path.cmp(&b.path)); + + Self { + license_files, + err: None, + } + } + + fn matches(&self, hashes: &[FileSource]) -> bool { + if self.license_files.len() != hashes.len() { + return false; + } + + for (expected, actual) in self.license_files.iter().zip(hashes.iter()) { + if !expected.path.ends_with(&actual.path) { + return false; + } + + match &expected.data { + PackFileData::Bad(_) => { + return false; + } + PackFileData::Good(lf) => { + if lf.hash != actual.hash { + return false; + } + } } } + + true } -} -impl slog::Value for LicenseSource { - fn serialize( + fn get_expression( &self, - _record: &slog::Record<'_>, - key: slog::Key, - serializer: &mut dyn slog::Serializer, - ) -> slog::Result { - serializer.emit_arguments(key, &format_args!("{}", self)) - } -} + krate: &KrateDetails, + file: codespan::FileId, + strat: &askalono::ScanStrategy<'_>, + confidence: f32, + ) -> Result<(String, spdx::Expression), (String, Vec