R核的Linux环境和Windows环境切换

背景说明:
客户发过来的MCU的R核编译环境是基于Linux设置的,目前私人电脑没有安装Linux环境,所以针对客户的工程我们面临一个问题:仅仅为了编译是否一定需要安装Linux环境?是否可以在windows环境下直接编译?

分析说明:
Step1:
使用gfile.exe查看已经编译的o文件,输出:ARM little endian relocatable (ELF)
1.ARM:指令集架构为标准 ARM;
2.little endian:小端字节序(ARM常用字节序,低位在前);
3.relocatable:可重定位目标文件(即尚未进行绝对地址绑定的中间.o,可供链接器任意布局);
4.ELF:遵循国际标准的可执行与可链接格式(Executable and Linking Format)。

img_v3_0215k_e12b5e7d-d0a7-4d8b-997e-7949a7cbe35g

Step2:
使用gnm.exe查看内部的函数符号

img_v3_0215k_71152f43-f0bc-4697-a07e-9b6defe3a09g

Step3:
查看makefile等文件中的编译设置
PLATFORM = ArmCommon
CPU_CORE = CORTEX_R52
INSTRUCTION_SET= THUMB

以上都证明这些.o文件与Windows下的GHS ARM工具链完全兼容,完全无需依赖Linux虚拟机即可在Windows下实现100%原生构建。

动作实施:
Step1:
解析原Linux下的脚本

#!/usr/bin/env bash

# Linux (Ubuntu) build entry for the B2 board, mirroring the Windows flow of
# build.bat -> build_b2.bat 1:1 (single-variant B2 build, secure image, size
# report, version extraction, artifact copy and MAP analysis).
#
# Usage: ./build_ubuntu.sh [options] [-- extra-make-arguments]

set -euo pipefail

SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
REPO_ROOT=$(cd -- "$SCRIPT_DIR/../.." && pwd)
LINUX_BUILD_DIR="$REPO_ROOT/MakeSupport/platform/J6P/linux_build"
PROJECT_MAKEFILE="$REPO_ROOT/MakeSupport/project/J6P/Makefile.project"
BUILD_WORK_DIR="$REPO_ROOT/MakeSupport/build/J6P"
IMAGE_DIR="$REPO_ROOT/MakeSupport/image/J6P"
FLASH_DIR="$REPO_ROOT/MakeSupport/flash/J6P"
MAP_DIR="$REPO_ROOT/MakeSupport/quality/J6P/map_report"
DEBUG_SCRIPT_DIR="$REPO_ROOT/MakeSupport/debug/J6P"
OUTPUT_DIR="$REPO_ROOT/output"
LOG_DIR="$BUILD_WORK_DIR/log"
LOG_FILE="$LOG_DIR/build_b2.log"

source "$LINUX_BUILD_DIR/ghs_env.sh"
GHS_PATH=$(find_ghs_path)
GHS_LICENSE_CHECK_SCRIPT=${GHS_LICENSE_CHECK_SCRIPT:-$LINUX_BUILD_DIR/check_ghs_license.sh}
if [[ -z ${JOBS:-} ]]; then
  if command -v nproc >/dev/null 2>&1; then
    JOBS=$(nproc)
  else
    JOBS=3
  fi
fi

# Mirrors the first-argument switches of build_b2.bat; anything after `--` (or
# any non-switch argument) is forwarded to make, like %* in the batch file.
BUILD_MODE=default
EXTRA_MAKE_ARGS=()
FAST=0
ORIG_ARGS=("$@")

usage() {
  cat <<'EOF'
Usage: ./build_ubuntu.sh [options] [-- extra-make-arguments]

Options:
  -r               Rebuild (make target `rebuild`)
  -rel             Release environment build (deletes generated .c/.asm/.s and
                   links from objects only, like build_b2.bat -rel)
  -i               Rebuild ELF and BIN, then generate the signed image
  clean            Clean intermediate files (no artifacts are produced)
  -j, --jobs N     Parallel job count (default: detected logical CPU count)
  --libobj         L3 platform object-tree build: compile platform sources
                   only (application directories excluded) and publish the
                   objects as a directory tree + customer kit tarball
  --app [plat_tree] [app_tree]
                   L3 application build: compile application sources and
                   pure-link them with the platform object tree. plat_tree
                   defaults to ../../platform_objtree (the unpacked-kit
                   layout), so `build_ubuntu.sh --app` is enough there.
                   Later, when the application ships as a library, pass
                   its prebuilt object tree as [app_tree] (same entry)
  -fast            Accelerated build: rsync the working tree to a fast disk
                   (/dev/shm, falls back to /tmp), build there, copy artifacts
                   back. Combine with other options, e.g. `./build_ubuntu.sh -fast -r`.
                   NAS repo is never modified; the fast work area is rebuilt on
                   every run and does not survive a reboot (by design).
  -h, --help       Show this help

Environment overrides:
  GHS_PATH           Linux GHS compiler directory
  GHS_LICENSE_CHECK_SCRIPT
                     License status script
  JOBS               Default parallel job count
EOF
}

