For example after I build the Release version I want it name changed to (for example) MyApp-1.2.exe. After I build the next version I want the exe to be named MyApp-1.3.exe. I still want to be able to run the MyApp-1.2.exe.
I would rather not use any external tool(I know there are nAnt and nMaven) and do it in VS. IF its not possible than I bet nAnt is the better option for me.
You could do it also via MSBuild with the help of the GetAssemblyIdentity task:
<GetAssemblyIdentity
AssemblyFiles="$(MSBuildProjectDirectory)\src\MyApp\bin\MyApp.exe">
<Output
TaskParameter="Assemblies"
ItemName="AssemblyIdentities"/>
</GetAssemblyIdentity>
And then rename your .exe file:
<Copy
SourceFiles="$(MSBuildProjectDirectory)\src\MyApp\bin\MyApp.exe"
DestinationFiles="$(MSBuildProjectDirectory)\src\MyApp\bin\MyApp-(AssemblyIdentities.Version).exe"></Copy>
<Delete
Files="$(MSBuildProjectDirectory)\src\MyApp\bin\MyApp.exe"></Delete>
If you really want to do it in VS, you can (again) do it using the Post-build Command function (from Project properties -> Build Events).
Running a script or batch file that will copy the file and then rename it. I used to do it based on current date (not version) a few years back (not even sure why).
You can do virtually anything in the build events if you can create batch files or scripts.
Using a build system is IMHO preferable to this approach.
Related
A common practice when staging unmanaged libraries or resources is to add those resources as links to the project and set them to copy to the output directory.
As of Visual Studio 2013, this is handled quite well and even if they are referenced Library Foo which is then consumed by Application Bar, they will end up in Bar's output directory.
The same doesn't appear to be true for directories of files though.
In this case, my application appears to only copy this directory of files to the output directory on rebuild only. Then inevitably I'll do a couple build actions and I'll notice the Libs directory is empty again. Then do a Rebuild and cef.pak is back.
My solution to this was to employ a Build Step to copy the Libs directory manually to the output directory. Though I cannot find the proper macro to generically express that even though this build step is part of Foo, I want the files copied to the Bar's (e.g. StartUp Project in VS terms) output directory.
Update
Thank you everyone for the great answers and testing this. I should clarify that I still see this issue when there is an extra level of library in between. That is to say Application Bar referencing library Foo which references library Other which is the one w/ these linked files. In that case, when Other's linked files are set to copy to output directory, they seem to only make it on rebuilds. My solution to this is less than ideal which is to have Foo reference Other directly.
unless you rebuild/clean solution, VS does remove files from output folder.
so i believe you forgot to say that your program(or third party) does so.
First: this is an example for similar/exact bug.
download the solution and do the following steps:
rebuild the solution
open the output folder of Bar
the file cef.pak will be exist in Libs
press F5 -> execute Bar -> everything alright
execute again with F5 -> you will receive an file not found exception
if you repeat steps 1-5 you will receive the same behavior
The issue exist only if there is no changes in Bar and something(the exe / other process/etc) has deleted the file.
Second: I've already faced this issue.
when one of my team member reported this issue, then the answer he received was a kind of "it's not a bug it's a feature"....
As i know, there is no "magic button" to solve for this problem.
But there are several workarounds:
(as you did) adding pre/post build events. I recommend you to do this in new build configuration.
change the build output folder of Foo to Bar output folder.
add in your code a path replacement with if debug:
#if DEBUG
cefFilePath = <build output of foo>
#endif
put the file as embedded resource then extract it.
create an extension\external program to deal with this problem. each project the file keep the information about those file:
<ItemGroup>
<None Include="Libs\cef.pak">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
the application/plugin will do the job for you.
I can offer more solutions/workarounds, but I believe that you did the right thing -> build event.
edit:
I updated my example in the link. now the solution contains 3 workarounds example.
Tried doing same with VS2013. It worked quite well, everytime I change something in Bar's File, Run Foo (Startup Project), the latest file is copied there. I am just writing steps I followed, may be I am missing something to replicate or you missed a step. Please have a look:
Create two Projects Foo and Bar
Created a directory Libs in Bar
Added a text file "TextFile1.txt" in Libs
Right Click -> Properties of the file -> Copy to Output Directory = Copy Always
Added reference of Bar from Foo Project
Started reading the TextFile's text from Foo's Main method. Reading file from Bin\Debug\Libs directory.
Every time I press F5 I see the latest file get copied there.
Edit "Foo.csproj" (make sure the project is closed in VS) and add this add the bottom, in place of a commented out example that is already there in every new project file (use your own paths):
<Target Name="AfterBuild">
<Copy SourceFiles="Libs\cef.pak;Libs\file2.ext" DestinationFolder="..\..\Bar Solution\Bar Project\output" SkipUnchangedFiles="True" OverwriteReadOnlyFiles="True" />
</Target>
Edit "Bar.csproj" as well, just in case changing those files doesn't trigger Foo build:
<Target Name="BeforeBuild">
<Copy SourceFiles="..\..\Foo Solution\Foo Project\Libs\cef.pak;..\..\Foo Solution\Foo Project\Libs\file2.ext" DestinationFolder="output" SkipUnchangedFiles="True" OverwriteReadOnlyFiles="True" />
</Target>
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?
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.
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:
How to create a basic batch file and MSBuild project file
(the actual question there is about building an installer with WiX, but in the beginning I'm creating a MSBuild project file)
How to create a ZIP file with MSBuild Community Tasks
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.
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.)
This worked for me:
if $(ConfigurationName) == Debug (powershell -Command "Get-ChildItem -Path '$(TargetDir)publish' -Recurse|Compress-Archive -DestinationPath '$(SolutionDir)PublishOutput\$(ProjectName)-$(ConfigurationName).zip' -Force")
Right Click on Project=> Select Properties
Click on BuildEvent
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
It will Generate the zip file of all .dll in "bin/release" folder
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" />
As part of our development life cycle we have a number of process that we run against the C# source in our projects.
The processes are driven off a GUI that currently reads the *.csproj file to find the source files used within the project. This works fine.
We now have a new requirement to provide some validation processes that require a call out to a web-service. The web-service needs to be provided with some credentials that are project specific. Ideally we could enter and store these credentials within the *.csproj file but I don't see a means of extending it - is there?
We don't really want to introduce a new config. file just for these settings if we can help it. Is it possible to store information like this is the *.csproj file, if not is there any other place to put it.
thanks
The .csproj file is basically an MSBuild file, as such you can extend it with custom values. If you right-click on a project in Visual Studio and choose "Unload Project", the project will "grey out" and you can then right-click again and choose Edit [ProjectFileName].csproj. You can then add something similar to the following:
<PropertyGroup Label="Custom">
<Badger>1</Badger>
</PropertyGroup>
This should be persisted when the project is modified (i.e. files added/removed) and you can retrieve the values from the file, using the method of your choice.
VS projects support "project extensions". These are custom data stored directly in csproj/vbproj files. You can very easily read and write them even from VS. For example, the following VS macro writes such custom setting:
Dim proj As Project = DirectCast(DTE.ActiveSolutionProjects(0), Project)
proj.Globals.VariableValue("MySettingName1") = "My value1"
proj.Globals.VariablePersists("MySettingName1") = True
The following reads it back:
proj.Globals.VariableValue("MySettingName1").ToString
And the code in csproj file looks like:
<ProjectExtensions>
<VisualStudio>
<UserProperties MySettingName1="My value1" />
</VisualStudio>
</ProjectExtensions>
Of course, this is persisted and will not be overwritten by VS.
I know you dismiss it but the most obvious, and probably recommended, place is in the config file. Albeit encrypted.
One config file per project does for most cases and is not a large overhead imho.
For some reason, we have a script that creates batch files to XCOPY our compiled assemblies, config files, and various other files to a network share for our beta testers. We do have an installer, but some don't have the permissions required to run the installer, or they're running over Citrix.
If you vomited all over your desk at the mentions of XCOPY and Citrix, use it as an excuse to go home early. You're welcome.
The code currently has hundreds of lines like:
CreateScripts(basePath, "Client", outputDir, FileType.EXE | FileType.DLL | FileType.XML | FileType.CONFIG);
It used to be worse, with 20 int parameters (one per file type) representing whether or not to copy that file type to the output directory.
These hundreds of lines create upload/download batch files with thousands of XCOPY lines. In our setup projects, we can reference things like "Primary output from Client" and "Content Files from Client". I'd love to be able to do that programmatically from a non-setup project, but I'm at a loss.
Obviously MS does it, either using an API or by parsing the .csproj files. How would I go about doing this? I'm just looking for a way to get a list of files for any of the setup categories, i.e.:
Primary Output
Localized Resources
Content Files
Documentation Files
EDIT:
I have a setup project like Hath suggested, and it's halfway to what I'm looking for. The only problem keeping that from being a perfect solution is that multiple projects depend on the same assemblies being in their own folder, and the setup will only copy the file once.
Example:
Projects Admin, Client, and Server all rely on ExceptionHandler.dll, and Admin and Client both rely on Util.dll, while Server does not. This is what I'm looking for:
Admin
Admin.exe
Admin.exe.config
ExceptionHandler.dll
Util.dll
Client
Client.exe
Client.exe.config
ExceptionHandler.dll
Util.dll
Server
Server.exe
Server.exe.config
ExceptionHandler.dll
Since the referenced assemblies are all the same, what I get is this:
Admin
Admin.exe
Admin.exe.config
ExceptionHandler.dll
Util.dll
Client
Client.exe
Client.exe.config
Server
Server.exe
Server.exe.config
This causes a FileNotFoundException when either Client or Server can't find one of the two DLLs it's expecting.
Is there a setup property I'm missing to make it always copy the output, even if it's duplicated elsewhere in another project's output?
EDIT AGAIN: All referenced DLLs are set to "Copy Local", and always have been. I found a decent article on using NAnt and XSLT to grab the list of files, so that may be a possible solution as well, as neouser99 suggested.
ACCEPTED SOLUTION: I'm pretty much back where I started. All .exe and .dll outputs are put into a "bin" directory in the setup project, loosely packed. The other per-application folders contain shortcuts to the executable in that directory.
The difference now is, I'm going to add a custom action to the installer to use reflection, enumerate the dependencies for each executable output, and copy the .exe and .dll files to the separate directories. Bit of a pain, as I just assumed there was a way to programmatically detect what files would be included via some setup library.
why not use another setup project and just set the 'Package files' setting to As Loose uncompressed files (setup project->properties)? then share the folder.. or something.
edit:
I see, you have 3 folders for your outputs. but the setup project only detects the ExceptionHandler.dll and Util.dll once, so it will just pick the first folder and put it in there.
You could do a setup project for each project - bit annoying maybe..
You could manually add in the dll's to the projects that are missing the assembly's
either by adding in the File by 'add file' or 'add assembly' or 'add project output' if you have those projects in the same solution.. (I doubt that's the case though).
or just dump all of them into one output directory...
Although it's designed as a build tool, you might find NAnt to be extremely useful in what you are talking about. The tasks (build, copy, move, delete, etc.) that you can define allow for very fine-grained file lookups, up to general, full folders. If you also incorporate NAnt into your build process, I think you could find that it helps out in more ways then one.
Another approach that has worked for me in the past is to add the shared resource (Assembly, DLL or project) as a reference to each of the Admin, Server and Client projects. Then open the properties panel for the referenced item in each project and set "Copy Local" to true.
Now when you build the projects, each will have its own instance of the Assembly copied into its output folder.
This should also cause the shared components added in this manner to be replicated in each of the output folders in the setup package.
A completely different approach could be to set them up as symbolic links on the network share. A symbolic link is basically a short-cut where the file-system hides the fact that it is a short-cut, so all other applications actually believes that the file has been copied (http://en.wikipedia.org/wiki/NTFS_symbolic_link).
One advantage of this approach is that the file is updated immediately as the file changes and not only when you build your projects. So when you for instance save one of the config-files with a text-editor the update is applied immediately.
The following MSBuild script part can build your SLN file (you can replace it with .csproj) and will report a list of all projects that were build (Dlls, EXEs).
<MSBuild Projects="MySolution.sln" Targets="Clean; Rebuild" Properties="Configuration=$(BuildMode);">
<Output TaskParameter="TargetOutputs"
ItemName="AssembliesBuilt" />
</MSBuild>
Now, this doesn't really solve your problem, but it gets you a list of everything that was build. You also have copylocal, so you could probably just take AssembiesBuild and copy all DLL and .CONFIG files from there.
Example:
AssembliesBuild = c:\myproj\something1\build.dll
you'd go to c:\myproj\something1\ and simply search for all *.dll and *.config files and include them. You can do this pretty easily with MSBuild or powershell, if you have it installed. To output a XCOPY script from MSBuild, I think you'll need MSBuild contrib projct installed.
I run an ASP.NET website solution with a few other projects in it. I've known that MSBuild projects are capable of this, but is it the best way? Are they easy to create? Is nAnt, CruiseControl.NET or any other solution better?
When I build the site (using Web Deployment Projects), can I automate part of the build so that it does not copy certain folders from the project into the Release folder? For instance, I have folders with local search indexes, images and other content part of the folder, but I never need or upload those when deploying the project.
I'm also looking toward this type of solution to automatically increment build and version numbers.
Here's an example of a Web Deployment Project scripting this sort of task in the .wdproj file:
<Target Name="AfterBuild">
<!-- ============================ Script Compression============================ -->
<MakeDir Directories="$(OutputPath)\compressed" />
<Exec Command="java -jar c:\yuicompressor-2.2.5\build\yuicompressor-2.2.5.jar --charset UTF-8 styles.css -o compressed/styles.css" WorkingDirectory="$(OutputPath)" />
<Exec Command="move /Y .\compressed\* .\" WorkingDirectory="$(OutputPath)" />
<RemoveDir Directories="$(OutputPath)\sql" />
<Exec Command="c:\7zip-4.4.2\7za.exe a $(ZipName).zip $(OutputPath)\*" />
</Target>
This would allow you to delete a folder.
(I suspect that if you wanted to not have the folder copy over at all, the solution file would be the place to specify that, though I haven't had to use that.)
MaseBase, you can use Web Deployment Projects to build and package Web Sites. We do that all the time for projects with a web application aspect. After you assign a WDP to a Web Site, you can open up the .wdproj file as plain-text XML file. At the end is a commented section of MSBuild targets that represent the sequence of events that fire during a build process.
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.WebDeployment.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="BeforeMerge">
</Target>
<Target Name="AfterMerge">
</Target>
<Target Name="AfterBuild">
</Target>
-->
You can uncomment the targets you want (e.g. "AfterBuild") and insert the necessary tasks there to carry out your repeated post-build activities.
You can set the Build Action/Copy to Output Directory property on individual files (select the file and hit F4 to open the properties window) to control what happens to them during build, but not for folders. This could probably be automated with a (pre) build task if you don't want to do it manually.
Alternatively, you can exclude these folders from the project (right click and 'exclude from project'); they'll still be there ("show all files" in solution explorer), but they won't be included when building the project.
CruiseControl.NET solves a different problem (continuous integration) ... however, I've had great success with NAnt for specifically what you're asking. There's a learning curve, but once you get proficient you'll wonder how you ever got along w/o it.
In addition to #Fredrik's tip about setting project items to "Copy to Output Directory", you can also specify a post-build action in the project's properties in the Build tab and include CMD commands like copy.exe and move.exe.
We use FinalBuilder to automate a bunch of post build / pre build tasks. There's also a web interface so you can kick off builds (or push websites) by logging in to the web site and clicking a button.
http://www.finalbuilder.com/
Can't you edit the Web Deployment project's MSBuild file for it to do what you want?