55using Microsoft . Build . Framework ;
66using Microsoft . Build . Utilities ;
77using System ;
8+ #if ! NETFRAMEWORK
9+ using System . Formats . Tar ;
10+ #endif
811using System . IO ;
912using System . IO . Compression ;
13+ using System . Linq ;
1014
1115namespace Microsoft . DotNet . Build . Tasks
1216{
@@ -33,7 +37,7 @@ public sealed class ExtractArchiveToDirectory : ToolTask
3337 public bool CleanDestination { get ; set ; }
3438
3539 /// <summary>
36- /// A list of directories, semicolon deliminated, relative to the root of the archive to include. If empty all directories will be copied.
40+ /// A list of directories, relative to the root of the archive to include. If empty all directories will be copied.
3741 /// </summary>
3842 public ITaskItem [ ] DirectoriesToCopy { get ; set ; }
3943
@@ -71,22 +75,26 @@ protected override bool ValidateParameters()
7175 public override bool Execute ( )
7276 {
7377 bool retVal = true ;
78+ bool isZipArchive = Path . GetExtension ( SourceArchive ) . Equals ( ".zip" , StringComparison . OrdinalIgnoreCase ) ;
79+ bool isTarballArchive = SourceArchive . EndsWith ( ".tar.gz" , StringComparison . OrdinalIgnoreCase ) ;
7480
75- // Inherits from ToolTask in order to shell out to tar.
81+ // Inherits from ToolTask in order to shell out to tar for complete extraction
7682 // If the file is a .zip, then don't call the base Execute method, just run as a normal task
77- if ( Path . GetExtension ( SourceArchive ) . Equals ( ".zip" , StringComparison . OrdinalIgnoreCase ) )
83+ // If the file is a .tar.gz, and DirectoriesToCopy isn't empty, also run a normal task.
84+ if ( isZipArchive || isTarballArchive )
7885 {
7986 if ( ValidateParameters ( ) )
8087 {
8188 if ( DirectoriesToCopy != null && DirectoriesToCopy . Length != 0 )
8289 {
83- var zip = new ZipArchive ( File . OpenRead ( SourceArchive ) ) ;
84- string loc = DestinationDirectory ;
85- foreach ( var entry in zip . Entries )
90+ // Partial archive extraction
91+ if ( isZipArchive )
8692 {
87- foreach ( var directory in DirectoriesToCopy )
93+ var zip = new ZipArchive ( File . OpenRead ( SourceArchive ) ) ;
94+ string loc = DestinationDirectory ;
95+ foreach ( var entry in zip . Entries )
8896 {
89- if ( entry . FullName . StartsWith ( directory . ItemSpec ) )
97+ if ( ShouldExtractItem ( entry . FullName ) )
9098 {
9199 if ( ! Directory . Exists ( Path . Combine ( DestinationDirectory , Path . GetDirectoryName ( entry . FullName ) ) ) )
92100 {
@@ -98,16 +106,57 @@ public override bool Execute()
98106 }
99107 }
100108 }
109+ else
110+ {
111+ #if NETFRAMEWORK
112+ // Run the base tool, which uses external 'tar' command
113+ retVal = base . Execute ( ) ;
114+ #else
115+ // Decompress GZip content
116+ using FileStream compressedFileStream = File . Open ( SourceArchive , FileMode . Open ) ;
117+ using var decompressor = new GZipStream ( compressedFileStream , CompressionMode . Decompress ) ;
118+ using var decompressedStream = new MemoryStream ( ) ;
119+ decompressor . CopyTo ( decompressedStream ) ;
120+ decompressedStream . Seek ( 0 , SeekOrigin . Begin ) ;
121+
122+ // Extract Tar content
123+ using TarReader tr = new TarReader ( decompressedStream ) ;
124+ while ( tr . GetNextEntry ( ) is TarEntry tarEntry )
125+ {
126+ if ( tarEntry . EntryType != TarEntryType . Directory )
127+ {
128+ string entryName = tarEntry . Name ;
129+ entryName = entryName . StartsWith ( "./" ) ? entryName [ 2 ..] : entryName ;
130+ if ( ShouldExtractItem ( entryName ) )
131+ {
132+ Log . LogMessage ( entryName ) ;
133+ string destinationPath = Path . Combine ( DestinationDirectory , entryName ) ;
134+ Directory . CreateDirectory ( Path . GetDirectoryName ( destinationPath ) ) ;
135+ tarEntry . ExtractToFile ( destinationPath , overwrite : true ) ;
136+ }
137+ }
138+ }
139+ #endif
140+ }
101141 }
102142 else
103- {
143+ {
144+ // Complete archive extraction
145+ if ( isZipArchive )
146+ {
104147#if NETFRAMEWORK
105- // .NET Framework doesn't have overload to overwrite files
106- ZipFile . ExtractToDirectory ( SourceArchive , DestinationDirectory ) ;
148+ // .NET Framework doesn't have overload to overwrite files
149+ ZipFile . ExtractToDirectory ( SourceArchive , DestinationDirectory ) ;
107150#else
108151
109- ZipFile . ExtractToDirectory ( SourceArchive , DestinationDirectory , overwriteFiles : true ) ;
152+ ZipFile . ExtractToDirectory ( SourceArchive , DestinationDirectory , overwriteFiles : true ) ;
110153#endif
154+ }
155+ else
156+ {
157+ // Run the base tool, which uses external 'tar' command
158+ retVal = base . Execute ( ) ;
159+ }
111160 }
112161 }
113162 else
@@ -129,6 +178,17 @@ public override bool Execute()
129178 return retVal ;
130179 }
131180
181+ private bool ShouldExtractItem ( string path )
182+ {
183+ if ( DirectoriesToCopy != null )
184+ {
185+ return DirectoriesToCopy . Any ( p => path . StartsWith ( p . ItemSpec ) ) ;
186+
187+ }
188+
189+ return false ;
190+ }
191+
132192 protected override string ToolName
133193 {
134194 get { return "tar" ; }
0 commit comments