fast_flow() {
  # Accelerated build: copy the working tree to a fast disk, build there,
  # copy artifacts back. The NAS repo itself is never modified.
  local fast_root candidate avail work lock t0 t1 rc real_commit
  fast_root=""
  for candidate in /dev/shm /tmp; do
    [[ -d $candidate && -w $candidate ]] || continue
    avail=$(df -B1G --output=avail "$candidate" 2>/dev/null | tail -1 | tr -d ' ')
    [[ -n $avail && $avail -ge 8 ]] && fast_root=$candidate && break
  done
  if [[ -z $fast_root ]]; then
    echo "[fast] no fast disk with >=8G free (/dev/shm or /tmp); run without -fast" >&2
    exit 1
  fi
  work=$fast_root/fb_$(basename "$REPO_ROOT")
  lock=$fast_root/.fb_$(basename "$REPO_ROOT").lock
  if ! mkdir "$lock" 2>/dev/null; then
    # stale-lock recovery: a killed build cannot run its EXIT trap
    if [[ -f "$lock/pid" ]] && ! kill -0 "$(cat "$lock/pid")" 2>/dev/null; then
      echo "[fast] removing stale lock (pid $(cat "$lock/pid") not running)"
      rm -rf "$lock"
      mkdir "$lock" || { echo "[fast] cannot acquire $lock" >&2; exit 1; }
    else
      echo "[fast] another build holds $lock" >&2
      exit 1
    fi
  fi
  printf '%s\n' $$ > "$lock/pid"
  trap 'rm -rf "$work" 2>/dev/null; rm -rf "$lock" 2>/dev/null' EXIT

  echo "[fast] $REPO_ROOT -> $work ($fast_root)"
  mkdir -p "$work"
  rsync_progress=()
  if [[ -t 1 ]]; then
    rsync_progress=(--info=progress2)
  fi
  rsync -a -L "${rsync_progress[@]}" --delete \
    --exclude=.git --exclude=output --exclude='MakeSupport/build' \
    "$REPO_ROOT/" "$work/repo/"

  # Fallback sweep: backslash includes are silently swallowed on Linux
  # (fixed upstream for ThirdParty; older branches still need this). The
  # sweep only touches the fast-disk copy, never the NAS repo.
  python3 - "$work/repo" <<'PYSWEEP'
import glob, io, os, re, sys
repo = sys.argv[1]
BS = chr(92)
pat = re.compile('^[ \t]*-?include[ \t]+.*' + re.escape(BS), re.M)
for p in [os.path.join(repo, 'App/Appl/Makefile.project.part.defines')] + \
         glob.glob(os.path.join(repo, 'App/Appl', '**', '*.mak'), recursive=True):
    try:
        t = io.open(p, newline='', encoding='utf-8', errors='replace').read()
    except OSError:
        continue
    new = pat.sub(lambda m: m.group(0).replace(BS, '/'), t)
    if new != t:
        io.open(p, 'w', newline='', encoding='utf-8').write(new)
PYSWEEP

  rm -rf "$work/build"
  mkdir -p "$work/build"
  ln -sfn "$work/build" "$work/repo/MakeSupport/build"

  # The fast-disk copy has no .git; hand the real commit id to the child so
  # git_version.h keeps the true revision instead of the fallback '0'.
  real_commit=$(git -C "$REPO_ROOT" rev-parse --short HEAD 2>/dev/null || printf '0')

  t0=$(date +%s)
  rc=0
  ( cd "$work/repo/App/Appl" && \
      FASTBUILD_CHILD=1 GIT_COMMIT_OVERRIDE=$real_commit ./build_ubuntu.sh "$@" ) || rc=$?
  t1=$(date +%s)
  echo "[fast] build took $(( (t1-t0)/60 )) min $(( (t1-t0)%60 )) sec (rc=$rc)"

  echo "[fast] copying artifacts back to NAS..."
  mkdir -p "$REPO_ROOT/output" "$REPO_ROOT/MakeSupport/build/J6P/output"
  [[ -d "$work/repo/output" ]] && rsync -a "${rsync_progress[@]}" --delete "$work/repo/output/" "$REPO_ROOT/output/"
  rsync -a "$work/build/J6P/output/" "$REPO_ROOT/MakeSupport/build/J6P/output/" 2>/dev/null || true
  [[ -d "$work/build/J6P/log" ]] && rsync -a "$work/build/J6P/log/" "$REPO_ROOT/MakeSupport/build/J6P/log/" 2>/dev/null || true
  rm -rf $work
  echo "[fast] work area cleared"
  echo "[fast] done. artifacts: $REPO_ROOT/output/"
  exit "$rc"
}

