diff --git a/Source/v2/Meadow.CLI/Commands/Current/App/AppTools.cs b/Source/v2/Meadow.CLI/Commands/Current/App/AppTools.cs index 2b3cd793..1372d6d4 100644 --- a/Source/v2/Meadow.CLI/Commands/Current/App/AppTools.cs +++ b/Source/v2/Meadow.CLI/Commands/Current/App/AppTools.cs @@ -7,6 +7,8 @@ namespace Meadow.CLI.Commands.DeviceManagement; internal static class AppTools { + internal const string MeadowRootFolder = "meadow0"; + internal static string ValidateAndSanitizeAppPath(string? path) { path ??= Directory.GetCurrentDirectory(); @@ -94,4 +96,32 @@ internal static async Task TrimApplication(string path, return true; } + + internal static string SanitiseMeadowFilename(string fileName) + { + var folder = Path.GetDirectoryName(fileName); + if (string.IsNullOrWhiteSpace(folder)) + { + folder = MeadowRootFolder; + } + else + { + if (folder.EndsWith('/') == false) + { + folder += "/"; + } + if (folder.StartsWith('/') == false) + { + folder = $"/{folder}"; + } + if (folder.Contains(MeadowRootFolder) == false) + { + folder = $"/{MeadowRootFolder}{folder}"; + } + } + + var meadowFileName = Path.Combine(folder, Path.GetFileName(fileName)); + + return meadowFileName!.Replace(Path.DirectorySeparatorChar, '/'); + } } \ No newline at end of file diff --git a/Source/v2/Meadow.Cli/Commands/Current/File/FileDeleteCommand.cs b/Source/v2/Meadow.Cli/Commands/Current/File/FileDeleteCommand.cs index d6aa6a9c..18b79987 100644 --- a/Source/v2/Meadow.Cli/Commands/Current/File/FileDeleteCommand.cs +++ b/Source/v2/Meadow.Cli/Commands/Current/File/FileDeleteCommand.cs @@ -10,8 +10,6 @@ public class FileDeleteCommand : BaseDeviceCommand [CommandParameter(0, Name = "MeadowFile", IsRequired = true)] public string MeadowFile { get; init; } = default!; - private const string MeadowRootFolder = "meadow0"; - public FileDeleteCommand(MeadowConnectionManager connectionManager, ILoggerFactory loggerFactory) : base(connectionManager, loggerFactory) { } @@ -33,10 +31,10 @@ protected override async ValueTask ExecuteCommand() var folder = Path.GetDirectoryName(MeadowFile)!.Replace(Path.DirectorySeparatorChar, '/'); if (string.IsNullOrWhiteSpace(folder)) { - folder = MeadowRootFolder; + folder = $"/{AppTools.MeadowRootFolder}/"; } - var fileList = await connection.GetFileList($"/{folder}/", false); + var fileList = await connection.GetFileList($"{folder}", false, CancellationToken); if (fileList == null || fileList.Length == 0) { @@ -48,7 +46,7 @@ protected override async ValueTask ExecuteCommand() { foreach (var file in fileList) { - await DeleteFileRecursive(device, file, CancellationToken); + await DeleteFileRecursive(device, folder, file, CancellationToken); } } else @@ -72,26 +70,29 @@ protected override async ValueTask ExecuteCommand() } Logger?.LogInformation($"Deleting file '{MeadowFile}' from device..."); - await device.DeleteFile(MeadowFile, CancellationToken); + await device.DeleteFile(AppTools.SanitiseMeadowFilename(MeadowFile), CancellationToken); } } } - private async Task DeleteFileRecursive(IMeadowDevice device, MeadowFileInfo fileInfo, CancellationToken cancellationToken) + private async Task DeleteFileRecursive(IMeadowDevice device, string directoryname, MeadowFileInfo fileInfo, CancellationToken cancellationToken) { + var meadowFile = AppTools.SanitiseMeadowFilename(Path.Combine(directoryname, fileInfo.Name)); if (fileInfo.IsDirectory) { - var subfolderFiles = await device.GetFileList(fileInfo.Name, false, cancellationToken); + // Add a backslash as we're a directory and not a file + meadowFile += "/"; + var subfolderFiles = await device.GetFileList(meadowFile, false, cancellationToken); foreach (var subfolderFile in subfolderFiles) { - await DeleteFileRecursive(device, subfolderFile, cancellationToken); + await DeleteFileRecursive(device, meadowFile, subfolderFile, cancellationToken); } return; } - var fileName = Path.GetFileName(fileInfo.Name); - Logger?.LogInformation($"Deleting file '{fileName}' from device..."); - await device.DeleteFile(fileName, cancellationToken); + Logger?.LogInformation($"Deleting file '{meadowFile}' from device..."); + + await device.DeleteFile(meadowFile, cancellationToken); } } \ No newline at end of file diff --git a/Source/v2/Meadow.Cli/Commands/Current/File/FileInitialCommand.cs b/Source/v2/Meadow.Cli/Commands/Current/File/FileInitialCommand.cs index 91c6e78f..c7d959b7 100644 --- a/Source/v2/Meadow.Cli/Commands/Current/File/FileInitialCommand.cs +++ b/Source/v2/Meadow.Cli/Commands/Current/File/FileInitialCommand.cs @@ -19,7 +19,7 @@ protected override async ValueTask ExecuteCommand() Logger?.LogInformation($"Reading file '{MeadowFile}' from device...\n"); - var data = await device.ReadFileString(MeadowFile, CancellationToken); + var data = await device.ReadFileString(AppTools.SanitiseMeadowFilename(MeadowFile), CancellationToken); if (data == null) { diff --git a/Source/v2/Meadow.Cli/Commands/Current/File/FileListCommand.cs b/Source/v2/Meadow.Cli/Commands/Current/File/FileListCommand.cs index 1031289d..a8e910b7 100644 --- a/Source/v2/Meadow.Cli/Commands/Current/File/FileListCommand.cs +++ b/Source/v2/Meadow.Cli/Commands/Current/File/FileListCommand.cs @@ -8,7 +8,6 @@ public class FileListCommand : BaseDeviceCommand { public const int FileSystemBlockSize = 4096; - private const string MeadowRootFolder = "meadow0"; private const string FolderLabel = "[folder]"; [CommandOption("verbose", 'v', IsRequired = false)] @@ -27,18 +26,7 @@ protected override async ValueTask ExecuteCommand() if (Folder != null) { - if (Folder.EndsWith('/') == false) - { - Folder += "/"; - } - if (Folder.StartsWith('/') == false) - { - Folder = $"/{Folder}"; - } - if (Folder.Contains(MeadowRootFolder) == false) - { - Folder = $"/{MeadowRootFolder}{Folder}"; - } + Folder = AppTools.SanitiseMeadowFilename(Folder); Logger?.LogInformation($"Getting file list from '{Folder}'..."); } @@ -47,7 +35,7 @@ protected override async ValueTask ExecuteCommand() Logger?.LogInformation($"Getting file list..."); } - var files = await device.GetFileList(Folder ?? $"/{MeadowRootFolder}/", Verbose, CancellationToken); + var files = await device.GetFileList(Folder ?? $"/{AppTools.MeadowRootFolder}/", Verbose, CancellationToken); if (files == null || files.Length == 0) { diff --git a/Source/v2/Meadow.Cli/Commands/Current/File/FileReadCommand.cs b/Source/v2/Meadow.Cli/Commands/Current/File/FileReadCommand.cs index cf2581ef..7ad1ecde 100644 --- a/Source/v2/Meadow.Cli/Commands/Current/File/FileReadCommand.cs +++ b/Source/v2/Meadow.Cli/Commands/Current/File/FileReadCommand.cs @@ -43,7 +43,7 @@ protected override async ValueTask ExecuteCommand() await device.RuntimeDisable(); } - var success = await device.ReadFile(MeadowFile, LocalFile, CancellationToken); + var success = await device.ReadFile(AppTools.SanitiseMeadowFilename(MeadowFile), LocalFile, CancellationToken); if (success) { diff --git a/Source/v2/Meadow.Cli/Commands/Current/File/FileWriteCommand.cs b/Source/v2/Meadow.Cli/Commands/Current/File/FileWriteCommand.cs index 4dabb554..9a85124b 100644 --- a/Source/v2/Meadow.Cli/Commands/Current/File/FileWriteCommand.cs +++ b/Source/v2/Meadow.Cli/Commands/Current/File/FileWriteCommand.cs @@ -54,7 +54,7 @@ protected override async ValueTask ExecuteCommand() } else { - var targetFileName = GetTargetFileName(i); + var targetFileName = AppTools.SanitiseMeadowFilename(GetTargetFileName(i)); Logger?.LogInformation( $"Writing '{Files[i]}' as '{targetFileName}' to device"); diff --git a/Source/v2/Meadow.Tooling.Core/App/AppManager.cs b/Source/v2/Meadow.Tooling.Core/App/AppManager.cs index 2a93e808..78e65c08 100644 --- a/Source/v2/Meadow.Tooling.Core/App/AppManager.cs +++ b/Source/v2/Meadow.Tooling.Core/App/AppManager.cs @@ -76,7 +76,7 @@ public static async Task DeployApplication( dependencies.Add(Path.Combine(localBinaryDirectory, "App.pdb")); } - var binaries = Directory.EnumerateFiles(localBinaryDirectory, "*.*", SearchOption.TopDirectoryOnly) + var binaries = Directory.EnumerateFiles(localBinaryDirectory, "*.*", SearchOption.AllDirectories) .Where(s => new FileInfo(s).Extension != ".dll") .Where(s => new FileInfo(s).Extension != ".pdb") .Where(s => !s.Contains(".DS_Store")).ToList(); @@ -126,6 +126,20 @@ public static async Task DeployApplication( // now send all files with differing CRCs foreach (var localFile in localFiles) { + string? meadowFilename = string.Empty; + if (!localFile.Key.Contains(MeadowLinker.PreLinkDirectoryName) + && !localFile.Key.Contains(MeadowLinker.PostLinkDirectoryName)) + { + meadowFilename = GetRelativePath(localBinaryDirectory, localFile.Key); + if (!meadowFilename.StartsWith("/meadow0/")) + { + meadowFilename = "/meadow0/" + meadowFilename; + } + } + else + { + meadowFilename = null; + } var existing = deviceFiles.FirstOrDefault(f => Path.GetFileName(f.Name) == Path.GetFileName(localFile.Key)); if (existing != null && existing.Crc != null) @@ -138,9 +152,10 @@ public static async Task DeployApplication( } } + logger?.LogInformation($"Sending '{localFile.Key}'"); send_file: - if (!await connection.WriteFile(localFile.Key, null, cancellationToken)) + if (!await connection.WriteFile(localFile.Key, meadowFilename, cancellationToken)) { logger?.LogWarning($"Error sending'{Path.GetFileName(localFile.Key)}' - retrying"); await Task.Delay(100); @@ -151,4 +166,12 @@ public static async Task DeployApplication( //on macOS, if we don't write a blank line we lose the writing notifcation for the last file logger?.LogInformation(string.Empty); } + + // Path.GetRelativePath is only available in .NET 8 but we also need to support netstandard2.0, hence using this + static string GetRelativePath(string relativeTo, string path) + { + // Determine the difference + var relativePath = path.Substring(relativeTo.Length + 1); + return relativePath; + } } \ No newline at end of file