Add TarDirectory and Untar tasks for creating and extracting tar archives#14451
Add TarDirectory and Untar tasks for creating and extracting tar archives#14451ViktorHofer with Copilot wants to merge 28 commits into
Conversation
|
Hello @copilot, I noticed that you’re changing an .swr file or any file under src/Package/MSBuild.VSSetup.. Please make sure to validate this change by an experimental VS insertion. This is accomplished by pushing to an exp/* branch, which requires write permissions to this repo. |
Co-authored-by: ViktorHofer <7412651+ViktorHofer@users.noreply.github.com>
Co-authored-by: ViktorHofer <7412651+ViktorHofer@users.noreply.github.com>
…rammar Co-authored-by: ViktorHofer <7412651+ViktorHofer@users.noreply.github.com>
|
@copilot looks like the current code falls back to not use gzip when a compression type is passed in that's unrecognized. Given that we will expand this matrix, I would throw an error if an unrecognized compression type is passed in instead of silently falling back to not use gzip. |
Co-authored-by: ViktorHofer <7412651+ViktorHofer@users.noreply.github.com>
Done — an unrecognized |
Co-authored-by: ViktorHofer <7412651+ViktorHofer@users.noreply.github.com>
… in TrimStart Co-authored-by: ViktorHofer <7412651+ViktorHofer@users.noreply.github.com>
Co-authored-by: ViktorHofer <7412651+ViktorHofer@users.noreply.github.com>
The stage1 and stage2 CI builds both default their binary log to Build.binlog, so publishing them to the same artifacts location collides. Declare ci/binaryLog/excludeCIBinarylog as first-class parameters, forward them to both stage builds, and pass -binaryLogName Build.stage2.binlog to stage2 when a binlog will actually be produced. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2a6b38b1-0da7-488f-9938-3ac1051e15e2
Moving the stage 1 artifacts aside relocated its Build.binlog out of the published artifacts/log directory, so CI only published the stage 2 binlog. Copy the stage 1 binlog back as Build.stage1.binlog right after the move (best-effort; skipped when no binlog was produced). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2a6b38b1-0da7-488f-9938-3ac1051e15e2
Instead of copying just the stage 1 binlog, copy the whole stage 1 log directory back into artifacts/log after the move, so CI publishes all stage 1 logs alongside the stage 2 ones. Runs before the stage 2 build, so it won't clobber stage 2 output. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2a6b38b1-0da7-488f-9938-3ac1051e15e2
… enum Co-authored-by: ViktorHofer <7412651+ViktorHofer@users.noreply.github.com>
|
@copilot add an |
Co-authored-by: ViktorHofer <7412651+ViktorHofer@users.noreply.github.com>
Added the |
Co-authored-by: ViktorHofer <7412651+ViktorHofer@users.noreply.github.com>
|
@copilot fix the build failures |
Co-authored-by: ViktorHofer <7412651+ViktorHofer@users.noreply.github.com>
Fixed the build failure. The CI break was |
|
@baronfel if you have some time would you taking a look? The tasks are now using the new task types that you mentioned. Not a high priority at all at the moment. |
There was a problem hiding this comment.
Pull request overview
Adds new .NET-only inbox tasks to create and extract tar archives (parity with existing ZipDirectory/Unzip tasks), plus supporting resource strings, task registration, and tests. It also extends engine parameter binding to support enum task parameters.
Changes:
- Introduces
TarDirectoryandUntartasks built onSystem.Formats.Tar, gated to MSBuild-on-.NET viaUsingTaskand.NETCoreApp-only compilation. - Adds new localized resources (MSB4321–MSB4340 bucket) and unit tests validating tar creation/extraction paths.
- Updates task parameter binding (
ValueTypeParser) and adds engine tests to validate enum parameter parsing.
Show a summary per file
| File | Description |
|---|---|
| src/Tasks/Untar.cs | New task for extracting tar archives with include/exclude filtering, incremental-skip behavior, and path traversal protection. |
| src/Tasks/TarDirectory.cs | New task for creating tar archives from directories, with overwrite and format/compression options. |
| src/Tasks/Resources/xlf/Strings.zh-Hant.xlf | Localization entries for new TarDirectory/Untar strings. |
| src/Tasks/Resources/xlf/Strings.zh-Hans.xlf | Localization entries for new TarDirectory/Untar strings. |
| src/Tasks/Resources/xlf/Strings.tr.xlf | Localization entries for new TarDirectory/Untar strings. |
| src/Tasks/Resources/xlf/Strings.ru.xlf | Localization entries for new TarDirectory/Untar strings. |
| src/Tasks/Resources/xlf/Strings.pt-BR.xlf | Localization entries for new TarDirectory/Untar strings. |
| src/Tasks/Resources/xlf/Strings.pl.xlf | Localization entries for new TarDirectory/Untar strings. |
| src/Tasks/Resources/xlf/Strings.ko.xlf | Localization entries for new TarDirectory/Untar strings. |
| src/Tasks/Resources/xlf/Strings.ja.xlf | Localization entries for new TarDirectory/Untar strings. |
| src/Tasks/Resources/xlf/Strings.it.xlf | Localization entries for new TarDirectory/Untar strings. |
| src/Tasks/Resources/xlf/Strings.fr.xlf | Localization entries for new TarDirectory/Untar strings. |
| src/Tasks/Resources/xlf/Strings.es.xlf | Localization entries for new TarDirectory/Untar strings. |
| src/Tasks/Resources/xlf/Strings.de.xlf | Localization entries for new TarDirectory/Untar strings. |
| src/Tasks/Resources/xlf/Strings.cs.xlf | Localization entries for new TarDirectory/Untar strings. |
| src/Tasks/Resources/Strings.resx | Adds TarDirectory/Untar resource strings and assigns MSB code ranges. |
| src/Tasks/Microsoft.Common.tasks | Registers the new tasks as .NET-only UsingTask entries (Core runtime only). |
| src/Tasks/Microsoft.Build.Tasks.csproj | Includes new task sources only when targeting .NETCoreApp. |
| src/Tasks.UnitTests/Untar_Tests.cs | New unit tests for Untar behavior (filters, skipping unchanged, error paths). |
| src/Tasks.UnitTests/TarDirectory_Tests.cs | New unit tests for TarDirectory (compression/format/overwrite/error paths). |
| src/Framework/ValueTypeParser.cs | Adds enum parsing support for task parameter binding. |
| src/Build.UnitTests/BackEnd/TaskExecutionHost_Tests.cs | Adds tests validating enum parameter binding behavior in the engine. |
| src/Build.UnitTests/BackEnd/TaskBuilderTestTask.cs | Adds a test enum + parameter setter used by engine binding tests. |
| documentation/specs/question.md | Documents TarDirectory “question/incremental” behavior (contains a correctness issue). |
Copilot's findings
- Files reviewed: 24/24 changed files
- Comments generated: 6
| using Stream? compressionStream = Compression switch | ||
| { | ||
| TarCompression.GZip => new GZipStream(destinationStream, CompressionLevel.Optimal), | ||
| TarCompression.ZStandard => new ZstandardStream(destinationStream, CompressionLevel.Optimal), | ||
| _ => null, | ||
| }; |
| // ZStandard magic number: 0x28 0xB5 0x2F 0xFD. | ||
| if (read >= 4 && magic[0] == 0x28 && magic[1] == 0xB5 && magic[2] == 0x2F && magic[3] == 0xFD) | ||
| { | ||
| return new ZstandardStream(stream, CompressionMode.Decompress, leaveOpen: true); | ||
| } |
| Stream? decompressionStream = compression switch | ||
| { | ||
| TarDirectory.TarCompression.None => null, | ||
| TarDirectory.TarCompression.GZip => new GZipStream(stream, CompressionMode.Decompress), | ||
| TarDirectory.TarCompression.ZStandard => new ZstandardStream(stream, CompressionMode.Decompress), | ||
| _ => throw new ArgumentException($"Unexpected compression '{compression}'.", nameof(compression)), | ||
| }; |
| // Enums bind by their (case-insensitive) member name, and values that do not map to a | ||
| // defined member are rejected so the engine surfaces a clear parameter-binding error. | ||
| if (targetType.IsEnum) | ||
| { | ||
| object result = Enum.Parse(targetType, value, ignoreCase: true); | ||
| if (!Enum.IsDefined(targetType, result)) | ||
| { | ||
| throw new ArgumentException($"'{value}' is not a defined value of enum type {targetType.Name}.", nameof(value)); | ||
| } | ||
|
|
||
| return result; | ||
| } |
| `TarDirectory` | ||
| Error if the destination tar file doesn't exist. |
| /// <summary> | ||
| /// Gets or sets the compression to apply to the tar archive. | ||
| /// The default is <see cref="TarCompression.None"/>. | ||
| /// This parameter is optional. | ||
| /// </summary> | ||
| public TarCompression Compression { get; set; } = TarCompression.None; | ||
|
|
baronfel
left a comment
There was a problem hiding this comment.
LGTM, but left a few comments for your consideration.
| public TarEntryFormat Format { get; set; } = TarEntryFormat.Pax; | ||
|
|
||
| /// <inheritdoc /> | ||
| public TaskEnvironment TaskEnvironment { get; set; } = TaskEnvironment.Fallback; |
There was a problem hiding this comment.
is this member required at all now? It's unused in the rest of the implementation.
| return false; | ||
| } | ||
|
|
||
| BuildEngine3.Yield(); |
There was a problem hiding this comment.
should we yield so early? feels like we could push the yield down do line 105 or so and not deal with pre-emptive yielding for cases where the preconditions fail for this Task.
| /// Gets or sets a <see cref="ITaskItem"/> with a destination folder path to untar the files to. | ||
| /// </summary> | ||
| [Required] | ||
| public ITaskItem DestinationFolder { get; set; } = null!; |
| /// The compression (none, GZip, or ZStandard) is detected automatically from the archive contents. | ||
| /// </summary> | ||
| [Required] | ||
| public ITaskItem[] SourceFiles { get; set; } = null!; |
| /// <summary> | ||
| /// Gets or sets an MSBuild glob expression that specifies which files to include being untarred from the archive. | ||
| /// </summary> | ||
| public string? Include { get; set; } |
There was a problem hiding this comment.
Now you're making me want to support binding directly to glob expressions too :D
| public bool FailIfNotIncremental { get; set; } | ||
|
|
||
| /// <inheritdoc /> | ||
| public TaskEnvironment TaskEnvironment { get; set; } = TaskEnvironment.Fallback; |
There was a problem hiding this comment.
similar question - if you use the typed bindings, using the FileInfo/DirectoryInfo APIs are safe so you may not need this member anymore.
Adds a
TarDirectoryinbox task that produces.tar(and optionally.tar.gz) archives from a directory, mirroring the existingZipDirectorytask. SinceSystem.Formats.Tarrequires .NET, the task is authored as .NET-only and is unavailable in Visual Studio / MSBuild.exe.Context
The existing
ZipDirectory/Unziptasks have no tar equivalent. The maintainer discussion settled on shipping this as the first .NET (core)-only inbox task, provided theUsingTaskis authored to only bind on the .NET runtime.Changes Made
TarDirectorytask (src/Tasks/TarDirectory.cs): creates a tar fromSourceDirectorytoDestinationFileviaSystem.Formats.Tar. Parameters mirrorZipDirectory:Overwrite,FailIfNotIncremental, plus an optionalCompression(Nonedefault, orGZip).MSB4322) unlessOverwrite="true".Compressionvalues warn (MSB4324) and fall back to an uncompressed tar.zstdwas omitted as it isn't in the BCL.TarDirectory.cscompiles only for.NETCoreApp; theUsingTaskinMicrosoft.Common.tasksis registered withRuntime="NET"andCondition="'$(MSBuildRuntimeType)' == 'Core'".MSB4321–MSB4330inStrings.resx, synced to all.xlffiles.TarDirectory_Tests.cs(net-only) covering plain/GZip creation, overwrite, and the error/warning paths.FailIfNotIncrementalbehavior indocumentation/specs/question.md.Testing
Unit tests in
TarDirectory_Tests.cs; also verified end-to-end via the bootstrap MSBuild that emitted.tarand.tar.gzarchives readable by the systemtarutility.Notes
Two Code Review comments were intentionally left to stay byte-for-byte consistent with
ZipDirectory: the "Question" doc-comment terminology, and reusing theCommentresource for question-mode output. Open to diverging if reviewers prefer.