while (($#)); do
  case "$1" in
    -r)
      BUILD_MODE=rebuild
      ;;
    -rel)
      BUILD_MODE=rel
      ;;
    -i)
      BUILD_MODE=image
      ;;
    clean)
      BUILD_MODE=clean
      ;;
    --libobj)
      BUILD_MODE=libobj
      ;;
    --app)
      shift
      # both value slots are optional: '-'-prefixed tokens (e.g. -fast)
      # never occupy a slot, so option order is free; the unpacked-kit
      # layout has the platform tree at a fixed place and covers the
      # no-argument case
      L3_PLAT_TREE_ARG=; L3_APP_TREE_ARG=
      if [[ $# -gt 0 && ! "$1" =~ ^- ]]; then
        L3_PLAT_TREE_ARG=$1
        shift || true
        if [[ $# -gt 0 && ! "$1" =~ ^- ]]; then
          L3_APP_TREE_ARG=$1
          shift || true
        fi
      fi
      : "${L3_PLAT_TREE_ARG:=../../platform_objtree}"
      BUILD_MODE=app
      ;;
    -fast)
      FAST=1
      ;;
    -h|-usage|--help)
      BUILD_MODE=help
      ;;
    -j|--jobs)
      shift
      [[ $# -gt 0 ]] || { echo "Missing job count" >&2; exit 2; }
      JOBS=$1
      ;;
    --)
      shift
      EXTRA_MAKE_ARGS+=("$@")
      break
      ;;
    *)
      EXTRA_MAKE_ARGS+=("$1")
      ;;
  esac
  (($#)) && shift || true
done

[[ "$JOBS" =~ ^[1-9][0-9]*$ ]] || {
  echo "Invalid job count: $JOBS" >&2
  exit 2
}

if (( FAST )) && [[ "${FASTBUILD_CHILD:-0}" != "1" && "$BUILD_MODE" != "help" ]]; then
  child_args=()
  for _a in "${ORIG_ARGS[@]}"; do
    if [[ "$_a" != "-fast" ]]; then
      child_args+=("$_a")
    fi
  done
  fast_flow "${child_args[@]}"
fi

export PATH="$LINUX_BUILD_DIR:$PATH"

require_file() {
  [[ -e "$1" ]] || { echo "Required file not found: $1" >&2; exit 1; }
}

command -v make >/dev/null || { echo "GNU make is required" >&2; exit 1; }
command -v python3 >/dev/null || { echo "python3 is required" >&2; exit 1; }

require_file "$GHS_PATH/ccarm"
require_file "$GHS_PATH/gsize"
require_file "$SCRIPT_DIR/Makefile"
require_file "$LINUX_BUILD_DIR/cygpath"

# Board names come from the same config build_b2.bat reads.
MCU_OUTPUT_AB=
MCUEXT_OUTPUT_AB=
MCU_OUTPUT_B2=
MCUEXT_OUTPUT_B2=
while IFS='=' read -r key value; do
  [[ -n "$key" && "$key" != \#* ]] || continue
  value=${value%$'\r'}
  case "$key" in
    MCU_OUTPUT_AB)   MCU_OUTPUT_AB=$value ;;
    MCUEXT_OUTPUT_AB) MCUEXT_OUTPUT_AB=$value ;;
    MCU_OUTPUT_B2)   MCU_OUTPUT_B2=$value ;;
    MCUEXT_OUTPUT_B2) MCUEXT_OUTPUT_B2=$value ;;
  esac
done <"$SCRIPT_DIR/makeconfig_mcu_names.mak"
[[ -n "$MCU_OUTPUT_B2" ]] || { echo "MCU_OUTPUT_B2 not found in makeconfig_mcu_names.mak" >&2; exit 1; }

run_license_check() {
  echo "[INFO] Checking GHS license status..."
  if ! GHS_PATH="$GHS_PATH" GHS_LICENSE_MAKEFILE="$PROJECT_MAKEFILE" bash "$GHS_LICENSE_CHECK_SCRIPT"; then
    echo "[ERROR] GHS license check failed; build will not start." >&2
    return 1
  fi
}

write_git_version() {
  local commit_id build_date build_time build_host build_user
  commit_id=${GIT_COMMIT_OVERRIDE:-$(git rev-parse --short HEAD 2>/dev/null || printf '0')}
  build_date=$(date '+%Y-%m-%d')
  build_time=$(date '+%H:%M:%S')
  # McuVersionBinInfo embeds the host name in a char[32] field; long cloud
  # host names must be truncated to keep the initializer valid.
  build_host=$(hostname | cut -c1-31)
  build_user=${USER:-$(id -un)}

  {
    printf '#define GIT_VER_SHORT_COMMIT_ID "%s"\n' "$commit_id"
    printf '#define GIT_VER_SHORT_COMMIT_ID_U64 0x%sULL\n' "$commit_id"
    printf '#define BUILD_COMPUTER_NAME "%s"\n' "$build_host"
    printf '#define BUILD_USER_NAME "%s"\n' "$build_user"
    printf '#define BUILD_DATE "%s"\n' "$build_date"
    printf '#define BUILD_TIME "%s"\n' "$build_time"
  } >"$SCRIPT_DIR/git_version.h"
}

format_duration() {
  local elapsed=$1
  printf '%d min %d sec' "$((elapsed / 60))" "$((elapsed % 60))"
}

run_size_report() {
  echo "CODE SIZE:"
  "$GHS_PATH/gsize" -nobss "$OUTPUT_DIR/$MCU_OUTPUT_B2.elf"
  echo "-------------------------------------------------------------------------------"
  echo "DATA SIZE:"
  "$GHS_PATH/gsize" -notext -nodata "$OUTPUT_DIR/$MCU_OUTPUT_B2.elf"
  echo "Make Bin Success."
}

run_extract_version() {
  local extract_ver_script="$SCRIPT_DIR/tools/inject_version/extract_version.py"
  if [[ -f "$extract_ver_script" ]]; then
    python3 "$extract_ver_script" "$OUTPUT_DIR/$MCU_OUTPUT_B2.bin" "$OUTPUT_DIR/$MCU_OUTPUT_B2.dnm" 2>&1
  fi
}

make_release_env() {
  local objdir="$BUILD_WORK_DIR/obj/b2"
  local projectdir="$REPO_ROOT"
  echo "********************************************************************************"
  echo "Preparing release environment..."
  echo "[WARNING] This deletes generated .c/.asm/.s sources and links from objects only,"
  echo "          exactly like build_b2.bat -rel. Recover sources with 'git checkout'."
  # delete generated source files (mirrors: for /r ..\..\ (*.c *.asm *.s))
  find "$REPO_ROOT/App" -type f \( -name '*.c' -o -name '*.asm' -o -name '*.s' \) -delete
  # delete object side files (mirrors: for /r obj (*.dbo *.d *.dirstamp))
  find "$objdir" -type f \( -name '*.dbo' -o -name '*.d' -o -name '*.dirstamp' \) -delete 2>/dev/null || true
  # copy object tree to the repository root so linking works without sources
  if [[ -d "$objdir" ]]; then
    cp -rf "$BUILD_WORK_DIR/obj/." "$projectdir/"
  fi
  rm -rf "$BUILD_WORK_DIR/err/b2" "$BUILD_WORK_DIR/lst/b2" "$objdir"
  rm -f "$OUTPUT_DIR/$MCU_OUTPUT_B2".*
  run_make default
  rm -rf "$BUILD_WORK_DIR/err/b2" "$BUILD_WORK_DIR/lst/b2" "$objdir"
  rm -f "$OUTPUT_DIR/$MCU_OUTPUT_B2".*
  echo "Binary Environment Compile Success."
}

run_make() {
  local target=$1
  # OBJ/LST/ERR/LOG_PATH come from J6P_BUILD_DIR in Makefile.project.part.defines
  # (shared with the Windows .bat flow); no need to pass them here.
  make -f Makefile -Otarget -j"$JOBS" \
    MAKESUPPORT_DIR=../../MakeSupport \
    GHS_PATH="$GHS_PATH" \
    BUILD_OUTPUT_DIR=../../output \
    build_b2=1 \
    "$target" \
    "${EXTRA_MAKE_ARGS[@]}"
}

make_secure_image() {
  echo "********************************************************************************"
  echo "make secure image bin..."
  local secure_image_script="$IMAGE_DIR/seucre_img/bin2image/FreeRtos/run_hbbin2img.py"
  local secure_private_key="$IMAGE_DIR/seucre_img/bin2image/FreeRtos/ia_p256_private.pem"
  rm -f "$OUTPUT_DIR/$MCU_OUTPUT_B2.img" "$OUTPUT_DIR/$MCUEXT_OUTPUT_B2.img" "$OUTPUT_DIR/$MCUEXT_OUTPUT_B2.bin"
  echo "Processing bin files..."
  # --mcu-sram-8m-layout matches build_b2.bat: repack the two SRAM ranges into
  # one MCU image; the legacy dual-image split (MCUExt) is not used anymore.
  if ! python3 "$secure_image_script" \
      "$OUTPUT_DIR/$MCU_OUTPUT_B2.bin" \
      "$OUTPUT_DIR/$MCU_OUTPUT_B2.img" \
      "$secure_private_key" \
      --mcu-sram-8m-layout; then
    echo "Python script failed" >&2
    return 1
  fi
  if [[ -f "$OUTPUT_DIR/$MCU_OUTPUT_B2.img" ]]; then
    echo "Main image file generated successfully"
    [[ -f "$OUTPUT_DIR/$MCUEXT_OUTPUT_B2.img" ]] && echo "Extended image file generated successfully"
    [[ -f "$OUTPUT_DIR/$MCUEXT_OUTPUT_B2.bin" ]] && echo "Extended bin file generated successfully"
    echo "Make Image Success."
  else
    echo "Make Image Failed - $MCU_OUTPUT_B2.img not generated." >&2
    return 1
  fi
}

archive_debug_sidecars() {
  local sidecar_dir="$REPO_ROOT/MakeSupport/build/J6P/output"
  mkdir -p "$sidecar_dir"
  local moved=0 ext f
  for ext in dla dle; do
    for f in "$SCRIPT_DIR"/*."$ext"; do
      [[ -f "$f" ]] || continue
      mv -f "$f" "$sidecar_dir/"
      moved=$((moved + 1))
    done
  done
  for ext in dla dle dnm elf.rsp; do
    for f in "$OUTPUT_DIR"/*."$ext"; do
      [[ -f "$f" ]] || continue
      mv -f "$f" "$sidecar_dir/"
      moved=$((moved + 1))
    done
  done
  ((moved)) && echo "[INFO] Debug sidecars archived in $sidecar_dir ($moved files)"
}

copy_artifacts_to_output() {
  local artifact
  mkdir -p "$OUTPUT_DIR"
  echo "[INFO] Copying Board B2 artifacts to output/ ..."
  # linker artifacts are emitted directly to OUTPUT_DIR (BUILD_OUTPUT_DIR);
  # never source them from App/Appl, stale leftovers must not leak into output
  # fastboot script lives in App/Appl with ..\..\output-relative img paths;
  # the output copy is rewritten to bare img names (img sits next to it).
  local fastboot_script="$SCRIPT_DIR/fastboot_flash_$MCU_OUTPUT_B2.bat"
  if [[ -f "$fastboot_script" ]]; then
    local fb_content
    fb_content="$(<"$fastboot_script")"
    fb_content="${fb_content//..\\..\\output\\/}"
    printf '%s\n' "$fb_content" >"$OUTPUT_DIR/fastboot_flash_$MCU_OUTPUT_B2.bat"
  fi
  if [[ -f "$SCRIPT_DIR/ASW/VOYAH_Version.h" ]]; then
    cp -f "$SCRIPT_DIR/ASW/VOYAH_Version.h" "$OUTPUT_DIR/VOYAH_Version.h"
  fi
  # Debug sidecars: allow Trace32 attach directly from output/ (L2.9 scheme)
  local debug_script
  for debug_script in Lauterbach.bat Lauterbach.cmm breaks.cmm; do
    [[ -f "$DEBUG_SCRIPT_DIR/$debug_script" ]] && cp -f "$DEBUG_SCRIPT_DIR/$debug_script" "$OUTPUT_DIR/$debug_script"
  done
  printf 'ready_for_ci_packaging\n' >"$OUTPUT_DIR/ready_for_ci_packaging"
  echo "[INFO] Artifacts copied to output/"
}

run_map_analyzer() {
  local map_script="$MAP_DIR/map_analyzer.py"
  if [[ ! -f "$map_script" ]]; then
    echo "[INFO] MAP analyzer script not found, skipping..."
    return 0
  fi

  # Match build.bat selection priority: AB map first, then B2.
  local map_file=
  [[ -f "$OUTPUT_DIR/$MCU_OUTPUT_AB.map" ]] && map_file="$OUTPUT_DIR/$MCU_OUTPUT_AB.map"
  [[ -z "$map_file" && -f "$OUTPUT_DIR/$MCU_OUTPUT_B2.map" ]] && map_file="$OUTPUT_DIR/$MCU_OUTPUT_B2.map"
  if [[ -z "$map_file" ]]; then
    echo "[WARN] MAP file not found, skipping analyzer..."
    return 0
  fi

  local ld_file="$SCRIPT_DIR/GenData/Stub/vLinkGen_Template.ld"
  echo "[INFO] Running MAP analyzer..."
  if ! python3 "$map_script" "$map_file" "$ld_file" --output all --out-dir "$MAP_DIR"; then
    echo "[WARN] MAP analyzer failed, skipping..."
    return 0
  fi
  if [[ -f "$MAP_DIR/Vexus_memory_report.html" ]]; then
    cp -f "$MAP_DIR/Vexus_memory_report.html" "$OUTPUT_DIR/"
    echo "[INFO] MAP report copied to output/"
  fi
}

# L3: unpack application prebuilt libraries (.a) declared in App.mak
# (APP_USER_LIBS += ...) into appusrobj/, one subdir per library, duplicate
# member names into dupN/ (base names preserved for .ld object placement).
extract_app_usrlibs() {
  local appmak="$SCRIPT_DIR/ASW/App.mak"
  [[ -f "$appmak" ]] || return 0
  local libs
  libs=$(grep -E "^[[:space:]]*APP_USER_LIBS[[:space:]]*\+=" "$appmak" \
          | grep -v "^[[:space:]]*#" | sed "s/.*+=[[:space:]]*//" | sed "s/#.*//" | tr "\\\\" "/")
  [[ -z "$libs" ]] && return 0
  local out="$BUILD_WORK_DIR/appusrobj"
  rm -rf "$out"
  mkdir -p "$out"
  local lib libname ex
  for lib in $libs; do
    if [[ ! -f "$SCRIPT_DIR/$lib" && ! -f "$lib" ]]; then
      echo "[ERROR] APP_USER_LIBS entry not found: $lib" >&2
      return 1
    fi
    [[ -f "$lib" ]] || lib="$SCRIPT_DIR/$lib"
    libname=$(basename "$lib" .a)
    ex="$out/$libname"
    mkdir -p "$ex"
    local members=() m
    mapfile -t members < <(ar t "$lib")
    (( ${#members[@]} )) || { echo "[ERROR] empty archive: $lib" >&2; return 1; }
    local -A cnt=()
    local singles=()
    for m in "${members[@]}"; do cnt[$m]=$(( ${cnt[$m]:-0} + 1 )); done
    for m in "${members[@]}"; do
      if [[ ${cnt[$m]} -eq 1 ]]; then singles+=("$m"); fi
    done
    local -A used=()
    for m in "${members[@]}"; do
      if [[ ${cnt[$m]} -le 1 ]]; then continue; fi
      used[$m]=$(( ${used[$m]:-0} + 1 ))
      mkdir -p "$ex/dup${used[$m]}"
      (cd "$ex/dup${used[$m]}" && ar x -N "${used[$m]}" "$lib" "$m")
    done
    if [[ ${#singles[@]} -gt 0 ]]; then
      (cd "$ex" && ar x "$lib" "${singles[@]}")
    fi
    echo "[L3] unpacked app library $lib -> $ex"
  done
}

build_impl() {
  cd "$SCRIPT_DIR"
  # Defensive sweep: stale artifacts left by the old App/Appl layout
  # must never leak into output (they are gitignored build outputs).
  rm -f "$SCRIPT_DIR"/MCU_*.{bin,elf,img,map,hex,dla,dle,dnm,rsp} \
        "$SCRIPT_DIR"/MCUExt_*.{bin,elf,img,map,hex,dla,dle,dnm,rsp}

  if [[ "$BUILD_MODE" != clean && "$BUILD_MODE" != help ]]; then
    # Mirror build.bat: start every publishing build with an empty output folder.
    mkdir -p "$OUTPUT_DIR"
    find "$OUTPUT_DIR" -mindepth 1 -delete
    run_license_check || return $?
  fi

  write_git_version

  # Mirror build_b2.bat: response file and debug sidecar are always regenerated.
  rm -f "$OUTPUT_DIR/$MCU_OUTPUT_B2.dnm" "$OUTPUT_DIR/$MCU_OUTPUT_B2.elf.rsp"

  case "$BUILD_MODE" in
    help)
      run_make help
      return $?
      ;;
    clean)
      run_make clean
      return $?
      ;;
    rebuild)
      echo "rebuild starting......"
      rm -f "$OUTPUT_DIR/$MCU_OUTPUT_B2.bin" "$OUTPUT_DIR/$MCUEXT_OUTPUT_B2.bin"
      run_make rebuild || return $?
      ;;
    rel)
      rm -f "$OUTPUT_DIR/$MCU_OUTPUT_B2.bin" "$OUTPUT_DIR/$MCUEXT_OUTPUT_B2.bin"
      run_make default || return $?
      ;;
    image)
      rm -f "$OUTPUT_DIR/$MCU_OUTPUT_B2.elf" "$OUTPUT_DIR/$MCU_OUTPUT_B2.bin" "$OUTPUT_DIR/$MCUEXT_OUTPUT_B2.bin"
      run_make default || return $?
      ;;
    libobj)
      EXTRA_MAKE_ARGS+=("L3_BUILD_MODE=libobj")
      run_make libobjtree || return $?
      local tree="$REPO_ROOT/MakeSupport/delivery/platobjtree"
      echo "[L3] platform object tree published: $(find "$tree" -name '*.o' | wc -l) objects -> $tree"
      # Package the customer kit (object tree + build framework, no source mutation)
      local pack_script="$SCRIPT_DIR/tools/dfzy/package_objtree.py"
      if [[ -f "$pack_script" ]]; then
        python3 "$pack_script" "$REPO_ROOT" "$OUTPUT_DIR/DFRD_objkit_$(date '+%Y%m%d').tar.gz" || return $?
      else
        echo "[WARN] packaging script not found: $pack_script (skipped)" >&2
      fi
      return 0
      ;;
    app)
      if [[ -z "${L3_PLAT_TREE_ARG:-}" ]]; then
        echo "[ERROR] --app requires the platform object tree path" >&2
        return 1
      fi
      # L3_TREE_IN_PLACE: tree dirs must be RELATIVE to App/Appl (absolute
      # paths overflow the shell in make $(shell find ...) consumers); the
      # trees are consumed in place through stable symlinks, never moved
      [[ -d "$L3_PLAT_TREE_ARG" ]] || { echo "[ERROR] platform tree not found: $L3_PLAT_TREE_ARG (default ../../platform_objtree = unpacked-kit layout; pass <plat_tree> explicitly otherwise)" >&2; return 1; }
      local plat_abs app_abs
      plat_abs=$(cd "$L3_PLAT_TREE_ARG" && pwd)
      ln -sfn "$plat_abs" "$BUILD_WORK_DIR/platobjtree"
      local app_args=("L3_BUILD_MODE=app" "L3_PLAT_OBJ_DIR=../../MakeSupport/build/J6P/platobjtree")
      if [[ -n "${L3_APP_TREE_ARG:-}" ]]; then
        # later phase: application ships as a prebuilt object tree
        [[ -d "$L3_APP_TREE_ARG" ]] || { echo "[ERROR] app tree not found: $L3_APP_TREE_ARG" >&2; return 1; }
        app_abs=$(cd "$L3_APP_TREE_ARG" && pwd)
        ln -sfn "$app_abs" "$BUILD_WORK_DIR/appobjtree"
        app_args+=("L3_APP_OBJ_DIR=../../MakeSupport/build/J6P/appobjtree")
      fi
      extract_app_usrlibs || return $?
      EXTRA_MAKE_ARGS+=("${app_args[@]}")
      rm -f "$OUTPUT_DIR/$MCU_OUTPUT_B2.bin" "$OUTPUT_DIR/$MCUEXT_OUTPUT_B2.bin"
      run_make default || return $?
      ;;
    default)
      rm -f "$OUTPUT_DIR/$MCU_OUTPUT_B2.bin" "$OUTPUT_DIR/$MCUEXT_OUTPUT_B2.bin"
      run_make default || return $?
      ;;
  esac

  if [[ ! -f "$OUTPUT_DIR/$MCU_OUTPUT_B2.bin" ]]; then
    echo "Make Bin Failed." >&2
    return 1
  fi

  run_size_report || return $?
  run_extract_version

  if [[ "$BUILD_MODE" == rel ]]; then
    make_release_env || return $?
  fi

  # build_b2.bat keeps image generation enabled for every producing mode.
  make_secure_image || return $?

  archive_debug_sidecars
  copy_artifacts_to_output || return $?
  run_map_analyzer
}

