I upgraded a netcore1.1 project to the new VS2017/csproj.
In my test projects only, it added:
<PropertyGroup>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
</PropertyGroup>
I did some digging to discover that it generates these files in the bin directory:
ProjectName.Tests.runtimeconfig.json
ProjectName.Tests.runtimeconfig.dev.json
What is this setting and these files, and why do I need them?
Why were they only generated for my test projects?
These are specific to .NET Core projects and specify
Which runtime and version to use. Typically Microsoft.NETCore.App. The "host framework resolver" looks for a matching folder inside the shared folder (e.g. C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.1.2). This is important since multiple runtimes can be installed side-by-side and the host needs to know which one to use when you run dotnet myapp.dll.
Additional options for the runtime. The most prominent is probably the garbage collection setting that switches between "desktop" and "server" mode. When you set <ServerGarbageCollection>true</ServerGarbageCollection> int he csproj file, this will cause a value in the runtimeconfig.json to be set. (This property is defaulted to true for web projects)
Additional options for the host. additionalProbingPath for example is set to your local NuGet cache which contains the restored packages. You may have noticed that referencing a NuGet package does not cause its dll files to be copied to the output directory (by default). The host uses the additional probing path to look for packages / dlls referenced to in this location (actually it is a two-step lookup: deps.json tells the host which packages to use and this property tells where to look for this package). Since this is only used for development and shouldn't end up in the published output (since this would mean relying on a NuGet cache on the target), this settings is put into a runtimeconfig.dev.json.
"Classic" .NET Framework projects also had a concept of letting the application set some runtime settings. This was accomplished by having an .exe.config file (which would be built from an App.config file in a project if present). You can think of runtimeconfig.json as "the new .exe.config" but the only have a few overlapping concerns.
Related
How can I <Import> a .targets file that is included in <PackageReference>d NuGet package in a non-SDK-style web application project that targets .NET 4.7.2?
Background
The web application project is not an SDK-style project. It references a class library project in the same solution. This class library project is an SDK-style project. It has a package reference to Microsoft.Data.SqlClient version 4.1.0, which depends on Microsoft.Data.SqlClient.SNI.runtime 4.0.0. The latter contains native binaries which are automatically copied to the bin directory of the project.
The native binaries are not copied to the bin directory of the web application project. This is probably because of this:
SNI is the native C++ library that SqlClient depends on for various network operations when running on Windows. In .NET Framework applications that are built with the MSBuild Project SDK, native DLLs aren't managed with restore commands. So a ".targets" file is included in the "Microsoft.Data.SqlClient.SNI" NuGet package that defines the necessary "Copy" operations.
The included ".targets" file is auto-referenced when a direct dependency is made to the "Microsoft.Data.SqlClient" library. In scenarios where a transitive (indirect) reference is made, this ".targets" file should be manually referenced to ensure "Copy" operations can execute when necessary.
Recommended Solution: Make sure the ".targets" file is referenced in the application's ".csproj" file to ensure "Copy" operations are executed.
Source
Not sure what "built with the MSBuild Project SDK" means exactly, but I think my scenario qualifies.
Now I'm trying to implement the recommended solution, but I cannot get it to work.
What I did
Step 1: add package reference to Microsoft.Data.SqlClient.SNI version 4.0.0:
<PackageReference Include="Microsoft.Data.SqlClient.SNI">
<GeneratePathProperty>true</GeneratePathProperty>
<Version>4.0.0</Version>
</PackageReference>
I added <GeneratePathProperty> because of step 2 below. According to the documentation this should make a property available that can be used to refer to files in the package:
Sometimes it is desirable to reference files in a package from an MSBuild target. In packages.config based projects, the packages are installed in a folder relative to the project file. However in PackageReference, the packages are consumed from the global-packages folder, which can vary from machine to machine.
To bridge that gap, NuGet introduced a property that points to the location from which the package will be consumed.
Step 2: import the targets file:
<Import Project="$(PkgMicrosoft_Data_SqlClient_SNI)\build\net46\Microsoft.Data.SqlClient.SNI.targets" Condition="Exists('$(PkgMicrosoft_Data_SqlClient_SNI)\build\net46\Microsoft.Data.SqlClient.SNI.targets')" />
You can see that the package contains this targets file using the NuGet package explorer (link). The PkgMicrosoft_Data_SqlClient_SNI property is named according to the documentation (linked above) and based on their example:
MSBuild properties and package identities do not have the same restrictions so the package identity needs to be changed to an MSBuild friendly name, prefixed by the word Pkg.
Step 3: verify the tool versions on our agent:
MSBuild 16.11.2.50704 (installed on the agent C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\bin)
NuGet 6.1.0.106 (installed via a pipeline task)
According to the documentation linked above, these fulfil the minimum requirements (MSBuild 16 and NuGet 5).
Step 4: run nuget restore on our build agent and then build the project using the Azure DevOps MSBuild#1 task with msbuildArguments: '/t:Build'.
Step 4: collect output and publish as an artifact.
Outcome:
The SNI files are not present in the output. When building locally with MSBuild 17.0.0.52104 (installed in 'C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\MSBuild\Current\bin') I do see the SNI files.
We have an internal JavaScript library that we'd like to share between multiple projects. Actually we are already sharing it via file copying, but this has (predictably) resulted in multiple forks of the code.
The consuming projects are a mix of "full" ASP.NET (MVC and Web Forms) and ASP.NET Core MVC. (I'm planning on creating two separate packages.)
Installing into ASP.NET projects seems to work fine, but I'm having problems with ASP.NET Core.
Initially I had all the artifacts within a files element, and nothing at all was showing up in the consuming project. After re-reading the docs, I realized that ASP.NET Core projects would use a PackageReference ... so I would have to use a contentFiles element instead of (or in addition to) a files element.
I created a contentFiles folder and a script to copy the requisite files from the source project folder structure into contentFiles/any/any/wwwroot/lib/ourAwesomeWidget, and modified the package manifest accordingly.
This works. Sort of. The package appears to get build correctly. The files do get added to the consuming project, but they get added as links; the actual files (the link targets) reside in my local package cache.
The relevant portion of the package manifest is:
<metadata minClientVersion="3.3">
...
<contentFiles>
<files include="**/*" buildAction="Content"
copyToOutput="true" flatten="false" />
</contentFiles>
</metadata>
<files>
<file src="contentFiles\**" target="contentFiles" />
</files>
Part of the issue is that I don't find the docs very clear concerning contentFiles. All the examples show a single file element ... but the include attribute on the files element is required, so it's not clear what the individual file elements would even do.
Is there a way to get the actual files (not links) added to the consuming project? Or, alternatively, is there a way to get the package to install as a "normal" package (rather than a PackageReference)?
Update:
I did some further digging and found this answer by #Martin to a similar question -- but he answered this one before I had a chance to update it.
It appears this behavior (adding files as links) is by design.
I find this highly unsatisfactory, because (as #Martin points out), our JavaScript library will not be available during development on consuming projects.
But part 2 of my question still stands. According to the docs,
By default, PackageReference is used for .NET Core projects, .NET Standard projects, and UWP projects targeting Windows 10 Build 15063 (Creators Update) and later.
Is there a way to trigger the non-default behavior, i.e. allow .NET Core projects to consume packages other than via PackageReference?
contentFiles are supposed to be added as a link. The contentFiles section controls the msbuild items that are generated for these files into the obj\projectname.csproj.nuget.g.props file.
The copyToOutput="true" will cause the items to be copied to the output and publish directory. However that does not help you when running the application during development, since it will be run from the project directory, not the output directory.
Consider consuming client libraries via npm (since bower is deprecated).
I have a winforms app, which is deployed via click-once publishing.
Assuming my tests are correct, the only way to have this same app installed more than once in the same machine, is the each publish be made for a different Assembly Name.
My problem is that I am running the msbuild via jenkins, and, to accomplish the above, I would add /p:AssemblyName={whatever}, which will rename all assemblies generated by the build to this same {whatever} value. If the output of my build is 1 exe and 5 dlls, all 6 files will be named {whatever}.
Which in turn raises CS1704: An assembly with the same simple name {whatever} has already been imported.
Is the only way out of this to perform all csc.exe calls that msbuild generates, myself, and see if I can set different assembly names per project ?
Or are there other ways to solve this ?
The problem here is that every property passed to MSBuild as a command line argument is considered a global property and will override the property on all projects built in that invocation. The solution is to introduce an intermediate property that only the "main" project consumes.
Edit the app's csproj file to include this (in the place where AssemblyName is already defined - assuming here that WindowsFormsApp1 is the name of the application):
<AssemblyName>WindowsFormsApp1</AssemblyName>
<AssemblyName Condition="'$(OverrideAssemblyName)' != ''">$(OverrideAssemblyName)</AssemblyName>
you can then use the msbuild commands using /p:OverrideAssemblyName=AnotherName without creating conflicting assembly names on referenced libraries. The property is still defined globally, but only one project consumes it.
What is the purpose of the 'runtimes' folder that gets published along with all my project files? I have a VS Online account, and have the build/deploy process configured through there. The 'runtimes' folder is certainly not a folder that exists in source control or my project folder.
'runtimes' folder contents:
example contents:
Thanks,
Drew
Like #Gregory_Ott I was facing a similar issue where my deployment was an FDD deployment and the 'runtimes' folder was still being created. All I did was, mentioned the runtime and set the self-contained property to false.
In the following example I have used dotnet to publish:
dotnet publish -c Release -o ..\publish --runtime win-x64 --self-contained false
The following link should help you with the deployment:
https://learn.microsoft.com/en-us/dotnet/core/deploying/deploy-with-cli
These exist because you are building your application as a self contained application as opposed to one that is dependent upon the framework being installed on the machine executing it. Documentation can be found at https://learn.microsoft.com/en-us/dotnet/articles/core/deploying/ describing the various options that can be used to control this output.
If portable target runtime is selected, runtime folder is created.
FWIW, I was facing this situation with a .NET 5 application. An empty "runtimes" project was there in the output directory. After wasting a few minutes I realized that the folder was a left-over from a previous build. I deleted bin/obj folders completely and published again and the "runtimes" folder is no longer there in the output. I didn't have to change anything in the project file or build options. Hope it saves someone else a few minutes too.
Could this explain the existence of a runtimes folder in an FDD deployment:
A framework-dependent deployment with third-party dependencies is only
as portable as its third-party dependencies. For example, if a
third-party library only supports macOS, the app isn't portable to
Windows systems. This happens if the third-party dependency itself
depends on native code. A good example of this is Kestrel server,
which requires a native dependency on libuv. When an FDD is created
for an application with this kind of third-party dependency, the
published output contains a folder for each Runtime Identifier (RID)
that the native dependency supports (and that exists in its NuGet
package).
Source: https://learn.microsoft.com/en-us/dotnet/core/deploying/deploy-with-vs?tabs=vs156#framework-dependent-deployment
Setting your RuntimeIdentifier might be the solution. In my case, working with an Azure Function, it cut about 500 megs and reduced my archive down to 174 megs. This is important because on Consumption plans you get very limited storage.
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>preview</LangVersion>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
<OutputType>Exe</OutputType>
<!--
Prevents the runtimes folder from being created in publish, which contained 500 megs of runtime files we don't need.
-->
<RuntimeIdentifier>win-x86</RuntimeIdentifier>
<PublishReadyToRun>true</PublishReadyToRun>
</PropertyGroup>
In the publish profile in Visual Studio, if Target runtime is set to 'Portable' then all possible runtimes are generated. This is the default, so the output can be reduced by a more selective choice if applicable:
I have an automated build process set up to run from a build definition in TFS, which publishes a web application and generates/executes a database project script successfully via publish profiles that are passed as msbuild arguments in the build process definition. Everything is now running as expected except that several unnecessary files are being deployed to the target folder, including the .csproj file, all of the config transforms, and the properties folder which contains all of my publish profiles.
This is strange because 1. It's definitely not including ALL files/folders and mostly appears to be including ones used by the publish profile like transforms, while applying the transform correctly and excluding any explicitly excluded file (as defined in the pubxml), and 2. The process works perfectly if I do it by publishing from the project in Visual Studio 2013. I have the profile configured to only include files needed by the application, and I've confirmed in the csproj file that this property is there.
I tried excluding the properties folder from deployment in the pubxml file, but this causes the build to crash because it can't find the assembly file. What I've gathered is that the process is keeping all files it needs to complete the build, and dropping all of those files in my destination folder. FWIW, I'm using the "file system" method and I'm not sure yet if web deploy will make a difference. I haven't been able yet to connect to the target server with web deploy, but that's a separate problem to solve. Is there something in the build that I can configure so that my destination folder has only the files it needs to run the application, and not the files needed to BUILD the application?
FYI I also have not been using a drop folder, I'm not sure if that makes a difference or not but that might be the only thing I haven't tested as it doesn't seem necessary since I'm using a publishprofile and don't want to use the default tfs build configuration.
I found a solution that works well enough, after reading this: http://www.asp.net/web-forms/tutorials/deployment/advanced-enterprise-web-deployment/excluding-files-and-folders-from-deployment
This was a little uglier solution than I wanted, since it requires hard-coding the names of excluded files, but it does the trick and only requires identifying the files and folders in one location instead of altering a publish profile for each target environment. I created a wpp.targets file and used the ExcludeFromPackageFolders and ExcludeFromPackageFiles elements to identify the extra files. Ironically, if I don't also name the wpp.targets file in the exclude element, THAT file is included in my package. It's possible MSDeploy doesn't have the same issues with TFS as filesystem, but after spending half a day trying to work through a different set of issues and permissions workarounds, we decided that file system is a cleaner publishing method.