Visual Studio. Publish project from command line - visual-studio

Is there a way to publish a web project in MS Visual Studio 2010 using CLI? I use DevEnv.exe /Build to build a project and it works fine, but I could not find option to Publish a project.
One other thing I want to mention. I am trying to publish web project NOT to the IIS directly. I have a location where I publish several projects and then build them automatically into NSIS bundle to be deployed.

From ASP.NET Web Deployment using Visual Studio: Command Line Deployment, you can use
msbuild myproject.csproj /p:DeployOnBuild=true /p:PublishProfile=MyPublishProfile
where MyPublishProfile is the profile name that you've already set up somewhere

What works best is to add following target to the project file:
<Target Name="AfterBuild">
<Message Text="Copying to Deployment Dir:" />
<Copy SourceFiles="#(Content)" DestinationFolder="..\XXX\%(Content.RelativeDir)" />
<CreateItem Include="$(OutputPath)\*">
<Output TaskParameter="Include" ItemName="Binaries"/>
</CreateItem>
<Copy SourceFiles="#(Binaries)" DestinationFolder="..\XXX\bin" />
</Target>
This way, whenever project got build (from command line or from IDE) it automatically get deployed to specified folder. Thank you everybody for pointing me to right direction.

The /t:publish switch is for ClickOnce applications only, it's not applicable to web projects. Hence the error saying it's unpublishable. :)

#RobKent As https://stackoverflow.com/a/2775437/21233364
using
<Target Name="AfterBuild" Condition=" '$(Configuration)' == 'Release' ">
<Message Text="Copying to Deployment Dir:" />
<Copy SourceFiles="#(Content)" DestinationFolder="..\XXX\%(Content.RelativeDir)" />
<CreateItem Include="$(OutputPath)\*">
<Output TaskParameter="Include" ItemName="Binaries"/>
</CreateItem>
<Copy SourceFiles="#(Binaries)" DestinationFolder="..\XXX\bin" />
</Target>
you can have publish only on release compiling.

Related

What is the right macro for platform?

I'm trying to write a pre-build event script that must be able to adapt the the build platform (Windows / Mac / Azure DevOps image) in my case.
So far I have found the $(Platform) macro but it returns me AnyCPU
What I need is Darwin or Win32/Win64, and I can't find it in the documentation.
Is there such a thing ?
Thanks in advance.
EDIT :
For Azure DevOps there is Agent.OS and Agent.OSArchitecture
I found a solution. It was as easy as moving from a build event to a MS Build copy file task.
Inside this task, you can have a condition on the OS.
For Windows:
<Target Name="CopyWindowsRTELogConfigFile" BeforeTargets="PreBuildEvent" Condition="'$(OS)' == 'Windows_NT'">
<ItemGroup>
<RTELogWindowsConfigurationJson Include="$(ProjectDir)Configuration\Windows_NT\$(ConfigurationName)\specific.rtelog.appsettings.json" />
</ItemGroup>
<Copy SourceFiles="#(RTELogWindowsConfigurationJson)" DestinationFolder="$(ProjectDir)" />
</Target>
And Mac:
<Target Name="CopyDarwinRTELogConfigFile" BeforeTargets="PreBuildEvent" Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))'">
<ItemGroup>
<RTELogWindowsConfigurationJson Include="$(ProjectDir)Configuration\Darwin\$(ConfigurationName)\specific.rtelog.appsettings.json" />
</ItemGroup>
<Copy SourceFiles="#(RTELogWindowsConfigurationJson)" DestinationFolder="$(ProjectDir)" />
</Target>

Azure: Include unreferenced DLLs when publish

