I have the following situation:
A SQL project in which i have a nuget package installed. This package (merely a PS script) is responsible for unpacking the DACPACs that the DB requires, using references to paths relative to the solution folder (to find the packages/ and dacpacs/ folders and to browse through the projects which it extracts from the .sln file). This is called as a pre-build event.
When building the entire solution, the $(SolutionDir) is defined, as expected (locally and ADO).
When building the test project, the $(SolutionDir) is either '' or '*Undefined*'. Again, as expected, because msbuild has no knowledge about the solution when building a single project. I can live with this caveat locally, no problem.
The question is this: is there something "magical" out there that i can use to make this work in Azure DevOps?
I can try various hacks, if anyone is aware of such methods, although i would like a clean solution.
Tried so far:
1) Adding the following PropertyGroup:
<PropertyGroup>
<SolutionDir Condition="'$(SolutionDir)' == '' Or '$(SolutionDir)' == '*Undefined*'">.\</SolutionDir>
</PropertyGroup>
to the test project.
2) Following these sugestions: Prebuild event in Visual Studio replacing $(SolutionDir) with *Undefined*
No effect.
If your folder structure is something like:
1.The Azure Devops Repos contains the solution folder(where exists the xx.sln file).
2.And those projects are under same solution folder.
You can try my script:
<PropertyGroup>
<ProjectFolder>$([System.IO.Directory]::GetParent($(ProjectDir)))</ProjectFolder>
<MySolutionDir>$([System.IO.Directory]::GetParent($(ProjectFolder)))\</MySolutionDir>
</PropertyGroup>
The $(MySolutionDir) represents the path where your sln file and project folders exists. Same like $(SolutionDir), it also has the \. So it's format looks like SomePath\.
And it's recommended to insert my script above the script of PreBuild event. Something like:
<PropertyGroup>
<ProjectFolder>$([System.IO.Directory]::GetParent($(ProjectDir)))</ProjectFolder>
<MySolutionDir>$([System.IO.Directory]::GetParent($(ProjectFolder)))\</MySolutionDir>
It's recommended to add my script and PreBuildEvent in same propertyGroup, and mine should be in the first.
<PreBuildEvent>echo $(MySolutionDir)</PreBuildEvent>
</PropertyGroup>
Edit1:
You can also add condition on that:
<PropertyGroup>
<ProjectFolder>$([System.IO.Directory]::GetParent($(ProjectDir)))</ProjectFolder>
<MySolutionDir>$([System.IO.Directory]::GetParent($(ProjectFolder)))\</MySolutionDir>
<SolutionDir Condition="xxxx">$(MySolutionDir)</SolutionDir>
It's recommended to add my script and PreBuildEvent in same propertyGroup, and mine should be in the first.
<PreBuildEvent>echo $(MySolutionDir)</PreBuildEvent>
</PropertyGroup>
Edit2:
Hmm, I now can reproduce the issue on my side. It's quite a strange behavior and I'm not sure about the root cause of this one. But a quick workaround is to create a new PropertyGroup to insert our custom script instead inserting it into existing PropertyGroup from default template:
It used to be:
....
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">11.0</VisualStudioVersion>
<!-- Default to the v11.0 targets path if the targets file for the current VS version is not found -->
<SSDTExists Condition="Exists('$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\SSDT\Microsoft.Data.Tools.Schema.SqlTasks.targets')">True</SSDTExists>
<VisualStudioVersion Condition="'$(SSDTExists)' == ''">11.0</VisualStudioVersion>
<ProjectFolder>$([System.IO.Directory]::GetParent($'(ProjectDir)'))</ProjectFolder>
<ParentFolder>$([System.IO.Directory]::GetParent($'(ProjectFolder)'))\</ParentFolder>
<SolutionDir Condition=" '$(SolutionDir)' == '' Or '$(SolutionDir)' == '*Undefined*' ">$(ParentFolder)</SolutionDir>
<PreBuildEvent>echo $(SolutionDir)</PreBuildEvent>
</PropertyGroup>
<Import Condition="'$(SQLDBExtensionsRefPath)' != ''" Project="$(SQLDBExtensionsRefPath)\Microsoft.Data.Tools.Schema.SqlTasks.targets" />
<Import Condition="'$(SQLDBExtensionsRefPath)' == ''" Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\SSDT\Microsoft.Data.Tools.Schema.SqlTasks.targets" />
<ItemGroup>
<Folder Include="Properties" />
</ItemGroup>
<ItemGroup>
<Build Include="test.sql" />
</ItemGroup>
<PropertyGroup>
<PreBuildEvent>echo $(SolutionDir)</PreBuildEvent>
</PropertyGroup>
</Project>
Now change it to be:
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">11.0</VisualStudioVersion>
<!-- Default to the v11.0 targets path if the targets file for the current VS version is not found -->
<SSDTExists Condition="Exists('$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\SSDT\Microsoft.Data.Tools.Schema.SqlTasks.targets')">True</SSDTExists>
<VisualStudioVersion Condition="'$(SSDTExists)' == ''">11.0</VisualStudioVersion>
</PropertyGroup>
<Import Condition="'$(SQLDBExtensionsRefPath)' != ''" Project="$(SQLDBExtensionsRefPath)\Microsoft.Data.Tools.Schema.SqlTasks.targets" />
<Import Condition="'$(SQLDBExtensionsRefPath)' == ''" Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\SSDT\Microsoft.Data.Tools.Schema.SqlTasks.targets" />
<ItemGroup>
<Folder Include="Properties" />
</ItemGroup>
<ItemGroup>
<Build Include="test.sql" />
</ItemGroup>
<PropertyGroup>
<ProjectFolder>$([System.IO.Directory]::GetParent($(ProjectDir)))</ProjectFolder>
<ParentFolder>$([System.IO.Directory]::GetParent($(ProjectFolder)))\</ParentFolder>
<SolutionDir Condition=" '$(SolutionDir)' == '' Or '$(SolutionDir)' == '*Undefined*' ">$(ParentFolder)</SolutionDir>
<PreBuildEvent>echo $(SolutionDir)</PreBuildEvent>
</PropertyGroup>
Also, remove the extra ' in $(ProjectDir). It should be $(ProjectDir) instead of $'(ProjectDir)' and $'(ProjectFolder)'. I also see you have two PreBuildEvent properties, just keep the one in our custom script. After above steps, you project now works well on my side:
Related
I want to provide variables for common path in hint path in .cspoj after installing nuget package.
Example:
After installing myNuget package in cs poject, few deliverables are added to project reference.
Unload the project, and edit the .csproj.
observe the deliverable hint path : ..\packages\myNuget1.0.0\lib\net46\sample.dll
Here instead of ..\packages\myNuget1.0.0\lib\net46\sample.dll path I want it as $(MyDLLSPath)\sample.dll.
I have no idea how to create these varibles.
I have .nuspec, .props, .targets and .nupkg file.
I'm trying to create properties for paths in .targets file see below, but still issue is not resolved:
<?xml version="1.0" encoding="utf-8"?>
<Project
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<UserTargetsPath>$(MSBuildProjectFullPath).user</UserTargetsPath>
</PropertyGroup>
<PropertyGroup>
<SDKInstallPath Condition=" '$(SDKInstallPath)' == ''">$(MSBuildThisFileDirectory)..\lib\net46</SDKInstallPath>
<SetupPath>$(SDKInstallPath)\Sample.dll</SetupPath>
<SDKExtDir Condition=" '$(SDKExtDir)' == ''">$(SDKInstallPath)</SDKExtDir>
</PropertyGroup>
<UsingTask TaskName="ResolveReferences" AssemblyFile="$(SDKInstallPath)\Intergraph.Setup.dll"/>
<ItemGroup>
<MyExtension Include="$(MyExtension)" Condition=" '$(MyExtension)' != '' " />
</ItemGroup>
<PropertyGroup>
<ResolveMyExtensionReferencesDependsOn>
PrepareForBuild
</ResolveMyExtensionReferencesDependsOn>
</PropertyGroup>
<Target
Name="ResolveMyExtensionReferences"
DependsOnTargets="$(ResolveMyExtensionReferencesDependsOn)"
Condition=" '#(MyExtension)' != ''">
<CreateProperty Condition=" '$(MyExtensionSearchPaths)' == '' " Value="
$(ReferencePaths);
{HintPathFromItem};
{RawFileName};
$(SDKExtDir)
">
<Output TaskParameter="Value" PropertyName="MyExtensionSearchPaths" />
</CreateProperty>
<ResolveReferences
MyReferences="#(MyExtension)"
SearchPaths="$(MyExtensionSearchPaths)"
SearchFilenameExtensions=".dll">
<Output TaskParameter="ResolvedMyReferences" ItemName="_AllResolvedMyExtensionPaths" />
</ResolveReferences>
<!-- Remove duplicate extension items that would cause build errors -->
<RemoveDuplicates Inputs="#(_AllResolvedMyExtensionPaths)">
<Output TaskParameter="Filtered" ItemName="_ResolvedMyExtensionPaths" />
</RemoveDuplicates>
</Target>
</Project>
Thanks in advance!
Our company uses a nuget package to distribute the Stylecop ruleset. This nuget package has a props file which I have modified to look like this:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<LinuxWarningFile>~/.nuget/packages/ourcompany.rulesets/0.0.0.0/content/OurCompanyRulesAsWarning.ruleset</LinuxWarningFile>
<IsLinux Condition = "Exists($(LinuxWarningFile))">true</IsLinux>
<IsLinux Condition = "$(IsLinux) == ''">false</IsLinux>
<IsWindows Condition = "$(IsLinux) == false">true</IsWindows>
<IsWindows Condition = "$(IsLinux) == true">false</IsWindows>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' And '$(IsLinux)'">
<CodeAnalysisRuleSet>~/.nuget/packages/ourcompany.rulesets/0.0.0.0/content/OurCompanyRulesAsWarning.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' And '$(IsWindows)'">
<CodeAnalysisRuleSet>$(UserProfile)\.nuget\packages\ourcompany.rulesets\0.0.0.0\content\OurCompanyRulesAsWarning.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' And '$(IsLinux)'">
<CodeAnalysisRuleSet>~/.nuget/packages/ourcompany.rulesets/0.0.0.0/content/OurCompanyRulesAsError.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' And '$(IsWindows)'">
<CodeAnalysisRuleSet>$(UserProfile)\.nuget\packages\ourcompany.rulesets\0.0.0.0\content\OurCompanyRulesAsError.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
</Project>
(Obviously during package creation the versioning is corrected and this is all working perfectly).
On Azure Devops:
It finds the correct ruleset when the Pool is set to Windows Build.
It cannot find the ruleset when the Pool is set to Linux Build.
The error: Warning MSB3884: Could not find rule set file ".nuget\packages\ourcompany.rulesets\1.0.20230.1\content\OurCompanyRulesAsError.ruleset".
given that it is leaving out the ~, i think that it has not found the file at the path given in the first logical block, has evaluated IsLinux to false and IsWindows to true and is using the path that includes the UserProfile which does not exist on the linux build.
From examining this document (https://learn.microsoft.com/en-us/nuget/reference/nuget-config-file), I think the default paths for the nuget packages are:
The location of the default global packages folder. The default is %userprofile%\.nuget\packages (Windows) or ~/.nuget/packages (Mac/Linux).
How can I fix the props file so that it can find the rulesets for a Linux build please?
Have you considered using the MSBuildThisFileDirectory property to avoid having to find the path? If the .props file and .ruleset files are in the same package you can use MSBuildThisFileDirectory and a relative path.
Assuming the .props file is also in a sub-directory of the package (.ruleset looks to be in a content subdirectory):
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)/../content/OurCompanyRulesAsWarning.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)/../content/OurCompanyRulesAsError.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
</Project>```
I have created a test project based on .Net Core 2 and wrote some NUnit test cases. After installing necessary NuGet packages i.e. NUnit3TestAdapter, I was able to see all test cases in "Test Explorer" and able to execute those. Now, when I looked into the project directory, I found that it's creating "obj" folder and some json files in it. So I tried to change the path of "obj" folder by modifying ".csproj" file. I provided some different path in the parameter "BaseIntermediateOutputPath" and that way, I was able to get rid of "obj" folder. The reason for providing different path was, I wanted to keep json files separate from source code.
However, after modifying that I am not able to see or execute any test cases from Test Explorer.
Is this a Microsoft bug?
Is any packages having dependency on "obj" folder?
P.S.
I am using "NUnit" and "NSubstitute" packages for my test project.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<TargetFramework>netcoreapp2.0</TargetFramework>
<OutputPath>..\..\build\$(Configuration)\UnitTests\</OutputPath>
<BaseIntermediateOutputPath>..\..\work\$(MSBuildProjectName)\</BaseIntermediateOutputPath>
</PropertyGroup>
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
<ItemGroup>
<PackageReference Include="Castle.Core" Version="4.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
<PackageReference Include="NSubstitute" Version="2.0.3" />
<PackageReference Include="NUnit" Version="3.8.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.8.0" />
</ItemGroup>
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
<ItemGroup>
<ProjectReference Include="..\UtilityLibrary\UtilityLibrary.csproj" />
</ItemGroup>
</Project>
When .NET Core projects build, they do not copy all referenced files into the bin folder. When you add Microsoft.NET.Test.Sdk to your test project, one of the things it does is add an AssemblyResolve event handler which loads other dependent assemblies from a list of searchDirectories.
BaseIntermediateOutputPath not working was reported against the VSTest project and is an issue with MSBuild. The workaround is noted in the dotnet sdk repository. From that, you need to use Sdk imports in your csproj instead of the Sdk attribute on the Project element.
<Project>
<PropertyGroup>
<BaseIntermediateOutputPath>obj\XXX\</BaseIntermediateOutputPath>
</PropertyGroup>
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
<!-- Body of project -->
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
</Project>
We have a solution with a website project which is hosted on a load balanced environment. At the moment no CI is being used, and deployments are manual using zip-files >_< however I'm looking on setting it up, and have run into some difficulties.
The solution requires a App_Config folder containing all the configurations for the site in the root, however these configurations differ from each of the hostingserver, where one is the management server and another is the delivery server.
Each individual server configurations is stored in a separate folder at /Configs/servername/ containing a web.config file and the App_Config folder. These have been manually copied from this folder to the root to overwrite those that already existed.
Also deployment of the /Configs/ folder is not wanted.
Preferably no changes should have to be done to the Visual Studio solution.
Is it possible to automate this before deployment in TeamCity?
regards
You may want to set properties with different values based on the computer name.
That's one trick of the trade.
<Choose>
<When Condition=" '$(Computername)'=='MyManagementServer01' ">
<PropertyGroup>
<MyCustomProperty001>Red</MyCustomProperty001>
<MyCustomProperty002>Yellow</MyCustomProperty002>
</PropertyGroup>
</When>
<When Condition=" '$(Computername)'=='MyDeliveryServer01' ">
<PropertyGroup>
<MyCustomProperty001>Black</MyCustomProperty001>
<MyCustomProperty002>White</MyCustomProperty002>
</PropertyGroup>
</When>
<Otherwise>
<PropertyGroup>
<MyCustomProperty001>NoMatchMyCustomProperty001</MyCustomProperty001>
<MyCustomProperty002>NoMatchMyCustomProperty002</MyCustomProperty002>
</PropertyGroup>
</Otherwise>
</Choose>
You could setup a property called
<ConfigurationSourceFolder>/Configs/MyManagementServer01/</ConfigurationSourceFolder>
Or setup a "DeploymentType"
<DeploymentType>ManagementServerType</DeploymentType>
You can also put Conditions on "Targets" and even Tasks.
<MakeDir Directories="C:\MyCoolDirectory" Condition="('$(MyCustomProperty001)'!='')"/>
//////Preferably no changes should have to be done to the Visual Studio solution.//////
So here is an "in-general" tip.
Instead of putting alot of custom sometimes-hard-to-follow changes in the csproj files....use a basic .msbuild file.
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="AllTargetsWrapped">
<PropertyGroup>
<!-- Always declare some kind of "base directory" and then work off of that in the majority of cases -->
<WorkingCheckout>.</WorkingCheckout>
</PropertyGroup>
<Target Name="AllTargetsWrapped">
<CallTarget Targets="BuildItUp" />
</Target>
<Target Name="BuildItUp" >
<MSBuild Projects="$(WorkingCheckout)\MySolution.sln" Targets="Build" Properties="Configuration=$(Configuration)">
<Output TaskParameter="TargetOutputs" ItemName="TargetOutputsItemName"/>
</MSBuild>
<Message Text="BuildItUp completed" />
</Target>
</Project>
I dont really know much about this to be honest with you...
I have managed to download mscommunity build and I have managed to use the script below to successfully compile and build my application, however I want to get my asp.net mvc application "published" so I want the same files that you when clicking "publish" inside visual studio. My current build file looks like this:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Import the MSBuild Tasks -->
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>
<ClassLibraryOutputDirectory>c:\publish\</ClassLibraryOutputDirectory>
<ProjectDir>..\PetProject\</ProjectDir >
<ProjectTestDir>$(ProjectDir)PetProject.WebUI\</ProjectTestDir >
<ProjectFile>$(ProjectDir)PetProject.sln</ProjectFile >
<TestProjectFile>$(ProjectTestDir)PetProject.WebUI.csproj</TestProjectFile >
</PropertyGroup>
<!-- Build projects by calling the Project files generated by VS -->
<Target Name="Build">
<MSBuild Projects="$(ProjectFile)" />
<MSBuild Projects="$(TestProjectFile)" />
</Target>
</Project>
I call this in command line using:
C:\Windows\Microsoft.NET\Framework\v3.5>msbuild.exe C:\Projects\PetProject\build
\PetProject.build
Help is greatly appreciated...
NOTE: I want to avoid CI, Nant etc. because I dont really know what they are and I ideally want to get the above working as First Base, then move onto other things like CI or whatever else, I dont want to confuse myself too much...
This should give you the same result as publishing from within Visual Studio:
<Project DefaultTargets="BuildAndPublish" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.targets" />
<PropertyGroup>
<ProjectFile>C:\PetProject\PetProject.csproj</ProjectFile >
<OutDir>C:\PetProject\MyPublish</OutDir>
</PropertyGroup>
<Target Name="BuildAndPublish">
<MSBuild Projects="$(ProjectFile)" Targets="Package" Properties="Configuration=Release;PackageLocation=$(OutDir)\MSDeploy\Package.zip;_PackageTempDir=$(OutDir)\Temp" />
</Target>
</Project>
for your project.
Don't forget to import Microsoft.Web.Publishing.targets which contains the Package target (which I mixed up with Publish in my inital answer).
If you want to build your solution your script should look something like this:
<Project DefaultTargets="BuildAndPublish" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.targets" />
<PropertyGroup>
<OutDir>C:\PetProject\MyPublish\</OutDir>
</PropertyGroup>
<ItemGroup>
<Solution Include="C:\PetProject\PetProject.sln">
<Properties>
OutDir=$(OutDir);
Platform=Any CPU;
Configuration=Release;
DeployOnBuild=True;
DeployTarget=Package;
PackageLocation=$(OutDir)\MSDeploy\Package.zip;
_PackageTempDir=$(OutDir)\Temp
</Properties>
</Solution>
</ItemGroup>
<Target Name="BuildAndPublish">
<MSBuild Projects="#(Solution)" />
</Target>
</Project>
There's a blog post by Code Inside which describes basically the same approach but didn't work when I tried it in my environment.