copy App.config when compiling with CodeAnalysis.Compilation.Emit - c#

I am compiling a C# project using the Roslyn CodeAnalysis APIs, as in the following snipet:
...
EmitResult emitResult = null;
using (FileStream outputFileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write))
{
emitResult = compilation.Emit(outputFileStream, null);
}
...
This works perfectly. However, when I want to compile an executable project that contains an App.Config file, this file is not emitted in the output directory (similar to how it is emitted when compiling via Visual Studio), which is problematic if you want to parse some configs from that file at runtime.
I realize that it should not be emitted by default, but I would probably need to enable some option, or to somehow extract the App.Config path from the Project object and emit it myself? I cannot seem to find any information regarding this, and the Project class does not seem to contain any information regarding config files (unless I am missing something).
Is there some specific API that I need to use to achieve the above? Any pointers to existing code that does this available?
Thanks a lot!

Roslyn is not a build system, it's a compiler. It can only transform code into assemblies (and PDBs). When you compile a .csproj in Visual Studio, MSBuild reads all the properties and items in the project file and translates them into a series of steps that, among other things, call the Roslyn compiler, copies App.config files to the output directory, copies references, etc.
While it's true Roslyn can read MSBuild project files, it only uses this information for compilation purposes, such as locating references.
So, if you're dynamically compiling assemblies, you'll have to copy the App.config file yourself (note - VS doesn't emit this file - it just copies and renames it).

Related

SuppressMessage compiler warning CS1591 for generated code (xsd.exe)

I'm using the tool xsd.exe in several projects to generate classes for my data model.
When turning on documentation file generation in my csproj the compiler starts to show lots of warnings of type: CS1591:Missing XML comment for publicly visible type or member pointing at generated constructors.
As this is kind of intended behavior I am trying to figure out how to suppress these warnings.
But only for the types generated by xsd.exe which are contained in a single source file.
The file content will be replaced by the xsd.exe the next time I run it.
Any modifications to the file will be lost in that process.
So adding a #pragma warning disable to the file is not a solution here
(I sometimes even use a build target which regenerates the code on Build).
But .NET seems to have a builtin mechanic for this case: SuppressMessageAttribute at assembly level (Microsoft Docs: Suppress warnings).
So I went and created a file GlobalSuppressions.cs with the following content:
[assembly: SuppressMessage("Compiler",
"CS1591:MissingXmlCommentForPubliclyVisibleTypeOrMember",
Justification = "Generated code",
Scope = "member",
Target = "M:Company.IO.Component.Concrete.Configuration.ConfigItem.#ctor")]
But the suppression is being ignored.
Anyone any ideas?
I found a solution without having to manipulate the generated source file.
Short
An .editorconfig can be placed in the directory of the generated source files.
The rules contained in the file are applied to this directory and descendants only.
Long
output generated code in a seperate directory containing only generated source files
add .editorconfig to that directory
configure message severity in .editorconfig as desired
In my case this looks as follows
Directory structure
\Configuration
\Schema
\.editorconfig # applied to code files in this directory only
\schema.xsd # template for xsd.exe
\schema.cs # generated by xsd.exe
Project.csproj
Code.cs
Content of .editorconfig
# message severity for generated code files
[*.cs]
dotnet_diagnostic.CS1591.severity = none
This might have worked out better if I generated the code directly to the obj/ folder naming it something like schema.g.cs.
But I wanted to keep the generated file in the repository.
This will possibly be changed later on.

Using Grpc.Tools with Protoc plug-in to generate additional C# files

I am using Grpc.Tools (2.38.1) to generate C# types and gRPC stubs from a Test.proto file containing some service definitions.
To do this I have the following in my project's .csproj file:
<ItemGroup>
<Protobuf Include="**/*.proto" />
</ItemGroup>
This is all working fine: my Test.proto gets compiled to Test.cs and TestGrpc.cs in the obj/Debug folder of my project. The types within them can be referenced from within other types in the project.
But I need to create a WCF interface for the service too, so I thought I could generate this using a custom Protoc plug-in. So I wrote a simple Protoc plug-in that writes out a TestWcf.cs file containing an interface. I then placed this plug-in executable on my path named protoc-gen-blah.exe and updated the entry in the .csproj file to this:
<ItemGroup>
<Protobuf Include="**/*.proto" AdditionalProtocArguments="--blah_out=obj\Debug" />
</ItemGroup>
This correctly creates the C# file, TestWcf.cs, with my interface in: fantastic.
The problem is that my interface within TestWcf.cs cannot be referenced from other types in the project unless I manually include the generated file in the project: something I do not have to do with the other generated files.
Whilst none of the files are included in the project by default―I have to enable 'Show All Files' to see them―Test.cs and TestGrpc.cs have arrows beside them in the Solution Explorer that allow them to be expanded to reveal the types inside. TestWcf.cs does not have this arrow. So Visual Studio is somehow aware that Test.cs and TestGrpc.cs are source code files.
Does anyone know what I need to do for my generated file to be automatically recognised by Visual Studio like the other two files are?
I suspect it has something to do with this part of the Grpc.Tools build target, as I noticed my TestWcf.cs file is not included in the files deleted by the Grpc.Tools clean either, but I can't see why it does not consider my generated file to be C#.
When I build, this is the Protoc call:
D:\...\Src\packages\Grpc.Tools.2.38.1\tools\windows_x86\protoc.exe --csharp_out=obj\Debug ⤶
--plugin=protoc-gen-grpc=D:\...\Src\packages\Grpc.Tools.2.38.1\tools\windows_x86\grpc_csharp_plugin.exe ⤶
--grpc_out=obj\Debug --proto_path=D:\...\Src\packages\Grpc.Tools.2.38.1\build\native\include ⤶
--proto_path=. --dependency_out=obj\Debug\xxxx_Test.protodep --error_format=msvs --blah_out=obj\Debug ⤶
Test.proto
The dependency file looks like this:
obj\Debug/Test.cs \
obj\Debug/TestGrpc.cs \
obj\Debug/TestWcf.cs: Test.proto
Thanks.
I believe the problem is caused some logic in Grpc.Tools that informs MSBuild of the files that have been generated:
public override string[] GetPossibleOutputs(ITaskItem protoItem)
{
...
var outputs = new string[doGrpc ? 2 : 1];
...
outputs[0] = Path.Combine(outdir, filename) + ".cs";
if (doGrpc)
{
...
outputs[1] = Path.Combine(grpcdir, filename) + "Grpc.cs";
}
return outputs;
}
This code only caters for two files being generated from a Protocol Buffer source (name.proto): the Protocol Buffers code generation (name.cs) and the gRPC code generation (nameGrpc.cs). It is not picking up the additional file and informing MSBuild that it exists, hence Visual Studio does not consider it to be code.
There is no away around this short of changing the Grpc.Tools code.

Generated AssembyInfo-file causes Multilingual-App-Toolkit to reset translations

When building our .Net application (Target Framework net461), MSBuild generates the AssemblyInfo files like:
[ApplicationName]_[randomHash]_wpftmp.AssemblyInfo.cs.
Additionally we are using MultilingualAppToolkit which references the *.resx file of the project
...original="[ApplicationName]/PROPERTIES/RESOURCES.RESX"
in the translation file (*.de.xlf). But this path is replaced by
...original="[ApplicationName]_[randomHash]_wpftmp/PROPERTIES/RESOURCES.RESX")
and therefore all translations (*.de.resx) are resetted, because they are not existing in the new namespace.
Is there any workaround to avoid replacing the "original"-references in the *.xlf file?

