Per-user environment variables in Xamarin Studio - xamarin-studio

I want to set environment variables to my execution environment of a .NET app in Xamarin Studio on a per-user basis. These environment variables may control things like logging or other debugging related configuration that should not be shared broadly.
Consider some trivial application:
using System;
public class TrivialApplication
{
public static void Main(string[] args)
{
Console.WriteLine(Environment.GetEnvironmentValue("NAME"));
}
}
I wish to be able to add an environment variable for NAME that appears when I run or debug the application.
I can add environment variables in Project Options > Run > General > Environment Variables, but these are saved in the .csproj, which is version controlled. I would instead like these to go to a per-user configuration.
I naively tried to simply place the EnvironmentVariables lines into a .csproj.user:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<EnvironmentVariables>
<EnvironmentVariables>
<Variable name="NAME" value="value" />
</EnvironmentVariables>
</EnvironmentVariables>
</PropertyGroup>
</Project>
However either my .csproj.user is not read, or the environment variables are not applied. How can I accomplish this?

Related

MSBuild: How may I access built-in properties while defining my own custom properties?

I have a file named Common.targets defined like so:
<Project>
<PropertyGroup>
<TlbExpPath>"c:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\x64\tlbexp"</TlbExpPath>
<TlbOutPath>"$(OutDir)..\TLB\$(TargetName).tlb"</TlbOutPath>
</PropertyGroup>
<Target Name="TlbExp" AfterTargets="CopyFilesToOutputDirectory" Inputs="$(TargetPath)" Outputs="$(TlbOutPath)">
<Exec Command='$(TlbExpPath) "$(TargetPath)" /nologo /win64 /out:$(TlbOutPath) /verbose' />
</Target>
</Project>
When I inspect the output of the TlbOutPath property, it looks like:
"..\TLB\.tlb"
Apparently, $(OutDir) and $(TargetName) produce nothing when used within a PropertyGroup. I'm not sure why. How can I make these paths/values reusable while still having access to built-in properties when they are defined?
I'm using MSBuild that comes bundled with Visual Studio 2019. I add an Import element to my actual .csproj projects to include this target where I need it. The csproj projects use the SDK format for the projects, e.g. <Project Sdk="Microsoft.NET.Sdk">.
Here is an example of what the import looks like:
<Project Sdk="Microsoft.NET.Sdk">
<!-- etc -->
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<!-- etc -->
</ItemGroup>
<Import Project="$(RepositoryRoot)\Common.targets" />
</Project>
MSBuild: How may I access built-in properties while defining my own
custom properties?
This is quite an issue in the new sdk format project. I have tested it and got the same issue as you said which quite bother me a lot. Like $(OutDir),$(TargetName),$(OutputPath),$(TargetPath) and some other common system properties cannot be used in a new property while $(Configuration) and $(AssemblyName) works well.
And not only us but also someone else also face the same issue about it.See this thread.
For the traditional old csproj format project, there was no problem with these properties being used this way, but in the new SDK format project, it is impossible to assign some common properties such as $(OutDir),$(TargetName) and $(TargetPath) to a new property. As we know, most of the common properties are defined in the Microsoft.Common.props file(old csproj format) which is quite different from the new sdk format project which does not have such file.
In order to get an answer,l have reported this issue to DC Forum. See this.You can enter this link and add any detailed comments to describe this issue. And anyone who interested in this issue will also vote it so that it will get more Microsoft staff's attention. All these efforts will speed up and get the final answer.
This process may take a while or you could try my suggestion.
Suggestion
1) You can customize this property $(OutDir) in Common.targets file, and use $(TargetFramework) instead of $(TargetName) since $(TargetFramework) is defined in the xxxx.csproj file.
<Project>
<PropertyGroup>
<OutDir>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName)\</OutDir>
<TlbExpPath>"c:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\x64\tlbexp"</TlbExpPath>
<TlbOutPath>"$(OutDir)..\TLB\$(TargetName).tlb"</TlbOutPath>
<TargetPath>xxxx\xxxx.dll(exe)</TargetPath>--------the absolute path of the output file
</PropertyGroup>
<Target Name="TlbExp" AfterTargets="CopyFilesToOutputDirectory" Inputs="$(TargetPath)" Outputs="$(TlbOutPath)">
<Exec Command='$(TlbExpPath) "$(TargetPath)" /nologo /win64 /out:$(TlbOutPath) /verbose' />
</Target>
</Project>
2) use Directory.Build.targets file rather than a custom targets file.
A) You should add a file named Directory.Build.targets(it must be named this and have its own rule to be imported into xxx.csproj) under the project folder.
B) add the content of Common.targets into it without any changes and then build your project directly. The Directory.Build.targets will be imported into your project automatically while build.
This function works well and will not lose any properties. However, l stil bother why it works.
Conclusion
I think #2 is more suitable and easier for you to achieve your goal.