I was struggling to publish my project because some DLLs were missing. After some investigations, I found what I was looking for:
http://sedodream.com/2010/05/01/WebDeploymentToolMSDeployBuildPackageIncludingExtraFilesOrExcludingSpecificFiles.aspx
Here my code:
<Target Name="CustomCollectFiles">
<Message Text="Publishing unreferenced DLLs" Importance="High" />
<ItemGroup>
<_CustomFiles Include="$(UnreferencedDlls)" />
<FilesForPackagingFromProject Include="%(_CustomFiles.Identity)">
<DestinationRelativePath>bin\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
</FilesForPackagingFromProject>
</ItemGroup>
</Target>
It is working fine now but only when I publish on local. When I try to publish on Azure, these same DLLs are missing. So I tried to add the following line:
<DestinationRelativePath>obj\Release\Package\PackageTmp\bin\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
Because when I publish on azure, the Output says:
Copying all files to temporary location below for package/publish:
obj\Release\Package\PackageTmp.
But still, the DLLs are missing and I have no idea how to add them when I'm publishing on Azure.
I find a solution. The first one add the DLLs when I deploy on my local machine and the second do the same thing but when I deploy the app on Azure.
<PropertyGroup>
<!-- Publish on the FILE SYSTEM -->
<CopyAllFilesToSingleFolderForPackageDependsOn>
CustomCollectFiles;
$(CopyAllFilesToSingleFolderForPackageDependsOn);
</CopyAllFilesToSingleFolderForPackageDependsOn>
<!-- Publish on AZURE: Web Deploy -->
<CopyAllFilesToSingleFolderForMSDeployDependsOn>
CustomCollectFiles;
$(CopyAllFilesToSingleFolderForMSDeployDependsOn);
</CopyAllFilesToSingleFolderForMSDeployDependsOn>
</PropertyGroup>

Force Visual Studio to rebuild on Start Debugging