How to use a resource file in Visual Studio 2015

I'm creating integration tests (web class library project) for my ASP5 MVC6 application. I want to add a resource file with raw SQL to the test project for DB preparation.
I wanted to use good old resources (.resx). But I it's not available in add new item project's menu.
I found this answer pointing to a github repo. I guess this is where he reads the file:
using (var stream = FileProvider.GetFileInfo("compiler/resources/layout.html").CreateReadStream())
using (var streamReader = new StreamReader(stream))
{
return streamReader.ReadToEnd().Replace("~", $"{basePath}/compiler/resources");
}
I tried using System.IO.File.ReadAllText("compiler/resources/my.sql") in my test helper project in TestHelper class.
When I used the TestHelper class in my actual test project it was looking for the the file in test project directory.
TestProject/compiler/resources/my.sql insetad of TestHelperProject/compiler/resources/my.sql
I can figure out a couple of workarounds. However I'd like to do it the right way. Preferably like I would do in with a resx file:
string sql = Resources.MySql;
Any suggestions?
EDIT
Project type
Available items
It is a little more complicated because there's no tooling support but it is doable:
Create a resx file or copy it from somewhere else like a classic C# project
If you can use sake as the build system, then you can use the same target that we, the ASP.NET team use: https://github.com/aspnet/Universe/blob/dev/build/_k-generate-resx.shade otherwise, you need to implement something similar in your own build scripts
Place the resources either compiler/resources. If you place them under any other folder then include them in the project json (like here)
Build using sake then build using dnx. Your resource should be available as a strong type object

Include file in C#, Mono (gmcs)

I'm writing a really small application in C# and I need to include an .cs file with some class. How do I do that? I'm looking for some equivalent to PHP's "include file" or Python's "from file import something". Obviously "using" is not enough and the file has to be somehow linked. And I really don't want to start some new super-huge-totally-useless project using MonoDevelop or VisualStudio, I would like to stay with simple gmcs and command line.
You simply include both file names on the command line and ensure that the namespaces are the same or that the namespace of the included file is imported via a using statement or via fully qualified references. Then the command line for compilation looks like this:
gmcs mainFile.cs includeFile.cs
Note that the mono command line is designed to support the exact same syntax (with a few additions) as the Microsoft compiler so this is true for both of them.
Fundamentally this is what the project files and visual studio are doing (albeit going through an in memory msbuild equivalent)
There are two ways to "include" a file in .NET (and Mono)
Compile several files together.
gmcs mainFile.cs includeFile.cs
then files are then compiled together to a single assembly
Compile the includeFile to a separate assembly and reference that from the main assembly
gmcs -target:library includeFile.cs
gmcs -r:includeFile.dll mainFile.cs
this way you get two assemblies

Categories