#!/usr/bin/env bash

# Copyright Materialize, Inc. and contributors. All rights reserved.
#
# Use of this software is governed by the Business Source License
# included in the LICENSE file at the root of this repository.
#
# As of the Change Date specified in that file, in accordance with
# the Business Source License, use of this software will be governed
# by the Apache License, Version 2.0.
#
# Compare dependencies and their flags against a ground truth. This serves against accidentally enabling dependencies
# or feature flags.
#
# Example usages:
#
#     $ ci/test/lint-deps.sh
#
# To rewrite the stored files, pass the --rewrite option.
#
# See the [README](lint-deps/README.md) for more details.

set -euo pipefail

. misc/shlib/shlib.bash

# Explicitly name targets to check dependencies. We support Apple and Linux on ARM64 and x86_64.
targets=(
    aarch64-apple-darwin
    x86_64-apple-darwin
    aarch64-unknown-linux-gnu
    x86_64-unknown-linux-gnu
)

if [[ "$(uname -s)" = Darwin ]]; then
    tac() {
        tail -r
    }
fi

rewrite=false
resources="$(dirname "$0")/lint-deps"

if [[ "${1:-}" = --rewrite ]]; then
    shift
    rewrite=true
fi

function deps() {
    echo "# generated by $0 -- see ${resources}/README.md for details"
    # We iterate $crates instead of passing all to `cargo tree` because it produces no
    # output if there is no dependency to any of the specified packages.
    for crate in "${crates[@]}"; do
        # Gather data for the current target, reverse lines, find the dependency hierarchy to the root, reverse lines.
        # shellcheck disable=SC2046
        output=$(cargo tree \
            $(printf -- "-p %s " "${entrypoints[@]}") \
            --format ':{lib}:{f}' \
            --prefix depth \
            --edges normal,build \
            --locked \
            --target "$target" \
            "$@" \
            | tac \
            | awk -F: 'BEGIN {f=-1} /^([0-9]+):'"$crate"'/{print;f=$0-1} f==$1{print;f=f-1}' \
            | tac)
        # Only print crate and dependency chain if there is a dependency.
        if [ -n "$output" ]; then
            echo "$crate"
            echo "$output"
        fi
    done
}

ci_uncollapsed_heading "Linting dependencies -- if the check fails, consult ci/test/lint-deps/README.md"

################################################
# Jemalloc lints
################################################

# List of crates to include in the dependency lint, including an explanation why they're listed.
crates=(
    # Checks that the default allocator is jemalloc on supported platforms, but can
    # be disabled using --no-default-features or explicitly enabled with --features=jemalloc
    tikv_jemalloc_ctl
    tikv_jemallocator
    tikv_jemalloc_sys
)

# The crates whose dependency graphs we want to lint.
entrypoints=(
    mz-clusterd
    mz-environmentd
    mz-materialized
)

for target in "${targets[@]}"; do
    if $rewrite; then
        deps > "$resources/$target-default"
        deps --no-default-features > "$resources/$target-no-default-features"
        deps --features jemalloc > "$resources/$target-jemalloc"
    else
        try diff "$resources/$target-default" <(deps)
        try diff "$resources/$target-no-default-features" <(deps --no-default-features)
        try diff "$resources/$target-jemalloc" <(deps --features jemalloc)
    fi
done

################################################
# workspace-hack lints
################################################

# List of crates to include in the dependency lint, including an explanation why they're listed.
crates=(
    # Should not be included in anything that may end up in the cloud repo.
    # Eventually, we should be able to disable this everywhere with --no-default-features.
    workspace_hack
)

# The crates whose dependency graphs we want to lint.
# This should include any current or potential dependencies
# used by the cloud repo.
entrypoints=(
    mz-alloc
    mz-aws-secrets-controller
    mz-aws-util
    mz-build-info
    mz-build-tools
    mz-cloud-provider
    mz-cloud-resources
    mz-http-util
    mz-license-keys
    mz-lowertest
    mz-lowertest-derive
    mz-npm
    mz-orchestrator
    mz-orchestrator-kubernetes
    mz-orchestrator-process
    mz-orchestrator-tracing
    mz-orchestratord
    mz-ore
    mz-ore-build
    mz-ore-proc
    mz-persist-types
    mz-pgrepr-consts
    mz-pgtz
    mz-prof
    mz-prof-http
    mz-proto
    mz-repr
    mz-secrets
    mz-segment
    mz-service
    mz-sql-lexer
    mz-sql-parser
    mz-tls-util
    mz-tracing
    mz-walkabout
)

for target in "${targets[@]}"; do
    if $rewrite; then
        deps --no-default-features > "$resources/$target-workspace-hack-no-default-features"
    else
        try diff "$resources/$target-workspace-hack-no-default-features" <(deps --no-default-features)
    fi
done

try_status_report
