blob: b1ff66071751c5144e4a1f8d1a9eaf867665428e [file]
#!/bin/bash
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
# Forbidden-import gate for fe-connector modules.
# See fe/fe-connector/README.md (architecture and invariants) and
# fe/fe-connector/AGENTS.md (gate obligations).
#
# Connector modules MUST NOT import fe-core internals (catalog / common /
# datasource / qe / analysis / nereids / planner / persist / transaction / fs /
# statistics / mysql / service). Anything they need from fe-core has to be
# exposed through the SPI in
# org.apache.doris.connector.{spi,metastore,...}
# or shared types in org.apache.doris.thrift / org.apache.doris.filesystem.
#
# The gate matches both plain and `import static` imports, scans src/main/java
# AND src/test/java, and anchors the SPI-allowed exclusion to the import target
# (not the file path). Self-test: build-support/tests/test-fe-connector-imports.sh.
#
# Usage:
# build-support/check-fe-connector-imports.sh # search default root
# build-support/check-fe-connector-imports.sh <fe-connector> # search supplied root
#
# Exit code:
# 0 — no forbidden imports
# 1 — at least one forbidden import found (offending lines printed)
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DEFAULT_ROOT="${SCRIPT_DIR}/../fe/fe-connector"
ROOT="${1:-${DEFAULT_ROOT}}"
if [ ! -d "${ROOT}" ]; then
echo "check-fe-connector-imports: search root not found: ${ROOT}" >&2
exit 2
fi
FORBIDDEN='org\.apache\.doris\.(catalog|common|datasource|qe|analysis|nereids|planner|persist|transaction|fs|statistics|mysql|service)'
# SPI-shared packages a connector MAY import. Anchor the exclusion to the IMPORT TARGET (":import ..."), NOT
# the whole "<path>:<lineno>:import ..." line: a bare 'grep -v org.apache.doris.connector' matches the FILE
# PATH too (regex '.' matches '/'), so a forbidden import inside a src/.../org/apache/doris/connector/** file
# would be dropped by its location — blinding the gate to the very modules it guards. Import-anchored keeps the
# real allowance (imports OF these packages) without the path-collision false negative.
ALLOWED_IMPORT=':import[[:space:]]+(static[[:space:]]+)?org[.]apache[.]doris[.](connector|thrift|extension|filesystem)[.]'
CANDIDATES=$(grep -rEn "^import[[:space:]]+(static[[:space:]]+)?${FORBIDDEN}[.]" \
"${ROOT}"/*/src/main/java "${ROOT}"/*/src/test/java 2>/dev/null \
| grep -vE "${ALLOWED_IMPORT}" || true)
# A flagged import is a FALSE POSITIVE when the connector module VENDORS its own self-contained copy of the
# class: a real source file inside a fe-connector module whose package happens to match a fe-core prefix (e.g.
# fe-connector-hms's patched HiveMetaStoreClient imports org.apache.doris.datasource.hive.HiveVersionUtil,
# which resolves to fe-connector-hms's OWN vendored HiveVersionUtil.java, not fe-core). The naive package-prefix
# grep cannot tell those apart. Skip an import when a connector-owned source file defines it; keep only imports
# that have NO in-tree definition (i.e. genuinely reach into fe-core).
is_vendored() {
local fqn="$1" path last
while [ -n "${fqn}" ]; do
path="${fqn//.//}.java"
if find "${ROOT}" -path "*/src/main/java/${path}" -print -quit 2>/dev/null | grep -q .; then
return 0
fi
# Peel a trailing nested-class segment (Uppercase) so a nested-type import resolves to its top-level
# file; stop at a package segment (lowercase) — a genuine fe-core import has no vendored file at any level.
last="${fqn##*.}"
case "${last}" in
[A-Z]*) case "${fqn}" in *.*) fqn="${fqn%.*}" ;; *) return 1 ;; esac ;;
*) return 1 ;;
esac
done
return 1
}
RESULT=""
if [ -n "${CANDIDATES}" ]; then
while IFS= read -r line; do
[ -z "${line}" ] && continue
# line = <file>:<lineno>:import <fqn>;
fqn=$(printf '%s\n' "${line}" | sed -E 's/.*import[[:space:]]+(static[[:space:]]+)?//; s/;.*//')
if is_vendored "${fqn}"; then
echo "check-fe-connector-imports: skipping vendored same-module import: ${fqn}" >&2
continue
fi
RESULT="${RESULT}${line}"$'\n'
done <<< "${CANDIDATES}"
fi
RESULT=$(printf '%s' "${RESULT}" | sed '/^$/d')
if [ -n "${RESULT}" ]; then
echo "FORBIDDEN IMPORTS in fe-connector modules:" >&2
echo "${RESULT}" >&2
echo "" >&2
echo "fe-connector modules MUST NOT depend on fe-core internals." >&2
echo "Expose what you need through the connector SPI instead." >&2
echo "See fe/fe-connector/AGENTS.md (Machine-Checked Obligations) and fe/fe-connector/README.md." >&2
exit 1
fi