24

I have a requirement where I need to zip some files after I build a solution file.

Could this be achieved automatically once I build my project in Release/Debug mode?

Matt
  • 25,467
  • 18
  • 120
  • 187
nishantcop
  • 977
  • 1
  • 8
  • 24

7 Answers7

27

Using powershell, only when doing Release build:
if $(ConfigurationName) == Release (powershell Compress-Archive -Path '$(TargetDir)*.dll', '$(TargetDir)*.pdb', '$(TargetDir)*.config' -DestinationPath '$(SolutionDir)PublishOutput\YourNameHere.zip' -Force)

It only zips the dll, pdb and config files.
-Force is used to overwrite the zip file on each build.

ArieKanarie
  • 944
  • 1
  • 15
  • 29
  • 3
    Unfortunately, Force doesn't create the output folder if its doesn't exist already. A slightly improved version: if $(ConfigurationName) == Debug (powershell.exe -Command $null=mkdir '$(SolutionDir)PublishOutput'; Compress-Archive -CompressionLevel Optimal -Path '$(TargetDir)*.dll', '$(TargetDir)*.pdb', '$(TargetDir)*.config' -DestinationPath '$(SolutionDir)PublishOutput\$(ProjectName).zip' -Force) – Tim Dec 19 '19 at 21:41
  • Add this command to the "Post-build event command line" box in the Visual Studio project Build Events properties. The initial "if" is required to be used only for the "Release" configuration because VS executes the command in all configurations. – Suncat2000 Mar 26 '20 at 12:27
11

Usually I don't put stuff like creating zip files, installers, NuGet packages etc. into my actual project.
Why? Because when I put it there, it gets executed each time I'm building the project in Visual Studio, for example when I'm debugging.
But zip files, installers etc. are only needed when I do a release, so I don't want to wait for them to be re-generated each time I press F5 in Visual Studio.

To make a release, I usually create a batch file that executes a MSBuild project file, which creates everything that's necessary to make a release.
IMO creating a ZIP file belongs into that MSBuild project file as well.

You can find all the information you need in these two previous answers by me:

Plus, here's an example MSBuild project file from one of my projects, which does the following:

  • build the project
  • run unit tests
  • create two release folders with binaries (one DLL and one .exe)
  • create two zip files, one for each of the folders with binaries
  • create a NuGet package for the DLL
  • create a ClickOnce setup for the .exe
  • automatically set the correct version number for everything

The great thing about this approach is that I can make a release, which includes everything I have just listed, with a single click (running a batch file).
Creating all this stuff takes some time, but as it's not part of the Visual Studio solution, it doesn't run each time I do a build in Visual Studio - I only execute it when I really need it.

Community
  • 1
  • 1
Christian Specht
  • 35,843
  • 15
  • 128
  • 182
  • 16
    Or use the much simpler approach of putting it in the post build event and only executing the steps needed for a release when running a Release build configuration... You can still execute this build from the command line, so there isn't really any disadvantage to using post build, as long as you implement it correctly. – Jason Williams Mar 29 '14 at 13:01
  • 3
    Sorry to resurrect this dead horse, but you could also just create a new target called Deploy. So basically, you'd have Debug, Release, and Deploy. Where Deploy is a variant of Release with the extra steps added. – code4life Dec 20 '17 at 14:24
  • My answer is based on my personal preferences, so it makes sense to mention them first, so people know why I’m doing it this way. If your personal preferences differ from mine, feel free to add another answer. – Christian Specht Sep 03 '18 at 08:42
4

Go to the properties of your project and in the 'Build Events' tab write your commands in the Post-Build event area. The commands there execute just like (or as) a Cmd batch file.

Also: there ara a few 'makros' available there, which may help referring to the project folders etc.. Check it out.

And, to add to Jason's comment, you can also call the batch file itself as the post-build command.

(One caveat about post-build events: They are executed after the build. But if you have CSC targets they are compiled after the build and after the post-build events. If you want to e.g.copy the output files of these CSC targets you need to do it in a post-compile event.)

TaW
  • 53,122
  • 8
  • 69
  • 111
  • 4
    +1. You will need a zip utility that you can call, such as 7zip, and then you just need to add the command line for 7zip to the post build script. You can work this out in isolation in a dos box and then copy the final commands into your post build event once it's working. – Jason Williams Mar 29 '14 at 13:03
  • @JasonWilliams - Is there no default zipping capability within Visual Studio? Having all developers have a 7zip or winzip and with it's path in the correct folder isn't scalable. – bschandramohan Jan 04 '16 at 06:32
  • 1
    @Chandra No, not built in to vs. However 7zip is a single exe file so you can easily check it in to source control and write your post build to use that specific location to guarantee that every dev has the right version of the tool to get a working and consistent build. – Jason Williams Jan 04 '16 at 17:05
  • 1
    @bschandramohan If you use PowerShell, the `Compress-Archive` command gives you zipping capability. PowerShell scripts can be launched from the build events. – Suncat2000 Jan 25 '21 at 15:30
