Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GH899: Copy directory structure #1145

Merged
merged 1 commit into from
Oct 27, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions src/Cake.Common.Tests/Unit/IO/FileAliasesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,20 @@ public void Should_Throw_If_Any_File_Do_Not_Exist()
Assert.Equal("The file '/Working/file2.txt' does not exist.", result?.Message);
}

[Fact]
public void Should_Keep_Folder_Structure()
{
// Given
var fixture = new FileCopyFixture();

// When
FileAliases.CopyFiles(fixture.Context, fixture.SourceFilePaths, "./target");

// Then
fixture.TargetFiles[0].Received(1).Copy(Arg.Any<FilePath>(), true);
fixture.TargetFiles[1].Received(1).Copy(Arg.Any<FilePath>(), true);
}

[Fact]
public void Should_Copy_Files()
{
Expand Down Expand Up @@ -401,6 +415,21 @@ public void Should_Throw_If_Any_File_Do_Not_Exist()
Assert.Equal("The file '/Working/file2.txt' does not exist.", result?.Message);
}

[Fact]
public void Should_Keep_Folder_Structure()
{
// Given
var fixture = new FileCopyFixture();
var filePaths = fixture.SourceFilePaths.Select(x => x.FullPath);

// When
FileAliases.CopyFiles(fixture.Context, filePaths, "./target");

// Then
fixture.TargetFiles[0].Received(1).Copy(Arg.Any<FilePath>(), true);
fixture.TargetFiles[1].Received(1).Copy(Arg.Any<FilePath>(), true);
}

[Fact]
public void Should_Copy_Files()
{
Expand Down Expand Up @@ -492,6 +521,20 @@ public void Should_Throw_If_Any_File_Do_Not_Exist()
Assert.Equal("The file '/Working/file2.txt' does not exist.", result?.Message);
}

[Fact]
public void Should_Keep_Folder_Structure()
{
// Given
var fixture = new FileCopyFixture();

// When
FileAliases.CopyFiles(fixture.Context, "*", "./target", true);

// Then
fixture.TargetFiles[0].Received(1).Copy(Arg.Any<FilePath>(), true);
fixture.TargetFiles[1].Received(1).Copy(Arg.Any<FilePath>(), true);
}

[Fact]
public void Should_Copy_Files()
{
Expand Down
74 changes: 71 additions & 3 deletions src/Cake.Common/IO/FileAliases.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public static void CopyFile(this ICakeContext context, FilePath filePath, FilePa
[CakeAliasCategory("Copy")]
public static void CopyFiles(this ICakeContext context, string pattern, DirectoryPath targetDirectoryPath)
{
FileCopier.CopyFiles(context, pattern, targetDirectoryPath);
FileCopier.CopyFiles(context, pattern, targetDirectoryPath, false);
}

/// <summary>
Expand All @@ -120,7 +120,7 @@ public static void CopyFiles(this ICakeContext context, string pattern, Director
[CakeAliasCategory("Copy")]
public static void CopyFiles(this ICakeContext context, IEnumerable<FilePath> filePaths, DirectoryPath targetDirectoryPath)
{
FileCopier.CopyFiles(context, filePaths, targetDirectoryPath);
FileCopier.CopyFiles(context, filePaths, targetDirectoryPath, false);
}

/// <summary>
Expand Down Expand Up @@ -148,7 +148,75 @@ public static void CopyFiles(this ICakeContext context, IEnumerable<string> file
throw new ArgumentNullException(nameof(filePaths));
}
var paths = filePaths.Select(p => new FilePath(p));
FileCopier.CopyFiles(context, paths, targetDirectoryPath);
FileCopier.CopyFiles(context, paths, targetDirectoryPath, false);
}

/// <summary>
/// Copies all files matching the provided pattern to a new location.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="pattern">The pattern.</param>
/// <param name="targetDirectoryPath">The target directory path.</param>
/// <param name="preserveFolderStructure">Keep the folder structure.</param>
/// <example>
/// <code>
/// CopyFiles("Cake.*", "./publish");
/// </code>
/// </example>
[CakeMethodAlias]
[CakeAliasCategory("Copy")]
public static void CopyFiles(this ICakeContext context, string pattern, DirectoryPath targetDirectoryPath, bool preserveFolderStructure)
{
FileCopier.CopyFiles(context, pattern, targetDirectoryPath, preserveFolderStructure);
}

/// <summary>
/// Copies existing files to a new location.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="filePaths">The file paths.</param>
/// <param name="targetDirectoryPath">The target directory path.</param>
/// <param name="preserveFolderStructure">Keep the folder structure.</param>
/// <example>
/// <code>
/// var files = GetFiles("./**/Cake.*");
/// CopyFiles(files, "destination");
/// </code>
/// </example>
[CakeMethodAlias]
[CakeAliasCategory("Copy")]
public static void CopyFiles(this ICakeContext context, IEnumerable<FilePath> filePaths, DirectoryPath targetDirectoryPath, bool preserveFolderStructure)
{
FileCopier.CopyFiles(context, filePaths, targetDirectoryPath, preserveFolderStructure);
}

/// <summary>
/// Copies existing files to a new location.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="filePaths">The file paths.</param>
/// <param name="targetDirectoryPath">The target directory path.</param>
/// <param name="preserveFolderStructure">Keep the folder structure.</param>
/// <example>
/// <code>
/// CreateDirectory("destination");
/// var files = new [] {
/// "Cake.exe",
/// "Cake.pdb"
/// };
/// CopyFiles(files, "destination");
/// </code>
/// </example>
[CakeMethodAlias]
[CakeAliasCategory("Copy")]
public static void CopyFiles(this ICakeContext context, IEnumerable<string> filePaths, DirectoryPath targetDirectoryPath, bool preserveFolderStructure)
{
if (filePaths == null)
{
throw new ArgumentNullException(nameof(filePaths));
}
var paths = filePaths.Select(p => new FilePath(p));
FileCopier.CopyFiles(context, paths, targetDirectoryPath, preserveFolderStructure);
}

/// <summary>
Expand Down
86 changes: 70 additions & 16 deletions src/Cake.Common/IO/FileCopier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using Cake.Core;
using Cake.Core.Diagnostics;
using Cake.Core.IO;
Expand Down Expand Up @@ -56,41 +57,42 @@ public static void CopyFile(ICakeContext context, FilePath filePath, FilePath ta
throw new DirectoryNotFoundException(message);
}

CopyFileCore(context, filePath, targetFilePath);
CopyFileCore(context, filePath, targetFilePath, null);
}

public static void CopyFiles(ICakeContext context, string pattern, DirectoryPath targetDirectoryPath)
public static void CopyFiles(ICakeContext context, string pattern, DirectoryPath targetDirectoryPath, bool preserverFolderStructure)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
throw new ArgumentNullException("context");
}
if (pattern == null)
{
throw new ArgumentNullException(nameof(pattern));
throw new ArgumentNullException("pattern");
}
var files = context.GetFiles(pattern);

