|
@@ -1,10 +1,146 @@
|
|
|
-using System.Diagnostics;
|
|
|
+using System.Buffers;
|
|
|
+using System.Diagnostics;
|
|
|
using System.IO.Compression;
|
|
|
|
|
|
namespace PackTool
|
|
|
{
|
|
|
+ internal static partial class ZipFileUtils
|
|
|
+ {
|
|
|
+ // Per the .ZIP File Format Specification 4.4.17.1 all slashes should be forward slashes
|
|
|
+ private const char PathSeparatorChar = '/';
|
|
|
+ private const string PathSeparatorString = "/";
|
|
|
+
|
|
|
+ public static string EntryFromPath(string entry, int offset, int length, ref char[] buffer, bool appendPathSeparator = false)
|
|
|
+ {
|
|
|
+ Debug.Assert(length <= entry.Length - offset);
|
|
|
+ Debug.Assert(buffer != null);
|
|
|
+
|
|
|
+ // Remove any leading slashes from the entry name:
|
|
|
+ while (length > 0)
|
|
|
+ {
|
|
|
+ if (entry[offset] != Path.DirectorySeparatorChar &&
|
|
|
+ entry[offset] != Path.AltDirectorySeparatorChar)
|
|
|
+ break;
|
|
|
+
|
|
|
+ offset++;
|
|
|
+ length--;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (length == 0)
|
|
|
+ return appendPathSeparator ? PathSeparatorString : string.Empty;
|
|
|
+
|
|
|
+ int resultLength = appendPathSeparator ? length + 1 : length;
|
|
|
+ EnsureCapacity(ref buffer, resultLength);
|
|
|
+ entry.CopyTo(offset, buffer, 0, length);
|
|
|
+
|
|
|
+ // '/' is a more broadly recognized directory separator on all platforms (eg: mac, linux)
|
|
|
+ // We don't use Path.DirectorySeparatorChar or AltDirectorySeparatorChar because this is
|
|
|
+ // explicitly trying to standardize to '/'
|
|
|
+ for (int i = 0; i < length; i++)
|
|
|
+ {
|
|
|
+ char ch = buffer[i];
|
|
|
+ if (ch == Path.DirectorySeparatorChar || ch == Path.AltDirectorySeparatorChar)
|
|
|
+ buffer[i] = PathSeparatorChar;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (appendPathSeparator)
|
|
|
+ buffer[length] = PathSeparatorChar;
|
|
|
+
|
|
|
+ return new string(buffer, 0, resultLength);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void EnsureCapacity(ref char[] buffer, int min)
|
|
|
+ {
|
|
|
+ Debug.Assert(buffer != null);
|
|
|
+ Debug.Assert(min > 0);
|
|
|
+
|
|
|
+ if (buffer.Length < min)
|
|
|
+ {
|
|
|
+ int newCapacity = buffer.Length * 2;
|
|
|
+ if (newCapacity < min)
|
|
|
+ newCapacity = min;
|
|
|
+
|
|
|
+ char[] oldBuffer = buffer;
|
|
|
+ buffer = ArrayPool<char>.Shared.Rent(newCapacity);
|
|
|
+ ArrayPool<char>.Shared.Return(oldBuffer);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static bool IsDirEmpty(DirectoryInfo possiblyEmptyDir)
|
|
|
+ {
|
|
|
+ using (IEnumerator<string> enumerator = Directory.EnumerateFileSystemEntries(possiblyEmptyDir.FullName).GetEnumerator())
|
|
|
+ return !enumerator.MoveNext();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
class Program
|
|
|
{
|
|
|
+ private static void CreateZipFileFromDir(string sourceDirectoryName, string destinationArchiveFileName, string[] excludeFolders)
|
|
|
+ {
|
|
|
+ sourceDirectoryName = Path.GetFullPath(sourceDirectoryName);
|
|
|
+ destinationArchiveFileName = Path.GetFullPath(destinationArchiveFileName);
|
|
|
+
|
|
|
+ using (ZipArchive archive = ZipFile.Open(destinationArchiveFileName, ZipArchiveMode.Create))
|
|
|
+ {
|
|
|
+ //add files and directories
|
|
|
+ DirectoryInfo di = new DirectoryInfo(sourceDirectoryName);
|
|
|
+
|
|
|
+ var basePath = di.FullName;
|
|
|
+
|
|
|
+ // Windows' MaxPath (260) is used as an arbitrary default capacity, as it is likely
|
|
|
+ // to be greater than the length of typical entry names from the file system, even
|
|
|
+ // on non-Windows platforms. The capacity will be increased, if needed.
|
|
|
+ const int DefaultCapacity = 260;
|
|
|
+ char[] entryNameBuffer = ArrayPool<char>.Shared.Rent(DefaultCapacity);
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ foreach (FileSystemInfo file in di.EnumerateFileSystemInfos("*", SearchOption.AllDirectories))
|
|
|
+ {
|
|
|
+ bool exclude = false;
|
|
|
+ var fileName = file.FullName.ToLower();
|
|
|
+ foreach(var excludeFolder in excludeFolders)
|
|
|
+ {
|
|
|
+ if (fileName.StartsWith(excludeFolder))
|
|
|
+ {
|
|
|
+ exclude = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (exclude)
|
|
|
+ {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ int entryNameLength = file.FullName.Length - basePath.Length;
|
|
|
+ if (file is FileInfo)
|
|
|
+ {
|
|
|
+ // Create entry for file:
|
|
|
+ string entryName = ZipFileUtils.EntryFromPath(file.FullName, basePath.Length, entryNameLength, ref entryNameBuffer);
|
|
|
+ ZipFileExtensions.CreateEntryFromFile(archive, file.FullName, entryName);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Entry marking an empty dir:
|
|
|
+ if (file is DirectoryInfo possiblyEmpty && ZipFileUtils.IsDirEmpty(possiblyEmpty))
|
|
|
+ {
|
|
|
+ // FullName never returns a directory separator character on the end,
|
|
|
+ // but Zip archives require it to specify an explicit directory:
|
|
|
+ string entryName = ZipFileUtils.EntryFromPath(file.FullName, basePath.Length, entryNameLength, ref entryNameBuffer, appendPathSeparator: true);
|
|
|
+ archive.CreateEntry(entryName);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } // foreach
|
|
|
+ }
|
|
|
+ finally
|
|
|
+ {
|
|
|
+ ArrayPool<char>.Shared.Return(entryNameBuffer);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
private static void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs)
|
|
|
{
|
|
|
// Get the subdirectories for the specified directory.
|
|
@@ -129,7 +265,7 @@ namespace PackTool
|
|
|
Directory.CreateDirectory(productRootFolder);
|
|
|
}
|
|
|
var productFolder = Path.Combine(productRootFolder, productName);
|
|
|
- if(!Directory.Exists(productFolder))
|
|
|
+ if (!Directory.Exists(productFolder))
|
|
|
{
|
|
|
Directory.CreateDirectory(productFolder);
|
|
|
}
|
|
@@ -166,7 +302,7 @@ namespace PackTool
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private static void RepackProduct(string productName, bool attachTimestamp)
|
|
|
+ private static void RepackProduct(string productName, string[] excludeFolderNames, bool attachTimestamp)
|
|
|
{
|
|
|
var productRootFolder = Environment.GetEnvironmentVariable("ProductFolder");
|
|
|
if (string.IsNullOrEmpty(productRootFolder))
|
|
@@ -185,8 +321,14 @@ namespace PackTool
|
|
|
Directory.CreateDirectory(productFolder);
|
|
|
}
|
|
|
|
|
|
+ var excludeFolders = new List<string>();
|
|
|
+ foreach (var excludeFolderName in excludeFolderNames)
|
|
|
+ {
|
|
|
+ excludeFolders.Add(Path.Combine(productFolder.ToLower(), excludeFolderName.ToLower()));
|
|
|
+ }
|
|
|
+
|
|
|
var repackRootFolder = Environment.GetEnvironmentVariable("RepackFolder");
|
|
|
- if(string.IsNullOrEmpty(repackRootFolder))
|
|
|
+ if (string.IsNullOrEmpty(repackRootFolder))
|
|
|
{
|
|
|
Console.WriteLine("The environment variable [RepackFolder] is missing, use default repack folder.");
|
|
|
//Using default repackFolder
|
|
@@ -196,8 +338,8 @@ namespace PackTool
|
|
|
{
|
|
|
Directory.CreateDirectory(repackRootFolder);
|
|
|
}
|
|
|
- var repackFolder = Path.Combine(repackRootFolder, productName);
|
|
|
- if(!Directory.Exists(repackFolder))
|
|
|
+ var repackFolder = Path.Combine(repackRootFolder, productName, DateTime.Now.ToString("yyyyMMdd"));
|
|
|
+ if (!Directory.Exists(repackFolder))
|
|
|
{
|
|
|
Directory.CreateDirectory(repackFolder);
|
|
|
}
|
|
@@ -219,10 +361,10 @@ namespace PackTool
|
|
|
{
|
|
|
File.Delete(repackFilePath);
|
|
|
}
|
|
|
- ZipFile.CreateFromDirectory(productFolder, repackFilePath);
|
|
|
+ CreateZipFileFromDir(productFolder, repackFilePath, excludeFolders.ToArray());
|
|
|
}
|
|
|
|
|
|
- private static void HandleCopy(Dictionary<string,string> argMap)
|
|
|
+ private static void HandleCopy(Dictionary<string, string> argMap)
|
|
|
{
|
|
|
//Copy the file/s
|
|
|
if (!argMap.TryGetValue("/rs", out var receivedSource))
|
|
@@ -256,7 +398,7 @@ namespace PackTool
|
|
|
CopyFiles(receivedSource, extractDest, repoName, productName, unzip);
|
|
|
}
|
|
|
|
|
|
- private static void HandleUpdateVersion(Dictionary<string,string> argMap)
|
|
|
+ private static void HandleUpdateVersion(Dictionary<string, string> argMap)
|
|
|
{
|
|
|
//Only update version.
|
|
|
if (!argMap.TryGetValue("/pn", out var productName))
|
|
@@ -277,13 +419,19 @@ namespace PackTool
|
|
|
UpdateVersion(productName, repoName, repoVersion);
|
|
|
}
|
|
|
|
|
|
- private static void HandleRepack(Dictionary<string,string> argMap)
|
|
|
+ private static void HandleRepack(Dictionary<string, string> argMap)
|
|
|
{
|
|
|
if (!argMap.TryGetValue("/pn", out var productName))
|
|
|
{
|
|
|
Console.WriteLine($"The [/pn] - product name argument is missing.");
|
|
|
return;
|
|
|
}
|
|
|
+ var excludFoldereNames = new List<string>();
|
|
|
+ if (argMap.TryGetValue("/ef", out var excludes))
|
|
|
+ {
|
|
|
+ excludFoldereNames.AddRange(excludes.Split(','));
|
|
|
+ }
|
|
|
+ Console.WriteLine($"Exclude folders: [{string.Join(',', excludFoldereNames)}]");
|
|
|
var attachTimestamp = true;
|
|
|
if (argMap.TryGetValue("/at", out var attachTimestampStr))
|
|
|
{
|
|
@@ -292,7 +440,7 @@ namespace PackTool
|
|
|
Console.WriteLine($"The attachTimestamp argument {attachTimestampStr} is not a bool value.");
|
|
|
}
|
|
|
}
|
|
|
- RepackProduct(productName, attachTimestamp);
|
|
|
+ RepackProduct(productName, excludFoldereNames.ToArray(), attachTimestamp);
|
|
|
}
|
|
|
|
|
|
|
|
@@ -324,6 +472,12 @@ namespace PackTool
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ var excludFoldereNames = new List<string>();
|
|
|
+ if (argMap.TryGetValue("/ef", out var excludes))
|
|
|
+ {
|
|
|
+ excludFoldereNames.AddRange(excludes.Split(','));
|
|
|
+ }
|
|
|
+
|
|
|
var unzip = false;
|
|
|
if (argMap.TryGetValue("/unzip", out var unzipStr))
|
|
|
{
|
|
@@ -342,10 +496,10 @@ namespace PackTool
|
|
|
Console.WriteLine($"The attachTimestamp argument {attachTimestampStr} is not a bool value.");
|
|
|
}
|
|
|
}
|
|
|
- RepackProduct(productName, attachTimestamp);
|
|
|
+ RepackProduct(productName, excludFoldereNames.ToArray(), attachTimestamp);
|
|
|
}
|
|
|
|
|
|
- private static void StartInno(string innoSourceFileName, string setupSource, string productName, string productVersion,string outputDir)
|
|
|
+ private static void StartInno(string innoSourceFileName, string setupSource, string productName, string productVersion, string outputDir)
|
|
|
{
|
|
|
var productRootFolder = Environment.GetEnvironmentVariable("ProductFolder");
|
|
|
if (string.IsNullOrEmpty(productRootFolder))
|
|
@@ -367,17 +521,17 @@ namespace PackTool
|
|
|
var projectSetupSource = Path.Combine(productFolder, setupSource);
|
|
|
var setupOutputDir = Path.Combine(productFolder, outputDir);
|
|
|
|
|
|
- if(!File.Exists(innoSourceFilePath))
|
|
|
+ if (!File.Exists(innoSourceFilePath))
|
|
|
{
|
|
|
Console.WriteLine("The inno setup source file does not exist.");
|
|
|
return;
|
|
|
}
|
|
|
- if(!Directory.Exists(projectSetupSource))
|
|
|
+ if (!Directory.Exists(projectSetupSource))
|
|
|
{
|
|
|
Console.WriteLine("The setup project source folder does not exist");
|
|
|
return;
|
|
|
}
|
|
|
- if(Directory.GetFiles(projectSetupSource).Length == 0)
|
|
|
+ if (Directory.GetFiles(projectSetupSource).Length == 0)
|
|
|
{
|
|
|
Console.WriteLine("No file in setup project source folder.");
|
|
|
return;
|
|
@@ -441,11 +595,11 @@ namespace PackTool
|
|
|
Directory.CreateDirectory(productFolder);
|
|
|
}
|
|
|
var target = Path.Combine(productFolder, deleteTraget);
|
|
|
- if(File.Exists(target))
|
|
|
+ if (File.Exists(target))
|
|
|
{
|
|
|
File.Delete(target);
|
|
|
}
|
|
|
- else if(Directory.Exists(target))
|
|
|
+ else if (Directory.Exists(target))
|
|
|
{
|
|
|
Directory.Delete(target, true);
|
|
|
}
|
|
@@ -498,7 +652,7 @@ namespace PackTool
|
|
|
mode = "full";
|
|
|
}
|
|
|
|
|
|
- Console.WriteLine($"Run in {mode} mode.");
|
|
|
+ Console.WriteLine($"Run in {mode} mode.");
|
|
|
switch (mode)
|
|
|
{
|
|
|
case "full":
|