1

This worked for me:

if $(ConfigurationName) == Debug (powershell -Command "Get-ChildItem -Path '$(TargetDir)publish' -Recurse|Compress-Archive -DestinationPath '$(SolutionDir)PublishOutput\$(ProjectName)-$(ConfigurationName).zip' -Force")
gpkarnik
  • 29
  • 1
  • 3
1
  1. Right Click on Project=> Select Properties

  2. Click on BuildEvent

  3. Add below Code in post-Build event command Line

    if exist $(AssemblyName).zip (  Del $(AssemblyName).zip)
    powershell.exe -command Compress-Archive -Path $(AssemblyName).dll, *dll -DestinationPath $(AssemblyName).zip
    
  4. It will Generate the zip file of all .dll in "bin/release" folder

Orace
  • 7,822
  • 30
  • 45
1

I could not get Build Events to work so I modified the MS Build configuration file - the *.csproj file. It's actually not black magic and documented by MS here: https://learn.microsoft.com/en-us/visualstudio/msbuild/build-process-overview?view=vs-2022

You have to Unload your project in VS, modify the *.csproj file (VS will load it automatically when you unload the project) and then Reload the project from Solution Explorer.

Here is a snipped from the Build target (in this case it only zips the application files):

<Target Name="Build">
  <CreateItem Include="app\**">
    <Output ItemName="ApplicationFiles" TaskParameter="Include" />
  </CreateItem>
  <Zip ZipFileName="out\$(AssemblyName).zip" WorkingDirectory="$(MSBuildProjectDirectory)\app" Files="@(ApplicationFiles)" />
</Target>

This Zip 'task' of MS Build seems to be much faster than the PowerShell Compress function.

Here is a list of available MS Build 'tasks':

https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-task-reference?view=vs-2022

There is also an MS Build task that will zip an entire directory including subdirectories, like so:

<ZipDirectory SourceDirectory="dist" DestinationFile="out\$(AssemblyName).zip" />
Martin Lisowski
  • 490
  • 1
  • 3
  • 10
0

Cross-platform zip for .Net Core projects:

<Project Sdk="Microsoft.NET.Sdk">
    ...
    <ItemGroup>
        <SourcesFiles Include="src/**/*.cs">
            <InProject>false</InProject>
        </SourcesFiles>
        <SourcesFiles Include="../shared/src/**/*.cs">
            <InProject>false</InProject>
            <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
            <CopyToOutputDirectory>Never</CopyToOutputDirectory>
        </SourcesFiles>
    </ItemGroup>
    <Target Name="ZipSources">
        <Zip Files="@(SourcesFiles)" OutputFilename="$(SourcesZipFile)"/>
    </Target>
    <UsingTask
        TaskName="Zip"
        TaskFactory="RoslynCodeTaskFactory"
        AssemblyFile="$(MSBuildToolsPath)/Microsoft.Build.Tasks.Core.dll" >
        <ParameterGroup>
            <Files ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
            <OutputFilename ParameterType="System.String" Required="true" />
        </ParameterGroup>
        <Task>
            <Using Namespace="System"/>
            <Using Namespace="System.IO"/>
            <Using Namespace="System.IO.Compression" />
            <Using Namespace="System.Linq" />
            <Code Type="Fragment" Language="cs"><![CDATA[
Log.LogMessage("Creating ZIP archive [OutputFilename: " + OutputFilename + ", Files: [\n\t" +
    Files.Select(it => it.GetMetadata("FullPath")).Aggregate((a, b) => a + ",\n\t" + b) + "\n]]");

string zipFile = Path.GetFullPath(OutputFilename);
Directory.CreateDirectory(Path.GetDirectoryName(zipFile));

using (Stream zipStream = new FileStream(zipFile, FileMode.Create, FileAccess.Write))
using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create))
{
    foreach (ITaskItem fileItem in Files) {
        string filename = fileItem.ItemSpec;
        string entryName = fileItem.GetMetadata("Link");
        if (entryName == string.Empty)
            entryName = fileItem.GetMetadata("RecursiveDir") + fileItem.GetMetadata("Filename") + fileItem.GetMetadata("Extension");
        //Log.LogMessage("entryName: " + entryName + ", filename: " + filename);

        using (Stream fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read))
        using (Stream fileStreamInZip = archive.CreateEntry(entryName).Open())
            fileStream.CopyTo(fileStreamInZip);
    }
}
            ]]></Code>
        </Task>
    </UsingTask>
ursa
  • 4,404
  • 1
  • 24
  • 38