mirror of
https://github.com/78/xiaozhi-esp32.git
synced 2026-02-17 17:38:08 +00:00
Compare commits
7 Commits
fix_early_
...
fix_releas
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3afeae16eb | ||
|
|
10cb8369b3 | ||
|
|
fa70ac0656 | ||
|
|
f0ee31fcda | ||
|
|
cf59430991 | ||
|
|
8e34995944 | ||
|
|
fe66f39ecc |
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -83,7 +83,7 @@ jobs:
|
||||
fi
|
||||
|
||||
build:
|
||||
name: Build ${{ matrix.name }}
|
||||
name: Build ${{ matrix.full_name }}
|
||||
needs: prepare
|
||||
if: ${{ needs.prepare.outputs.variants != '[]' }}
|
||||
strategy:
|
||||
@@ -106,6 +106,6 @@ jobs:
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: xiaozhi_${{ matrix.name }}_${{ github.sha }}.bin
|
||||
name: xiaozhi_${{ matrix.full_name }}_${{ github.sha }}
|
||||
path: build/merged-binary.bin
|
||||
if-no-files-found: error
|
||||
|
||||
@@ -9,5 +9,5 @@ include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
idf_build_set_property(MINIMAL_BUILD ON)
|
||||
|
||||
set(PROJECT_VER "2.2.2")
|
||||
set(PROJECT_VER "2.2.3")
|
||||
project(xiaozhi)
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
.
|
||||
@@ -705,17 +705,17 @@ elseif(CONFIG_BOARD_TYPE_HU_087)
|
||||
set(BUILTIN_ICON_FONT font_awesome_14_1)
|
||||
endif()
|
||||
|
||||
file(GLOB BOARD_SOURCES
|
||||
if(MANUFACTURER)
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/boards/${MANUFACTURER}/${BOARD_TYPE}/*.cc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/boards/${MANUFACTURER}/${BOARD_TYPE}/*.c
|
||||
else
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.cc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.c
|
||||
file(GLOB BOARD_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/boards/${MANUFACTURER}/${BOARD_TYPE}/*.cc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/boards/${MANUFACTURER}/${BOARD_TYPE}/*.c
|
||||
)
|
||||
else()
|
||||
file(GLOB BOARD_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.cc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.c
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
)
|
||||
list(APPEND SOURCES ${BOARD_SOURCES})
|
||||
|
||||
# Select audio processor according to Kconfig
|
||||
|
||||
@@ -246,7 +246,7 @@ size_t OggDemuxer::Process(const uint8_t* data, size_t size)
|
||||
(ctx_.packet_buf[13] << 8) |
|
||||
(ctx_.packet_buf[14] << 16) |
|
||||
(ctx_.packet_buf[15] << 24);
|
||||
ESP_LOGI(TAG, "OpusHead found, sample_rate=%d", opus_info_.sample_rate);
|
||||
ESP_LOGD(TAG, "OpusHead found, sample_rate=%d", opus_info_.sample_rate);
|
||||
}
|
||||
ctx_.packet_len = 0;
|
||||
ctx_.packet_continued = false;
|
||||
@@ -258,7 +258,7 @@ size_t OggDemuxer::Process(const uint8_t* data, size_t size)
|
||||
if (!opus_info_.tags_seen) {
|
||||
if (ctx_.packet_len >= 8 && memcmp(ctx_.packet_buf, "OpusTags", 8) == 0) {
|
||||
opus_info_.tags_seen = true;
|
||||
ESP_LOGI(TAG, "OpusTags found.");
|
||||
ESP_LOGD(TAG, "OpusTags found.");
|
||||
ctx_.packet_len = 0;
|
||||
ctx_.packet_continued = false;
|
||||
ctx_.seg_index++;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
void NoAudioProcessor::Initialize(AudioCodec* codec, int frame_duration_ms, srmodel_list_t* models_list) {
|
||||
codec_ = codec;
|
||||
frame_samples_ = frame_duration_ms * 16000 / 1000;
|
||||
output_buffer_.reserve(frame_samples_);
|
||||
}
|
||||
|
||||
void NoAudioProcessor::Feed(std::vector<int16_t>&& data) {
|
||||
@@ -13,15 +14,25 @@ void NoAudioProcessor::Feed(std::vector<int16_t>&& data) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert stereo to mono if needed
|
||||
if (codec_->input_channels() == 2) {
|
||||
// If input channels is 2, we need to fetch the left channel data
|
||||
auto mono_data = std::vector<int16_t>(data.size() / 2);
|
||||
for (size_t i = 0, j = 0; i < mono_data.size(); ++i, j += 2) {
|
||||
mono_data[i] = data[j];
|
||||
for (size_t i = 0, j = 0; i < data.size() / 2; ++i, j += 2) {
|
||||
output_buffer_.push_back(data[j]);
|
||||
}
|
||||
output_callback_(std::move(mono_data));
|
||||
} else {
|
||||
output_callback_(std::move(data));
|
||||
output_buffer_.insert(output_buffer_.end(), data.begin(), data.end());
|
||||
}
|
||||
|
||||
// Output complete frames when buffer has enough data
|
||||
while (output_buffer_.size() >= (size_t)frame_samples_) {
|
||||
if (output_buffer_.size() == (size_t)frame_samples_) {
|
||||
output_callback_(std::move(output_buffer_));
|
||||
output_buffer_.clear();
|
||||
output_buffer_.reserve(frame_samples_);
|
||||
} else {
|
||||
output_callback_(std::vector<int16_t>(output_buffer_.begin(), output_buffer_.begin() + frame_samples_));
|
||||
output_buffer_.erase(output_buffer_.begin(), output_buffer_.begin() + frame_samples_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +42,7 @@ void NoAudioProcessor::Start() {
|
||||
|
||||
void NoAudioProcessor::Stop() {
|
||||
is_running_ = false;
|
||||
output_buffer_.clear();
|
||||
}
|
||||
|
||||
bool NoAudioProcessor::IsRunning() {
|
||||
|
||||
@@ -26,6 +26,7 @@ public:
|
||||
private:
|
||||
AudioCodec* codec_ = nullptr;
|
||||
int frame_samples_ = 0;
|
||||
std::vector<int16_t> output_buffer_;
|
||||
std::function<void(std::vector<int16_t>&& data)> output_callback_;
|
||||
std::function<void(bool speaking)> vad_state_change_callback_;
|
||||
std::atomic<bool> is_running_ = false;
|
||||
|
||||
@@ -59,17 +59,16 @@ void WifiBoard::StartNetwork() {
|
||||
wifi_manager.Initialize(config);
|
||||
|
||||
// Set unified event callback - forward to NetworkEvent with SSID data
|
||||
wifi_manager.SetEventCallback([this, &wifi_manager](WifiEvent event) {
|
||||
std::string ssid = wifi_manager.GetSsid();
|
||||
wifi_manager.SetEventCallback([this](WifiEvent event, const std::string& data) {
|
||||
switch (event) {
|
||||
case WifiEvent::Scanning:
|
||||
OnNetworkEvent(NetworkEvent::Scanning);
|
||||
break;
|
||||
case WifiEvent::Connecting:
|
||||
OnNetworkEvent(NetworkEvent::Connecting, ssid);
|
||||
OnNetworkEvent(NetworkEvent::Connecting, data);
|
||||
break;
|
||||
case WifiEvent::Connected:
|
||||
OnNetworkEvent(NetworkEvent::Connected, ssid);
|
||||
OnNetworkEvent(NetworkEvent::Connected, data);
|
||||
break;
|
||||
case WifiEvent::Disconnected:
|
||||
OnNetworkEvent(NetworkEvent::Disconnected);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"manufacturer": "lceda-course-examples",
|
||||
"target": "esp32s3",
|
||||
"builds": [
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"manufacturer": "lceda-course-examples",
|
||||
"target": "esp32s3",
|
||||
"builds": [
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"manufacturer": "lceda-course-examples",
|
||||
"target": "esp32s3",
|
||||
"builds": [
|
||||
{
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
"sdkconfig_append": [
|
||||
"CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y",
|
||||
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v2/16m.csv\"",
|
||||
"CONFIG_PM_ENABLE=y",
|
||||
"CONFIG_FREERTOS_USE_TICKLESS_IDLE=y",
|
||||
"CONFIG_SPIRAM_MODE_QUAD=y",
|
||||
"CONFIG_SPIRAM_SPEED_80M=y",
|
||||
|
||||
@@ -227,16 +227,19 @@ private:
|
||||
void InitializeBatteryMonitor() {
|
||||
adc_battery_monitor_ = new AdcBatteryMonitor(ADC_UNIT_1, ADC_CHANNEL_3, 5100000, 5100000, GPIO_NUM_NC);
|
||||
adc_battery_monitor_->OnChargingStatusChanged([this](bool is_charging) {
|
||||
if (is_charging) {
|
||||
power_save_timer_->SetEnabled(false);
|
||||
} else {
|
||||
power_save_timer_->SetEnabled(true);
|
||||
if (power_save_timer_ != nullptr){
|
||||
if (is_charging) {
|
||||
power_save_timer_->SetEnabled(false);
|
||||
} else {
|
||||
power_save_timer_->SetEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
void InitializePowerSaveTimer() {
|
||||
power_save_timer_ = new PowerSaveTimer(240, 300);
|
||||
power_save_timer_ = new PowerSaveTimer(240, -1, -1);
|
||||
power_save_timer_->OnEnterSleepMode([this]() {
|
||||
GetDisplay()->SetPowerSaveMode(true);
|
||||
});
|
||||
@@ -348,8 +351,8 @@ private:
|
||||
public:
|
||||
MovecallMoji2ESP32C5() : boot_button_(BOOT_BUTTON_GPIO) {
|
||||
InitializeCodecI2c();
|
||||
InitializeBatteryMonitor();
|
||||
InitializePowerSaveTimer();
|
||||
InitializeBatteryMonitor();
|
||||
InitializeSpi();
|
||||
InitializeSt77916Display();
|
||||
InitializeButtons();
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "esp_lcd_mipi_dsi.h"
|
||||
#include "esp_lcd_jd9365.h"
|
||||
#include "lcd_init_cmds.h"
|
||||
#include "config.h"
|
||||
|
||||
#include <esp_log.h>
|
||||
|
||||
@@ -19,7 +19,7 @@ dependencies:
|
||||
waveshare/custom_io_expander_ch32v003: ^1.0.0
|
||||
espressif/esp_lcd_panel_io_additions: ^1.0.1
|
||||
78/esp_lcd_nv3023: ~1.0.0
|
||||
78/esp-wifi-connect: ~3.0.2
|
||||
78/esp-wifi-connect: ~3.1.1
|
||||
espressif/esp_audio_effects: ~1.2.1
|
||||
espressif/esp_audio_codec: ~2.4.1
|
||||
78/esp-ml307: ~3.6.4
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <freertos/task.h>
|
||||
|
||||
#include "application.h"
|
||||
#include "system_info.h"
|
||||
|
||||
#define TAG "main"
|
||||
|
||||
|
||||
@@ -126,11 +126,14 @@ def rename_artifact(original_name: str, version: str) -> str:
|
||||
- Remove "xiaozhi_" prefix
|
||||
- Remove hash suffix (underscore followed by hex string)
|
||||
- Add version prefix (e.g., "v2.0.4_")
|
||||
- Change extension to .zip
|
||||
- Add .zip extension
|
||||
|
||||
Example:
|
||||
xiaozhi_atk-dnesp32s3-box0_43ef2f4e7f0957dc62ec7d628ac2819d226127b8.bin
|
||||
Examples:
|
||||
xiaozhi_atk-dnesp32s3-box0_43ef2f4e7f0957dc62ec7d628ac2819d226127b8
|
||||
-> v2.0.4_atk-dnesp32s3-box0.zip
|
||||
|
||||
xiaozhi_waveshare-esp32-p4-nano-10.1-a_43ef2f4e7f0957dc62ec7d628ac2819d226127b8
|
||||
-> v2.0.4_waveshare-esp32-p4-nano-10.1-a.zip
|
||||
|
||||
Args:
|
||||
original_name: Original artifact name
|
||||
|
||||
@@ -74,9 +74,12 @@ def _collect_variants(config_filename: str = "config.json") -> list[dict[str, st
|
||||
"""Traverse all boards under main/boards, collect variant information.
|
||||
|
||||
Return example:
|
||||
[{"board": "bread-compact-ml307", "name": "bread-compact-ml307"}, ...]
|
||||
[{"board": "bread-compact-ml307", "name": "bread-compact-ml307", "full_name": "bread-compact-ml307"}, ...]
|
||||
[{"board": "waveshare/esp32-p4-nano", "name": "esp32-p4-nano-10.1-a", "full_name": "waveshare-esp32-p4-nano-10.1-a"}, ...]
|
||||
"""
|
||||
variants: list[dict[str, str]] = []
|
||||
errors: list[str] = []
|
||||
|
||||
for cfg_path in _BOARDS_DIR.rglob(config_filename):
|
||||
board_dir = cfg_path.parent
|
||||
if board_dir.name == "common":
|
||||
@@ -87,41 +90,74 @@ def _collect_variants(config_filename: str = "config.json") -> list[dict[str, st
|
||||
with cfg_path.open() as f:
|
||||
cfg = json.load(f)
|
||||
|
||||
manufacturer = _get_manufacturer(cfg)
|
||||
|
||||
# Check manufacturer consistency with directory structure
|
||||
if "/" in board:
|
||||
# Board is in a subdirectory (e.g., waveshare/esp32-p4-nano)
|
||||
expected_manufacturer = board.split("/")[0]
|
||||
if not manufacturer:
|
||||
errors.append(
|
||||
f"{cfg_path}: Board is in '{expected_manufacturer}/' subdirectory, "
|
||||
f"but config.json is missing \"manufacturer\": \"{expected_manufacturer}\""
|
||||
)
|
||||
elif manufacturer != expected_manufacturer:
|
||||
errors.append(
|
||||
f"{cfg_path}: manufacturer mismatch, "
|
||||
f"directory is '{expected_manufacturer}/' but config.json has \"{manufacturer}\""
|
||||
)
|
||||
else:
|
||||
# Board is directly under boards/ directory
|
||||
if manufacturer:
|
||||
errors.append(
|
||||
f"{cfg_path}: Board is not in a manufacturer subdirectory, "
|
||||
f"but config.json defines manufacturer \"{manufacturer}\", "
|
||||
f"please move board to main/boards/{manufacturer}/{board}/"
|
||||
)
|
||||
|
||||
for build in cfg.get("builds", []):
|
||||
name = build["name"]
|
||||
full_name = f"{manufacturer}-{name}" if manufacturer else name
|
||||
variants.append({
|
||||
"board": board,
|
||||
"name": name
|
||||
"name": name,
|
||||
"full_name": full_name
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
print(f"[ERROR] 解析 {cfg_path} 失败: {e}", file=sys.stderr)
|
||||
print(f"[ERROR] Failed to parse {cfg_path}: {e}", file=sys.stderr)
|
||||
|
||||
# Report all errors at once
|
||||
if errors:
|
||||
print("\n[ERROR] Found manufacturer configuration issues:", file=sys.stderr)
|
||||
for err in errors:
|
||||
print(f" - {err}", file=sys.stderr)
|
||||
print(file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
return variants
|
||||
|
||||
|
||||
|
||||
def _parse_board_config_map() -> dict[str, str]:
|
||||
"""Build the mapping of CONFIG_BOARD_TYPE_xxx and board_type from main/CMakeLists.txt"""
|
||||
cmake_file = Path("main/CMakeLists.txt")
|
||||
mapping: dict[str, str] = {}
|
||||
lines = cmake_file.read_text(encoding="utf-8").splitlines()
|
||||
for idx, line in enumerate(lines):
|
||||
if "if(CONFIG_BOARD_TYPE_" in line:
|
||||
config_name = line.strip().split("if(")[1].split(")")[0]
|
||||
if idx + 1 < len(lines):
|
||||
next_line = lines[idx + 1].strip()
|
||||
if next_line.startswith("set(BOARD_TYPE"):
|
||||
board_type = next_line.split('"')[1]
|
||||
mapping[config_name] = board_type
|
||||
return mapping
|
||||
|
||||
|
||||
def _find_board_config(board_type: str) -> Optional[str]:
|
||||
"""Find the corresponding CONFIG_BOARD_TYPE_xxx for the given board_type"""
|
||||
for config, b_type in _parse_board_config_map().items():
|
||||
if b_type == board_type:
|
||||
return config
|
||||
"""Find the corresponding CONFIG_BOARD_TYPE_xxx for the given board_type
|
||||
|
||||
Search backwards from 'set(BOARD_TYPE "xxx")' to find the nearest if(CONFIG_BOARD_TYPE_).
|
||||
"""
|
||||
board_leaf = board_type.split("/")[-1]
|
||||
pattern = f'set(BOARD_TYPE "{board_leaf}")'
|
||||
|
||||
cmake_file = Path("main/CMakeLists.txt")
|
||||
lines = cmake_file.read_text(encoding="utf-8").splitlines()
|
||||
|
||||
for idx, line in enumerate(lines):
|
||||
if pattern in line:
|
||||
# Found the BOARD_TYPE line, search backwards for the config
|
||||
for back_idx in range(idx - 1, -1, -1):
|
||||
back_line = lines[back_idx]
|
||||
if "if(CONFIG_BOARD_TYPE_" in back_line:
|
||||
return back_line.strip().split("if(")[1].split(")")[0]
|
||||
break
|
||||
return None
|
||||
|
||||
|
||||
@@ -190,7 +226,7 @@ def release(board_type: str, config_filename: str = "config.json", *, filter_nam
|
||||
"""
|
||||
cfg_path = _BOARDS_DIR / Path(board_type) / config_filename
|
||||
if not cfg_path.exists():
|
||||
print(f"[WARN] {cfg_path} 不存在,跳过 {board_type}")
|
||||
print(f"[WARN] {cfg_path} does not exist, skipping {board_type}")
|
||||
return
|
||||
|
||||
project_version = get_project_version()
|
||||
@@ -205,7 +241,7 @@ def release(board_type: str, config_filename: str = "config.json", *, filter_nam
|
||||
if filter_name:
|
||||
builds = [b for b in builds if b["name"] == filter_name]
|
||||
if not builds:
|
||||
print(f"[ERROR] 未在 {board_type} 的 {config_filename} 中找到变体 {filter_name}", file=sys.stderr)
|
||||
print(f"[ERROR] Variant {filter_name} not found in {board_type}'s {config_filename}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
for build in builds:
|
||||
@@ -213,12 +249,12 @@ def release(board_type: str, config_filename: str = "config.json", *, filter_nam
|
||||
board_leaf = board_type.split("/")[-1]
|
||||
|
||||
if board_leaf not in name:
|
||||
raise ValueError(f"build.name {name} 必须包含 {board_leaf}")
|
||||
raise ValueError(f"build.name {name} must contain {board_leaf}")
|
||||
|
||||
final_name = f"{manufacturer}-{name}" if manufacturer else name
|
||||
output_path = Path("releases") / f"v{project_version}_{final_name}.zip"
|
||||
if output_path.exists():
|
||||
print(f"跳过 {final_name} 因为 {output_path} 已存在")
|
||||
print(f"Skipping {final_name} because {output_path} already exists")
|
||||
continue
|
||||
|
||||
# Process sdkconfig_append
|
||||
@@ -265,11 +301,11 @@ def release(board_type: str, config_filename: str = "config.json", *, filter_nam
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("board", nargs="?", default=None, help="板子类型或 all")
|
||||
parser.add_argument("-c", "--config", default="config.json", help="指定 config 文件名,默认 config.json")
|
||||
parser.add_argument("--list-boards", action="store_true", help="列出所有支持的 board 及变体列表")
|
||||
parser.add_argument("--json", action="store_true", help="配合 --list-boards,JSON 格式输出")
|
||||
parser.add_argument("--name", help="指定变体名称,仅编译匹配的变体(使用原始name,不带厂商前缀)")
|
||||
parser.add_argument("board", nargs="?", default=None, help="Board type or 'all'")
|
||||
parser.add_argument("-c", "--config", default="config.json", help="Config filename (default: config.json)")
|
||||
parser.add_argument("--list-boards", action="store_true", help="List all supported boards and variants")
|
||||
parser.add_argument("--json", action="store_true", help="Output in JSON format (use with --list-boards)")
|
||||
parser.add_argument("--name", help="Variant name to compile (original name without manufacturer prefix)")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
@@ -288,7 +324,7 @@ if __name__ == "__main__":
|
||||
merge_bin()
|
||||
curr_board_type = get_board_type_from_compile_commands()
|
||||
if curr_board_type is None:
|
||||
print("未能从 compile_commands.json 解析 board_type", file=sys.stderr)
|
||||
print("Failed to parse board_type from compile_commands.json", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
project_ver = get_project_version()
|
||||
zip_bin(curr_board_type, project_ver)
|
||||
@@ -300,7 +336,7 @@ if __name__ == "__main__":
|
||||
|
||||
# Check board_type in CMakeLists
|
||||
if board_type_input != "all" and not _board_type_exists(board_type_input):
|
||||
print(f"[ERROR] main/CMakeLists.txt 中未找到 board_type {board_type_input}", file=sys.stderr)
|
||||
print(f"[ERROR] board_type {board_type_input} not found in main/CMakeLists.txt", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
variants_all = _collect_variants(config_filename=args.config)
|
||||
@@ -314,10 +350,10 @@ if __name__ == "__main__":
|
||||
|
||||
for bt in sorted(target_board_types):
|
||||
if not _board_type_exists(bt):
|
||||
print(f"[ERROR] main/CMakeLists.txt 中未找到 board_type {bt}", file=sys.stderr)
|
||||
print(f"[ERROR] board_type {bt} not found in main/CMakeLists.txt", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
cfg_path = _BOARDS_DIR / bt / args.config
|
||||
if bt == board_type_input and not cfg_path.exists():
|
||||
print(f"开发板 {bt} 未定义 {args.config} 配置文件,跳过")
|
||||
print(f"Board {bt} has no {args.config} config file, skipping")
|
||||
sys.exit(0)
|
||||
release(bt, config_filename=args.config, filter_name=name_filter if bt == board_type_input else None)
|
||||
|
||||
Reference in New Issue
Block a user