How to ignore the property value given on the command line from within the respective csproj file?

Our TFS build controllers build all the projects from the same solution into the same shared bin directory, because the TFS build workflow passes the OutDir parameter to the msbuild command responsible for building the solution.
I have a project where I want to suppress this behavior and let it build into the standard relative bin\Debug or bin\Release directory.
But I cannot find how to do it. Indeed, observe the following trivial msbuild script:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<OutDir>$(MSBuildThisFileDirectory)bin\$(Configuration)</OutDir>
</PropertyGroup>
<Target Name="Build">
<Message Text="$(OutDir)" Importance="High"/>
</Target>
</Project>
Now, I am running it:
PS C:\> msbuild .\1.csproj /p:OutDir=XoXo /nologo
Build started 11/13/2015 9:50:57 PM.
Project "C:\1.csproj" on node 1 (default targets).
Build:
XoXo
Done Building Project "C:\1.csproj" (default targets).
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:00.03
PS C:\>
Notice it displays XoXo, ignoring my attempt to override it from within.
So, is it possible?
This is a bit of a classic RTFM situation but interesting nonetheless. See the documentation for MSBuild Properties in particular the part on Global Properties and how to make properties not being overridden by the former:
MSBuild lets you set properties on the command line by using the /property (or /p) switch. These global property values override property values that are set in the project file. This includes environment properties, but does not include reserved properties, which cannot be changed.
Global properties can also be set or modified for child projects in a multi-project build by using the Properties attribute of the MSBuild task
If you specify a property by using the TreatAsLocalProperty attribute
in a project tag, that global property value doesn't override the
property value that's set in the project file.
It also links to the Project element documentation which basically repeats the same info and says multiple properties in the attribute should be seperated by semi-colons.
In short, code applied to your case:
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
TreatAsLocalProperty="OutDir">
Note that this will completely disable altering OutDir from outside the project though. An alternate solution which is more configurable could be to have a small stub project which you make TFS build instead of the main project. In that project you can then decide on whether to pass OutDir to the actual project or override it, e.g. by fetching the value by importing a file which might or might not be defined, or based on an environment variable or so. This gives the basic idea:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Try import if this file exists, it should supply the value for CustomOutDir-->
<Import Project="$(MSBuildThisFileDirectory)customoutdir.props" Condition="Exists('$(MSBuildThisFileDirectory)customoutdir.props')"/>
<PropertyGroup>
<!-- Default value for CustomOutDir if not set elsewhere -->
<CustomOutDir Condition="'$(CustomOutDir)' == ''">$(MSBuildThisFileDirectory)bin\$(Configuration)</CustomOutDir>
<!-- ApplyCustomOutDir specifies whether or not to apply CustomOutDir -->
<ActualOutDir Condition="'$(ApplyCustomOutDir)' == 'True'">$(CustomOutDir)</ActualOutDir>
<ActualOutDir Condition="'$(ApplyCustomOutDir)' != 'True'">$(OutDir)</ActualOutDir>
</PropertyGroup>
<Target Name="Build">
<MSBuild Projects="$(MasterProject)" Properties="OutDir=$(ActualOutDir)"/>
</Target>
</Project>
And should be invoked by passing the neede properties like
msbuild stub.targets /p:MasterProject=/path/to/main.vcxproj;ApplyCustomOutDir=True
(I have never used TFS so the way to get the properties passed might be different)

