diff --git a/.github/workflows/compile_all.yml b/.github/workflows/compile_all.yml index fd0a2c363..d081e0e16 100644 --- a/.github/workflows/compile_all.yml +++ b/.github/workflows/compile_all.yml @@ -99,6 +99,19 @@ jobs: fi echo "Local bootloader found at $BOOTLOADER_PATH." + - name: Create Merged Binary + run: | + . ~/esp-idf/export.sh + MERGED_BIN_NAME="${{ matrix.target.name }}-merged-gesp.bin" + idf.py merge-bin -o "$MERGED_BIN_NAME" || { + echo "Warning: Failed to create merged binary, but build succeeded" + } + if [ -f "build/$MERGED_BIN_NAME" ]; then + echo "Merged binary created: $MERGED_BIN_NAME" + else + echo "Warning: Merged binary was not created" + fi + - name: Package Artifacts into ZIP run: | ARTIFACT_DIR="packaged_artifacts" @@ -119,6 +132,13 @@ jobs: echo "Failed to copy binaries" exit 1 } + # Copy merged binary if it exists + MERGED_BIN_NAME="${{ matrix.target.name }}-merged-gesp.bin" + if [ -f "build/$MERGED_BIN_NAME" ]; then + cp "build/$MERGED_BIN_NAME" "$ARTIFACT_DIR/" || { + echo "Warning: Failed to copy merged binary" + } + fi cd "$ARTIFACT_DIR" zip "../$ZIP_FILE" ./* || { echo "Failed to create ZIP" diff --git a/.github/workflows/compile_prerelease.yml b/.github/workflows/compile_prerelease.yml index 89f880381..0b51e7a0c 100644 --- a/.github/workflows/compile_prerelease.yml +++ b/.github/workflows/compile_prerelease.yml @@ -99,6 +99,19 @@ jobs: fi echo "Local bootloader found at $BOOTLOADER_PATH." + - name: Create Merged Binary + run: | + . ~/esp-idf/export.sh + MERGED_BIN_NAME="${{ matrix.target.name }}-merged-gesp.bin" + idf.py merge-bin -o "$MERGED_BIN_NAME" || { + echo "Warning: Failed to create merged binary, but build succeeded" + } + if [ -f "build/$MERGED_BIN_NAME" ]; then + echo "Merged binary created: $MERGED_BIN_NAME" + else + echo "Warning: Merged binary was not created" + fi + - name: Package Artifacts into ZIP run: | ARTIFACT_DIR="packaged_artifacts_${{ matrix.target.name }}" # Unique dir per target @@ -110,6 +123,13 @@ jobs: cp build/bootloader/bootloader.bin "$ARTIFACT_DIR/bootloader.bin" || { echo "Failed to copy bootloader"; exit 1; } # Copy app binary cp build/*.bin "$ARTIFACT_DIR/firmware.bin" || { echo "Failed to copy app binary"; exit 1; } + # Copy merged binary if it exists + MERGED_BIN_NAME="${{ matrix.target.name }}-merged-gesp.bin" + if [ -f "build/$MERGED_BIN_NAME" ]; then + cp "build/$MERGED_BIN_NAME" "$ARTIFACT_DIR/" || { + echo "Warning: Failed to copy merged binary" + } + fi echo "Contents of $ARTIFACT_DIR:" ls -l "$ARTIFACT_DIR" diff --git a/.github/workflows/custom_build.yml b/.github/workflows/custom_build.yml index 3b2444295..5c3ff9549 100644 --- a/.github/workflows/custom_build.yml +++ b/.github/workflows/custom_build.yml @@ -198,12 +198,32 @@ jobs: idf.py clean idf.py build + - name: Create Merged Binary + run: | + . ~/esp-idf/export.sh + MERGED_BIN_NAME="ghost_esp_${{ github.event.inputs.board_type }}_${{ github.event.inputs.display_type }}-merged-gesp.bin" + idf.py merge-bin -o "$MERGED_BIN_NAME" || { + echo "Warning: Failed to create merged binary, but build succeeded" + } + if [ -f "build/$MERGED_BIN_NAME" ]; then + echo "Merged binary created: $MERGED_BIN_NAME" + else + echo "Warning: Merged binary was not created" + fi + - name: Package Artifacts into ZIP run: | ZIP_FILE="ghost_esp_${{ github.event.inputs.board_type }}_${{ github.event.inputs.display_type }}.zip" mkdir -p artifacts cp build/partition_table/partition-table.bin artifacts/ cp build/*.bin artifacts/ + # Copy merged binary if it exists + MERGED_BIN_NAME="ghost_esp_${{ github.event.inputs.board_type }}_${{ github.event.inputs.display_type }}-merged-gesp.bin" + if [ -f "build/$MERGED_BIN_NAME" ]; then + cp "build/$MERGED_BIN_NAME" artifacts/ || { + echo "Warning: Failed to copy merged binary" + } + fi cd artifacts zip "../$ZIP_FILE" ./* cd .. diff --git a/build.py b/build.py index f7bf6ad22..227ab5d41 100644 --- a/build.py +++ b/build.py @@ -194,6 +194,7 @@ def find_esp_idf(auto_download: bool = False) -> Optional[str]: f"{home}/esp/esp-idf-v5.4.1", f"{home}/esp/v5.5/esp-idf", f"{home}/esp/v5.4.1/esp-idf", + f"{home}/esp/v5.5.1/esp-idf", os.path.join(script_dir, "esp-idf-v5.5"), os.path.join(script_dir, "esp-idf-v5.4.1"), os.path.join(script_dir, "esp-idf") @@ -228,6 +229,7 @@ def find_esp_idf(auto_download: bool = False) -> Optional[str]: if os.path.exists(base_path): try: for item in os.listdir(base_path): + # Check for esp-idf-* pattern (e.g., esp-idf-v5.5.1) if item.startswith("esp-idf-"): full_path = os.path.join(base_path, item) export_script = "export.bat" if os.name == 'nt' else "export.sh" @@ -238,6 +240,20 @@ def find_esp_idf(auto_download: bool = False) -> Optional[str]: return full_path else: print("skipping detected ESP-IDF path per user choice") + # Check for ~/esp/{version}/esp-idf pattern (e.g., ~/esp/v5.5.1/esp-idf) + elif os.path.isdir(os.path.join(base_path, item)): + # Look for esp-idf subdirectory inside version folders + version_dir = os.path.join(base_path, item) + esp_idf_path = os.path.join(version_dir, "esp-idf") + if os.path.isdir(esp_idf_path): + export_script = "export.bat" if os.name == 'nt' else "export.sh" + if os.path.exists(os.path.join(esp_idf_path, export_script)): + print(f"Found ESP-IDF at: {esp_idf_path}") + choice = input("Use this ESP-IDF path? [y/n]: ").strip().lower() + if choice not in ['n', 'no']: + return esp_idf_path + else: + print("skipping detected ESP-IDF path per user choice") except (PermissionError, OSError): continue @@ -679,17 +695,41 @@ def build_target(target: Dict[str, str], env: Dict[str, str], cmd_prefix: str = print(f"ERROR: Failed to copy build artifacts: {e}") return False + # Merge binaries using idf.py merge-bin (uses flash_args for correct offsets) + # Note: Merged binary is copied to local_builds but NOT included in ZIP + print("Creating merged binary...") + merged_bin_name = target['name'] + "-merged-gesp.bin" + merged_bin_path = os.path.join("build", merged_bin_name) + + # Use idf.py merge-bin which automatically reads offsets from flash_args + if not run_idf_command(['idf.py', 'merge-bin', '-o', merged_bin_name], env, cmd_prefix): + print("WARNING: Failed to create merged binary, but build succeeded") + # Don't fail the build if merge-bin fails, as the individual binaries are still available + else: + # Copy merged binary to artifact directory (but exclude from ZIP) + if os.path.exists(merged_bin_path): + try: + shutil.copy2(merged_bin_path, os.path.join(artifact_dir, merged_bin_name)) + print(f"Merged binary created: {os.path.join(artifact_dir, merged_bin_name)} (not included in ZIP)") + except Exception as e: + print(f"WARNING: Failed to copy merged binary to artifact directory: {e}") + else: + print("WARNING: Merged binary was not created in build directory") + print(f"Contents of {artifact_dir}:") for item in os.listdir(artifact_dir): print(f" {item}") - # Create ZIP file + # Create ZIP file (excluding merged binary) zip_path = os.path.join("local_builds", target['zip_name']) print(f"Creating ZIP file: {zip_path}") try: with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf: for item in os.listdir(artifact_dir): + # Exclude merged binary from ZIP + if item.endswith("-merged-gesp.bin"): + continue item_path = os.path.join(artifact_dir, item) zipf.write(item_path, item) @@ -698,52 +738,7 @@ def build_target(target: Dict[str, str], env: Dict[str, str], cmd_prefix: str = except Exception as e: print(f"ERROR: Failed to create ZIP file: {e}") return False - -# --- Merge binaries using esptool.py merge_bin --- - merged_bin_path = os.path.join("local_builds", target['name'] + "-merged-gesp.bin") - bootloader_bin = os.path.join("build", "bootloader", "bootloader.bin") - partition_bin = os.path.join("build", "partition_table", "partition-table.bin") - firmware_bin = None - for bin_file in build_dir.glob("*.bin"): - if bin_file.name not in ["bootloader.bin", "partition-table.bin"]: - firmware_bin = str(bin_file) - break - - if firmware_bin: - # Determine offsets (adjust if needed for your project) - boot_offset = "0x1000" if target['idf_target'] in ["esp32", "esp32s2"] else "0x0" - partition_offset = "0x8000" - firmware_offset = "0x10000" - import sys - merge_cmd = [ - sys.executable, "-m", "esptool", - "--chip", target['idf_target'], - "merge-bin", - "-o", merged_bin_path, - "--flash-mode", "dio", - "--flash-freq", "40m", - "--flash-size", "4MB", - boot_offset, bootloader_bin, - partition_offset, partition_bin, - firmware_offset, firmware_bin - ] - print(f"Merging binaries with: {' '.join(merge_cmd)}") - try: - result = subprocess.run(merge_cmd, check=True, capture_output=True, text=True) - print("esptool.py merge_bin output:") - print(result.stdout) - if os.path.exists(merged_bin_path): - print(f"Merged binary created: {merged_bin_path}") - else: - print("ERROR: Merged binary was not created.") - except subprocess.CalledProcessError as e: - print(f"ERROR: esptool.py merge_bin failed: {e}") - print(e.stdout) - print(e.stderr) - return False - else: - print("ERROR: Firmware binary not found for merging.") - return + return True def main(): diff --git a/scripts/control app/gui.py b/scripts/control app/gui.py index 48ff09e66..25eecaa19 100644 --- a/scripts/control app/gui.py +++ b/scripts/control app/gui.py @@ -2289,6 +2289,30 @@ def run_idf_build(self): if hasattr(self, 'flash_console'): if process.returncode == 0: self.flash_console.append("idf.py build finished successfully.") + # Create merged binary after successful build + self.flash_console.append("\nCreating merged binary...") + merge_cmd = ["idf.py", "merge-bin", "-o", "merged-binary.bin"] + merge_process = subprocess.Popen( + merge_cmd, + cwd=project_root, + env=env, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True + ) + for line in merge_process.stdout: + self.flash_console.append(line.rstrip()) + self.flash_console.ensureCursorVisible() + QApplication.processEvents() + merge_process.wait() + if merge_process.returncode == 0: + merged_bin_path = os.path.join(project_root, "build", "merged-binary.bin") + if os.path.exists(merged_bin_path): + self.flash_console.append(f"Merged binary created: {merged_bin_path}") + else: + self.flash_console.append("Warning: Merged binary command succeeded but file not found.") + else: + self.flash_console.append("Warning: Failed to create merged binary, but build succeeded.") else: self.flash_console.append("idf.py build exited with errors.") except Exception as e: