Scenario:
I'm working on an aspect framework and intercept the build process using MSBuild Tasks. I want to manipulate source files prior to the internal build process using roslyn.
For development purposes i've hardlinked the inserted references like this (which i can't use in the actual solution):
yield return MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
which is resolved to this:
C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll
The .csproj file contains references in this form:
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
With this:
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
Problem:
My problem is that the path of the assembly for "System" (for example) depends on the project file .net version, so i'm wondering about the best way to get the location depending on what .net version to build the project file with.
Research so far:
MSbuild:
I have looked into MSBuild commandline args to see if this offers a way to get the location of an assembly by its name and net version here:
https://msdn.microsoft.com/de-de/library/ms164311.aspx
Roslyn:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/63f716d8-734e-4ea3-b72a-1e64a5773bc9/how-do-i-use-assemblynamereference?forum=roslyn (this does not work with current version of roslyn anymore, which makes sense since it has no version info either)
http://zacharysnow.net/2014/04/15/adding-assembly-references-in-roslyn.html
All resources # https://github.com/dotnet/roslyn/blob/master/docs/samples/
Currently i can only think of one rather ugly solution which would be going through all framework directories available on the host and mapping versions to names and locations manually. (Using info obtained here https://msdn.microsoft.com/en-us/library/hh925568(v=vs.110).aspx, since that provides me with framework install paths or using this method: http://www.hexcentral.com/articles/framework-folder.htm)
I'm just hoping there is a better way.
Any help is welcome. Unfortunately searching for words like "assembly by name and version" and its' variations didn't find anything helpful for me.
Update 1:
Roslyn / Find References - Can't properly load Workspace
This would fix the problem for me but it requires MSBuild 14. unfortunate - i don't want to depend on that being installed on the building host.
Related
Can someone explain the purpose of this two in csproj file (VS2017):
<TargetFramework>netstandard1.6</TargetFramework>
<RuntimeIdentifier>win7</RuntimeIdentifier>
I just migrated from VS2015 and now can't publish my web api because it looks I should use only one target framework. In addition I can't specify multiple RIDs.
All these changed things make me frustrated. Nothing works from scratch, should overcome something over and over.
I just want developing my web-api on windows, run xUnit tests here and then deploy web-api to run on linux (ubuntu) server.
What I should put in both parameters in csproj ?
Links with good explanation is highly appreciated.
Update1
I have web api with referenced .net core libraries. Everything where migrated from VS2015. Now in root project I have
<TargetFrameworks>netcoreapp1.1;net461</TargetFrameworks>. When I publish via VS2017 I got error:
C:\Program
Files\dotnet\sdk\1.0.3\Sdks\Microsoft.NET.Sdk\buildCrossTargeting\Microsoft.NET.Sdk.targets(31,5):
error : The 'Publish' target is not supported without specifying a
target framework. The current project targets multiple frameworks,
please specify the framework for the published application.
But I have specified target framework in publish as netcoreapp1.1.
OK. Then I updated my csproj with <PropertyGroup Condition="$(TargetFramework)'=='netcoreapp1.1'">
<RuntimeIdentifier>ubuntu.16.10-x64</RuntimeIdentifier>
</PropertyGroup> as suggested below.
But now I even can't build app, get error:
5>C:\Program Files (x86)\Microsoft Visual
Studio\2017\Enterprise\MSBuild\Sdks\Microsoft.NET.Sdk\build\Microsoft.NET.Sdk.targets(92,5):
error : Assets file '\obj\project.assets.json' doesn't
have a target for '.NETCoreApp,Version=v1.1/ubuntu.16.10-x64'. Ensure
you have restored this project for TargetFramework='netcoreapp1.1' and
RuntimeIdentifier='ubuntu.16.10-x64'.
I just want develop with VS2017 at windows 8.1/windows7 and deploy to ubuntu 16.10.
What I'm doing wrong ?
Update2
I have 8 projects in solution. 3 of them are xUnit tests. Thus we have 5 projects. 4 of these 5 are class libraries and 1 is my web-app.
All 4 class libraries have this:
<TargetFrameworks>netstandard1.6;net461</TargetFrameworks>
<ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
<Reference Include="System" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
My web app:
<TargetFrameworks>netcoreapp1.1;net461</TargetFrameworks>
<ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
<Reference Include="System" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
How to publish my web-app ?
The <TargetFramework> (or <TargetFrameworks> when you want have multiple targets, such as net451, one or multiple netstandard1.x etc). Per <TargetFramework> / <TargetFrameworks> entry one set of assemblies will be created and located inside bin\Debug\<targetframeworkid>).
This is useful, when you want to use a different library in .NET Core (because the library you used only works with full .NET Framework such as 4.5.1) or remove this feature from i.e. .NET Core because it's unsupported.
It is used for both, building and NuGet restore. i.e. you can't use a net451 only library in a .NET Core project (netstandard 1.1 for example - but you can use netstandard1.1 in a net451 project).
<RuntimeIdentifier> / <RuntimeIdentifiers> on the other side is used for NuGet mainly. It tells NuGet which packages you need. For example if you want to target Linux, Mac and Windows, certain assemblies require native libraries (such as encryption. On windows CryptoAPI will be used, but on Linux and Mac you need OpenSSL). This includes non-managed dlls and *.so (Linux) files.
i.e. <RuntimeIdentifiers>win7-x64;win7-x86;ubuntu.16.10-x64</RuntimeIdentifiers> will make nuget restore packages for win7 (both x64 and x86) versions and x64 only for ubuntu. This is required, because when you work on windows you need to download these native libraries too so you deploy/package them with dotnet publish.
Here's a little catch though: When you have a full .NET Framework reference in <TargetFramework> or <TargetFrameworks>, then you must specify a single <RuntimeIdentifier> (singular, not plural <RuntimeIdentifiers>), otherwise you will get an error.
For example:
<PropertyGroup>
<TargetFrameworks>netstandard1.0;net451</TargetFrameworks>
<RuntimeIdentifiers>win7-x64;win7-x86;ubuntu.16.10-x64</RuntimeIdentifiers>
</PropertyGroup>
<!-- This entry will only be used for the .NET Framework 4.5.1 output -->
<PropertyGroup Condition="'$(TargetFramework)' == 'net451'">
<RuntimeIdentifier>win7-x64</RuntimeIdentifier>
</PropertyGroup>
RID is short for Runtime IDentifier. RIDs are used to identify target
operating systems where an application or asset (that is, assembly)
will run. They look like this: "ubuntu.14.04-x64", "win7-x64",
"osx.10.11-x64". For the packages with native dependencies, it will
designate on which platforms the package can be restored.
More in docs
First change to proper RID from win7 to win7-x64 or win7-x86. Next add other RID like ubuntu. For example:
<PropertyGroup>
<TargetFramework>netstandard1.6</TargetFramework>
<RuntimeIdentifier>win7-x64;ubuntu.16.10-x64</RuntimeIdentifier>
</PropertyGroup>
Target framework looking good. For more read docs
With .net core (project.json) I used to switch between nuget packages and source code by adding the path to source code to the projects field in the global.json. After I did that it would add all the projects that it could find in that path that could replace the nuget packages I referenced.
I used this feature alot because I have my own nuget packages that I use, but I want to test the changes in my other project before I publish. But once I switched to Sdk 1.0.0/VS 2017/csproj .net core that feature seemed to disappear.
The alternative is just manually adding each project, switch the references manually (since they are broken up into project, nuget and sdk references), and then after switch it all back.
Any thoughts or advice would be great.
UPDATE:
Sounds like there is no equivalent in csproj (as expected), but there are msbuild workarounds for now (As of the initial VS 2017/.NET Core SDK 1.0.0 release)
Yes, I too had gotten used to this functionality and built my workflow around it. I am still looking for a solution but I'm currently playing with the idea of using conditional logic in the csproj files. Since it's now msbuild, you can do things like this:
<Choose>
<When Condition="Exists('..\..\..\MyProject')">
<ItemGroup>
<ProjectReference Include="..\..\..\MyProject\src\MyProject\MyProject.csproj" />
</ItemGroup>
</When>
<Otherwise>
<ItemGroup>
<PackageReference Include="MyProject" Version="1.0.0" />
</ItemGroup>
</Otherwise>
</Choose>
This replaces the hard reference to a package with a conditional that uses a project reference if it can find the source code (in this case the directory), and a package reference if can't.
So by default you would be referencing the package, but if you want to debug one of your projects, you check it out in the location that the conditional checks, and add the project to your solution.
This way you only need to change your solution file (by adding the project) when you want to include source code, instead of rewiring all your project references.
For others that are interested in attempting to emulate with Global.json did, I worked around this for now using a couple powershell scripts and a custom json file that mimics it. Check out my answer here:
https://stackoverflow.com/a/43795974/5504245
I am writing a MSBuild file for one of my .NET projects. I do not want to use the Visual Studio generated .sln and .csproj files for certain reasons. I am also aiming to get some experience writing my own build files.
One of my source files is referencing the System.Windows.Media namespace and the build failed with the below message
error CS0234: The type or namespace name 'Media' does not exist in the namespace 'System.Windows' (are you missing an assembly reference?)
So I added the following snippet in my build file...
<ItemGroup>
<Reference Include="PresentationCore">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
</ItemGroup>
I made changes in the CSC task as follows...
<Target Name="ReleaseBuild">
<MakeDir Directories="$(ReleaseDir)"/>
<Csc Sources="#(SrcFile)"
OutputAssembly="$(ReleaseDir)\$(OutputAssemblyName)"
TargetType="Library"
References="#(Reference);
$(LibDir)\MyOwnLib.DLL"
Platform="$(ProcessorPlatform)">
</Csc>
</Target>
only to get the following error - CSC : error CS0006: Metadata file 'PresentationCore' could not be found
Why is MSBuild not able to find the PresentationCore assembly in the GAC? Is there some additional version information that I should give? I searched in many forums but seem to be going around in circles and I would be glad for any useful pointers as to what the problem could be.
I am using MSBuild 4.0 on an x64 machine and building for an x86 target. My build files which referenced only the MS core libraries worked fine. My VS2008 projects also build fine.
Some Progress
I was able to progress in this issue by making the following changes...
1) I added a property to specify the path of the presentation core library - <PresentationCoreLibDir>C:\Windows\Microsoft.NET\assembly\GAC_32\PresentationCore\v4.0_4.0.0.0__31bf3856ad364e35</PresentationCoreLibDir>
2) Added an additional parameter to the CSC task like this - AdditionalLibPaths="$(PresentationCoreLibDir)"
3) Finally appended the .DLL suffix to the reference tag -
<ItemGroup>
<Reference Include="PresentationCore.DLL">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
</ItemGroup>
I am relieved that I was able to get around this, but is there a neater way of doing this? I thought that the MSBuild engine would be able to figure this out in a more seamless way. Can anyone throw more light on this?
For x86 make sure you use the correct MSBuild which should be at:
c:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe
Rather than the x64 MSBuild that is located at:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe
I have tried searching the web for a solution to this problem but it seems very unclear. It sounds like people are seeing this but the solutions don't seem to work for me.
I have downloaded and installed YUICompressor.NET.MSBuild 2.3.0.0 from NuGet into my project.
I have created an MSBuild target file that closely resembles the example file included and exists as part of my .csproj file so I can run it in the AfterBuild step.
I am getting the following error everytime I try to build my VS project:
The "JavaScriptCompressorTask" task could not be loaded from the assembly SolutionDir\packages\YUICompressor.NET.MSBuild.2.3.0.0\lib\NET20\Yahoo.Yui.Compressor.Build.MsBuild.dll.
Could not load file or assembly 'Yahoo.Yui.Compressor, Version=2.3.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
Confirm that the declaration is correct, that the assembly and all its dependencies are available, and that the task contains a public class that implements Microsoft.Build.Framework.ITask.
My folder structure is as follows:
\Solution
\packages
YUICompressor.NET.MSBuild.2.3.0.0\lib\NET20\
\build
ProjectFile.csproj
In the csproj I have:
<UsingTask TaskName="CssCompressorTask" AssemblyFile="packages\YUICompressor.NET.MSBuild.2.3.0.0\lib\NET20\Yahoo.Yui.Compressor.Build.MsBuild.dll" />
<UsingTask TaskName="JavaScriptCompressorTask" AssemblyFile="packages\YUICompressor.NET.MSBuild.2.3.0.0\lib\NET20\Yahoo.Yui.Compressor.Build.MsBuild.dll" />
<Target Name="AfterBuild">
<ItemGroup>
<JavaScriptFiles Include="someFile.js" />
</ItemGroup>
<JavaScriptCompressorTask SourceFiles="#(JavaScriptFiles)" OutputFile="build\combined.js" DeleteSourceFiles="false" CompressionType="None" ObfuscateJavaScript="false" PreserveAllSemicolons="true" />
<JavaScriptCompressorTask SourceFiles="#(JavaScriptFiles)" OutputFile="build\combined.min.js" DeleteSourceFiles="false" CompressionType="Standard" />
</Target>
Am I missing something? Can anyone help me? Thank you.
Did you try to download version 2.3.0.0 directly from codeplex? Then make sure that the Yahoo.Yui.Compressor.Build.MsBuild.dll and Yahoo.Yui.Compressor.dll reside in the same directory. Like this I was able to make it work.
It's been a while since i've played around with the AfterBuild .. but how this works is that the the msbuild program is ran from some directory .. and therefore looks for those assembly files RELATIVE to where the msbuild is being executed from.
I'm not sure if this means the msbuild is being run from C:\program files(x86)\Microsoft Visual Studio\<whatever...>
TAKE NOTE: it's the folder/path (aka execution path) where visual studio is running the msbuild command from ... NOT where msbuild exists.
So therefore, it can't find the assemblies.
Try putting in the full path to the assemblies (just to see if that works).
eg. C:\Projects\Solution\packages\YUICompressor.NET.MSBuild.2.3.0.0\lib\NET20\Yahoo.Yui.Compressor.Build.MsBuild.dll
If it's running it from the \solution\bin directory, then you're in luck! cause then you can do ..\packages\YUICompressor.NET.MSBuild.2.3.0.0\lib\NET20\Yahoo.Yui.Compressor.Build.MsBuild.dll' (the..means: from thisbindirectory, go up one level tosolutionthen down intopackagesand then down intoYUIComp`... etc.
So the answer to your question is this: Find the location Visual Studio is running the msbuild command from, during an AfterBuild. Maybe put in some code in there to say 'write to file => current path i'm in :P'
One of our underlying projects is packaged via NuGet and distributed (internally) so that other teams can consume the library - let's call it "Core." With NuGet 1.x, we got a reference in our .csproj file that looked something like this:
<Reference Include="Core">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Core.1.4.1.381\lib\net40\Core.dll</HintPath>
After upgrading to NuGet 2.0, this same reference looks like this:
<Reference Include="Core, Version=1.4.1.381, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Core.1.4.1.381\lib\net40\Core.dll</HintPath>
In the Include attribute, does the Version matter? I'm explicitly setting SpecificVersion to false. I've looked through the MSBuild Project File Schema Reference, but it didn't see anything regarding how this attribute is parsed.
Thanks for input on this.
-SethO
I tried under VS2010,
if you specify :
<SpecificVersion>False</SpecificVersion>
the version under <Reference></Reference> is ignored.