if (files.Count == 0)
{
context.Log.Verbose("The provided pattern did not match any files.");
return;
}
CopyFiles(context, files, targetDirectoryPath);
CopyFiles(context, files, targetDirectoryPath, preserverFolderStructure);
}

public static void CopyFiles(ICakeContext context, IEnumerable<FilePath> filePaths, DirectoryPath targetDirectoryPath)
public static void CopyFiles(ICakeContext context, IEnumerable<FilePath> filePaths, DirectoryPath targetDirectoryPath, bool preserverFolderStructure)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
throw new ArgumentNullException("context");
}
if (filePaths == null)
{
throw new ArgumentNullException(nameof(filePaths));
throw new ArgumentNullException("filePaths");
}
if (targetDirectoryPath == null)
{
throw new ArgumentNullException(nameof(targetDirectoryPath));
throw new ArgumentNullException("targetDirectoryPath");
}

var absoluteTargetDirectoryPath = targetDirectoryPath.MakeAbsolute(context.Environment);
Expand All @@ -103,14 +105,47 @@ public static void CopyFiles(ICakeContext context, IEnumerable<FilePath> filePat
throw new DirectoryNotFoundException(message);
}

// Iterate all files and copy them.
foreach (var filePath in filePaths)
if (preserverFolderStructure)
{
var commonPath = string.Empty;
List<string> separatedPath = filePaths
.First(str => str.ToString().Length == filePaths.Max(st2 => st2.ToString().Length)).ToString()
.Split(new string[] { "/" }, StringSplitOptions.RemoveEmptyEntries)
.ToList();

foreach (string pathSegment in separatedPath)
{
if (commonPath.Length == 0 && filePaths.All(str => str.ToString().StartsWith(pathSegment)))
{
commonPath = pathSegment;
}
else if (filePaths.All(str => str.ToString().StartsWith(commonPath + "/" + pathSegment)))
{
commonPath += "/" + pathSegment;
}
else
{
break;
}
}

// Iterate all files and copy them.
foreach (var filePath in filePaths)
{
CopyFileCore(context, filePath, absoluteTargetDirectoryPath.GetFilePath(filePath), context.DirectoryExists(commonPath) ? commonPath : null);
}
}
else
{
CopyFileCore(context, filePath, absoluteTargetDirectoryPath.GetFilePath(filePath));
// Iterate all files and copy them.
foreach (var filePath in filePaths)
{
CopyFileCore(context, filePath, absoluteTargetDirectoryPath.GetFilePath(filePath), null);
}
}
}

private static void CopyFileCore(ICakeContext context, FilePath filePath, FilePath targetFilePath)
private static void CopyFileCore(ICakeContext context, FilePath filePath, FilePath targetFilePath, string commonPath)
{
var absoluteFilePath = filePath.MakeAbsolute(context.Environment);

Expand All @@ -124,9 +159,28 @@ private static void CopyFileCore(ICakeContext context, FilePath filePath, FilePa

// Copy the file.
var absoluteTargetPath = targetFilePath.MakeAbsolute(context.Environment);
context.Log.Verbose("Copying file {0} to {1}", absoluteFilePath.GetFilename(), absoluteTargetPath);
var file = context.FileSystem.GetFile(absoluteFilePath);
file.Copy(absoluteTargetPath, true);

if (!string.IsNullOrEmpty(commonPath))
{
// Get the parent folder structure and create it.
var newRelativeFolderPath = context.Directory(commonPath).Path.GetRelativePath(filePath.GetDirectory());
var newTargetPath = targetFilePath.GetDirectory().Combine(newRelativeFolderPath);
var newAbsoluteTargetPath = newTargetPath.CombineWithFilePath(filePath.GetFilename());
context.Log.Verbose("Copying file {0} to {1}", absoluteFilePath.GetFilename(), newAbsoluteTargetPath);

if (!context.DirectoryExists(newTargetPath))
{
context.CreateDirectory(newTargetPath);
}

file.Copy(newAbsoluteTargetPath, true);
}
else
{
context.Log.Verbose("Copying file {0} to {1}", absoluteFilePath.GetFilename(), absoluteTargetPath);
file.Copy(absoluteTargetPath, true);
}
}
}
}
}