| 1 | #!/usr/bin/env bash |
| 2 | # Guard the OpenHarmony target dependency graph. |
| 3 | # |
| 4 | # This check intentionally does not require an OpenHarmony SDK or sysroot. It |
| 5 | # only asks Cargo to resolve the codewhale-tui dependency graph for the OHOS |
| 6 | # target and fails if crates known to break or be unsupported on OHOS re-enter |
| 7 | # that graph. It also proves the OHOS target activates the rquickjs-sys |
| 8 | # `bindgen` feature for codewhale-workflow-js, which is the only reason the |
| 9 | # crate compiles for a target with no pre-generated QuickJS bindings. |
| 10 | set -euo pipefail |
| 11 | |
| 12 | cd "$(dirname "$0")/../.." |
| 13 | |
| 14 | target="${1:-aarch64-unknown-linux-ohos}" |
| 15 | package="${CODEWHALE_OHOS_DEP_PACKAGE:-codewhale-tui}" |
| 16 | workflow_js_package="${CODEWHALE_OHOS_WORKFLOW_JS_PACKAGE:-codewhale-workflow-js}" |
| 17 | |
| 18 | require_literal() { |
| 19 | local file="$1" |
| 20 | local literal="$2" |
| 21 | local description="$3" |
| 22 | |
| 23 | if ! grep -qF -- "${literal}" "${file}"; then |
| 24 | echo "::error::OHOS Windows linker contract lost ${description}: ${file}" >&2 |
| 25 | return 1 |
| 26 | fi |
| 27 | } |
| 28 | |
| 29 | # Cargo invokes its configured linker directly. On Windows that entry point |
| 30 | # must be the repository launcher, not the SDK's bare clang.exe, so the final |
| 31 | # Rust link receives the OHOS target, sysroot, and musl flags as reliably as C, |
| 32 | # C++, and bindgen invocations do. This is a static, no-SDK contract check; the |
| 33 | # launcher delegates to the PowerShell compiler wrapper that validates the SDK |
| 34 | # and preserves Cargo's linker arguments and exit status. The launcher must |
| 35 | # also re-quote each argument it forwards: cmd's %* expansion strips the quotes |
| 36 | # rustc puts around arguments containing spaces, so a spaced SDK path (for |
| 37 | # example the default "D:\DevEco Studio\..." install) would otherwise arrive at |
| 38 | # clang with --sysroot split on the space. |
| 39 | require_literal \ |
| 40 | scripts/ohos-env.ps1 \ |
| 41 | '$linker = [System.IO.Path]::Combine($PSScriptRoot, "ohos", "ohos-clang.cmd")' \ |
| 42 | "the repository-local linker launcher" |
| 43 | require_literal \ |
| 44 | scripts/ohos-env.ps1 \ |
| 45 | '$env:CARGO_TARGET_AARCH64_UNKNOWN_LINUX_OHOS_LINKER = $linker' \ |
| 46 | "Cargo's target-specific linker assignment" |
| 47 | require_literal \ |
| 48 | scripts/ohos-env.ps1 \ |
| 49 | '$env:OHOS_NATIVE_SDK = $sdk' \ |
| 50 | "the absolute SDK path inherited by Cargo" |
| 51 | require_literal \ |
| 52 | scripts/ohos/ohos-clang.cmd \ |
| 53 | 'ohos-clang.ps1' \ |
| 54 | "the cmd-to-PowerShell delegation" |
| 55 | require_literal \ |
| 56 | scripts/ohos/ohos-clang.cmd \ |
| 57 | '-File "%OHOS_LINKER_SCRIPT%" %ARGS%' \ |
| 58 | "linker argument forwarding" |
| 59 | require_literal \ |
| 60 | scripts/ohos/ohos-clang.cmd \ |
| 61 | 'set "ARGS=%ARGS% "%~1""' \ |
| 62 | "argument re-quoting for space-containing paths" |
| 63 | require_literal \ |
| 64 | scripts/ohos/ohos-clang.cmd \ |
| 65 | 'exit /b %ERRORLEVEL%' \ |
| 66 | "PowerShell exit-status propagation" |
| 67 | require_literal \ |
| 68 | scripts/ohos/ohos-clang.ps1 \ |
| 69 | '& $clang -target aarch64-linux-ohos "--sysroot=$sysroot" -D__MUSL__ @args' \ |
| 70 | "the target/sysroot/musl linker flags" |
| 71 | require_literal \ |
| 72 | scripts/ohos/ohos-clang.ps1 \ |
| 73 | 'exit $LASTEXITCODE' \ |
| 74 | "native linker exit-status propagation" |
| 75 | |
| 76 | if grep -qF -- '$env:CARGO_TARGET_AARCH64_UNKNOWN_LINUX_OHOS_LINKER = $clang' scripts/ohos-env.ps1; then |
| 77 | echo "::error::OHOS Windows Cargo linker points directly at clang.exe instead of the target-aware wrapper." >&2 |
| 78 | exit 1 |
| 79 | fi |
| 80 | |
| 81 | echo "OHOS Windows linker wrapper contract OK." |
| 82 | |
| 83 | cargo_tree_with_retry() { |
| 84 | local package="$1" |
| 85 | shift |
| 86 | local attempt |
| 87 | local max_attempts="${CODEWHALE_OHOS_DEP_RETRIES:-3}" |
| 88 | local delay_seconds="${CODEWHALE_OHOS_DEP_RETRY_DELAY_SECONDS:-10}" |
| 89 | local err_file |
| 90 | local output |
| 91 | local status |
| 92 | |
| 93 | if ! [[ "${max_attempts}" =~ ^[0-9]+$ ]] || ((max_attempts < 1)); then |
| 94 | echo "CODEWHALE_OHOS_DEP_RETRIES must be an integer greater than or equal to 1." >&2 |
| 95 | return 1 |
| 96 | fi |
| 97 | |
| 98 | err_file="$(mktemp)" |
| 99 | for ((attempt = 1; attempt <= max_attempts; attempt++)); do |
| 100 | if output="$( |
| 101 | cargo tree \ |
| 102 | --locked \ |
| 103 | --package "${package}" \ |
| 104 | --target "${target}" \ |
| 105 | --prefix none \ |
| 106 | "$@" \ |
| 107 | 2>"${err_file}" |
| 108 | )"; then |
| 109 | rm -f "${err_file}" |
| 110 | printf '%s\n' "${output}" |
| 111 | return 0 |
| 112 | else |
| 113 | status=$? |
| 114 | fi |
| 115 | |
| 116 | cat "${err_file}" >&2 |
| 117 | if ((attempt >= max_attempts)); then |
| 118 | rm -f "${err_file}" |
| 119 | return "${status}" |
| 120 | fi |
| 121 | echo "cargo tree for OHOS dependency graph failed (attempt ${attempt}/${max_attempts}); retrying in ${delay_seconds}s..." >&2 |
| 122 | sleep "${delay_seconds}" |
| 123 | done |
| 124 | } |
| 125 | |
| 126 | tree="$(cargo_tree_with_retry "${package}" --all-features --no-dedupe)" |
| 127 | |
| 128 | disallowed="$( |
| 129 | grep -E '^(nix v0\.(28|29)\.|portable-pty v|starlark v|arboard v|keyring v)' <<<"${tree}" || true |
| 130 | )" |
| 131 | |
| 132 | if [[ -n "${disallowed}" ]]; then |
| 133 | { |
| 134 | echo "::error::OHOS target graph for ${package} includes unsupported dependencies:" |
| 135 | echo "${disallowed}" |
| 136 | echo |
| 137 | echo "The OpenHarmony port avoids the rustyline/starlark/portable-pty/nix chain" |
| 138 | echo "by target-gating those crates away from target_env=ohos. Keep this graph" |
| 139 | echo "clean unless a real OHOS-compatible dependency update lands." |
| 140 | } >&2 |
| 141 | exit 1 |
| 142 | fi |
| 143 | |
| 144 | echo "OHOS dependency graph OK for ${package} on ${target}." |
| 145 | |
| 146 | # codewhale-workflow-js only compiles for OHOS because its |
| 147 | # `cfg(target_env = "ohos")` dependency gate activates rquickjs's `bindgen` |
| 148 | # feature, which forwards to rquickjs-sys so QuickJS bindings are generated at |
| 149 | # build time. Resolve the feature graph for the OHOS target (pure metadata, no |
| 150 | # SDK or target toolchain needed) and fail loudly if either feature edge |
| 151 | # disappears — for example because the target gate was dropped or an rquickjs |
| 152 | # upgrade renamed the feature. |
| 153 | workflow_js_features="$(cargo_tree_with_retry "${workflow_js_package}" --edges features)" |
| 154 | |
| 155 | if ! grep -qF 'rquickjs feature "bindgen"' <<<"${workflow_js_features}" \ |
| 156 | || ! grep -qF 'rquickjs-sys feature "bindgen"' <<<"${workflow_js_features}"; then |
| 157 | { |
| 158 | echo "::error::OHOS target graph for ${workflow_js_package} lost the rquickjs bindgen feature edges:" |
| 159 | grep -E '^(rquickjs|rquickjs-sys|rquickjs-core|bindgen)( v| feature)' <<<"${workflow_js_features}" || true |
| 160 | echo |
| 161 | echo "crates/workflow-js/Cargo.toml must keep activating rquickjs's \`bindgen\`" |
| 162 | echo "feature under cfg(target_env = \"ohos\"); rquickjs ships no pre-generated" |
| 163 | echo "QuickJS bindings for OpenHarmony, so without that edge the crate cannot" |
| 164 | echo "compile for ${target}." |
| 165 | } >&2 |
| 166 | exit 1 |
| 167 | fi |
| 168 | |
| 169 | echo "OHOS rquickjs-sys bindgen feature edge OK for ${workflow_js_package} on ${target}." |
| 170 |