How To Set An MSBuild Condition on the MSBuild Version - msbuild

Is there a Condition I can set to change the TargetFrameworks on a given project depending on the version on MSBuild itself?
I have a solution which is a mixture of .NetFramework 4.8, NetStandard2 and .net 6 libraries.
I'm trying to get the solution building under Mono on a Linux host but I have to select a specific MSBuild to use in Rider - I can choose 17 from net 7 which will build the net core stack or 15 from Mono which builder the net48 libraries - Neither will do both.
To work around this, I'm trying to adapt the project files to adjust the TargetFrameworks property appropriately. A bit like this...
<TargetFrameworks Condition="'$(MSBuildVersion)' == '15'">netstandard2.0;net48</TargetFrameworks>
<TargetFrameworks Condition="'$(MSBuildVersion)' != '15'">netstandard2.0;net48;net6</TargetFrameworks>
From a pseudo-logic perspective, this would let the code build correctly and just ignore any code building for the net6 builds.
For now I'm doing this...
<TargetFrameworks Condition="'$(OS)' != 'Windows_NT'">netstandard2.0;net48</TargetFrameworks>
<TargetFrameworks Condition="'$(OS)' == 'Windows_NT'">netstandard2.0;net48;net6</TargetFrameworks>
```

The actual condition check should be against MSBuildAssemblyVersion.
<TargetFrameworks Condition="'$(MSBuildAssemblyVersion)' == '17.0' ">net6.0;netstandard2.0;net471</TargetFrameworks>
<TargetFrameworks Condition="'$(MSBuildAssemblyVersion)' == '16.0' ">netstandard2.0;net471</TargetFrameworks>
Copied from
https://github.com/lextudio/sharpsnmplib/blob/12.5.1/SharpSnmpLib/SharpSnmpLib.csproj
But seriously, migrate away from Mono when you can.

Related

Using msbuild, how can I build once and then publish with multiple transform profiles?

I have a .net solution that I can build with msbuild and successfully generate a deploy package with a PublishProfile.pubxml transform for deploying to a single web server.
I need to take that build and generate deploy packages for different environments, which are set up as transforms using various .pubxml profile files.
I know I could build each profile separately, and it'd be negligible risk of injecting a change into a build, but it's time and space that aren't necessary to consume. I would only end up keeping one of them anyway, and just copying the unique web.configs from each transform into their own deploy package's folder (sorry if this isn't clear, happy to clarify).
I'm looking for something like this pseudocode, which I know is syntactically incorrect but should get the point across:
// step 1
msbuild myapp.csproj target=Build
// step 2
foreach ($profile in $profileList) {
msbuild myapp.csproj outputdir="releaseDir/$profile" target=Publish publishProfile=$profile
}
I am working in a Jenkins context and can't use Visual Studio functions unless they're available as command-line options.
I've also tried desperately to get msdeploy to work, which I think might simplify some of this, but for the life of me I can't get it to work with the tempAgent option, which is really the only way I want to go in my environment. Details on that particular struggle here.
Thanks in advance!
Update 1: This post works well for me, but as I say in a comment, it's still creating multiple deploy packages that take up a ton of space and aren't necessary. What I'd really like is a way to use a single build artifact and create multiple transforms of the Web.config files. Then on deploy, copy that build artifact and the appropriate config file. That would really be build-once/deploy-many!
FWIW, this is the code i came up with using the link above:
function Initialize-Build {
$ErrorActionPreference = 'Stop'
$appName = 'myApp'
$projsToBuild = 'Ui', 'DataService'
$projPath = "$appName/$appName"
$buildPath = 'obj/Release/Package/'
$releasePath = 'Release-Packages'
$commonArgs = '/p:Configuration=Release',
'/nologo',
'/verbosity:quiet'
}
function Invoke-Build {
foreach ($proj in $projsToBuild) {
$projName = $proj -eq 'Ui' ? 'WebUi' : $proj
$p = "$projPath.$proj/$appName.$projName.csproj"
$buildArgs = '/t:Clean,Build',
'/p:AutoParameterizationWebConfigConnectionStrings=False'
Write-Output "Building $p"
msbuild $p #commonArgs #buildArgs
if ($LASTEXITCODE) { exit 1 }
}
}
function Invoke-Transform {
$xformArgs = "/p:deployOnBuild=True", "/p:Targets=Publish"
foreach ($proj in $projsToBuild) {
$p = "$projPath.$proj/$appName.$projName.csproj"
$pubProfiles = 'Dev', 'QA', 'UAT', 'Prod'
foreach ($prof in $pubProfiles) {
Write-Output "Building $p ($prof)"
$pubProfileArg = "/p:PublishProfile=$prof"
msbuild $p #commonArgs #xformArgs $pubProfileArg
if ($LASTEXITCODE) { exit 1 }
}
}
}
. Initialize-PrivLogBuild
Invoke-Build
Invoke-Transform
I realized I wasn't really asking the right question. When I started searching for msbuild transform posts, I found a way to do what I need.
I landed on updating the .csproj files of the apps I'm building with an AfterBuild target.
There are 4 transforms required, each with their own .config file as the transform source. I was fortunate that these files had already been created by the application developer.
This is the code I ended up with, placed at the end of the .csproj file, inside the </project> tag. To reduce repetition of paths and filenames, I created configDir and xformFile properties. I like this pattern because it's easily scalable and generic!
<!-- all the rest of the .csproj above this -->
<UsingTask TaskName="TransformXml"
AssemblyFile="$(MSBuildExtensionsPath32)/Microsoft/VisualStudio/v16.0/Web/Microsoft.Web.Publishing.Tasks.dll" />
<Target Name="AfterBuild">
<PropertyGroup>
<configDir>../../Release-Packages/configs</configDir>
<xformFile>Web.config</xformFile>
</PropertyGroup>
<MakeDir Directories="$(configDir)" />
<TransformXml Source="$(xformFile)"
Transform="Web.Dev.config"
Destination="$(configDir)/$(AssemblyName).$(xformFile).DEV" />
<TransformXml Source="$(xformFile)"
Transform="Web.QA.config"
Destination="$(configDir)/$(AssemblyName).$(xformFile).QA" />
<TransformXml Source="$(xformFile)"
Transform="Web.Prod.config"
Destination="$(configDir)/$(AssemblyName).$(xformFile).Prod" />
<TransformXml Source="$(xformFile)"
Transform="Web.UAT.config"
Destination="$(configDir)/$(AssemblyName).$(xformFile).UAT" />
</Target>
</Project>
many thanks to these posts for lighting the way to this solution
https://dougrathbone.com/blog/2011/09/14/using-custom-webconfig-transformations-in-msbuild
How do I use custom variables in MSBuild scripts?
https://github.com/sayedihashimi/sayed-samples/blob/master/TransformMultipleWebConfigs/transform.proj
https://www.locktar.nl/general/use-config-transforms-when-debugging-your-web-application/
https://johan.driessen.se/posts/Applying-MSBuild-Config-Transformations-to-any-config-file-without-using-any-Visual-Studio-extensions/
Package multiple publish profiles .pubxml after a successful build in Jenkins CI
https://bartlomiejmucha.com/en/blog/msbuild/how-to-extend-msbuild-publish-pipeline-to-apply-transform-files/

VB.Net - Use wild card in My.Computer.Registry.GetValue

I have a VB.NET script that looks up the current version of java installed.
Everything worked great until java 8 came out.
Back in Java 7 i would do this.
My.Computer.Registry.GetValue("HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment", "Java7FamilyVersion", Nothing)
In Java 8 (Java8FamilyVersion) is gone and has replaced with (FullVersion).
The problem is FullVersion is behind two more folders one with the version (18.0_25) Then another folder call MSI
So here is the problem; right now the first folder is called 18.0_25, but in the future it would be changed to something like 18.0.55ish.
I can't update my software that often, so i would like to use a wilcard in the getvalue
IE something like this
My.Computer.Registry.GetValue("HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\JavaSoft\Java Runtime Environment\1.8.*\MSI", "FullVersion", Nothing)
Above didn't work is their anything that would work?
Use the GetSubKeyNamesmethod to enumerate the subkey(s) of "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\JavaSoft\Java Runtime Environment", then pick the alphabetically last (so that you do not fall for any old 1.7_xx keys or the 1.8 key)
You could grab a file version from one of the Java .dll files. Sorry, I don't have Java installed, but something like this might help you:
Dim fvi As FileVersionInfo = FileVersionInfo.GetVersionInfo("somefilename.dll")
Debug.Print(fvi.ProductVersion)
You can fiddle with the returned properties for major, minor, etc. You should be able to build a version string to get what you need.

How to import multi-module project in IntelliJ IDEA?

I'm used to Spring and Maven projects where I set up a multi-module project in Maven with projects like:
app-web
app-models
app-services
app-common
I'm now getting into using Play Framework 2 (Scala) and sbt.
Is there a similar concept with Play and sbt that I could group all of these projects into a single IntelliJ IDEA solution and sbt?
IntelliJ IDEA 13 (the latest version is 13.1.3) comes with the built-in SBT support, and the Ultimate edition adds Play support.
A multi-module sbt project can be imported to IDEA and is fully supported out of the box however it's Play-based or not (they're sbt projects after all).
You should try it out yourself with the following very simplistic build file build.sbt (or just generate a Play project with play new or better now activator new [your-project-name] play-scala):
lazy val a, b, c = project
and the following project/build.properties:
sbt.version=0.13.5
Nothing but these two above files are needed to get you started with sbt/activator.
In IDEA, open the project using File > Open... and select build.sbt.
Click OK to see another window where you specify additional configuration options for the project. Then the modules show up.
You probably wouldn't be able to group them into one single project in idea but you could have multiple projects for sure:
in your project/Build.scala:
{
val baseDependencies = Seq(
"org1" % "dep" % "latest.integration",
"org2" % "dep2" % "version"
)
val modelDependencies = baseDependencies ++ Seq("org3" % "dep3" % "version")
val appWeb = play.Project("app-web", "1.0", baseDependencies)
val appModels = play.Project("app-models", "1.0", modelDependencies, path = file("modules/models"))
val app = play.Project("app", "1.0", Nil).aggregate(appWeb, appModels)
}
In this case, you'll have a regular app called "app-web", a module appModels under modules/models without the project directory and regular confs like application.conf, and an aggregated app called "app".
When you start the play console you could switch to certain projects by typing "project (name)". For example you could type "project app-web" and then "idea" to generate the app-web project solution for idea. You can also switch to project "app" where all commands entered under it will be applied to all sub-projects.
For more, check the documentation here: http://www.playframework.com/documentation/2.2.x/SBTSubProjects

Can I use a regular System.dll in a Compact Framework project?

In my test Winforms app (in which I'm targeting .NET 3.5, to simulate the Windows CE / Compact Framework 3.5 app that this is a first-line test for as much as possible), I added some JSON.NET code to deserialize json returned from WebAPI methods:
try
{
const string uri = "http://localhost:48614/api/departments";
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
var webResponse = (HttpWebResponse)webRequest.GetResponse();
if ((webResponse.StatusCode == HttpStatusCode.OK) && (webResponse.ContentLength > 0))
{
var reader = new StreamReader(webResponse.GetResponseStream());
string s = reader.ReadToEnd();
MessageBox.Show(string.Format("Content from HttpWebRequest is {0}", s));
var arr = JsonConvert.DeserializeObject<JArray>(s);
int i = 1;
foreach (JObject obj in arr)
{
var id = (string)obj["Id"];
var accountId = (double)obj["AccountId"];
var departmentName = (string)obj["DeptName"];
MessageBox.Show(string.Format("Object {0} in JSON array: id == {1}, accountId == {2}, deptName == {3}", i, id, accountId, departmentName));
i++;
}
}
else
{
MessageBox.Show(string.Format("Status code == {0}", webResponse.StatusCode));
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
...This runs fine in the .NET 3.5 Winforms app, but when I copied it over to the Windows CE-targetted app, the code wouldn't run, with the following errors spilling forth:
The type 'System.ComponentModel.IBindingList' is defined in an assembly that is not referenced. You must add a reference to assembly 'System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
The type 'System.ComponentModel.ITypedList' is defined in an assembly that is not referenced. You must add a reference to assembly 'System, Version=2.0.0.0...
The type 'System.ComponentModel.INotifyPropertyChanging' is defined in an assembly that is not referenced....
The type 'System.ComponentModel.ICustomTypeDescriptor' is defined in an assembly...
The type 'System.ComponentModel.INotifyPropertyChanged' ...
The type 'System.Uri'...
I saw that in the Winforms (testbed) app, I'm using version 2.0.0.0 of the "regular" (or "deluxe" when compared to CF) System.dll. In the Windows CE app, though, I was using the CF flavor of version 3.5 found here:
C:\Program Files (x86)\Microsoft.NET\SDK\CompactFramework\v3.5\WindowsCE\System.dll
I tried using version 2 CF from C:\Program Files (x86)\Microsoft.NET\SDK\CompactFramework\v2.0\WindowsCE\System.dll, but that failed, too - so it's apparently not really the version (3.5 vs. 2.0), but the "flavor" (CF vs "deluxe"/regular System.dll).
SO...I replaced the CF-flavored System.dll[s] with the one successfully used by the Winforms test app, explicitly the one in C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.dll (I have no System.dll in C:\Windows\Microsoft.NET\Framework\v3.5, anyway).
It no longer gives those same err msgs as listed above, but there is another compile error that may (or may not be) related (Can I give an emulator more disk space?) now.
Whether it is or not (related), it brings up the intriguing question: Will using a regular System.dll in a Windows CE project cause a problem?
If it will -- or there's a good chance that it will -- cause a problem, since it was apparently the JSON.NET code that required the change to an "off-colored" version of System.dll, is there a CF-ready / CF-specific version of JSON.NET? Will I have to create my own CF-targeted version of an assembly from the JSON.NET source?
UPDATE
In the JSON.NET readme, it states:
For a Compact Framework 3.5 build download Json.NET 3.5.
Which I assumed meant the .DLL in \Json50r7\Bin\Net35
Am I wrong about that?
UPDATE 2
When I attempt to open Newtonsoft.Json.Net35.sln in Windows 2008, with the intention of creating a CE-targeted assembly, it doesn't allow me, saying, "The selected file is a solution file, but was created by a newer version of this appllication and cannot be opened*"
It also says in the JSON.NET read me:
Microsoft stopped support for the Compact Framework in Visual Studio 2010.
...so I don't think I can open it in a newer version of VS2008 and create a CF-friendly DLL, either...
UPDATE 3
Looking for a "Compact" folder in the download from http://json.codeplex.com/releases/view/113546, but I see no such folder:
It's not the "Portable" folder, is it?
As Robert Harvey suggests, the tile and the actual question here don't match. You probably should fix that.
The answer to the current title "Can I use a regular System.dll in a Compact Framework Project?" is absolutely, definitively no. You cannot mix and match. Full-framework assemblies cannot run under the Compact Framework. There's no way to make them work. Period. Stop trying this.
The answer to "How do I use JSON.NET is a Compact Framework Project" is that you should go to the JSON.NET project site on GitHub and specifically look at the last JSON.NET 3.5 release (it was Release 8) and download it. Inside that zip file is a folder named "Compact" that contains an assembly named Newtonsoft.Json.Compact.dll. Add a reference to that DLL to your Compact Framework 3.5 project.

msbuild reference resolution

I've been doing some work recently that analyzes relationships between various projects in source control. So far I've been using PowerShell and XPath through the Select-Xml cmdlet to process our csproj files, however this relies on my tenuous knowledge of how MSBuild uses the ProjectReference and Reference elements in the project files. It dawned on me that it would be much better if I could use MSBuild itself to resolve the references and then somehow inspect the results of the reference resolution process.
MSBuild experts: does this seem possible? Would this entail writing a custom targets file or something? Would I be forced into also building the projects as well since csproj files also import Microsoft.CSharp.targets?
Any insight would be nice. Thanks!
It is really quite easy. First reference these assemblies:
Microsoft.Build
Microsoft.Build.Engine
Microsoft.Build.Framework
Microsoft.Build.Utilities.v4.0
...and you can create some tooling around the MSBuild object model. I've got a custom MSBuild task that does this analysis right in the build, snippet below:
private bool CheckReferences(string projectFullPath)
{
var project = new Project(projectFullPath);
var items = project.GetItems("Reference");
if (items == null)
return true;
foreach (var item in items)
{
if (item == null)
continue;
if (string.IsNullOrWhiteSpace(item.UnevaluatedInclude))
continue;
if (!item.HasMetadata("HintPath"))
continue;
string include = item.UnevaluatedInclude;
string hintPath = item.GetMetadata("HintPath").UnevaluatedValue;
if (!string.IsNullOrWhiteSpace(hintPath))
if (hintPath.Contains(#"C:\") || hintPath.Contains("C:/"))
LogWarning("Absolute path Reference in project {0}", projectFullPath);
}
return true;
}