diff --git a/ref/Microsoft.Build.Tasks.Core/net/Microsoft.Build.Tasks.Core.cs b/ref/Microsoft.Build.Tasks.Core/net/Microsoft.Build.Tasks.Core.cs index dc26ebeb9ba..6c6511693bf 100644 --- a/ref/Microsoft.Build.Tasks.Core/net/Microsoft.Build.Tasks.Core.cs +++ b/ref/Microsoft.Build.Tasks.Core/net/Microsoft.Build.Tasks.Core.cs @@ -1217,6 +1217,8 @@ public sealed partial class Unzip : Microsoft.Build.Tasks.TaskExtension, Microso public Unzip() { } [Microsoft.Build.Framework.RequiredAttribute] public Microsoft.Build.Framework.ITaskItem DestinationFolder { get { throw null; } set { } } + public string Exclude { get { throw null; } set { } } + public string Include { get { throw null; } set { } } public bool OverwriteReadOnlyFiles { get { throw null; } set { } } public bool SkipUnchangedFiles { get { throw null; } set { } } [Microsoft.Build.Framework.RequiredAttribute] diff --git a/ref/Microsoft.Build.Tasks.Core/netstandard/Microsoft.Build.Tasks.Core.cs b/ref/Microsoft.Build.Tasks.Core/netstandard/Microsoft.Build.Tasks.Core.cs index 0d85a2cc928..e788b3e532f 100644 --- a/ref/Microsoft.Build.Tasks.Core/netstandard/Microsoft.Build.Tasks.Core.cs +++ b/ref/Microsoft.Build.Tasks.Core/netstandard/Microsoft.Build.Tasks.Core.cs @@ -894,6 +894,8 @@ public sealed partial class Unzip : Microsoft.Build.Tasks.TaskExtension, Microso public Unzip() { } [Microsoft.Build.Framework.RequiredAttribute] public Microsoft.Build.Framework.ITaskItem DestinationFolder { get { throw null; } set { } } + public string Exclude { get { throw null; } set { } } + public string Include { get { throw null; } set { } } public bool OverwriteReadOnlyFiles { get { throw null; } set { } } public bool SkipUnchangedFiles { get { throw null; } set { } } [Microsoft.Build.Framework.RequiredAttribute] diff --git a/src/Shared/FileMatcher.cs b/src/Shared/FileMatcher.cs index a9c8fa2b2f3..0b187644116 100644 --- a/src/Shared/FileMatcher.cs +++ b/src/Shared/FileMatcher.cs @@ -31,6 +31,8 @@ internal class FileMatcher private static readonly char[] s_wildcardCharacters = { '*', '?' }; private static readonly char[] s_wildcardAndSemicolonCharacters = { '*', '?', ';' }; + private static readonly string[] s_propertyAndItemReferences = { "$(", "@(" }; + // on OSX both System.IO.Path separators are '/', so we have to use the literals internal static readonly char[] directorySeparatorCharacters = { '/', '\\' }; internal static readonly string[] directorySeparatorStrings = directorySeparatorCharacters.Select(c => c.ToString()).ToArray(); @@ -166,8 +168,6 @@ internal static void ClearFileEnumerationsCache() /// /// Determines whether the given path has any wild card characters. /// - /// - /// internal static bool HasWildcards(string filespec) { // Perf Note: Doing a [Last]IndexOfAny(...) is much faster than compiling a @@ -180,18 +180,33 @@ internal static bool HasWildcards(string filespec) } /// - /// Determines whether the given path has any wild card characters or any semicolons. + /// Determines whether the given path has any wild card characters or semicolons. + /// + internal static bool HasWildcardsOrSemicolon(string filespec) + { + return -1 != filespec.LastIndexOfAny(s_wildcardAndSemicolonCharacters); + } + + /// + /// Determines whether the given path has any wild card characters, any semicolons or any property references. /// internal static bool HasWildcardsSemicolonItemOrPropertyReferences(string filespec) { return (-1 != filespec.IndexOfAny(s_wildcardAndSemicolonCharacters)) || - filespec.Contains("$(") || - filespec.Contains("@(") + HasPropertyOrItemReferences(filespec) ; } + /// + /// Determines whether the given path has any property references. + /// + internal static bool HasPropertyOrItemReferences(string filespec) + { + return s_propertyAndItemReferences.Any(filespec.Contains); + } + /// /// Get the files and\or folders specified by the given path and pattern. /// diff --git a/src/Tasks.UnitTests/Unzip_Tests.cs b/src/Tasks.UnitTests/Unzip_Tests.cs index 4ccb35c6a2d..8b48e6ccaf8 100644 --- a/src/Tasks.UnitTests/Unzip_Tests.cs +++ b/src/Tasks.UnitTests/Unzip_Tests.cs @@ -214,5 +214,212 @@ public void LogsErrorIfSourceFileDoesNotExist() _mockEngine.Log.ShouldContain("MSB3932", () => _mockEngine.Log); } } + + [Fact] + public void CanUnzip_WithIncludeFilter() + { + using (TestEnvironment testEnvironment = TestEnvironment.Create()) + { + TransientTestFolder source = testEnvironment.CreateFolder(createFolder: true); + TransientTestFolder destination = testEnvironment.CreateFolder(createFolder: false); + testEnvironment.CreateFile(source, "BE78A17D30144B549D21F71D5C633F7D.txt", "file1"); + testEnvironment.CreateFile(source, "A04FF4B88DF14860B7C73A8E75A4FB76.txt", "file2"); + + TransientZipArchive zipArchive = TransientZipArchive.Create(source, testEnvironment.CreateFolder(createFolder: true)); + + Unzip unzip = new Unzip + { + BuildEngine = _mockEngine, + DestinationFolder = new TaskItem(destination.Path), + OverwriteReadOnlyFiles = true, + SkipUnchangedFiles = false, + SourceFiles = new ITaskItem[] { new TaskItem(zipArchive.Path) }, + Include = "BE78A17D30144B549D21F71D5C633F7D.txt" + }; + + unzip.Execute().ShouldBeTrue(() => _mockEngine.Log); + + _mockEngine.Log.ShouldContain(Path.Combine(destination.Path, "BE78A17D30144B549D21F71D5C633F7D.txt"), () => _mockEngine.Log); + _mockEngine.Log.ShouldNotContain(Path.Combine(destination.Path, "A04FF4B88DF14860B7C73A8E75A4FB76.txt"), () => _mockEngine.Log); + } + } + + [Fact] + public void CanUnzip_WithExcludeFilter() + { + using (TestEnvironment testEnvironment = TestEnvironment.Create()) + { + TransientTestFolder source = testEnvironment.CreateFolder(createFolder: true); + TransientTestFolder destination = testEnvironment.CreateFolder(createFolder: false); + testEnvironment.CreateFile(source, "BE78A17D30144B549D21F71D5C633F7D.txt", "file1"); + testEnvironment.CreateFile(source, "A04FF4B88DF14860B7C73A8E75A4FB76.txt", "file2"); + + TransientZipArchive zipArchive = TransientZipArchive.Create(source, testEnvironment.CreateFolder(createFolder: true)); + + Unzip unzip = new Unzip + { + BuildEngine = _mockEngine, + DestinationFolder = new TaskItem(destination.Path), + OverwriteReadOnlyFiles = true, + SkipUnchangedFiles = false, + SourceFiles = new ITaskItem[] { new TaskItem(zipArchive.Path) }, + Exclude = "BE78A17D30144B549D21F71D5C633F7D.txt" + }; + + unzip.Execute().ShouldBeTrue(() => _mockEngine.Log); + + _mockEngine.Log.ShouldNotContain(Path.Combine(destination.Path, "BE78A17D30144B549D21F71D5C633F7D.txt"), () => _mockEngine.Log); + _mockEngine.Log.ShouldContain(Path.Combine(destination.Path, "A04FF4B88DF14860B7C73A8E75A4FB76.txt"), () => _mockEngine.Log); + } + } + + [Fact] + public void CanUnzip_WithIncludeAndExcludeFilter() + { + using (TestEnvironment testEnvironment = TestEnvironment.Create()) + { + TransientTestFolder source = testEnvironment.CreateFolder(createFolder: true); + TransientTestFolder destination = testEnvironment.CreateFolder(createFolder: false); + TransientTestFolder sub = source.CreateDirectory("sub"); + testEnvironment.CreateFile(source, "file1.js", "file1"); + testEnvironment.CreateFile(source, "file1.js.map", "file2"); + testEnvironment.CreateFile(source, "file2.js", "file3"); + testEnvironment.CreateFile(source, "readme.txt", "file4"); + testEnvironment.CreateFile(sub, "subfile.js", "File5"); + + TransientZipArchive zipArchive = TransientZipArchive.Create(source, testEnvironment.CreateFolder(createFolder: true)); + + Unzip unzip = new Unzip + { + BuildEngine = _mockEngine, + DestinationFolder = new TaskItem(destination.Path), + OverwriteReadOnlyFiles = true, + SkipUnchangedFiles = false, + SourceFiles = new ITaskItem[] { new TaskItem(zipArchive.Path) }, + Include = "*.js", + Exclude = "*.js.map;sub\\*.js" + }; + + unzip.Execute().ShouldBeTrue(() => _mockEngine.Log); + + _mockEngine.Log.ShouldContain(Path.Combine(destination.Path, "file1.js"), () => _mockEngine.Log); + _mockEngine.Log.ShouldNotContain(Path.Combine(destination.Path, "file1.js.map"), () => _mockEngine.Log); + _mockEngine.Log.ShouldContain(Path.Combine(destination.Path, "file2.js"), () => _mockEngine.Log); + _mockEngine.Log.ShouldNotContain(Path.Combine(destination.Path, "readme.txt"), () => _mockEngine.Log); + _mockEngine.Log.ShouldNotContain(Path.Combine(destination.Path, "sub", "subfile.js"), () => _mockEngine.Log); + } + } + + [Fact] + public void LogsErrorIfIncludeContainsInvalidPathCharacters() + { + using (TestEnvironment testEnvironment = TestEnvironment.Create()) + { + TransientTestFolder source = testEnvironment.CreateFolder(createFolder: true); + TransientTestFolder destination = testEnvironment.CreateFolder(createFolder: false); + testEnvironment.CreateFile(source, "BE78A17D30144B549D21F71D5C633F7D.txt", "file1"); + testEnvironment.CreateFile(source, "A04FF4B88DF14860B7C73A8E75A4FB76.txt", "file2"); + + TransientZipArchive zipArchive = TransientZipArchive.Create(source, testEnvironment.CreateFolder(createFolder: true)); + + Unzip unzip = new Unzip + { + BuildEngine = _mockEngine, + DestinationFolder = new TaskItem(destination.Path), + OverwriteReadOnlyFiles = true, + SkipUnchangedFiles = false, + SourceFiles = new ITaskItem[] { new TaskItem(zipArchive.Path) }, + Include = " _mockEngine.Log); + + _mockEngine.Log.ShouldContain("MSB3937", () => _mockEngine.Log); + } + } + + [Fact] + public void LogsErrorIfIncludeContainsPropertyReferences() + { + using (TestEnvironment testEnvironment = TestEnvironment.Create()) + { + TransientTestFolder source = testEnvironment.CreateFolder(createFolder: true); + TransientTestFolder destination = testEnvironment.CreateFolder(createFolder: false); + testEnvironment.CreateFile(source, "BE78A17D30144B549D21F71D5C633F7D.txt", "file1"); + testEnvironment.CreateFile(source, "A04FF4B88DF14860B7C73A8E75A4FB76.txt", "file2"); + + TransientZipArchive zipArchive = TransientZipArchive.Create(source, testEnvironment.CreateFolder(createFolder: true)); + + Unzip unzip = new Unzip + { + BuildEngine = _mockEngine, + DestinationFolder = new TaskItem(destination.Path), + OverwriteReadOnlyFiles = true, + SkipUnchangedFiles = false, + SourceFiles = new ITaskItem[] { new TaskItem(zipArchive.Path) }, + Include = "$(Include)" + }; + + unzip.Execute().ShouldBeFalse(() => _mockEngine.Log); + + _mockEngine.Log.ShouldContain("MSB3938", () => _mockEngine.Log); + } + } + + [Fact] + public void LogsErrorIfExcludeContainsInvalidPathCharacters() + { + using (TestEnvironment testEnvironment = TestEnvironment.Create()) + { + TransientTestFolder source = testEnvironment.CreateFolder(createFolder: true); + TransientTestFolder destination = testEnvironment.CreateFolder(createFolder: false); + testEnvironment.CreateFile(source, "BE78A17D30144B549D21F71D5C633F7D.txt", "file1"); + testEnvironment.CreateFile(source, "A04FF4B88DF14860B7C73A8E75A4FB76.txt", "file2"); + + TransientZipArchive zipArchive = TransientZipArchive.Create(source, testEnvironment.CreateFolder(createFolder: true)); + + Unzip unzip = new Unzip + { + BuildEngine = _mockEngine, + DestinationFolder = new TaskItem(destination.Path), + OverwriteReadOnlyFiles = true, + SkipUnchangedFiles = false, + SourceFiles = new ITaskItem[] { new TaskItem(zipArchive.Path) }, + Exclude = " _mockEngine.Log); + + _mockEngine.Log.ShouldContain("MSB3937", () => _mockEngine.Log); + } + } + + [Fact] + public void LogsErrorIfExcludeContainsPropertyReferences() + { + using (TestEnvironment testEnvironment = TestEnvironment.Create()) + { + TransientTestFolder source = testEnvironment.CreateFolder(createFolder: true); + TransientTestFolder destination = testEnvironment.CreateFolder(createFolder: false); + testEnvironment.CreateFile(source, "BE78A17D30144B549D21F71D5C633F7D.txt", "file1"); + testEnvironment.CreateFile(source, "A04FF4B88DF14860B7C73A8E75A4FB76.txt", "file2"); + + TransientZipArchive zipArchive = TransientZipArchive.Create(source, testEnvironment.CreateFolder(createFolder: true)); + + Unzip unzip = new Unzip + { + BuildEngine = _mockEngine, + DestinationFolder = new TaskItem(destination.Path), + OverwriteReadOnlyFiles = true, + SkipUnchangedFiles = false, + SourceFiles = new ITaskItem[] { new TaskItem(zipArchive.Path) }, + Exclude = "$(Include)" + }; + + unzip.Execute().ShouldBeFalse(() => _mockEngine.Log); + + _mockEngine.Log.ShouldContain("MSB3938", () => _mockEngine.Log); + } + } } } diff --git a/src/Tasks/Resources/Strings.resx b/src/Tasks/Resources/Strings.resx index b465dd10789..a054ea6c65a 100644 --- a/src/Tasks/Resources/Strings.resx +++ b/src/Tasks/Resources/Strings.resx @@ -2789,9 +2789,20 @@ MSB3936: Failed to open unzip file "{0}" to "{1}". {2} {StrBegin="MSB3936: "} + + MSB3937: Failed to parse pattern "{0}" because it contains an invalid path character. + {StrBegin="MSB3937: "} + + + MSB3938: Failed to parse pattern "{0}" because it contains a property reference which isn't supported. + {StrBegin="MSB3938: "} + Did not unzip from file "{0}" to file "{1}" because the "{2}" parameter was set to "{3}" in the project and the files' sizes and timestamps match. + + Did not unzip file "{0}" because it didn't match the include filter or because it matched the exclude filter. + Unzipping file "{0}" to "{1}". diff --git a/src/Tasks/Resources/xlf/Strings.cs.xlf b/src/Tasks/Resources/xlf/Strings.cs.xlf index d63041a77a8..c6ebbc2bf98 100644 --- a/src/Tasks/Resources/xlf/Strings.cs.xlf +++ b/src/Tasks/Resources/xlf/Strings.cs.xlf @@ -2480,6 +2480,11 @@ Rozzipování ze souboru {0} do souboru {1} neproběhlo, protože parametr {2} byl v projektu nastaven na hodnotu {3} a velikosti souborů a časová razítka se shodují. + + Did not unzip file "{0}" because it didn't match the include filter or because it matched the exclude filter. + Did not unzip file "{0}" because it didn't match the include filter or because it matched the exclude filter. + + MSB3931: Failed to unzip to directory "{0}" because it could not be created. {1} MSB3931: Rozzipování do adresáře {0} se nepodařilo, protože ho nebylo možné vytvořit. {1} @@ -2510,6 +2515,16 @@ MSB3932: Soubor {0} se nepodařilo rozzipovat, protože neexistuje nebo není přístupný. {StrBegin="MSB3932: "} + + MSB3937: Failed to parse pattern "{0}" because it contains an invalid path character. + MSB3937: Failed to parse pattern "{0}" because it contains an invalid path character. + {StrBegin="MSB3937: "} + + + MSB3938: Failed to parse pattern "{0}" because it contains a property reference which isn't supported. + MSB3938: Failed to parse pattern "{0}" because it contains a property reference which isn't supported. + {StrBegin="MSB3938: "} + Unzipping file "{0}" to "{1}". Soubor {0} se rozzipovává do {1}. diff --git a/src/Tasks/Resources/xlf/Strings.de.xlf b/src/Tasks/Resources/xlf/Strings.de.xlf index 6f55e8ffb7d..9d261b2b46f 100644 --- a/src/Tasks/Resources/xlf/Strings.de.xlf +++ b/src/Tasks/Resources/xlf/Strings.de.xlf @@ -2480,6 +2480,11 @@ Die Datei "{0}" wurde nicht in die Datei "{1}" entzippt, weil der Parameter "{2}" im Projekt auf "{3}" festgelegt war und die Größen und Zeitstempel der Dateien übereinstimmen. + + Did not unzip file "{0}" because it didn't match the include filter or because it matched the exclude filter. + Did not unzip file "{0}" because it didn't match the include filter or because it matched the exclude filter. + + MSB3931: Failed to unzip to directory "{0}" because it could not be created. {1} MSB3931: Fehler beim Entzippen in das Verzeichnis "{0}", weil dieses nicht erstellt werden konnte. {1} @@ -2510,6 +2515,16 @@ MSB3932: Die Datei "{0}" konnte nicht entzippt werden, weil sie nicht vorhanden ist oder nicht darauf zugegriffen werden kann. {StrBegin="MSB3932: "} + + MSB3937: Failed to parse pattern "{0}" because it contains an invalid path character. + MSB3937: Failed to parse pattern "{0}" because it contains an invalid path character. + {StrBegin="MSB3937: "} + + + MSB3938: Failed to parse pattern "{0}" because it contains a property reference which isn't supported. + MSB3938: Failed to parse pattern "{0}" because it contains a property reference which isn't supported. + {StrBegin="MSB3938: "} + Unzipping file "{0}" to "{1}". Die Datei "{0}" wird in "{1}" entzippt. diff --git a/src/Tasks/Resources/xlf/Strings.en.xlf b/src/Tasks/Resources/xlf/Strings.en.xlf index 0c447b05ab3..df627557c4a 100644 --- a/src/Tasks/Resources/xlf/Strings.en.xlf +++ b/src/Tasks/Resources/xlf/Strings.en.xlf @@ -2530,6 +2530,11 @@ Did not unzip from file "{0}" to file "{1}" because the "{2}" parameter was set to "{3}" in the project and the files' sizes and timestamps match. + + Did not unzip file "{0}" because it didn't match the include filter or because it matched the exclude filter. + Did not unzip file "{0}" because it didn't match the include filter or because it matched the exclude filter. + + MSB3931: Failed to unzip to directory "{0}" because it could not be created. {1} MSB3931: Failed to unzip to directory "{0}" because it could not be created. {1} @@ -2560,6 +2565,16 @@ MSB3932: Failed to unzip file "{0}" because the file does not exist or is inaccessible. {StrBegin="MSB3932: "} + + MSB3937: Failed to parse pattern "{0}" because it contains an invalid path character. + MSB3937: Failed to parse pattern "{0}" because it contains an invalid path character. + {StrBegin="MSB3937: "} + + + MSB3938: Failed to parse pattern "{0}" because it contains a property reference which isn't supported. + MSB3938: Failed to parse pattern "{0}" because it contains a property reference which isn't supported. + {StrBegin="MSB3938: "} + Unzipping file "{0}" to "{1}". Unzipping file "{0}" to "{1}". diff --git a/src/Tasks/Resources/xlf/Strings.es.xlf b/src/Tasks/Resources/xlf/Strings.es.xlf index 620929ddfe8..8a28657818f 100644 --- a/src/Tasks/Resources/xlf/Strings.es.xlf +++ b/src/Tasks/Resources/xlf/Strings.es.xlf @@ -2480,6 +2480,11 @@ No se descomprimió del archivo "{0}" en el archivo "{1}" porque el parámetro "{2}" se estableció como "{3}" en el proyecto y los tamaños y las marcas de tiempo de los archivos coinciden. + + Did not unzip file "{0}" because it didn't match the include filter or because it matched the exclude filter. + Did not unzip file "{0}" because it didn't match the include filter or because it matched the exclude filter. + + MSB3931: Failed to unzip to directory "{0}" because it could not be created. {1} MSB3931: No se pudo descomprimir en el directorio "{0}" porque no se pudo crear. {1} @@ -2510,6 +2515,16 @@ MSB3932: No se pudo descomprimir el archivo "{0}" porque no existe o no se puede tener acceso a él. {StrBegin="MSB3932: "} + + MSB3937: Failed to parse pattern "{0}" because it contains an invalid path character. + MSB3937: Failed to parse pattern "{0}" because it contains an invalid path character. + {StrBegin="MSB3937: "} + + + MSB3938: Failed to parse pattern "{0}" because it contains a property reference which isn't supported. + MSB3938: Failed to parse pattern "{0}" because it contains a property reference which isn't supported. + {StrBegin="MSB3938: "} + Unzipping file "{0}" to "{1}". Descomprimiendo el archivo "{0}" en "{1}". diff --git a/src/Tasks/Resources/xlf/Strings.fr.xlf b/src/Tasks/Resources/xlf/Strings.fr.xlf index 11359a59620..3854fcf0c19 100644 --- a/src/Tasks/Resources/xlf/Strings.fr.xlf +++ b/src/Tasks/Resources/xlf/Strings.fr.xlf @@ -2480,6 +2480,11 @@ Impossible de décompresser le fichier "{0}" vers le fichier "{1}", car le paramètre "{2}" a la valeur "{3}" dans le projet, et les tailles et horodatages des fichiers correspondent. + + Did not unzip file "{0}" because it didn't match the include filter or because it matched the exclude filter. + Did not unzip file "{0}" because it didn't match the include filter or because it matched the exclude filter. + + MSB3931: Failed to unzip to directory "{0}" because it could not be created. {1} MSB3931: Échec de la décompression dans le répertoire "{0}", car il n'a pas pu être créé. {1} @@ -2510,6 +2515,16 @@ MSB3932: Échec de la décompression du fichier "{0}", car le fichier n'existe pas ou est inaccessible. {StrBegin="MSB3932: "} + + MSB3937: Failed to parse pattern "{0}" because it contains an invalid path character. + MSB3937: Failed to parse pattern "{0}" because it contains an invalid path character. + {StrBegin="MSB3937: "} + + + MSB3938: Failed to parse pattern "{0}" because it contains a property reference which isn't supported. + MSB3938: Failed to parse pattern "{0}" because it contains a property reference which isn't supported. + {StrBegin="MSB3938: "} + Unzipping file "{0}" to "{1}". Décompression du fichier "{0}" dans "{1}". diff --git a/src/Tasks/Resources/xlf/Strings.it.xlf b/src/Tasks/Resources/xlf/Strings.it.xlf index 39b30cfbacb..14bdb0e8baf 100644 --- a/src/Tasks/Resources/xlf/Strings.it.xlf +++ b/src/Tasks/Resources/xlf/Strings.it.xlf @@ -2480,6 +2480,11 @@ Non è stato possibile decomprimere il file "{0}" nel file "{1}". Il parametro "{2}" è stato impostato su "{3}" nel progetto e le dimensioni e il timestamp dei file corrispondono. + + Did not unzip file "{0}" because it didn't match the include filter or because it matched the exclude filter. + Did not unzip file "{0}" because it didn't match the include filter or because it matched the exclude filter. + + MSB3931: Failed to unzip to directory "{0}" because it could not be created. {1} MSB3931: non è stato possibile decomprimere nella directory "{0}" perché non è stato possibile crearla. {1} @@ -2510,6 +2515,16 @@ MSB3932: non è stato possibile decomprimere il file "{0}" perché non esiste oppure è inaccessibile. {StrBegin="MSB3932: "} + + MSB3937: Failed to parse pattern "{0}" because it contains an invalid path character. + MSB3937: Failed to parse pattern "{0}" because it contains an invalid path character. + {StrBegin="MSB3937: "} + + + MSB3938: Failed to parse pattern "{0}" because it contains a property reference which isn't supported. + MSB3938: Failed to parse pattern "{0}" because it contains a property reference which isn't supported. + {StrBegin="MSB3938: "} + Unzipping file "{0}" to "{1}". Decompressione del file "{0}" in "{1}". diff --git a/src/Tasks/Resources/xlf/Strings.ja.xlf b/src/Tasks/Resources/xlf/Strings.ja.xlf index 8166fd4d2dd..a4b07fadb16 100644 --- a/src/Tasks/Resources/xlf/Strings.ja.xlf +++ b/src/Tasks/Resources/xlf/Strings.ja.xlf @@ -2480,6 +2480,11 @@ "{2}" パラメーターがプロジェクトで "{3}" に設定されているため、またファイルのサイズとタイムスタンプが一致するため、ファイル "{0}" からファイル "{1}" に解凍しませんでした。 + + Did not unzip file "{0}" because it didn't match the include filter or because it matched the exclude filter. + Did not unzip file "{0}" because it didn't match the include filter or because it matched the exclude filter. + + MSB3931: Failed to unzip to directory "{0}" because it could not be created. {1} MSB3931: ディレクトリ "{0}" への解凍は、そのディレクトリを作成できなかったため、失敗しました。{1} @@ -2510,6 +2515,16 @@ MSB3932: ファイルが存在しないか、アクセスできないため、ファイル "{0}" を解凍できませんでした。 {StrBegin="MSB3932: "} + + MSB3937: Failed to parse pattern "{0}" because it contains an invalid path character. + MSB3937: Failed to parse pattern "{0}" because it contains an invalid path character. + {StrBegin="MSB3937: "} + + + MSB3938: Failed to parse pattern "{0}" because it contains a property reference which isn't supported. + MSB3938: Failed to parse pattern "{0}" because it contains a property reference which isn't supported. + {StrBegin="MSB3938: "} + Unzipping file "{0}" to "{1}". ファイル "{0}" を "{1}" に解凍しています。 diff --git a/src/Tasks/Resources/xlf/Strings.ko.xlf b/src/Tasks/Resources/xlf/Strings.ko.xlf index dd566fd77b2..e801bdd91a6 100644 --- a/src/Tasks/Resources/xlf/Strings.ko.xlf +++ b/src/Tasks/Resources/xlf/Strings.ko.xlf @@ -2480,6 +2480,11 @@ "{2}" 매개 변수가 프로젝트에 "{3}"(으)로 설정되었고 파일 크기와 타임스탬프가 일치하기 때문에 "{0}" 파일에서 "{1}" 파일로 압축을 풀 수 없습니다. + + Did not unzip file "{0}" because it didn't match the include filter or because it matched the exclude filter. + Did not unzip file "{0}" because it didn't match the include filter or because it matched the exclude filter. + + MSB3931: Failed to unzip to directory "{0}" because it could not be created. {1} MSB3931: "{0}" 디렉터리를 생성할 수 없기 때문에 이 디렉터리에 압축을 풀지 못했습니다. {1} @@ -2510,6 +2515,16 @@ MSB3932: 파일이 존재하지 않거나 액세스할 수 없기 때문에 파일 "{0}"의 압축을 풀지 못했습니다. {StrBegin="MSB3932: "} + + MSB3937: Failed to parse pattern "{0}" because it contains an invalid path character. + MSB3937: Failed to parse pattern "{0}" because it contains an invalid path character. + {StrBegin="MSB3937: "} + + + MSB3938: Failed to parse pattern "{0}" because it contains a property reference which isn't supported. + MSB3938: Failed to parse pattern "{0}" because it contains a property reference which isn't supported. + {StrBegin="MSB3938: "} + Unzipping file "{0}" to "{1}". 파일 "{0}"의 압축을 "{1}"에 푸는 중입니다. diff --git a/src/Tasks/Resources/xlf/Strings.pl.xlf b/src/Tasks/Resources/xlf/Strings.pl.xlf index bf99b39d5ea..4251be9ff36 100644 --- a/src/Tasks/Resources/xlf/Strings.pl.xlf +++ b/src/Tasks/Resources/xlf/Strings.pl.xlf @@ -2480,6 +2480,11 @@ Nie wykonano rozpakowywania z pliku „{0}” do pliku „{1}”, ponieważ parametr „{2}” w projekcie został ustawiony na wartość „{3}”, a rozmiary plików i sygnatury czasowe pasują do siebie. + + Did not unzip file "{0}" because it didn't match the include filter or because it matched the exclude filter. + Did not unzip file "{0}" because it didn't match the include filter or because it matched the exclude filter. + + MSB3931: Failed to unzip to directory "{0}" because it could not be created. {1} MSB3931: Nie można rozpakować do katalogu „{0}”, ponieważ nie można go utworzyć. {1} @@ -2510,6 +2515,16 @@ MSB3932: Nie można rozpakować pliku „{0}”, ponieważ plik nie istnieje lub jest niedostępny. {StrBegin="MSB3932: "} + + MSB3937: Failed to parse pattern "{0}" because it contains an invalid path character. + MSB3937: Failed to parse pattern "{0}" because it contains an invalid path character. + {StrBegin="MSB3937: "} + + + MSB3938: Failed to parse pattern "{0}" because it contains a property reference which isn't supported. + MSB3938: Failed to parse pattern "{0}" because it contains a property reference which isn't supported. + {StrBegin="MSB3938: "} + Unzipping file "{0}" to "{1}". Rozpakowywanie pliku „{0}” do pliku „{1}”. diff --git a/src/Tasks/Resources/xlf/Strings.pt-BR.xlf b/src/Tasks/Resources/xlf/Strings.pt-BR.xlf index dc823788e5c..1dfff7329b4 100644 --- a/src/Tasks/Resources/xlf/Strings.pt-BR.xlf +++ b/src/Tasks/Resources/xlf/Strings.pt-BR.xlf @@ -2480,6 +2480,11 @@ Não foi possível descompactar o arquivo "{0}" para o arquivo "{1}", pois o parâmetro "{2}" foi definido como "{3}" no projeto, e os tamanhos de arquivos e os carimbos de data/hora não correspondem. + + Did not unzip file "{0}" because it didn't match the include filter or because it matched the exclude filter. + Did not unzip file "{0}" because it didn't match the include filter or because it matched the exclude filter. + + MSB3931: Failed to unzip to directory "{0}" because it could not be created. {1} MSB3931: Falha ao descompactar no diretório "{0}" porque ele não pôde ser criado. {1} @@ -2510,6 +2515,16 @@ MSB3932: Falha ao descompactar o arquivo "{0}" porque ele não existe ou está inacessível. {StrBegin="MSB3932: "} + + MSB3937: Failed to parse pattern "{0}" because it contains an invalid path character. + MSB3937: Failed to parse pattern "{0}" because it contains an invalid path character. + {StrBegin="MSB3937: "} + + + MSB3938: Failed to parse pattern "{0}" because it contains a property reference which isn't supported. + MSB3938: Failed to parse pattern "{0}" because it contains a property reference which isn't supported. + {StrBegin="MSB3938: "} + Unzipping file "{0}" to "{1}". Descompactando o arquivo "{0}" em "{1}". diff --git a/src/Tasks/Resources/xlf/Strings.ru.xlf b/src/Tasks/Resources/xlf/Strings.ru.xlf index 9e7588f33eb..9e883f29af7 100644 --- a/src/Tasks/Resources/xlf/Strings.ru.xlf +++ b/src/Tasks/Resources/xlf/Strings.ru.xlf @@ -2480,6 +2480,11 @@ Не удалось выполнить распаковку из файла "{0}" в файл "{1}", так как для параметра "{2}" в проекте было задано значение "{3}", а размеры файлов и отметки времени совпадают. + + Did not unzip file "{0}" because it didn't match the include filter or because it matched the exclude filter. + Did not unzip file "{0}" because it didn't match the include filter or because it matched the exclude filter. + + MSB3931: Failed to unzip to directory "{0}" because it could not be created. {1} MSB3931: не удалось выполнить распаковку в каталог "{0}", так как создать его не удалось. {1} @@ -2510,6 +2515,16 @@ MSB3932: не удалось распаковать файл "{0}", так как он не существует или недоступен. {StrBegin="MSB3932: "} + + MSB3937: Failed to parse pattern "{0}" because it contains an invalid path character. + MSB3937: Failed to parse pattern "{0}" because it contains an invalid path character. + {StrBegin="MSB3937: "} + + + MSB3938: Failed to parse pattern "{0}" because it contains a property reference which isn't supported. + MSB3938: Failed to parse pattern "{0}" because it contains a property reference which isn't supported. + {StrBegin="MSB3938: "} + Unzipping file "{0}" to "{1}". Распаковка файла "{0}" в"{1}". diff --git a/src/Tasks/Resources/xlf/Strings.tr.xlf b/src/Tasks/Resources/xlf/Strings.tr.xlf index f4e09678061..194e8b3f3e1 100644 --- a/src/Tasks/Resources/xlf/Strings.tr.xlf +++ b/src/Tasks/Resources/xlf/Strings.tr.xlf @@ -2480,6 +2480,11 @@ Projede "{2}" parametresi "{3}" olarak ayarlandığından ve dosya boyutlarıyla zaman damgaları eşleştiğinden "{0}" dosyasını "{1}" dosyasına çıkarma işlemi gerçekleştirilmedi. + + Did not unzip file "{0}" because it didn't match the include filter or because it matched the exclude filter. + Did not unzip file "{0}" because it didn't match the include filter or because it matched the exclude filter. + + MSB3931: Failed to unzip to directory "{0}" because it could not be created. {1} MSB3931: "{0}" dizini oluşturulamadığından bu dizine çıkarılamadı. {1} @@ -2510,6 +2515,16 @@ MSB3932: Dosya mevcut olmadığından veya erişilebilir olmadığından "{0}" dosyasının sıkıştırması açılamadı. {StrBegin="MSB3932: "} + + MSB3937: Failed to parse pattern "{0}" because it contains an invalid path character. + MSB3937: Failed to parse pattern "{0}" because it contains an invalid path character. + {StrBegin="MSB3937: "} + + + MSB3938: Failed to parse pattern "{0}" because it contains a property reference which isn't supported. + MSB3938: Failed to parse pattern "{0}" because it contains a property reference which isn't supported. + {StrBegin="MSB3938: "} + Unzipping file "{0}" to "{1}". "{0}" dosyasının sıkıştırması "{1}" hedefine açılıyor. diff --git a/src/Tasks/Resources/xlf/Strings.zh-Hans.xlf b/src/Tasks/Resources/xlf/Strings.zh-Hans.xlf index 37aa968f49b..44e2fce72d4 100644 --- a/src/Tasks/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/Tasks/Resources/xlf/Strings.zh-Hans.xlf @@ -2480,6 +2480,11 @@ 未从文件“{0}”解压缩到文件“{1}”,因为“{2}”参数在项目中设置为“{3}”,而两个文件的大小及时间戳一致。 + + Did not unzip file "{0}" because it didn't match the include filter or because it matched the exclude filter. + Did not unzip file "{0}" because it didn't match the include filter or because it matched the exclude filter. + + MSB3931: Failed to unzip to directory "{0}" because it could not be created. {1} MSB3931: 未能解压缩到目录“{0}”,因为无法创建它。{1} @@ -2510,6 +2515,16 @@ MSB3932: 未能解压缩文件“{0}”,因为该文件不存在或无法访问。 {StrBegin="MSB3932: "} + + MSB3937: Failed to parse pattern "{0}" because it contains an invalid path character. + MSB3937: Failed to parse pattern "{0}" because it contains an invalid path character. + {StrBegin="MSB3937: "} + + + MSB3938: Failed to parse pattern "{0}" because it contains a property reference which isn't supported. + MSB3938: Failed to parse pattern "{0}" because it contains a property reference which isn't supported. + {StrBegin="MSB3938: "} + Unzipping file "{0}" to "{1}". 将文件“{0}”解压缩到“{1}”。 diff --git a/src/Tasks/Resources/xlf/Strings.zh-Hant.xlf b/src/Tasks/Resources/xlf/Strings.zh-Hant.xlf index 2fa9517589f..a70ec8fb257 100644 --- a/src/Tasks/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/Tasks/Resources/xlf/Strings.zh-Hant.xlf @@ -2480,6 +2480,11 @@ 並未從檔案 "{0}" 解壓縮到檔案 "{1}",因為在專案中的 "{2}" 參數原先設定為 "{3}",且檔案的大小與時間戳記相符。 + + Did not unzip file "{0}" because it didn't match the include filter or because it matched the exclude filter. + Did not unzip file "{0}" because it didn't match the include filter or because it matched the exclude filter. + + MSB3931: Failed to unzip to directory "{0}" because it could not be created. {1} MSB3931: 因為無法建立目錄 "{0}",所以無法解壓縮至該目錄。{1} @@ -2510,6 +2515,16 @@ MSB3932: 因為檔案不存在或無法存取,所以無法解壓縮檔案 "{0}"。 {StrBegin="MSB3932: "} + + MSB3937: Failed to parse pattern "{0}" because it contains an invalid path character. + MSB3937: Failed to parse pattern "{0}" because it contains an invalid path character. + {StrBegin="MSB3937: "} + + + MSB3938: Failed to parse pattern "{0}" because it contains a property reference which isn't supported. + MSB3938: Failed to parse pattern "{0}" because it contains a property reference which isn't supported. + {StrBegin="MSB3938: "} + Unzipping file "{0}" to "{1}". 正在將檔案 "{0}" 解壓縮到 "{1}"。 diff --git a/src/Tasks/Unzip.cs b/src/Tasks/Unzip.cs index 401829e2f65..4e00a677831 100644 --- a/src/Tasks/Unzip.cs +++ b/src/Tasks/Unzip.cs @@ -1,14 +1,16 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; using System; using System.IO; using System.IO.Compression; using System.Linq; using System.Threading; + +using Microsoft.Build.Framework; +using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; +using Microsoft.Build.Utilities; namespace Microsoft.Build.Tasks { @@ -27,6 +29,16 @@ public sealed class Unzip : TaskExtension, ICancelableTask /// private readonly CancellationTokenSource _cancellationToken = new CancellationTokenSource(); + /// + /// Stores the include patterns after parsing. + /// + private string[] _includePatterns; + + /// + /// Stores the exclude patterns after parsing. + /// + private string[] _excludePatterns; + /// /// Gets or sets a with a destination folder path to unzip the files to. /// @@ -49,6 +61,16 @@ public sealed class Unzip : TaskExtension, ICancelableTask [Required] public ITaskItem[] SourceFiles { get; set; } + /// + /// Gets or sets an MSBuild glob expression that will be used to determine which files to include being unzipped from the archive. + /// + public string Include { get; set; } + + /// + /// Gets or sets an MSBuild glob expression that will be used to determine which files to exclude from being unzipped from the archive. + /// + public string Exclude { get; set; } + /// public void Cancel() { @@ -74,41 +96,46 @@ public override bool Execute() try { - foreach (ITaskItem sourceFile in SourceFiles.TakeWhile(i => !_cancellationToken.IsCancellationRequested)) + ParseIncludeExclude(); + + if (!Log.HasLoggedErrors) { - if (!FileSystems.Default.FileExists(sourceFile.ItemSpec)) + foreach (ITaskItem sourceFile in SourceFiles.TakeWhile(i => !_cancellationToken.IsCancellationRequested)) { - Log.LogErrorWithCodeFromResources("Unzip.ErrorFileDoesNotExist", sourceFile.ItemSpec); - continue; - } + if (!FileSystems.Default.FileExists(sourceFile.ItemSpec)) + { + Log.LogErrorWithCodeFromResources("Unzip.ErrorFileDoesNotExist", sourceFile.ItemSpec); + continue; + } - try - { - using (FileStream stream = new FileStream(sourceFile.ItemSpec, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 0x1000, useAsync: false)) + try { - using (ZipArchive zipArchive = new ZipArchive(stream, ZipArchiveMode.Read, leaveOpen: false)) + using (FileStream stream = new FileStream(sourceFile.ItemSpec, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 0x1000, useAsync: false)) { - try - { - Extract(zipArchive, destinationDirectory); - } - catch (Exception e) + using (ZipArchive zipArchive = new ZipArchive(stream, ZipArchiveMode.Read, leaveOpen: false)) { - // Unhandled exception in Extract() is a bug! - Log.LogErrorFromException(e, showStackTrace: true); - return false; + try + { + Extract(zipArchive, destinationDirectory); + } + catch (Exception e) + { + // Unhandled exception in Extract() is a bug! + Log.LogErrorFromException(e, showStackTrace: true); + return false; + } } } } - } - catch (OperationCanceledException) - { - break; - } - catch (Exception e) - { - // Should only be thrown if the archive could not be opened (Access denied, corrupt file, etc) - Log.LogErrorWithCodeFromResources("Unzip.ErrorCouldNotOpenFile", sourceFile.ItemSpec, e.Message); + catch (OperationCanceledException) + { + break; + } + catch (Exception e) + { + // Should only be thrown if the archive could not be opened (Access denied, corrupt file, etc) + Log.LogErrorWithCodeFromResources("Unzip.ErrorCouldNotOpenFile", sourceFile.ItemSpec, e.Message); + } } } } @@ -129,6 +156,12 @@ private void Extract(ZipArchive sourceArchive, DirectoryInfo destinationDirector { foreach (ZipArchiveEntry zipArchiveEntry in sourceArchive.Entries.TakeWhile(i => !_cancellationToken.IsCancellationRequested)) { + if (ShouldSkipEntry(zipArchiveEntry)) + { + Log.LogMessageFromResources(MessageImportance.Low, "Unzip.DidNotUnzipBecauseOfFilter", zipArchiveEntry.FullName); + continue; + } + FileInfo destinationPath = new FileInfo(Path.Combine(destinationDirectory.FullName, zipArchiveEntry.FullName)); // Zip archives can have directory entries listed explicitly. @@ -199,6 +232,28 @@ private void Extract(ZipArchive sourceArchive, DirectoryInfo destinationDirector } } + /// + /// Determines whether or not a file should be skipped when unzipping by filtering. + /// + /// The object containing information about the file in the zip archive. + /// true if the file should be skipped, otherwise false. + private bool ShouldSkipEntry(ZipArchiveEntry zipArchiveEntry) + { + bool result = false; + + if (_includePatterns.Length > 0) + { + result = _includePatterns.All(pattern => !FileMatcher.IsMatch(FileMatcher.Normalize(zipArchiveEntry.FullName), pattern, true)); + } + + if (_excludePatterns.Length > 0) + { + result |= _excludePatterns.Any(pattern => FileMatcher.IsMatch(FileMatcher.Normalize(zipArchiveEntry.FullName), pattern, true)); + } + + return result; + } + /// /// Determines whether or not a file should be skipped when unzipping. /// @@ -212,5 +267,34 @@ private bool ShouldSkipEntry(ZipArchiveEntry zipArchiveEntry, FileInfo fileInfo) && zipArchiveEntry.LastWriteTime == fileInfo.LastWriteTimeUtc && zipArchiveEntry.Length == fileInfo.Length; } + + private void ParseIncludeExclude() + { + ParsePattern(Include, out _includePatterns); + ParsePattern(Exclude, out _excludePatterns); + } + + private void ParsePattern(string pattern, out string[] patterns) + { + patterns = Array.Empty(); + if (!string.IsNullOrWhiteSpace(pattern)) + { + if (FileMatcher.HasPropertyOrItemReferences(pattern)) + { + // Supporting property references would require access to Expander which is unavailable in Microsoft.Build.Tasks + Log.LogErrorWithCodeFromResources("Unzip.ErrorParsingPatternPropertyReferences", pattern); + } + else if (pattern.IndexOfAny(FileUtilities.InvalidPathChars) != -1) + { + Log.LogErrorWithCodeFromResources("Unzip.ErrorParsingPatternInvalidPath", pattern); + } + else + { + patterns = pattern.Contains(';') + ? pattern.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries).Select(FileMatcher.Normalize).ToArray() + : new[] { pattern }; + } + } + } } }