diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d0ddeed..d8b90d5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,7 +21,8 @@ jobs: name: Build on ${{ matrix.os }} strategy: matrix: - os: [ubuntu-22.04, ubuntu-24.04, macos-13] + os: [ubuntu-22.04, ubuntu-24.04, macos-15-intel, macos-15] + #os: [ubuntu-22.04, ubuntu-24.04, macos-13] runs-on: ${{ matrix.os }} steps: - name: Checkout repository diff --git a/IDEIntegration.md b/IDEIntegration.md index 406221b..faccc26 100644 --- a/IDEIntegration.md +++ b/IDEIntegration.md @@ -31,4 +31,5 @@ After these procedures, the project should be initialized correctlly ## Download a project 1. `cd` into the project directory -2. `mm-sdk/usr/mm/mm download` \ No newline at end of file +2. `mm-sdk/usr/mm/mm download` + diff --git a/README.md b/README.md index cc264e9..45d8e01 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ The latest features would be added to this SDK first and then integrated into th ### macOS -Install XCode and open it so it could install any components that needed. +Install [swiftly](https://www.swift.org/install/macos) first, then use it to install the Swift toolchain ### Ubuntu 22.04 @@ -153,4 +153,4 @@ or python3 ~/mm-sdk/mm/src/mm.py download ``` -This command would find the correspond img file, copy it to the flash storage. \ No newline at end of file +This command would find the correspond img file, copy it to the flash storage. diff --git a/lib/swift/6.1/MadGraphics.swiftmodule/armv7em-none-none-eabi.swiftdoc b/lib/swift/6.1/MadGraphics.swiftmodule/armv7em-none-none-eabi.swiftdoc new file mode 100644 index 0000000..2a2336b Binary files /dev/null and b/lib/swift/6.1/MadGraphics.swiftmodule/armv7em-none-none-eabi.swiftdoc differ diff --git a/lib/swift/6.1/MadGraphics.swiftmodule/armv7em-none-none-eabi.swiftmodule b/lib/swift/6.1/MadGraphics.swiftmodule/armv7em-none-none-eabi.swiftmodule new file mode 100644 index 0000000..9973928 Binary files /dev/null and b/lib/swift/6.1/MadGraphics.swiftmodule/armv7em-none-none-eabi.swiftmodule differ diff --git a/lib/swift/6.2.3/MadGraphics.swiftmodule/armv7em-none-none-eabi.swiftdoc b/lib/swift/6.2.3/MadGraphics.swiftmodule/armv7em-none-none-eabi.swiftdoc new file mode 100644 index 0000000..7e72ddf Binary files /dev/null and b/lib/swift/6.2.3/MadGraphics.swiftmodule/armv7em-none-none-eabi.swiftdoc differ diff --git a/lib/swift/6.2.3/MadGraphics.swiftmodule/armv7em-none-none-eabi.swiftmodule b/lib/swift/6.2.3/MadGraphics.swiftmodule/armv7em-none-none-eabi.swiftmodule new file mode 100644 index 0000000..80f14ae Binary files /dev/null and b/lib/swift/6.2.3/MadGraphics.swiftmodule/armv7em-none-none-eabi.swiftmodule differ diff --git a/lib/swift/6.2/MadGraphics.swiftmodule/armv7em-none-none-eabi.swiftdoc b/lib/swift/6.2/MadGraphics.swiftmodule/armv7em-none-none-eabi.swiftdoc new file mode 100644 index 0000000..4de201a Binary files /dev/null and b/lib/swift/6.2/MadGraphics.swiftmodule/armv7em-none-none-eabi.swiftdoc differ diff --git a/lib/swift/6.2/MadGraphics.swiftmodule/armv7em-none-none-eabi.swiftmodule b/lib/swift/6.2/MadGraphics.swiftmodule/armv7em-none-none-eabi.swiftmodule new file mode 100644 index 0000000..8761dcd Binary files /dev/null and b/lib/swift/6.2/MadGraphics.swiftmodule/armv7em-none-none-eabi.swiftmodule differ diff --git a/lib/swift/MadGraphics.swiftmodule/armv7em-none-none-eabi.swiftdoc b/lib/swift/MadGraphics.swiftmodule/armv7em-none-none-eabi.swiftdoc deleted file mode 100644 index 0e1973a..0000000 Binary files a/lib/swift/MadGraphics.swiftmodule/armv7em-none-none-eabi.swiftdoc and /dev/null differ diff --git a/lib/swift/MadGraphics.swiftmodule/armv7em-none-none-eabi.swiftmodule b/lib/swift/MadGraphics.swiftmodule/armv7em-none-none-eabi.swiftmodule deleted file mode 100644 index 623141f..0000000 Binary files a/lib/swift/MadGraphics.swiftmodule/armv7em-none-none-eabi.swiftmodule and /dev/null differ diff --git a/mm/src/download.py b/mm/src/download.py index 5740c00..acc93ab 100644 --- a/mm/src/download.py +++ b/mm/src/download.py @@ -71,7 +71,7 @@ def darwin_download(source): if DARWIN_MOUNT_PATH is None: board_name = mmp.get_board_name() - log.die('Cannot find ' + board_name + ', please make sure it is successfully mounted') + log.die('cannot find ' + board_name + ', please make sure it is successfully mounted') log.inf(DARWIN_MOUNT_PATH + ' found') file_name = source.name diff --git a/mm/src/image.py b/mm/src/image.py index 3baeb89..3c64925 100644 --- a/mm/src/image.py +++ b/mm/src/image.py @@ -64,4 +64,4 @@ def create_swiftio_bin(bin_path, out_path, out_name): log.inf('Creating image ' + image_name + '...') image_raw_binary = bin_path.read_bytes() image_crc = crc32(image_raw_binary).to_bytes(4, byteorder='little') - image_path.write_bytes(image_raw_binary + image_crc) \ No newline at end of file + image_path.write_bytes(image_raw_binary + image_crc) diff --git a/mm/src/mm.py b/mm/src/mm.py index eb35eab..ea70804 100755 --- a/mm/src/mm.py +++ b/mm/src/mm.py @@ -1,5 +1,5 @@ import os, sys, platform, argparse, shutil -from pathlib import Path +from pathlib import Path, PurePosixPath import log, util, spm, mmp, download, version import serial_download, image import multiprocessing @@ -42,7 +42,7 @@ def init_project(args): # if p_type == 'executable' and (build_path / p_name).exists(): # bin_path = mmp.create_binary(build_path=build_path, name=p_name) -# image_name = mmp.get_board_info('sd_image_name') +# image_name = mmp.get_board_info('image_name') # board_name = mmp.get_board_name() # if board_name == 'SwiftIOMicro': # image.create_image(bin_path, build_path, image_name) @@ -56,7 +56,7 @@ def build_with_sdk(build_path, p_type, p_name): if p_type == 'executable' and (build_path / p_name).exists(): bin_path = mmp.create_binary(build_path=build_path, name=p_name) - image_name = mmp.get_board_info('sd_image_name') + image_name = mmp.get_board_info('image_name') board_name = mmp.get_board_name() if board_name == 'SwiftIOMicro': image.create_image(bin_path, build_path, image_name) @@ -65,43 +65,10 @@ def build_with_sdk(build_path, p_type, p_name): else: log.die('Board name is not specified') -# def build_project(args): -# mmp_manifest = Path(PROJECT_PATH / 'Package.mmp') - -# if not mmp_manifest.is_file(): -# log.die('Package.mmp is required to build the project') - -# mmp_content = mmp_manifest.read_text() -# mmp.initialize(mmp_content) - -# mmp.clean(p_path=PROJECT_PATH) -# spm.initialize() -# p_name = spm.get_project_name() -# p_type = spm.get_project_type() - -# build_path = '' -# triple = None - -# if p_type == 'executable': -# triple = mmp.get_triple() -# build_path = PROJECT_PATH / '.build' / triple / 'release' - -# mmp.create_temp_sdk_des(p_path=PROJECT_PATH, build_path=build_path, p_type=p_type, p_name=p_name) - -# destination = mmp.create_destination(p_path=PROJECT_PATH, build_path=build_path, p_type=p_type, p_name=p_name) -# build_with_destination(build_path=build_path, p_type=p_type, p_name=p_name, destination=destination) - -# log.inf('Done!') - def build_project(args): mmp_manifest = Path(PROJECT_PATH / 'Package.mmp') - - if not mmp_manifest.is_file(): - log.die('Package.mmp is required to build the project') - - mmp_content = mmp_manifest.read_text() - mmp.initialize(mmp_content) + mmp.initialize(mmp_manifest) mmp.clean(p_path=PROJECT_PATH) spm.initialize() @@ -121,163 +88,107 @@ def build_project(args): log.inf('Done!') -def download_project_to_partition(partition): - mmp_manifest = Path(PROJECT_PATH / 'Package.mmp') - - if not mmp_manifest.is_file(): - log.die('Package.mmp is required to download the project') - - content = mmp_manifest.read_text() - mmp.initialize(content) - - board_name = mmp.get_board_name() - if board_name is None or board_name == '': - log.die('Board name is not specified') - - if board_name != 'SwiftIOMicro': - log.die('Download to partition is not supported on SwiftIOBoard') - - file_name = mmp.get_board_info('sd_image_name') - triple = mmp.get_triple() - image = PROJECT_PATH / '.build' / triple / 'release' / file_name - - if not image.is_file(): - log.die('Cannot find ' + file_name) - - serial_name = mmp.get_board_info('usb2serial_device') - - if board_name == 'SwiftIOMicro': - serial_download.load_to_partition(serial_name, image, partition) +def download_file_to_partition(args): + log.dbg('Partition: ' + str(args.partition)) + serial_download.load_to_partition(args.serial, args.file, args.partition) log.inf('Done!') -def download_project_to_sd(): +# Only for SwiftIOBoard +# Not recommended to use this function +# Please use 'mm copy' command instead +def download_file_to_sd(args): mmp_manifest = Path(PROJECT_PATH / 'Package.mmp') - - if not mmp_manifest.is_file(): - log.die('Package.mmp is required to download the project') - - content = mmp_manifest.read_text() - mmp.initialize(content) - + mmp.initialize(mmp_manifest) board_name = mmp.get_board_name() - if board_name is None: - log.die('Board name is not specified') - - system = platform.system() - if board_name == 'SwiftIOBoard' and system != 'Darwin': - log.die(system + ' is not supported currently, please copy the image file manually') - file_name = mmp.get_board_info('sd_image_name') - triple = mmp.get_triple() - image = PROJECT_PATH / '.build' / triple / 'release' / file_name + file_new_name = str(args.file.name) - if not image.is_file(): - log.die('Cannot find ' + file_name) - - serial_name = mmp.get_board_info('usb2serial_device') - - if board_name == 'SwiftIOMicro': - serial_download.load_to_sdcard(serial_name, image, file_name) - elif board_name == 'SwiftIOBoard': - download.darwin_download(source=image) + if board_name != 'SwiftIOBoard': + log.wrn('Deprecated command, please use "mm copy" instead') + serial_download.load_to_sdcard(args.serial, args.file, file_new_name) + else: + download.darwin_download(source=args.file) log.inf('Done!') -def download_to_sd_with_target_name(serial_name, image, file_name): - serial_download.load_to_sdcard(serial_name, image, file_name) - - -def download_to_sd(args): - if args.file is None: - log.die('Please specify the file path') - - f = args.file - if not f.is_file(): - log.die('open file ' + str(f) + ' failed!') +def download_file_to_ram(args): + if args.address is None: + log.die('Please specify the target RAM address') - path = Path(f) - if path.suffix == '': - file_name = path.stem - else: - file_name = path.name + address = int(args.address, 16) + log.inf('Download to RAM address: ' + str(address)) - download_to_sd_with_target_name('wch', f, file_name) + serial_download.load_to_ram(args.serial, args.file, address) -def download_to_partition(args): - if args.file is None or args.partition is None: - log.die('Please specify the file path and target partition name') - - f = args.file - if not f.is_file(): - log.die('open file ' + str(f) + ' failed!') - serial_download.load_to_partition('wch', f, args.partition) +def download_file(args): + if args.file is None or args.serial is None: + mmp_manifest = Path(PROJECT_PATH / 'Package.mmp') + mmp.initialize(mmp_manifest) + board_name = mmp.get_board_name() + if board_name == 'SwiftIOBoard' and args.type == 'partition': + log.die('Download to partition is not supported on SwiftIOBoard') -def download_to_ram(args): - if args.file is None or args.address is None: - log.die('Please specify the file path and target RAM address') + if args.file is None: + log.dbg('No file specified, using the default image file') + args.file = mmp.get_default_image_path(PROJECT_PATH) - address = int(args.address, 16) - log.inf(address) + args.file = Path(args.file) + if not args.file.is_file(): + log.die('cannot find ' + str(args.file)) - f = args.file - if not f.is_file(): - log.die('open file ' + str(f) + ' failed!') - - serial_download.load_to_ram('wch', f, address) + if args.serial is None: + log.dbg('No serial name specified, using the default serial name') + args.serial = mmp.get_board_info('usb2serial_device') + log.dbg('File: ' + str(args.file)) + log.dbg('Serial: ' + str(args.serial)) -def download_img(args): - if args.type == 'sd': - if args.file is None: - download_project_to_sd() - else: - download_to_sd(args) - elif args.type == 'partition': - if args.file is None: - download_project_to_partition(args.partition) - else: - download_to_partition(args) + if args.type == 'partition': + download_file_to_partition(args) elif args.type == 'ram': - download_to_ram(args) - + download_file_to_ram(args) + elif args.type == 'sd': + download_file_to_sd(args) def copy_resources(args): - source = Path(args.source) - destination = Path(args.destination) - delete_first = True - - if source.is_absolute(): - log.die('Absolute resource path is not supported at this time') - - if not destination.is_absolute(): - log.die('The destination must be an absolute path') - - if not source.is_dir(): - log.die(str(args.source) + ' directory not exist') + if args.serial is None: + mmp_manifest = Path(PROJECT_PATH / 'Package.mmp') + mmp.initialize(mmp_manifest) + board_name = mmp.get_board_name() + if board_name == 'SwiftIOBoard': + log.die('copy command is not supported on SwiftIOBoard') + args.serial = mmp.get_board_info('usb2serial_device') + log.dbg('No serial name specified, using the default serial info: ' + args.serial) - log.dbg('Destination: ' + str(args.destination)) + source = Path(args.source).resolve() + if not source.exists(): + log.die(str(args.source) + ' not exists') - files = [] - for item in list(source.glob('**/*')): - if item.is_file(): - files.append(item) + # Path works differently on Windows and Unix-like systems + # Use PurePosixPath here + if not str(args.destination).startswith('/'): + log.die('The destination is supposed be an absolute path, now it is: ' + str(args.destination)) - if len(files) == 0: - log.die(str(args.source) + ' is empty') + destination = PurePosixPath(args.destination) if args.mode == 'sync': delete_first = True else: delete_first = False - serial_download.copy_to_filesystem('wch', delete_first, source, destination, files) + # if source.is_dir() and source.is_absolute(): + # log.die('Absolute directory path is not supported') - for file in files: - log.dbg(str(file)) + log.dbg('Source: ' + str(args.source)) + log.dbg('Destination: ' + str(args.destination)) + log.dbg('Device: ' + str(args.serial)) + log.dbg('Mode: ' + str(args.mode)) + + serial_download.copy_to_filesystem(args.serial, delete_first, source, destination) def add_header(args): @@ -285,7 +196,7 @@ def add_header(args): log.die('Please specify the file path') if args.address is None: - log.wrn('Image default address 0x80000000') + log.wrn('Using default image address: 0x80000000') address = 0x80000000 else: address = int(args.address, 16) @@ -320,7 +231,7 @@ def ci_build(args): if (PROJECT_PATH / '.build').exists(): shutil.rmtree((PROJECT_PATH / '.build')) mmp_content = mmp.init_manifest(board=board, p_type=p_type, triple=triple, hard_float=hard_float, float_abi=float_abi) - mmp.initialize(mmp_content) + mmp.initialize_by_content(mmp_content) build_path = PROJECT_PATH / '.build' / triple / 'release' # destination = mmp.create_destination(p_path=PROJECT_PATH, build_path=build_path, p_type=p_type, p_name=p_name) @@ -338,7 +249,7 @@ def ci_build(args): float_type = 'nofp' log.inf('Building for ' + board) - source = build_path / mmp.get_board_info('sd_image_name') + source = build_path / mmp.get_board_info('image_name') target = PROJECT_PATH / triple / float_type / board / p_name target.mkdir(parents=True, exist_ok=True) shutil.copy(source, target) @@ -403,6 +314,7 @@ def clean_project(args): if args.deep: spm.clean() + def get_info(args): if args.info == 'usb': mmp_manifest = Path(PROJECT_PATH / 'Package.mmp') @@ -411,8 +323,7 @@ def get_info(args): system = platform.system() if system != 'Darwin': log.die(system + ' is not supported currently, please copy the bin file manually') - content = mmp_manifest.read_text() - mmp.initialize(content) + mmp.initialize(mmp_manifest) board_name = mmp.get_board_name() mount_path = download.darwin_get_mount_point() if mount_path is None: @@ -427,6 +338,7 @@ def get_info(args): p_name = spm.get_project_name() log.inf(p_name) + def main(): global PROJECT_PATH @@ -436,77 +348,90 @@ def main(): subparsers = parser.add_subparsers() init_parser = subparsers.add_parser('init', help = 'Initialize a new project') + init_parser.add_argument('--toolchain', type = Path, default = None, help = 'Set a specific Swift toolchain path, if not set, the default toolchain will be used') init_parser.add_argument('-t', '--type', type = str, choices = ['executable', 'library'], default = 'executable', help = 'Project type: The default type is executable') - init_parser.add_argument('--name', type = str, help = 'Initialize a new project with a specified name. If no name is provided, the project name will default to the name of the current directory') init_parser.add_argument('-b', '--board', type = str, choices =['SwiftIOBoard', 'SwiftIOMicro'], help = 'Pass this parameter to generate the MadMachine project file') + init_parser.add_argument('--name', type = str, help = 'Initialize a new project with a specified name. If no name is provided, the project name will default to the name of the current directory') init_parser.add_argument('-v', '--verbose', action = 'store_true', help = "Increase the verbosity of the output") init_parser.set_defaults(func = init_project) build_parser = subparsers.add_parser('build', help = 'Build a project') + build_parser.add_argument('--toolchain', type = Path, default = None, help = 'Set a specific Swift toolchain path, if not set, the default toolchain will be used') build_parser.add_argument('-v', '--verbose', action = 'store_true', help = "Increase the verbosity of the output") build_parser.set_defaults(func = build_project) - header_parser = subparsers.add_parser('add_header', help = 'Add a header to the binary file') - header_parser.add_argument('-v', '--verbose', action = 'store_true', help = "Increase the verbosity of the output") - header_parser.add_argument('-f', '--file', type = Path, default = None, help = "Path to the binary file") - header_parser.add_argument('-a', '--address', type = str, default = None, help = "Target load address") - header_parser.add_argument('--verify', type = str, choices =['crc32', 'sha256'], default = 'crc32', help = 'Type of verification') - header_parser.set_defaults(func = add_header) - download_parser = subparsers.add_parser('download', help = 'Download the target executable to the board\'s RAM, Flash, or SD card') - download_parser.add_argument('-t', '--type', type = str, choices = ['ram', 'partition', 'sd'], default = 'partition', help = "Download type: The default is Flash partition") + download_parser.add_argument('-f', '--file', type = Path, default = None, help = 'Path to the image file') + download_parser.add_argument('-t', '--type', type = str, choices = ['partition', 'ram', 'sd'], default = 'partition', help = "Download type: The default is Flash partition") download_parser.add_argument('-p', '--partition', type = str, default = 'user', help = "Target flash partition, the default is 'user'") download_parser.add_argument('-a', '--address', type = str, default = '0x80000000', help = "Target RAM address") - download_parser.add_argument('-f', '--file', type = Path, default = None, help = "Path to the image file") + download_parser.add_argument('--serial', type = str, default = None, help = "Name, description or hwid of the serial device") download_parser.add_argument('-v', '--verbose', action = 'store_true', help = "Increase the verbosity of the output") - download_parser.set_defaults(func = download_img) + download_parser.set_defaults(func = download_file) - sync_parser = subparsers.add_parser('copy', help = 'Copy the resources to the Flash or SD card filesystem') - sync_parser.add_argument('-m', '--mode', type = str, choices = ['sync', 'merge'], default = 'merge', help = "Copy the resources to the destination, the default mode is merge") - sync_parser.add_argument('-s', '--source', type = Path, default = 'Resources', help = "Source path: The default path is 'Resources' within the project") - sync_parser.add_argument('-d', '--destination', type = Path, default = '/SD:', help = "Destination path: The default path is '/SD:'") - sync_parser.add_argument('-v', '--verbose', action = 'store_true', help = "Increase the verbosity of the output") - sync_parser.set_defaults(func = copy_resources) + copy_parser = subparsers.add_parser('copy', help = 'Copy the resources to the Flash or SD card filesystem') + copy_parser.add_argument('-m', '--mode', type = str, choices = ['sync', 'merge'], default = 'merge', help = "Copy the resources to the destination, the default mode is merge") + copy_parser.add_argument('-s', '--source', type = Path, default = 'Resources', help = "Source path: The default path is 'Resources' within the project") + copy_parser.add_argument('-d', '--destination', type = PurePosixPath, default = '/SD:', help = "Destination path: The default path is '/SD:'") + copy_parser.add_argument('--serial', type = str, default = None, help = "Name, description or hwid of the serial device") + copy_parser.add_argument('-v', '--verbose', action = 'store_true', help = "Increase the verbosity of the output") + copy_parser.set_defaults(func = copy_resources) clean_parser = subparsers.add_parser('clean', help = 'Clean project') clean_parser.add_argument('--deep', action = 'store_true', help = "Clean all compilation outputs") + clean_parser.add_argument('--toolchain', type = Path, default = None, help = 'Set a specific Swift toolchain path, if not set, the default toolchain will be used') clean_parser.add_argument('-v', '--verbose', action = 'store_true', help = "Increase the verbosity of the output") clean_parser.set_defaults(func = clean_project) get_parser = subparsers.add_parser('get', help = 'Retrieve specified information for use by the IDE') get_parser.add_argument('--info', type = str, choices =['name', 'usb'], help = 'Type of information') + get_parser.add_argument('--toolchain', type = Path, default = None, help = 'Set a specific Swift toolchain path, if not set, the default toolchain will be used') get_parser.add_argument('-v', '--verbose', action = 'store_true', help = "Increase the verbosity of the output") get_parser.set_defaults(func = get_info) ci_build_parser = subparsers.add_parser('ci-build', help = 'CI Build') + ci_build_parser.add_argument('--toolchain', type = Path, default = None, help = 'Set a specific Swift toolchain path, if not set, the default toolchain will be used') ci_build_parser.add_argument('-v', '--verbose', action = 'store_true', help = "Increase the verbosity of the output") ci_build_parser.set_defaults(func = ci_build) + header_parser = subparsers.add_parser('add_header', help = 'Add a header to the binary file') + header_parser.add_argument('-f', '--file', type = Path, default = None, help = "Path to the binary file") + header_parser.add_argument('-a', '--address', type = str, default = None, help = "Target load address") + header_parser.add_argument('--verify', type = str, choices =['crc32', 'sha256'], default = 'sha256', help = 'Type of verification') + header_parser.add_argument('-v', '--verbose', action = 'store_true', help = "Increase the verbosity of the output") + header_parser.set_defaults(func = add_header) + host_test_parser = subparsers.add_parser('host-test', help = 'Test a project on the host using the SwiftIO mock') + host_test_parser.add_argument('--toolchain', type = Path, default = None, help = 'Set a specific Swift toolchain path, if not set, the default toolchain will be used') host_test_parser.add_argument('-v', '--verbose', action = 'store_true', help = "Increase the verbosity of the output") host_test_parser.set_defaults(func = host_test) + PROJECT_PATH = Path('.').resolve() + sdk_path = Path(os.path.realpath(sys.argv[0])) / '../../..' + sdk_path = sdk_path.resolve() + args = parser.parse_args() if vars(args).get('version'): print(version.__VERSION__) sys.exit(0) - if vars(args).get('func') is None: - log.die('subcommand is required, use \'mm --help\' to get more information') - if args.verbose: log.set_verbosity(log.VERBOSE_DBG) - sdk_path = Path(os.path.realpath(sys.argv[0])).parent.parent.parent - system = platform.system() - if system == 'Darwin': - swift_path = Path('/Library/Developer/Toolchains/swift-latest.xctoolchain') - elif system == 'Linux': - swift_path = sdk_path + function = vars(args).get('func') + if function is None: + log.die('subcommand is required, use \'mm --help\' to get more information') + + toolchain_path = None + if vars(args).get('toolchain') is not None: + toolchain_path = vars(args).get('toolchain') + + if function == download_file or function == copy_resources or function == add_header: + util.init_sdk_and_swift_path(sdk_path, toolchain_path, needSwiftToolchain=False) + else: + util.init_sdk_and_swift_path(sdk_path, toolchain_path, needSwiftToolchain=True) - util.set_sdk_path(swift_path, sdk_path) - PROJECT_PATH = Path('.').resolve() args.func(args) if __name__ == "__main__": diff --git a/mm/src/mmp.py b/mm/src/mmp.py index b32cb3a..6016728 100644 --- a/mm/src/mmp.py +++ b/mm/src/mmp.py @@ -1,6 +1,4 @@ -import platform import toml, json -from zlib import crc32 from pathlib import Path import util, log, version @@ -18,14 +16,15 @@ SWIFTIO_BOARD = {'vid': '0x1fc9', 'pid': '0x0093', 'serial_number': '012345671FC90093', - 'sd_image_name': 'swiftio.bin', + 'image_name': 'swiftio.bin', 'usb2serial_device': 'DAPLink CMSIS-DAP'} SWIFTIO_MICRO = {'vid': '0x1fc9', 'pid': '0x0095', 'serial_number': '012345671FC90095', - 'sd_image_name': 'micro.img', - 'usb2serial_device': '/dev/ttyACM0' if platform.system() == 'Linux' else 'wch'} + 'image_name': 'micro.img', + #'usb2serial_device': '/dev/ttyACM0' if platform.system() == 'Linux' else 'wch'} + 'usb2serial_device': '1A86:55D3'} DEFAULT_MMP_MANIFEST = """# This is a MadMachine project file in TOML format @@ -60,7 +59,7 @@ TOML_CONTENT = None -def initialize(content): +def initialize_by_content(content: str): global TOML_CONTENT try: @@ -68,6 +67,18 @@ def initialize(content): except: log.die('decoding Package.mmp failed!') +def initialize(manifest: Path): + global TOML_CONTENT + + if not manifest.is_file(): + log.die('Package.mmp not found!') + + content = manifest.read_text() + + try: + TOML_CONTENT = toml.loads(content) + except: + log.die('decoding Package.mmp failed!') def init_manifest(board, p_type, triple='armv7em-none-none-eabi', hard_float='true', float_abi='false'): if p_type == 'library': @@ -89,20 +100,21 @@ def init_manifest(board, p_type, triple='armv7em-none-none-eabi', hard_float='tr def get_board_name(): - board = TOML_CONTENT.get('board').strip() + board = TOML_CONTENT.get('board') if board is None: log.die('Unable to recognize the board type in Package.mmp!') - return board + return board.strip() def get_triple(): - triple = TOML_CONTENT.get('triple').strip() + triple = TOML_CONTENT.get('triple') hard_float = TOML_CONTENT.get('hard-float') - if len(triple) == 0: + if triple is None: log.die('The triple configuration is missing in Package.mmp!') + triple = triple.strip() if SUPPORTED_ARCHS.count(triple) == 0: log.die('Unknown triple: ' + triple) @@ -115,6 +127,13 @@ def get_triple(): return triple +def get_default_image_path(project_path): + triple = get_triple() + file_name = get_board_info('image_name') + + file_path = project_path / '.build' / triple / 'release' / file_name + + return file_path def get_float_type(wrn=False): hard_float = TOML_CONTENT.get('hard-float') @@ -125,12 +144,12 @@ def get_float_type(wrn=False): if hard_float is None: if wrn: - log.wrn('The hard-float setting is missing in Package.mmp, defualting to true!') + log.wrn('hard-float setting not found in Package.mmp, using default: true') hard_float = True if float_abi is None: if wrn: - log.wrn('The float-abi setting is missing in Package.mmp, defualting to false!') + log.wrn('float-abi setting not found in Package.mmp, using default: false') float_abi = False return hard_float, float_abi @@ -339,9 +358,10 @@ def get_swift_predefined(p_type): def get_swift_library(): sdk_path = util.get_sdk_path() + module_version = util.get_swift_module_version() flags = [ - 'lib/swift' + 'lib/swift/' + module_version ] flags = ['-I ' + str(sdk_path / item) for item in flags] @@ -623,7 +643,7 @@ def get_sdk_info(name): } js_text = json.dumps(info_dic, indent=2) - log.dbg(js_text) + # log.dbg(js_text) return js_text @@ -648,7 +668,7 @@ def get_swift_sdk(): } js_text = json.dumps(swift_sdk_dic, indent=2) - log.dbg(js_text) + # log.dbg(js_text) return js_text @@ -687,7 +707,7 @@ def get_toolset(build_path, p_type, p_name): } js_text = json.dumps(toolset_dic, indent = 2) - log.dbg(js_text) + # log.dbg(js_text) return js_text @@ -722,7 +742,7 @@ def create_binary(build_path, name): bin_path = build_path / (name + '.bin') flags = [ - util.get_tool('objcopy'), + util.get_tool_string('objcopy'), '-S', '-Obinary', '--gap-fill', @@ -740,4 +760,4 @@ def create_binary(build_path, name): if util.command(flags): log.die('creating binary failed!') else: - return bin_path \ No newline at end of file + return bin_path diff --git a/mm/src/requirements.txt b/mm/src/requirements.txt index e545d99..1ff8169 100644 --- a/mm/src/requirements.txt +++ b/mm/src/requirements.txt @@ -1,6 +1,6 @@ toml==0.10.2 -tqdm==4.66.6 +tqdm==4.67.1 pyserial==3.5 colorama==0.4.6 -pyinstaller==6.11.0 -pyinstaller-hooks-contrib==2024.9 +pyinstaller==6.12.0 +pyinstaller-hooks-contrib==2025.2 diff --git a/mm/src/serial_download.py b/mm/src/serial_download.py index 365dff0..cd1be0f 100644 --- a/mm/src/serial_download.py +++ b/mm/src/serial_download.py @@ -1,7 +1,7 @@ from pickletools import read_stringnl_noescape import serial, serial.tools.list_ports from time import sleep -from pathlib import Path +from pathlib import Path, PosixPath from tqdm import tqdm from zlib import crc32 import log, util @@ -63,9 +63,25 @@ def get_uint64_big_bytes(number): return number.to_bytes(8, byteorder='big') - -def find_serial_device(device_name): - port_list = list(serial.tools.list_ports.grep(device_name)) +def list_all_the_serial_ports(): + port_list = serial.tools.list_ports.comports() + for port in port_list: + log.inf(' ') + log.inf('device: ' + port.device) + log.inf('name: ' + port.name) + log.inf('description: ' + port.description) + log.inf('hwid: ' + port.hwid) + log.inf('vid: ' + str(port.vid)) + log.inf('pid: ' + str(port.pid)) + log.inf('serial_number: ' + str(port.serial_number)) + log.inf('location: ' + str(port.location)) + log.inf('manufacturer: ' + str(port.manufacturer)) + log.inf('product: ' + str(port.product)) + log.inf('interface: ' + str(port.interface)) + +# only support name, description, hwid +def find_serial_device(device: str): + port_list = list(serial.tools.list_ports.grep(device)) port_path_list = list() for port in port_list: port_path_list.append(port.device) @@ -76,21 +92,20 @@ def find_serial_device(device_name): return port_path_list -def init_serial_device(device_name): +def init_serial_device(device): global SERIAL_PORT - port_path_list = find_serial_device(device_name) - + port_path_list = find_serial_device(device) if port_path_list is None: - log.die('Please confirm ' + device_name + ' is correctly connected to your computer!') + log.die('Please confirm ' + str(device) + ' is correctly connected to your computer!') elif len(port_path_list) > 1: - log.wrn('Found more than one ' + device_name) + log.wrn('Multiple ' + str(device) + ' devices found') for port_path in port_path_list: try: SERIAL_PORT = serial.Serial(port_path, SERIAL_INIT_BAUDRATE, 8, 'N', 1) except IOError: - log.wrn('Device or resource busy! Please make sure it is not in use!') + log.wrn('Cannot connect to serial port, please ensure the device is not in use and you have the right permission') if SERIAL_PORT is not None and SERIAL_PORT.is_open: SERIAL_PORT.timeout = SERIAL_PORT_READ_TIMEOUT @@ -102,18 +117,17 @@ def init_serial_device(device_name): if ret: SERIAL_PORT.reset_output_buffer() SERIAL_PORT.reset_input_buffer() - log.inf('Open ' + port_path + ' success') + log.inf('Successfully opened ' + port_path) break else: deinit_serial_device() else: - log.inf('Open ' + port_path + ' success') + log.inf('Successfully opened ' + port_path) else: - log.wrn('Open ' + port_path + ' failed!') + log.wrn('Failed to open ' + port_path) if SERIAL_PORT is None or not SERIAL_PORT.is_open: - log.die('Open ' + device_name + ' failed!') - + log.die('Open ' + str(device) + ' failed!') def deinit_serial_device(): @@ -152,6 +166,7 @@ def send_request(tag, payload = None): log.dbg(' payload: None') log.dbg(' crc: 0x' + crc.hex()) SERIAL_PORT.write(FRAME_PREAMBLE + tag + length + crc) + SERIAL_PORT.flush() else: if not isinstance(payload, bytes) and not isinstance(payload, bytearray): log.dbg('payload must be bytes or bytearray') @@ -162,9 +177,10 @@ def send_request(tag, payload = None): log.dbg('request:') log.dbg(' tag: 0x' + tag.hex()) log.dbg(' length: 0x' + length.hex()) - log.dbg(' payload: ' + str(int.from_bytes(length, 'big', signed='False')) + 'bytes') + log.dbg(' payload: ' + str(int.from_bytes(length, 'big', signed=False)) + 'bytes') log.dbg(' crc: 0x' + crc.hex()) SERIAL_PORT.write(FRAME_PREAMBLE + tag + length + payload + crc) + SERIAL_PORT.flush() def wait_response(): header = SERIAL_PORT.read(16) @@ -271,7 +287,7 @@ def sync(try_count = 6): print('', flush=True) SERIAL_PORT.timeout = previous_timeout if not result: - log.wrn('serial port synchronization failed!') + log.wrn('Failed to synchronize with serial port') return result @@ -375,11 +391,11 @@ def partion_set_boot(name): -def sdcard_begin(image_length, image_path): +def sdcard_begin(image_length, image_name): image_length = get_uint32_big_bytes(image_length) - image_path = bytes(image_path, 'utf-8') + b'\x00' + image_name = bytes(image_name, 'utf-8') + b'\x00' - payload = image_length + image_path + payload = image_length + image_name send_request(FS_BEGIN_TAG, payload) response = wait_response() @@ -453,16 +469,16 @@ def rm(path): previous_timeout = SERIAL_PORT.timeout SERIAL_PORT.timeout = SERIAL_PORT_FS_TIMEOUT - log.inf('Deleteing ' + str(path)) + log.inf('Deleting ' + str(path)) payload = bytes(path, 'utf-8') + b'\x00' log.dbg(list(payload)) send_request(FS_RM_TAG, payload) response = wait_response() if not response_verify(response, FS_RM_TAG): - log.wrn('Deletion of the ' + path + ' failed') + log.wrn('Failed to delete ' + path) else: - log.inf('Deletion of the ' + path + ' was successful') + log.inf('Successfully deleted ' + path) SERIAL_PORT.timeout = previous_timeout @@ -498,8 +514,8 @@ def cp(src, dst): -def send_file2mem(file_name, addr, bar=False): - f = Path(file_name) +def send_file2mem(file_path, addr, bar=False): + f = Path(file_path) if not f.is_file(): log.die('open file ' + str(f) + ' failed!') @@ -525,7 +541,7 @@ def send_file2mem(file_name, addr, bar=False): mem_end(file_crc) -def send_file2flash(file_name, addr, run_addr): +def send_file2flash(file_name, addr): f = Path(file_name) if not f.is_file(): @@ -546,12 +562,11 @@ def send_file2flash(file_name, addr, run_addr): process_bar.update(len(payload)) process_bar.close() - flash_end(file_crc, run_addr) + flash_end(file_crc) -def send_file2sdcard(file_name, target_name): - f = Path(file_name) - +def send_file2sdcard(file_path, file_rename): + f = Path(file_path) if not f.is_file(): log.die('open file ' + str(f) + ' failed!') @@ -560,7 +575,7 @@ def send_file2sdcard(file_name, target_name): file_crc = crc32(file_bytes) process_bar = tqdm(total=file_length, unit='B', unit_scale=True) - sdcard_begin(file_length, target_name) + sdcard_begin(file_length, file_rename) offset = 0 while offset < file_length: @@ -573,9 +588,8 @@ def send_file2sdcard(file_name, target_name): sdcard_end(file_crc) -def send_file2partion(file_name, partition_name): - f = Path(file_name) - +def send_file2partion(file_path, partition_name): + f = Path(file_path) if not f.is_file(): log.die('open file ' + str(f) + ' failed!') @@ -691,7 +705,7 @@ def test_list_serial_port(): log.inf(port.description) -def load_to_ram(serial_name, image, address): +def load_to_ram(serial_name, file_path, address): init_serial_device(serial_name) reset_to_download() @@ -702,13 +716,13 @@ def load_to_ram(serial_name, image, address): if sync() == False: log.die("Sync failed!") - send_file2mem(image, address) + send_file2mem(file_path, address) execute(address) deinit_serial_device() -def load_to_partition(serial_name, image, partition): +def load_to_partition(serial_name, file_path, partition): init_serial_device(serial_name) reset_to_download() @@ -730,7 +744,7 @@ def load_to_partition(serial_name, image, partition): if sync() == False: log.die("Sync failed!") - send_file2partion(image, partition) + send_file2partion(file_path, partition) partion_set_boot(partition) @@ -739,7 +753,7 @@ def load_to_partition(serial_name, image, partition): deinit_serial_device() -def load_to_sdcard(serial_name, image, target_name): +def load_to_sdcard(serial_name, file_path, file_rename): init_serial_device(serial_name) reset_to_download() @@ -761,7 +775,7 @@ def load_to_sdcard(serial_name, image, target_name): if sync() == False: log.die("Sync failed!") - send_file2sdcard(image, target_name) + send_file2sdcard(file_path, file_rename) reboot() deinit_serial_device() @@ -769,7 +783,24 @@ def load_to_sdcard(serial_name, image, target_name): -def copy_to_filesystem(serial_name, delete, source, destination, files): +def copy_to_filesystem(serial_name, delete, source, destination): + source = source.resolve() + + files = [] + if source.is_dir(): + file_paths = sorted(source.glob('**/*')) + for item in file_paths: + if item.is_file(): + files.append(item.relative_to(source)) + elif source.is_file(): + files.append(source) + + if len(files) == 0: + log.die(str(source) + ' is empty') + + for file in files: + log.dbg(str(file)) + init_serial_device(serial_name) reset_to_download() @@ -788,12 +819,20 @@ def copy_to_filesystem(serial_name, delete, source, destination, files): if sync() == False: log.die("Sync failed!") - if delete: - rm(str(destination / source)) + if source.is_dir() and delete: + des = destination / source.name + log.dbg('Deleting ' + str(des)) + rm(str(des)) + + if source.is_dir(): + for file in files: + file_path = (source / file).resolve() + des = destination / source.name / file + cp(str(file_path), str(des)) + else: # source is file + des = destination / source.name + cp(str(source), str(des)) - for file in files: - des = destination / file - cp(str(file), str(des)) reboot() deinit_serial_device() @@ -831,7 +870,7 @@ def test_load_to_ram(serial_name, address): #deinit_serial_device() count += 1 - log.inf('------ count = ' + str(count) + ' transfer ' + str(mbytes) + 'mb' + ' ------') + log.inf('------ Transferring ' + str(mbytes) + 'MB (count: ' + str(count) + ') ------') #log.set_verbosity(log.VERBOSE_DBG) diff --git a/mm/src/spm.py b/mm/src/spm.py index bdc424b..619fa29 100644 --- a/mm/src/spm.py +++ b/mm/src/spm.py @@ -63,7 +63,7 @@ def initialize(): global PKG_DESCRIBE_JSON flags = [ - util.get_tool('swift-package'), + util.get_tool_string('swift-package'), 'describe', '--type json' ] @@ -72,14 +72,13 @@ def initialize(): def init_manifest(p_name, p_type): flags = [ - util.get_tool('swift-package'), + util.get_tool_string('swift-package'), 'init', '--type ' + p_type, '--name ' + p_name ] - ret = util.run_command(flags) - log.inf(ret, level=log.VERBOSE_DBG) + _ = util.run_command(flags) if p_type == 'library': content = DEFAULT_LIB_MANIFEST @@ -126,7 +125,7 @@ def get_project_type(): # def destination_build(p_type, destination): # flags = [ -# util.get_tool('swift-build'), +# util.get_tool_string('swift-build'), # '-c release', # '--destination', # util.quote_string(destination), @@ -145,7 +144,7 @@ def get_project_type(): def build(p_path, p_type): flags = [ - util.get_tool('swift-build'), + util.get_tool_string('swift-build'), '-c release', '--swift-sdks-path', util.quote_string(p_path / '.build'), @@ -164,7 +163,7 @@ def build(p_path, p_type): def clean(): flags = [ - util.get_tool('swift-package'), + util.get_tool_string('swift-package'), 'clean' ] ret = util.command(flags) @@ -199,7 +198,7 @@ def get_mock_revision(): def edit_package(package, revision): flags = [ - util.get_tool('swift-package'), + util.get_tool_string('swift-package'), 'edit', package, '--revision', @@ -212,7 +211,7 @@ def edit_package(package, revision): def host_test(): flags = [ - util.get_tool('swift-test'), + util.get_tool_string('swift-test'), '-c', 'release', '--enable-code-coverage', @@ -228,7 +227,7 @@ def host_test(): def get_codecov_path(): flags = [ - util.get_tool('swift-test'), + util.get_tool_string('swift-test'), '-c', 'release', '--show-codecov-path' @@ -240,7 +239,7 @@ def get_codecov_path(): def generate_code_report(test_path, prof_path): flags = [ - util.get_tool('llvm-cov'), + util.get_tool_string('llvm-cov'), 'export -format=lcov', test_path, '-instr-profile', diff --git a/mm/src/util.py b/mm/src/util.py index 190c779..886430d 100644 --- a/mm/src/util.py +++ b/mm/src/util.py @@ -1,87 +1,211 @@ -import os, subprocess +import os, platform, subprocess from pathlib import Path import log, version +import re +SDK_ENV = None +SDK_PATH = None -SDK_ENV = '' -SDK_PATH = '' - -SWIFT_PATH = '' +SWIFT_PATH = None +SWIFT_VERSION_MAJOR_MINOR = None +SWIFT_VERSION_FULL = None +#MACOS_SWIFT_PATH = Path('/Library/Developer/Toolchains/swift-latest.xctoolchain') SDK_ID = 'madmachine-sdk' +MINIMUM_SWIFT_VERSION = '6.1.0' ARTIFACT_PATH = SDK_ID + '-' + str(version.__VERSION__) + '.artifactbundle' - -sdk_tool_set = { - 'ld': 'usr/bin/arm-none-eabi-ld', - 'objcopy': 'usr/bin/arm-none-eabi-objcopy', - 'serial-loader': 'Boards/SerialLoader.bin' -} - swift_tool_set = { + 'swiftc': 'usr/bin/swiftc', 'swift-build': 'usr/bin/swift-build', 'swift-package': 'usr/bin/swift-package', 'swift-test': 'usr/bin/swift-test', 'llvm-cov': 'usr/bin/llvm-cov' } -def quote_string(path): - return '"%s"' % str(path) +gcc_tool_set = { + 'ld': 'usr/bin/arm-none-eabi-ld', + 'objcopy': 'usr/bin/arm-none-eabi-objcopy' +} + +sdk_tool_set = { + 'serial-loader': 'boards/SerialLoader.bin' +} -def set_sdk_path(swift_path, tool_path, save=False, env_name=None): - global SDK_ENV - global SDK_PATH - global SWIFT_PATH - if not swift_path.is_dir(): - log.die(str(swift_path) + " doesn't exists") - if not tool_path.is_dir(): - log.die(str(tool_path) + " doesn't exists") +def quote_string(path): + return '"%s"' % str(path) - SWIFT_PATH = swift_path +def init_sdk_and_swift_path(sdk_path, swift_path=None, needSwiftToolchain=True): + global SDK_PATH + global SWIFT_PATH + global SDK_ENV - SDK_PATH = tool_path + if not sdk_path.is_dir(): + log.die(str(sdk_path) + " doesn't exist") + SDK_PATH = sdk_path SDK_ENV = os.environ.copy() - if save and env_name is not None: - SDK_ENV[env_name] = str(tool_path) - + log.dbg('Set mm-sdk path to: ' + str(SDK_PATH)) + + # Try to find Swift toolchain: + # 1. from main command parameter '--toolchain' + # 2. from environment variable 'TOOLCHAIN' + if needSwiftToolchain and swift_path is None: + try: + swift_path = Path(SDK_ENV['TOOLCHAIN']).resolve() + if swift_path.is_dir(): + log.dbg('Using Swift toolchain from environment variable TOOLCHAIN: ' + str(swift_path)) + else: + swift_path = None + except KeyError: + swift_path = None + + # 3. from command 'swiftly use --print-location' + if swift_path is None: + swift_path = find_default_swift_path() + if swift_path is not None: + log.dbg('Using default Swift toolchain at: ' + str(swift_path)) + SWIFT_PATH = swift_path + if needSwiftToolchain: + if SWIFT_PATH is None: + log.die('Cannot find a Swift toolchain, please install Swift toolchain for your platform') + elif not check_swift_version(MINIMUM_SWIFT_VERSION): + log.die('The current Swift toolchain version is too old, please update to at least ' + MINIMUM_SWIFT_VERSION) + def get_sdk_path(): return SDK_PATH def get_swift_path(): return SWIFT_PATH +def get_swift_version_major_minor(): + return SWIFT_VERSION_MAJOR_MINOR + +def get_swift_version_full(): + return SWIFT_VERSION_FULL + +def get_swift_module_version(): + # Some patch releases can break binary module compatibility. + overrides = { + '6.2.3': '6.2.3' + } + if SWIFT_VERSION_FULL in overrides: + return overrides[SWIFT_VERSION_FULL] + return SWIFT_VERSION_MAJOR_MINOR + def get_tool_path(tool): - pos = swift_tool_set.get(tool) - if pos is not None: - path = Path(SWIFT_PATH / pos) + subpath = swift_tool_set.get(tool) + if subpath is not None: + if platform.system() == 'Windows': + subpath += '.exe' + tool_path = Path(SWIFT_PATH / subpath) + if not tool_path.is_file(): + log.die('cannot find ' + str(tool_path)) + return tool_path + + subpath = gcc_tool_set.get(tool) + if subpath is not None: + if platform.system() == 'Windows': + subpath += '.exe' + tool_path = Path(SDK_PATH / subpath) + if not tool_path.is_file(): + log.die('cannot find ' + str(tool_path)) + return tool_path + + subpath = sdk_tool_set.get(tool) + if subpath is not None: + tool_path = Path(SDK_PATH / subpath) + if not tool_path.is_file(): + log.die('cannot find ' + str(tool_path)) + return tool_path + + log.die('unknown tool: ' + tool) + +def get_tool_string(tool): + return quote_string(get_tool_path(tool)) + +def find_default_swift_path(): + system = platform.system() + + if system == 'Windows': + flags = ['(Get-Command swiftc).Source'] else: - pos = sdk_tool_set.get(tool) - path = Path(SDK_PATH / pos) + flags = ['swiftly', 'use', '--print-location'] - if not path.is_file(): - log.die('cannot find ' + str(path)) + log.dbg('Trying to find Swift toolchain with command: ' + ' '.join(flags)) + ret = run_command(flags).strip() + if ret: + # Keep only the first line in case the tool prints update notices. + ret = ret.splitlines()[0].strip() - return path + if ret is None or ret == '': + ret = None + else: + ret = Path(ret) + ret = ret.resolve() -def get_tool(tool): - return quote_string(get_tool_path(tool)) + log.dbg('Found Swift toolchain at: ' + str(ret)) + return ret + +def check_swift_version(minimum): + global SWIFT_VERSION_MAJOR_MINOR + global SWIFT_VERSION_FULL + + swiftc = get_tool_string('swiftc') + cmd = swiftc + ' -v' + p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + ret = p.wait() + cmd_out, cmd_err = p.communicate() + + if ret: + return False + + if cmd_err: + ret = cmd_err.decode('utf-8').rstrip() + + #match = re.search(r'\b\d+(\.\d+)+\b', ret) + match = re.search(r'\b(?P\d+)\.(?P\d+)(?:\.(?P\d+))?\b', ret) + if match: + full_version = match.group(0) + check_ret = is_newer(full_version, minimum) + if check_ret: + SWIFT_VERSION_FULL = full_version + SWIFT_VERSION_MAJOR_MINOR = f"{match.group('major')}.{match.group('minor')}" + log.dbg('Swift toolchain version ' + full_version) + log.dbg('Swift toolchain version without patch ' + SWIFT_VERSION_MAJOR_MINOR) + return True + else: + log.wrn('Swift toolchain version ' + full_version + ' is older than required minimum version ' + minimum) + + return False + +def is_newer(version, target): + def normalize_version(v, length=3): + parts = list(map(int, v.split("."))) + while len(parts) < length: + parts.append(0) + return tuple(parts) + return normalize_version(version) >= normalize_version(target) def command(flags): - cmd = '' + if platform.system() == 'Windows': + cmd = 'powershell.exe -Command ' + else: + cmd = '' + for item in flags: cmd += item + ' ' if log.VERBOSE > log.VERBOSE_INF: cmd += '-v' - log.inf(cmd, prefix=False, level=log.VERBOSE_DBG) + log.inf(cmd, level=log.VERBOSE_DBG) p = subprocess.Popen(cmd, shell=True, env=SDK_ENV) ret = p.wait() @@ -90,20 +214,30 @@ def command(flags): def run_command(flags): - cmd = '' + if platform.system() == 'Windows': + cmd = 'powershell.exe -Command ' + else: + cmd = '' + for item in flags: cmd += item + ' ' - #if log.VERBOSE > log.VERBOSE_INF: - # cmd += '-v' - - log.inf(cmd, prefix=False, level=log.VERBOSE_DBG) + log.inf(cmd, level=log.VERBOSE_DBG) p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=SDK_ENV) ret = p.wait() cmd_out, cmd_err = p.communicate() + if ret: - log.die(cmd_err.decode('utf-8'), prefix=False) + log.dbg('Command failed: ' + cmd) + + cmd_out_text = cmd_out.decode('utf-8') if cmd_out else '' + cmd_err_text = cmd_err.decode('utf-8') if cmd_err else '' + + if cmd_err_text: + log.wrn(cmd_err_text) + if cmd_out_text: + log.inf(cmd_out_text, level=log.VERBOSE_DBG) + return cmd_out_text - log.inf(cmd_out.decode('utf-8'), prefix=False, level=log.VERBOSE_DBG) - return cmd_out.decode('utf-8') \ No newline at end of file + return cmd_err_text diff --git a/mm/src/version.py b/mm/src/version.py index a89421b..ccea1e4 100644 --- a/mm/src/version.py +++ b/mm/src/version.py @@ -5,7 +5,7 @@ # This is the Python 3 version of option 3 in: # https://packaging.python.org/guides/single-sourcing-package-version/#single-sourcing-the-version -__VERSION__ = '1.1.0-alpha.9' +__VERSION__ = '2.2.0-alpha.0' # # MAINTAINERS: #