mkdir -p "$LOG_DIR"
: >"$LOG_FILE"

START_EPOCH=$(date +%s)
printf '[B2] Build started at %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" | tee -a "$LOG_FILE"

set +e
(set -euo pipefail; build_impl) 2>&1 | tee -a "$LOG_FILE"
BUILD_RESULT=${PIPESTATUS[0]}
set -e

END_EPOCH=$(date +%s)
printf '[B2] Build finished at %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" | tee -a "$LOG_FILE"
printf '[B2] Build duration: %s\n' \
  "$(format_duration "$((END_EPOCH - START_EPOCH))")" | tee -a "$LOG_FILE"

if ((BUILD_RESULT != 0)); then
  echo "[ERROR] Board B2 build failed! Error code: $BUILD_RESULT" | tee -a "$LOG_FILE" >&2
  exit "$BUILD_RESULT"
fi
echo "[INFO] Board B2 build completed successfully"

Step2:
构建纯Windows环境下的编译脚本

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
================================================================================
DFRD_objkit Windows One-Click Build Runner
Platform: Horizon Journey 6P (J6P) B2 Board
Compiler: Green Hills MULTI ARM Compiler (ccarm.exe / comp_202114)
================================================================================
"""

import os
import sys
import glob
import subprocess
import time

def find_ghs_dir():
    candidates = [
        r"D:\ghs\comp_202114",
        r"C:\ghs\comp_202114",
        r"D:\ghs\comp_202214",
        r"C:\ghs\comp_202214",
    ]
    if "GHS_PATH" in os.environ and os.path.exists(os.environ["GHS_PATH"]):
        return os.environ["GHS_PATH"]
    for c in candidates:
        if os.path.exists(os.path.join(c, "ccarm.exe")):
            return c
    return None

def parse_app_mak(appl_dir):
    app_mak = os.path.join(appl_dir, "ASW", "App.mak")
    sources = []
    includes = []
    if not os.path.exists(app_mak):
        return sources, includes
        
    with open(app_mak, "r", encoding="utf-8", errors="ignore") as f:
        for line in f:
            line = line.strip()
            if line.startswith("#"):
                continue
            if "APP_USER_SOURCES" in line and "+=" in line:
                val = line.split("+=")[-1].strip().replace("/", os.sep).replace("\\", os.sep)
                if val:
                    full_p = os.path.join(appl_dir, val)
                    if os.path.exists(full_p):
                        sources.append(full_p)
            elif "ADDITIONAL_INCLUDES" in line and "+=" in line:
                val = line.split("+=")[-1].strip().replace("/", os.sep).replace("\\", os.sep)
                if val:
                    full_p = os.path.join(appl_dir, val)
                    if os.path.exists(full_p):
                        includes.append(full_p)
                        
    return sources, includes

def build_windows():
    print("="*80)
    print(" [J6P B2] DFRD_objkit Windows One-Click Build System")
    print("="*80)
    
    script_dir = os.path.abspath(os.path.dirname(__file__))
    # If placed in App/Appl or in root
    if os.path.exists(os.path.join(script_dir, "ASW")):
        appl_dir = script_dir
        repo_root = os.path.abspath(os.path.join(appl_dir, "..", ".."))
    else:
        repo_root = script_dir
        appl_dir = os.path.join(repo_root, "App", "Appl")
        
    ghs_dir = find_ghs_dir()
    if not ghs_dir:
        print("[ERROR] Could not find GHS compiler installation (ccarm.exe)!")
        print("        Please set GHS_PATH=D:\\ghs\\comp_xxxxxx or ensure D:\\ghs\\comp_202114 exists.")
        return False
        
    cc_exe = os.path.join(ghs_dir, "ccarm.exe")
    gsize_exe = os.path.join(ghs_dir, "gsize.exe")
    
    print(f"[ENV] Repo Root:    {repo_root}")
    print(f"[ENV] App Directory:{appl_dir}")
    print(f"[ENV] GHS Toolchain:{ghs_dir}")
    
    out_dir = os.path.join(repo_root, "output")
    os.makedirs(out_dir, exist_ok=True)
    
    build_work_dir = os.path.join(repo_root, "MakeSupport", "build", "J6P")
    obj_b2_dir = os.path.join(build_work_dir, "obj", "b2")
    os.makedirs(obj_b2_dir, exist_ok=True)
    
    # 1. Generate git_version.h
    git_ver_h = os.path.join(appl_dir, "git_version.h")
    commit_id = "0000000"
    try:
        git_res = subprocess.run(["git", "rev-parse", "--short", "HEAD"], capture_output=True, text=True, cwd=repo_root)
        if git_res.returncode == 0 and git_res.stdout.strip():
            commit_id = git_res.stdout.strip()
    except Exception:
        pass
        
    date_str = time.strftime("%Y-%m-%d")
    time_str = time.strftime("%H:%M:%S")
    with open(git_ver_h, "w", encoding="utf-8") as f:
        f.write(f'#define GIT_VER_SHORT_COMMIT_ID "{commit_id}"\n')
        f.write(f'#define GIT_VER_SHORT_COMMIT_ID_U64 0x{commit_id}ULL\n')
        f.write(f'#define BUILD_COMPUTER_NAME "{os.environ.get("COMPUTERNAME", "WIN-BUILD")}"\n')
        f.write(f'#define BUILD_USER_NAME "{os.environ.get("USERNAME", "builder")}"\n')
        f.write(f'#define BUILD_DATE "{date_str}"\n')
        f.write(f'#define BUILD_TIME "{time_str}"\n')
    print(f"\n[Step 1/5] Generated build info header: git_version.h")
    
    # 2. Collect include paths
    inc_dirs = [
        os.path.join(ghs_dir, "include", "arm"),
        os.path.join(ghs_dir, "ansi"),
    ]
    for root, dirs, files in os.walk(repo_root):
        if any(f.endswith('.h') for f in files):
            if 'MakeSupport' not in root and 'output' not in root and 'platform_objtree' not in root:
                inc_dirs.append(root)
                
    # Extra includes from App.mak
    asw_srcs_from_mak, asw_incs_from_mak = parse_app_mak(appl_dir)
    for d in asw_incs_from_mak:
        if d not in inc_dirs:
            inc_dirs.append(d)
            
    inc_flags = [f"-I{d}" for d in inc_dirs if os.path.exists(d)]
    
    # Common CFLAGS for Horizon J6P ARM Cortex-R52
    cflags = [
        "-cpu=cortexr52",
        "-c99",
        "-noobj",
        "--long_long",
        "-G",
        "-dual_debug",
        "-farcalls",
        "-dwarf2",
        "-no_misalign_pack",
        "-pragma_asm_inline",
        "--gnu_asm",
        "-align8",
        "-Osize",
        "--diag_suppress=1",
        "-DDISABLE_MCAL_INTERMODULE_ASR_CHECK",
        "-DMCU_SRAM_8M_LAYOUT",
        "-DBRS_PLATFORM_ArmCommon",
        "-DBRS_COMP_GreenHills",
        "-DMATRIX6P_A_ADAPTATION",
    ] + inc_flags
    
    # 3. Compile ASW sources
    if not asw_srcs_from_mak:
        # Default fallback
        asw_srcs_from_mak = [
            os.path.join(appl_dir, "ASW", "DfzyDemo", "DfzyDemo.c"),
            os.path.join(appl_dir, "ASW", "EthProxy", "STA_Someip_Com.c"),
            os.path.join(appl_dir, "ASW", "EthProxy", "EthProxy_SWC.c"),
        ]
        
    asw_objs = []
    print(f"[Step 2/5] Compiling {len(asw_srcs_from_mak)} ASW application sources...")
    for src in asw_srcs_from_mak:
        if not os.path.exists(src):
            continue
        rel_src = os.path.relpath(src, appl_dir)
        obj_name = os.path.splitext(os.path.basename(src))[0] + ".o"
        out_obj = os.path.join(obj_b2_dir, "ASW", os.path.dirname(rel_src), obj_name)
        os.makedirs(os.path.dirname(out_obj), exist_ok=True)
        
        cmd = [cc_exe, "-c", src, "-o", out_obj] + cflags
        res = subprocess.run(cmd, capture_output=True, text=True, cwd=appl_dir)
        if res.returncode != 0:
            print(f"\n[ERROR] Compilation failed for {rel_src}:")
            print(res.stderr)
            return False
        asw_objs.append(out_obj)
        print(f"  [CC OK] {rel_src} -> {os.path.basename(out_obj)}")
        
    # 4. Collect precompiled platform objects & SafetyLib
    plat_tree = os.path.join(repo_root, "platform_objtree")
    print(f"[Step 3/5] Loading precompiled platform object tree...")
    plat_objs = []
    if os.path.exists(plat_tree):
        for root, dirs, files in os.walk(plat_tree):
            for f in files:
                if f.endswith(".o"):
                    plat_objs.append(os.path.join(root, f))
    print(f"  Loaded {len(plat_objs)} platform object files from platform_objtree.")
    
    extra_libs = []
    safety_lib = os.path.join(appl_dir, "SafetyLib", "SafetyLib.a")
    if os.path.exists(safety_lib):
        print(f"  Loaded Safety Library: SafetyLib.a")
        extra_libs.append(safety_lib)
        
    # 5. Link Target ELF / BIN / HEX / MAP
    target_name = "MCU_L4_H56E_V1.0"
    target_base = os.path.join(out_dir, target_name)
    target_elf = target_base + ".elf"
    target_map = target_base + ".map"
    target_hex = target_base + ".hex"
    target_bin = target_base + ".bin"
    target_img = target_base + ".img"
    linker_script = os.path.join(appl_dir, "GenData", "Stub", "vLinkGen_Template.ld")
    
    rsp_file = target_elf + ".rsp"
    all_objs = asw_objs + plat_objs + extra_libs
    with open(rsp_file, "w", encoding="utf-8") as f:
        for obj in all_objs:
            f.write(f'"{obj.replace(os.sep, "/")}"\n')
            
    print(f"[Step 4/5] Generated linker response file ({len(all_objs)} objects): {os.path.basename(rsp_file)}")
    
    ldflags = [
        "--preprocess_linker_directive_full",
        "-nostartfiles",
        "-e=intvect_CoreExceptions",
        linker_script.replace(os.sep, "/"),
        "-o", target_elf.replace(os.sep, "/"),
        "-cpu=cortexr52",
        "-pragma_asm_inline",
        "-g",
        "-keepmap",
        "-dual_debug",
        f"-map={target_map.replace(os.sep, '/')}",
        f"-hex={target_hex.replace(os.sep, '/')}",
        f"-memory={target_bin.replace(os.sep, '/')}",
        "-thumb",
        "-thumb_lib",
        "-dwarf2",
        "-Ogeneral",
        "-Omax",
        "-Xgvared",
        "--no_vla",
        "--gnu_asm",
        "--no_commons",
        "-no_discard_zero_initializers",
        "-preprocess_assembly_files",
        "-split_data_sections_by_alignment",
        "-individual_data_sections",
        "-individual_pragma_data_sections",
        "-individual_function_sections",
        "-individual_pragma_function_sections",
        "-individual_attribute_data_sections",
        "-individual_attribute_function_sections",
        "-individual_section_name_extra_dot",
        "-align8",
        "--unknown_pragma_errors",
        "--incorrect_pragma_errors",
        "-passsource",
        "-globalcheck=normal",
        "-mapfile_type=2",
        "-Man", "-Ml", "-Mx", "-Mu",
        "-ignore_debug_references",
        "-delete",
        "-data_delete",
        "-retainlocals",
        f"@{rsp_file.replace(os.sep, '/')}"
    ]
    
    print(f"[Step 5/5] Linking final firmware: {os.path.basename(target_elf)} ...")
    t0 = time.time()
    res = subprocess.run([cc_exe] + ldflags, capture_output=True, text=True, cwd=appl_dir)
    t1 = time.time()
    
    if res.returncode != 0:
        print(f"\n[ERROR] Link failed (Return Code: {res.returncode}):")
        print(res.stderr)
        return False
        
    # Generate signed image (.img)
    sign_script = os.path.join(repo_root, "MakeSupport", "image", "J6P", "seucre_img", "bin2image", "FreeRtos", "run_hbbin2img.py")
    sign_key = os.path.join(repo_root, "MakeSupport", "image", "J6P", "seucre_img", "bin2image", "FreeRtos", "ia_p256_private.pem")
    if os.path.exists(sign_script) and os.path.exists(sign_key) and os.path.exists(target_bin):
        print(f"\n[Pack] Packaging signed boot image (.img)...")
        sign_cmd = [sys.executable, sign_script, target_bin, target_img, sign_key, "--mcu-sram-8m-layout"]
        subprocess.run(sign_cmd, capture_output=True, text=True, cwd=appl_dir)
        
    print("\n" + "="*80)
    print(f" [SUCCESS] Build completed successfully in {t1-t0:.2f} seconds!")
    print("="*80)
    print(f"Firmware output artifacts located in: {out_dir}")
    for ext, path in [("ELF", target_elf), ("BIN", target_bin), ("HEX", target_hex), ("IMG", target_img), ("MAP", target_map)]:
        if os.path.exists(path):
            print(f"  * [{ext:<3}] {os.path.basename(path):<28} ({os.path.getsize(path)/1024/1024:.2f} MB / {os.path.getsize(path):,} bytes)")
            
    # Print size summary
    if os.path.exists(gsize_exe) and os.path.exists(target_elf):
        print("\n=== FIRMWARE MEMORY LAYOUT SUMMARY ===")
        res_size = subprocess.run([gsize_exe, target_elf], capture_output=True, text=True)
        lines = res_size.stdout.strip().split("\n")
        # print header and total
        for line in lines[:5]:
            print(line)
        if len(lines) > 10:
            print("  ...")
            for line in lines[-5:]:
                print(line)
                
    return True

if __name__ == "__main__":
    success = build_windows()
    sys.exit(0 if success else 1)

最后:
1.脚本之间的转换现在用AI十分的快捷,能极大的提高开发效率。但是特么的现在极高的开发效率被极多的开发任务又抹平了。
2.用点好的AI,别被垃圾的费钱的AI道德绑架了。

posted @ 2026-09-17 21:24  日暮_途远  阅读(7)  评论(0)    收藏  举报