Programmatically (using C#) determine current Visual Studio build configuration

My Visual Studio 2013 C# app needs to programmatically determine if another Visual Studio solution has been saved in "build mode" or "release mode". Is there an API for accomplishing this?
Alternatively, I was thinking of using the MSBuild API to build the solution and then check to see if the app has debug symbols. Is there a way of doing this?
Debug, Release or any other custom configuration are just names, anyone can make one to look like the other one in Advanced Build Settings of Project Properties or by tweaking properties in the .csproj directly. Those flags will dictate how you identify a "debug" assembly and how much of "debug" debug really means. That said, the difference in output for default combination of properties is in Debuggable assembly attribute, both default configurations have it and you can see it for yourself using ILSpy.
Debug
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default
| DebuggableAttribute.DebuggingModes.DisableOptimizations
| DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints
| DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
Release
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
On how to read this Debuggable attribute value -- see #MobyDisk answer.
When you build an assembly in debug mode, the compiler adds the [assembly: DebuggableAttribute] automatically. You can use reflection to see if that attribute is present on the assembly. Take a look here for details on how to read attributes from an assembly: How to read assembly attributes
I'm currently solving the same problem using Web.config:
My Web.config (as default this will be equivalent to Web.Debug.config and Web.Development.config) file contains (using your naming above):
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<appSettings>
<add key="mode" value="build"/>
<add key="anotherKey" value="another value"/>
</appSettings>
</configuration>
My Web.Test.config and Web.Release.config files contain:
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<appSettings>
<add key="mode" value="release" xdt:Transform="Replace" xdt:Locator="Match(key)"/>
</appSettings>
</configuration>
Then you can access the 'mode' using:
WebConfigurationManager.AppSettings.Get("mode");
[you don't have to use 'mode' for the key, as long as you are consistent]

Specify Machine Name in Web.Config Transform

I am using Web.config transforms to successfully create debug and release versions of the my web.config - this is working correctly.
I am interested to know whether there is a 'machine name' property to specify the current machine name which I can use in a debug URL, rather than hard-coding a specific machine name (using localhost isn't an option in the case), e.g.
<add name="XrmService" connectionString="http://$(ComputerName):5555/Service.svc" />
Are there any properties available using Web.config transforms? Similar to MSBuild's $(ComputerName) property?
I faced a similar issue, what I ended up doing is :
1) Added the following build target to the project file. (Which is an MSBuild script effectively)
<Target Name="AfterBuild">
<TransformXml Source="Web.config" Condition="Exists('Web.$(Computername).config') " Transform="Web.$(Computername).config" Destination="Web.config" />
</Target>
2) Added a Web.MyMachineName.config config transform file to the project. In your case it should look like this:
<?xml version="1.0"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<connectionStrings>
<add name="XrmService"
connectionString="http://MyMachineName:5555/Service.svc"
xdt:Transform="SetAttributes"
xdt:Locator="Match(name)"/>
</connectionStrings>
</configuration>
This has the benefit of running different transformations based on the machine name, without creating a separate build configuration. You can configure it to be debug only by specifying Condition="'$(Configuration)' == 'Debug'".
There is an Environment Variable that you can use. It is $(COMPUTERNAME).
Open a command window, type "set" (without the double quotes) and press Enter. You will see this Environment Variable somewhere at the top of the screen.

FxCop in MSBuild and disabling CA0060 exceptions

I am very new to MSBuild and it is taking me a little while to work out how to do things.
So I am trying to integrate FxCop into my project to be automatically run when I build them on the build server.
At the moment the way to go seems to be to add a custom task to the build that you call when you build. So I have so far created the following:
<Target Name="ExecuteFxCop">
<ItemGroup>
<Files Include="bin\XXXX.dll" />
</ItemGroup>
<!-- Call the task using a collection of files and all default rules -->
<MSBuild.ExtensionPack.CodeQuality.FxCop
TaskAction="Analyse"
Files="#(Files)"
SearchGac="True"
OutputFile="FxCopReport.xml">
</MSBuild.ExtensionPack.CodeQuality.FxCop>
</Target>
However when I run this >msbuild XXXX.csproj /t:ExecuteFxCop it fails with error 512 which I have narrowed down to an exception from indirectly-referenced assemblys:
<Exception Keyword="CA0060" Kind="Engine" TreatAsWarning="True">
<Type>Microsoft.FxCop.Sdk.FxCopException</Type>
<ExceptionMessage>The indirectly-referenced Silverlight assembly 'System.Runtime.Serialization, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e' could not be found. This assembly is not required for analysis, however, analysis results could be incomplete. Silverlight reference assemblies should be specified with the '/reference' switch. This assembly was referenced by: XXX\bin\ESRI.ArcGIS.Client.dll.</ExceptionMessage>
</Exception>
But I cannot add this reference. Is there a way to get the build to see this reference or preferably just disable the error altogether?
I did try: http://social.msdn.microsoft.com/Forums/en-US/vstscode/thread/c6780439-bc04-459e-80c3-d1712b2f5456/ but it doesn't work
Try the work-around here: http://geekswithblogs.net/blachniet/archive/2011/07/12/avoiding-fxcop-warning-ca0060.aspx
Edit
For example, using the FxCop MsBuild task, set ContinueOnError and check the ExitCode as follows:
<Target Name="ExecuteFxCop">
<ItemGroup>
<Files Include="bin\XXXX.dll" />
</ItemGroup>
<!-- Call the task using a collection of files and all default rules -->
<MSBuild.ExtensionPack.CodeQuality.FxCop
TaskAction="Analyse"
Files="#(Files)"
SearchGac="True"
OutputFile="FxCopReport.xml"
ContinueOnError="WarnAndContinue">
<Output TaskParameter="ExitCode" PropertyName="ExitCode"/>
</MSBuild.ExtensionPack.CodeQuality.FxCop>
<Error Condition="$(ExitCode) != 512" Text="FxCop failed with exit code: $(ExitCode)"/>
</Target>
P.S. (This is not tested)
Not sure if you're still looking for a solution but what usually works for me is adding the
fxcop cmd-option /d:{dir-of-random-assemblies}
which essentially tells fxcop to look in that additional directory for assemblies.
Adding a reference to a proj that doesn't need it is a bad idea in my opinion.
http://msdn.microsoft.com/en-US/library/bb429449(v=vs.80).aspx