This is very different compared to How to tell visual studio to rebuild every time I make a change?
The issue is that I have modified a csproj that is not referenced a project (because it's run-time dependency).
So for example, in my csproj file I have:
<Import Project=".\UnreferencedProjects-Developer.targets" />
In my .targets file, I have:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="BuildDependencyForDevelopers" AfterTargets="Build">
<Message Text="========================================" />
<Message Text="Developer Building Unreferenced Projects" />
<Message Text="========================================" />
<!--MSBuild Projects="$(ProjectToBuild)"-->
<MSBuild Projects="../OtherProject/OtherProject.csproj">
<Output ItemName="ProjectOutputs" TaskParameter="TargetOutputs"/>
</MSBuild>
<Message Text="#(ProjectOutputs)"/>
<Message Text="=======================================" />
<Message Text="Developer Copying Unreferenced Projects" />
<Message Text="=======================================" />
<Copy SourceFiles="$(ProjectDir)\..\OtherProject\bin\$(Configuration)\OtherProject.dll" DestinationFolder="$(OutDir)" ContinueOnError="true"/>
<Copy SourceFiles="$(ProjectDir)\..\OtherProject\bin\$(Configuration)\OtherProject.pdb" DestinationFolder="$(OutDir)" ContinueOnError="true"/>
<Message Text="=============================================" />
<Message Text="Developer Finished with Unreferenced Projects" />
<Message Text="=============================================" />
</Target>
</Project>
The issue is that the .targets. file seems to only execute on a manual build/rebuild, and not on a Start Debugging.
All Configurations are set to Build. Options -> Project and Solutions -> Build and Run -> On Run, when projects are out of date: Always build.
I think the issue is that because the project is unreferenced, when I Start Debugging there are no out of date projects so it launches without a build/rebuild. How do I force it to literally always (re)build.
Add to project:
<PropertyGroup><DisableFastUpToDateCheck>true</DisableFastUpToDateCheck></PropertyGroup>
Keyword here is "fast update check": Visual Studio uses it when you debug to avoid even starting up msbuild.
Related topic: MSBuild Target that always runs when clicking build in VS2013
Background info: https://msdn.microsoft.com/library/ms171468%28v=vs.110%29.aspx
If you start debugging in the in Visual Studio UI (either by choosing the F5 key or by choosing Debug, Start Debugging on the menu bar), the build process uses a fast update check to improve performance. In some cases where customized builds create files that get built in turn, the fast update check does not correctly identify the changed files. Projects that need more thorough update checks can turn off the fast checking by setting the environment variable DISABLEFASTUPTODATECHECK=1. Alternatively, projects can set this as an MSBuild property in the project or in a file the project imports.

MSBuild projects with different build-configs, without using sln

Related
I have two projects in my VS solution, BookApp.Web and BookApp.Domain.
BookApp.Web references BookApp.Domain.
BookApp.Web has the following build configurations: debug, staging, prod-eu, prod-us and prod-as. We have three datacenters for production and a staging environment.
BookApp.Domain so far only has two build configurations, debug.
When building the solution from within Visual Studio, I can use the build configurator to make sure that no matter what build config is selected for the Web project, the debug config is always used for the Domain project.
However, when building with MSBuild on my continuous integration server, things go wrong. I use this in my rollout.msbuild file:
<MSBuild Projects="BookApp.Web\BookApp.Web.csproj" Properties="Configuration=Prod-us" />
When I run this, MSBuild expects all dependent projects to have the same build configuration. Since that's not the case (and shouldn't be IMO), it fails with this error message:
The OutputPath property is not set for project 'BookApp.Domain.csproj'. Please check to make sure that you have specified a valid combination of Configuration and Platform for this project. Configuration='Prod-us' Platform='AnyCPU'.
An answer to a related question suggests creating separate .sln solutions for each build configuration and running that with MSBuild. To me that doesn't sound like a good idea.
Copying all the build configurations to the Domain project is also not ideal.
Is there a better way of telling MSBuild to use different build configs?
Have a look at this answer it explains how the configurations are passed from project to Project through the MSBuild Task and Using the MetaData of the configuration to pass the desired configuration for the target project
here
UPDATE
I created a Solution with a class library(Sample.Domain) and ConsoleApplication(SampleApp.Console). I added two more configurations to the SamplApp.Console: prod-us;prod-eu, Sample.Domain remained with debug;release.
I then Changed the csproj file of the ConsoleApplication, like so:
ProjectReferences
<!--<ItemGroup>
<ProjectReference Include="..\Sample.Domain\Sample.Domain.csproj">
<Project>{73e8a7fd-0a24-47c5-a527-7601550d4b92}</Project>
<Name>Sample.Domain</Name>
</ProjectReference>
</ItemGroup>-->
<ItemGroup>
<ProjectReference Include="..\Sample.Domain\Sample.Domain.csproj" >
<Targets>Build</Targets>
</ProjectReference>
</ItemGroup>
Added a switch case on the configuration passed to MSBuild, to configure some properties for Outputfiles and reference files:
<Choose>
<When Condition="'$(Configuration)' != 'Debug'">
<PropertyGroup>
<OutputProperty>$(OutputPath)\$(Configuration)</OutputProperty>
<FileCopy>$(OutputProperty)</FileCopy>
</PropertyGroup>
</When>
<Otherwise>
<PropertyGroup>
<OutputProperty>$(OutputPath)</OutputProperty>
<FileCopy>$(OutputProperty)</FileCopy>
</PropertyGroup>
</Otherwise>
</Choose>
Created a Target to switch the Configuration passed to MSBuild, so that Debug will pass Debug to Sample.Domain, everything else it will pass Release
<Target Name="MultiConfiguration" >
<CreateProperty Value="Debug">
<Output TaskParameter="Value" PropertyName="LibConfiguration" Condition="'$(Configuration)' == 'Debug'"/>
</CreateProperty>
<CreateProperty Value="Release">
<Output TaskParameter="Value" PropertyName="LibConfiguration" Condition="'$(Configuration)' != 'Debug' "/>
</CreateProperty>
</Target>
The Build Target uses the Properties we have added so the Output and Copy of references files will have the right values according to Configuration value
<!--Build Process-->
<Target Name="Build" DependsOnTargets="Clean;MultiConfiguration;ComputeProjectReference" >
<Csc Sources="#(Compile)" References="#(NewAssemblies)" TargetType="exe" OutputAssembly="$(OutputProperty)\$(AssemblyName).exe"/>
</Target>
<Target Name="ComputeProjectReference" Inputs="#(ProjectReference)" Outputs="%(ProjectReference.Identity)__Forced">
<MSBuild Projects="#(ProjectReference)" Targets="%(ProjectReference.Targets)" Properties="Configuration=$(LibConfiguration);Platform=AnyCPU;OutputPath=bin\$(LibConfiguration)">
<Output TaskParameter="TargetOutputs" ItemName="ResolvedProjectReferences"/>
</MSBuild>
</Target>
<Target Name="AfterProjectReference" AfterTargets="ComputeProjectReference">
<CreateItem Include="#(ResolvedProjectReferences)">
<Output TaskParameter="Include" ItemName="CopyFiles" />
</CreateItem>
<Copy SourceFiles="#(CopyFiles)" DestinationFolder="$(FileCopy)" SkipUnchangedFiles="false" />
<ItemGroup>
<NewAssemblies Include="$(OutputProperty)\%(CopyFiles.FileName)%(CopyFiles.Extension)" />
</ItemGroup>
</Target>
To call the Debug configuration is done like this
msbuild SampleApp.Console.csproj
To call (Release;prod-us;prod-eu;...) is done like this
msbuild SampleApp.Console.csproj /p:Configuration="prod-us" /p:OutputPath="bin"
I'm sure it can be optimized, and might be some way easier, but it works.

Web Setup replacement to create MSI in VS2012?

I have a project in VS2010 that uses Web Setup projects for deployment. I'm now looking into migrating it to VS2012 and have to find a replacement setup routine.
The requirements I have:
One-step build to create a deploy package/installer on a dev machine.
Setup program/routine that can be run on the server - without Visual Studio available.
No direct interaction between Visual Studio and the server. I have to copy the setup files over through an RDP session.
Setup of web applications (MVC) and Windows Services, preferably bundled in one single installer (new requirement currently not solve din Web Setup project).
Possibility to run EF Migrations as part of setup (currently done through a custom action).
Where should I start? Should I look into the improved publishing features in VS2012? Should I look at Wix? Something else?
Looking deeper into Visual Studio 2012 and trying to work with it the way it was intended, instead of against it, we ended up using web deploy packages. It doesn't create an MSI file, but instead a zip file that can be easily imported into IIS on the target machine.
The Windows service project was added as a reference to the web site project. That way the binaries for the service are included in the bin directory of the web site. The migrate.exe file from Entity framework was added as a link from the bin directory which means it is deployed too.
Finally we added a project.wpp.targets file to the project that runs the required commands to install and start the service and to get the service's config file included in the deploy. This worked for us, but is not really that elegant (e.g. the install paths of the site for different configurations is hard coded).
The project.wpp.targets file:
<?xml version="1.0" encoding="utf-8" ?>
<!--
*** WARNING ***
This file is cached by visual studio and changes won't take effect until
visual studio is restarted. When editing this file, it is better to run the
build step for packaging from the command line (a VS command prompt).
There are some problems with dependencies not being correctly identified that
way, but at least the archive.xml file can be verified from the command prompt.
msbuild orderportal.csproj /t:package /p:Configuration=SysTest /p:DeployOnBuild=true;DeployTarget=Package
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<IncludeRunMigrations>TRUE</IncludeRunMigrations>
<AfterAddIisSettingAndFileContentsToSourceManifest Condition="'$(AfterAddIisSettingAndFileContentsToSourceManifest)'==''">
$(AfterAddIisSettingAndFileContentsToSourceManifest);
RunMigrations;
ServiceInstall;
</AfterAddIisSettingAndFileContentsToSourceManifest>
<IncludeServiceInstall>TRUE</IncludeServiceInstall>
<BeforeAddContentPathToSourceManifest Condition="'$(BeforeAddContentPathToSourceManifest)' == ''">
$(BeforeAddContentPathToSourceManifest);
ServiceUnInstall;
</BeforeAddContentPathToSourceManifest>
<DeploymentDir Condition="'$(Configuration)'=='SysTest' AND '$(DeploymentDir)'==''">c:\inetpub\wwwroot\SysTest\</DeploymentDir>
<DeploymentDir Condition="'$(Configuration)'=='IntTest' AND '$(DeploymentDir)'==''">c:\inetpub\wwwroot\IntTest\</DeploymentDir>
<DeploymentDir Condition="'$(Configuration)'=='Prod' AND '$(DeploymentDir)'==''">c:\inetpub\wwwroot\</DeploymentDir>
<CopyAllFilesToSingleFolderForPackageDependsOn>
IncludeServicesAppConfig;
$(CopyAllFilesToSingleFolderForPackageDependsOn);
</CopyAllFilesToSingleFolderForPackageDependsOn>
</PropertyGroup>
<Target Name="RunMigrations" Condition="'$(IncludeRunMigrations)' == 'TRUE'">
<Message Text="Adding migration running"/>
<ItemGroup>
<MsDeploySourceManifest Include="runCommand">
<path>$(DeploymentDir)bin\migrate.exe /startupdirectory:$(DeploymentDir)bin Topsi.Core.dll /startUpConfigurationFile:$(DeploymentDir)web.config</path>
<waitAttempts>1</waitAttempts>
<waitInterval>60000</waitInterval>
<dontUseCommandExe>true</dontUseCommandExe>
<AdditionalProviderSettings>waitInterval;waitAttempts;dontUseCommandExe</AdditionalProviderSettings>
</MsDeploySourceManifest>
</ItemGroup>
</Target>
<Target Name="ServiceUnInstall" Condition="'$(IncludeServiceInstall)' == 'TRUE'">
<Message Text="Adding service uninstall" />
<ItemGroup>
<MsDeploySourceManifest Include="runCommand">
<path>net stop "Topsi Schedule Service $(Configuration)"</path>
<waitAttempts>1</waitAttempts>
<waitInterval>60000</waitInterval>
<dontUseCommandExe>true</dontUseCommandExe>
<AdditionalProviderSettings>waitInterval;waitAttempts;dontUseCommandExe</AdditionalProviderSettings>
</MsDeploySourceManifest>
<MsDeploySourceManifest Include="runCommand">
<path>C:\Windows\Microsoft.NET\Framework\v4.0.30319\installutil.exe /u $(DeploymentDir)bin\Topsi.Services.exe</path>
<waitAttempts>1</waitAttempts>
<waitInterval>60000</waitInterval>
<dontUseCommandExe>true</dontUseCommandExe>
<AdditionalProviderSettings>waitInterval;waitAttempts;dontUseCommandExe</AdditionalProviderSettings>
</MsDeploySourceManifest>
</ItemGroup>
</Target>
<Target Name="ServiceInstall" Condition="'$(IncludeServiceInstall)' == 'TRUE'">
<Message Text="Adding service install"/>
<ItemGroup>
<MsDeploySourceManifest Include="runCommand">
<path>C:\Windows\Microsoft.NET\Framework\v4.0.30319\installutil.exe $(DeploymentDir)bin\Topsi.Services.exe</path>
<waitAttempts>1</waitAttempts>
<waitInterval>60000</waitInterval>
<dontUseCommandExe>true</dontUseCommandExe>
<AdditionalProviderSettings>waitInterval;waitAttempts;dontUseCommandExe</AdditionalProviderSettings>
</MsDeploySourceManifest>
<MsDeploySourceManifest Include="runCommand">
<path>net start "Topsi Schedule Service $(Configuration)"</path>
<waitAttempts>1</waitAttempts>
<waitInterval>60000</waitInterval>
<dontUseCommandExe>true</dontUseCommandExe>
<AdditionalProviderSettings>waitInterval;waitAttempts;dontUseCommandExe</AdditionalProviderSettings>
</MsDeploySourceManifest>
</ItemGroup>
</Target>
<Target Name="IncludeServicesAppConfig">
<ItemGroup>
<_CustomFiles Include="..\Services\bin\$(Configuration)\Topsi.Services.exe.config">
<DestinationRelativePath>%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
</_CustomFiles>
<FilesForPackagingFromProject Include="%(_CustomFiles.Identity)">
<DestinationRelativePath>bin\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
</FilesForPackagingFromProject>
</ItemGroup>
</Target>
</Project>

Resources