Linking different Protobuf files C# - protocol-buffers

I'm trying to link protobuf together from different projects in my solution.
I seem to get File Not Found errors every time I build and can't find a way for the protobuf compiler to find the files.
The following protobuf files i'm using also a note that I trimmed some small things.
Project A
Service/Identity/Protos/User.Proto
import "Identity/Protos/Common.proto";
option csharp_namespace = "Service.Identity";
package user;
service ProfileController {
rpc CreateProfile(CreateProfileRequest) returns (common.GenericResult);
}
Project B
Shared/Protos/Common.Proto
option csharp_namespace = "Shared";
package common;
message GenericResult {
bool success = 1;
string exceptions = 2;
}
In the csproj files the following settings have been made.
Project A
<ItemGroup>
<Protobuf Include="Identity\Protos\User.proto" GrpcServices="Server" />
<Protobuf Include="..\Shared\Protos\Common.proto" Link="Identity\Protos\Common.proto" ProtoRoot=".." GrpcServices="None" >
</Protobuf>
Project B
<ItemGroup>
<Protobuf Include="Protos\Common.proto" GrpcServices="None" />
</ItemGroup>
The link seems to work but I can't import it in my user.proto
When I try and import it which I have tried in the following import statements in my user.proto seems to be able to find it.
import "Identity/Protos/Common.proto";
import "Protos/Common.proto";
import "Common.proto";
import "/Identity/Protos/Common.proto";
It gives me the following error.
All i want to do is be able to use the Common.Proto in my User.Proto. But it just does not work.

Related

Create NuGet package that when installed copies content to $(OutputPath) regardless of machine

I am trying to get a content file that is included with my NuGet package to be copied to the $(OutputPath) (in my case bin\Debug\netcoreapp3.1 where all the DLLs end up) of a target C# project when the target is built. This needs to work on developers' boxes and on build machines. Here's what I have so far.
I created a NuGet package project with a content node
<ItemGroup>
<Content Include="testsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
I am using the default values for the pack target inputs IncludeContentInPack and ContentTargetFolders, which are true and content;contentFiles respectively.
When I build the package, I get the following structure where testsettings.json appears in contentFiles\any\netcoreapp3.1 and content folders of the .nupkg.
When I install the package into a target .csproj,
the testsettings.json file is added to the project's root folder as expected
the <PackageReference Include="..." Version="..."/> line is added to the target .csproj as expected
no entry appears in the target .csproj for testsettings.json
When I build the target project, the DLLs go to $(OutputPath) but testsettings.json is not copied there, which makes sense because the default for a Content node's action is None). However, I need it to act like <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>.
I thought, "OK I will manually change testsettings.json using the Properties window to Copy if newer":
This adds a node to the target .csproj that looks like:
<ItemGroup>
<Content Update="C:\redacted\path\to\nuget\cache\package.name\N.N.N\contentFiles\any\netcoreapp3.1\testsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
which works great for my machine. This will fail for any build machine and any other developer's machine because that path is specific to my machine.
How can I create the package so that when installing the package it will result in a repeatable way of getting testsettings.json in the right place?
In the course of researching this, I noticed that testhost.exe and testhost.dll somehow magically end up in the right spot even though their source NuGet package's .nuspec and structure are similar to mine. This has left me at bit of a loss on what I need to do to modify my NuGet package's .nuspec, .nupkg, build sequence, etc.
Preferable: Is there a way to do this, and if so how? Highly not preferable: Is there a manual change I can make in the target project such that I can get a reliable way of copying the file that works everywhere?
<ItemGroup>
<Content Include="XXXXX.json">
<PackageCopyToOutput>true</PackageCopyToOutput>
</Content>
</ItemGroup>
should meet your requirements.
In your NuGet project's xxproj file, add codes like above and use <PackageCopyToOutput>.
In the new project which you'd like to test, before installing your NuGet Package, remember to clear the NuGet Package cache first, and then install your NuGet package and build your project, check if the XXXXX.json file has been copied to Output.
Similar thread: Copy JSON file to bin from class library nuget package.

How can I use Conan to both provide dependencies for the Visual Studio IDE and create a package of the same project, again using Conan dependencies?

In the documentation for Conan, I found this page:
https://docs.conan.io/en/latest/howtos/visual_studio_packages.html
This page steps through 2 examples, one is using Conan to generate a .props file to point the Visual Studio IDE to dependencies for your project, and the other is creating a package of a Visual Studio solution/project(s) that has dependencies using Conan. Both of these work for me separately, however combining them to both be able to use VS IDE with Conan dependencies and package the VS solution fails for the following reason:
When using Conan (or the Conan extension for VS) to handle dependencies while using the IDE, the visual studio project files (.vcxproj) are modified to include and import call to the Conan-generated .props files. The project files are source-controlled through Git. Then when calling conan create on the same conanfile.py, my source method clones down the repo at the specified tag. This will include project files that are told to import the Conan-generated build files, and then the build method in conanfile.py will "inject" its own .props file to serve the same purpose. I see two issues here:
The build fails because the project files fail to load, because I do not commit the Conan-generated .props files to the repo. Those are considered temporary build files. (You can change my mind here but please look at #2)
Even if I commit the Conan-generated props to the repo, and then they will be cloned down and allow msbuild to load the project files, then the Conan msbuild helper is trying to load its own dependencies, and that will cause to doubly-defined symbols and other issues.
Is there some way to have the MSBuild helper (in the build() method of the conanfile.py) use the same .props that would be generated by the conan install command instead of its own when using a command line arg to msbuild as part of the conan create command? Or a better solution than that?
I tried to be as clear as possible but please let me know if I can clarify the issue further. Any help is appreciated.
Below is an example conanfile.py trying to accomplish this:
from conans import ConanFile, MSBuild, tools
from conans.tools import load, save
import re, os
class TestConan(ConanFile):
name = "test"
version = "1.1.2"
settings = "os", "compiler", "build_type", "arch"
generators = "visual_studio_multi", "visual_studio"
requires = "snappy/1.1.8", "boost/1.73.0", "rapidjson/1.1.0"
options = {"shared": [True, False]}
default_options = {"shared": True, "boost:shared" : "True", "snappy:shared" : "True"}
def source(self):
git = tools.Git(folder=self.name)
git.clone("http://path/to/test/repo/testrepo.git", branch="1.1.2")
def build(self):
msbuild = MSBuild(self)
msbuild.build("testrepo/testrepo.sln", platforms={"x86":"Win32"}, upgrade_project=False)
def package(self):
self.copy("*.h", dst="include/testrepo", src="testrepo")
self.copy("*.hpp", dst="include/testrepo", src="testrepo")
self.copy("*.lib", dst="lib", keep_path=False)
self.copy("*.dll", dst="bin", keep_path=False)
def package_info(self):
self.cpp_info.libs = ["test"]
self.cpp_info.includedirs = ["include"]
self.cpp_info.bindires = ["bin"]
self.cpp_info.libdirs = ["lib"]
EDIT: Potential Workaround
The Visual Studio Project files allow you to conditionally import projects. I adjusted them to only import if the Conan property sheets were found. This made it so that when creating the package using the conanfile.py above, the cloned repo would correctly load for msbuild and then Conan could inject the dependencies accordingly. The Conan extension for Visual Studio doesn't override the conditions so you can still run conan install and use the IDE for development, then clean the temporary build files (prop sheets) and commit. In my case I just added the .conan folder the extension creates to .gitignore.
Example code for the workaround:
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project=".conan\conanbuildinfo.props" Condition="exists('.conan\conanbuildinfo.props')" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project=".conan\conanbuildinfo.props" Condition="exists('.conan\conanbuildinfo.props')" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project=".conan\conanbuildinfo.props" Condition="exists('.conan\conanbuildinfo.props')" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project=".conan\conanbuildinfo.props" Condition="exists('.conan\conanbuildinfo.props')" />
</ImportGroup>
Warning: I am not a project file xml expert, there might be a better way to accomplish the same as above, but this worked for me.
This is a workaround. What I have found is the examples in the Conan how-tos docs I have at the beginning of this post use "exports_sources" for obtaining their source. It seems that Conan correctly handles this case, but not the case where you instead obtain your source (using the source() method) from SCM, in my case git cloning a specific tag. I tested this with my project repo but for our development flow we prefer the ability to package a tagged git commit.
Take a look at the new "msbuild" generator. I found that it fixes a lot of the issues I was having with this, because it changes the architecture a bit.
At the top-level, it creates a conan_deps.props file (that could(?) be committed to the repo.
From there, when you perform a conan install, it creates one "top-level" .props file per dependency, with individual .props files below it for each of the configurations.
I've found that this has worked much better for me, as it gave me much more flexibility in my dev environment as well.
The only problem I'm having now is that the VS Extension hasn't 'officially' been updated to support it yet... but I've been getting around that by simply using the embedded Developer Command Prompt for now :)
EDIT (11/4/2020): I actually opened a Github issue in the VS Extension Repository, and the devs got back super quickly! Looks like they are waiting for things to stabilize a bit before making changes to the extension.

Using import in proto file with Visual Studio/Rider

I am getting a "File not found" error when using import in a .proto file.
I am using Rider but have the same problem when using Visual Studio.
First proto File:
syntax = "proto3";
import "/fileToImport.proto";
service GreeterAPI {
rpc SayHello (SayHelloRequest) returns (SayHelloResponse);
}
message SayHelloRequest {
string name = 1;
}
message SayHelloResponse {
string answer = 1;
}
Second proto file that i want to import:
syntax = "proto3";
message Foo {
string bar = 1;
}
Both files are located next to each other in the project directory.
.csprjo File:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.10.1" />
<PackageReference Include="Grpc.Core" Version="2.25.0" />
<PackageReference Include="Grpc.Tools" Version="2.25.0" />
<Protobuf Include="**/*.proto" />
</ItemGroup>
</Project>
If i build the project without the import line everything is fine. But with the import line i get "File not found"
I know i can use the --proto_path to tell protoc all the files.
But i don't want to build an extra pre-build script or something like that.
I want to use the build in support of the IDE.
I had the same problem as you, the fix that worked for me was adding the containing folder of the .proto files to the import. Assuming both .proto file are in a folder "Protos", try changing
import "/fileToImport.proto";
to
import "Protos/fileToImport.proto".
Also try changing in the .csproj file from
<Protobuf Include="**/*.proto" />
to
<ItemGroup>
<Protobuf Include="Protos/includingFile.proto" Link="includingFile.proto"/>
<Protobuf Include="Protos/fileToInclude.proto" Link="fileToInclude.proto"/>
</ItemGroup>
Hope that helps

how use properties from Directory.Build.props in Import from project file of Visual Studio

I have this situation:
I have a .proj file in project directory:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Compile Include="PQExtensionTest.pq">
<SubType>Code</SubType>
</Compile>
<Content Include="PQExtensionTest.query.pq">
<SubType>Code</SubType>
</Content>
</ItemGroup>
<!-- <Import Project="..\Directory.Build.props" /> -->
<Import Project="$(aProperty)add.targets" />
</Project>
In the solution directory (..\ from project directory) I have file Directory.Build.props:
<Project DefaultTargets="BuildExtension" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<aProperty>$(MSBuildProjectDirectory)/Subdir/</aProperty>
</PropertyGroup>
</Project>
In the project directory I have subdirectory "Subdir", where there is file add.targets, which contains all the targets I need (do not show it's contains here because it is not relevant to the problem).
So all above has this folder structure:
Solution directory
Directory.Build.props
Project Directory
Project.mproj
Subdir
add.targets
Preparing all the above, I expected that aProperty will be initiated before the import and the import of add.targets will happen without problem. But I get error that imported project is not found, and I see in error message that MSBuild tries to import from project directory, and not from subdirectory Subdir.
If I uncomment this row:
<Import Project="..\Directory.Build.props" />
all works fine.
The only reasonable explanation for me of such behavior is that aProperty is empty at the moment of importing, because explicit import happens before implicit one.
Is there any way to force MSBuild to inexplicitly import Directory.Build.props before any other imports, while work in Visual Studio?
"While in Visual Studio"
For C# and VB language project, we don't need to import
Directory.Build.props manually or force it before other imports.
When creating a new project(C# or VB) in VS, open its proj file we can find the format is like this:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
...
</PropertyGroup>
<ItemGroup>
...
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
Every time when creating new C# or VB project, the top line within the <Project>node is Import project="Microsoft.Common.props", and we can find the sentence from this document:
When MSBuild runs, Microsoft.Common.props searches your directory structure for the Directory.Build.props file (and Microsoft.Common.targets looks for Directory.Build.targets). If it finds one, it imports the property.
So in visual studio, we don't need to force it before other imports.Its always called after import Microsoft.Common.props, and since the import Microsoft.Common.props is always first line of project node by default, the Directory.Build.Targets is always implicitly imported right after the Microsoft.Common.props and before others.
Note: This feature only supports C# and VB, cause only these two kinds of projects will import the Microsoft.Common.Props in proj file.
And for other kinds of projects, just like your .mproj or
.vcxproj(C++), this feature(Directory.Build.props) is not supported
yet.
So the Directory.Build.Targets or .props is the same as any custom .props. It doesn't make difference between Directory.Build.Targets and anyName.props.
In this way,to read the value in it we have to use import project to call it manually. And that's why the build can't succeed until you uncomment the row:<Import Project="..\Directory.Build.props" />
The way to import properties from 'Directory.Build.props' file from nested folder structure is given below:
Refer: https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-your-build?view=vs-2019
Note that:
1. These property file definition works from MSBuild tools version 15.0
2. You need to be aware of where to place this import: At the beginning of the file or at the end of the file. Generally it is good to place at the end as nested properties will be visible to parent properties.

Set content files to "copy local : always" in a nuget package

I generate a nuget package from a project with this command in the post-build event. the variable %conf% is set to the right configuration (debug or release) and %1 is the project name (e.g. "MyCompany.MyProject").
nuget pack -Prop Configuration=%conf% "%1.csproj" -exclude *.sql -IncludeReferencedProjects
This package is for our own usage only, it will never be published on nuget. It ends in our private repository.
In the project, there is a file that is set to generate action : content and copy local : always. (My Visual Studio is in French, so I'm not 100% sure of the translation). Let's name it importantfile.xml.
In the generated package, I end up with this structure :
- content
- importantfile.xml
- lib
-net45 (.NetFramework,Version=v4.5)
-MyCompany.MyProject.dll
Which is fine, I want importantfile.xml to be deployed in the package, because, well, this file is important!
When I install the package in another project, importantfile.xml is deployed at the root of the project. That's OK. But it is not set to copy local : always.
I need importantfile.xml to be copy local : always in this project where I install my package.
How can I achieve that?
Notes :
I can set copy local : always on the file just after installing the package, that's no big deal. I would live with it if later updates of the package would let this property as-is, which is not the case. When updating the package, copy local is reset to never (as stated here).
There's a nuspec file in the project's folder, here it is :
<?xml version="1.0"?>
<package >
<metadata>
<id>$id$</id>
<version>$version$</version>
<title>$title$</title>
<authors>$author$</authors>
<owners>$author$</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>$description$</description>
<copyright>Copyright 2014</copyright>
<tags>some random tags</tags>
</metadata>
</package>
Instead of using a PowerShell script another approach is to use an MSBuild targets or props file with the same name as the package id:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)importantfile.xml">
<Link>importantfile.xml</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
In the nuspec file then, instead of adding the required files to the Content directory, add them to the Build directory along with the targets file.
Build
importantfile.xml
MyPackage.targets
lib
net45
MyAssembly.dll
If you require different content for different architectures then you can add architecture folders under Build also each with their own targets file.
Benefits to using a targets file over the PowerShell script with NuGet Content directory:
required content files aren't shown in the project in Visual Studio
content files are linked to rather than copied into the directory of each project which references the NuGet package (preventing there being multiple copies and keeping behaviour the same as for assemblies / libraries from NuGet packages)
PowerShell scripts only work in Visual Studio and aren't run when NuGet is run from the commandline (build servers, other IDEs and other OS), this approach will work everywhere
PowerShell install scripts are not supported in NuGet 3.x project.json system.
I know you guys got a working solution to this but it didn't work for me so I'm going to share what I pulled out of the NLog.config NuGet package install.ps1 (github source here).
NOTE: this is not my code, this is the content of the install.ps1 from
the NLog.config nuget package just sharing the knowledge.
It seems a little more straight forward to me and just hoping to help others that will likely stumble upon this.
You can find the accepted int values for BuildAction here and the accepted values for CopyToOutputDirectory here.
if the link breaks again
Fields
prjBuildActionCompile 1
The file is compiled.
prjBuildActionContent 2
The file is included in the Content project output group (see Deploying Applications, Services, and Components)
prjBuildActionEmbeddedResource 3
The file is included in the main generated assembly or in a satellite assembly as a resource.
prjBuildActionNone 0
No action is taken.
param($installPath, $toolsPath, $package, $project)
$configItem = $project.ProjectItems.Item("NLog.config")
# set 'Copy To Output Directory' to 'Copy if newer'
$copyToOutput = $configItem.Properties.Item("CopyToOutputDirectory")
# Copy Always Always copyToOutput.Value = 1
# Copy if Newer copyToOutput.Value = 2
$copyToOutput.Value = 2
# set 'Build Action' to 'Content'
$buildAction = $configItem.Properties.Item("BuildAction")
$buildAction.Value = 2
I have made this which copies files from my build folder to the output folder (bin/debug or bin/release). Works like a charm for me.
Nuspec file:
<package>
<files>
<file src="\bin\Release\*.dll" target="lib" />
<file src="\bin\Release\x64\*.dll" target="build\x64" />
<file src="\bin\Release\x86\*.dll" target="build\x86" />
<file src="MyProject.targets" target="build\" />
</files>
</package>
MyProject.targets
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<NativeLibs Include="$(MSBuildThisFileDirectory)**\*.dll" />
<None Include="#(NativeLibs)">
<Link>%(RecursiveDir)%(FileName)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
You can use PowerShell and the Install.ps1 hook provided by NuGet.
See the documentation.
Via PowerShell you have to 'search' for the content element which includes your importantfile.xml in an attribute. When the script found it, it has to add <CopyToOutputDirectory>Always</CopyToOutputDirectory> as a child element.
<Content Include="importantfile.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
You can find some PowerShell snippets here. Just take a look at the .ps1 files.
You could try the following (not tested). The file has to be named Install.ps1 and copied into the tools folder:
param($installPath, $toolsPath, $package, $project)
# Load project XML.
$doc = New-Object System.Xml.XmlDocument
$doc.Load($project.FullName)
$namespace = 'http://schemas.microsoft.com/developer/msbuild/2003'
# Find the node containing the file. The tag "Content" may be replace by "None" depending of the case, check your .csproj file.
$xmlNode = Select-Xml "//msb:Project/msb:ItemGroup/msb:Content[#Include='importantfile.xml']" $doc -Namespace #{msb = $namespace}
#check if the node exists.
if($xmlNode -ne $null)
{
$nodeName = "CopyToOutputDirectory"
#Check if the property already exists, just in case.
$property = $xmlNode.Node.SelectSingleNode($nodeName)
if($property -eq $null)
{
$property = $doc.CreateElement($nodeName, $namespace)
$property.AppendChild($doc.CreateTextNode("Always"))
$xmlNode.Node.AppendChild($property)
# Save changes.
$doc.Save($project.FullName)
}
}
You should also check if everything is removed completely when uninstalling the package.
Note by Jonhhy5
When updating the package via update-package, Visual Studio warns that the project is modified "outside the environnment". That's caused by $doc.Save($project.FullName). If I click reload before the command is fully terminated, it sometimes causes errors. The trick is to leave the dialog there until the process finishes, and then reload the projects.
I have written a little tool called NuGetLib to automatically add files to the nuget package after build.
create a tools folder with your Install.ps1 script
build your nugetPackage
add the tools folder to the built nugetPackage
https://stackoverflow.com/a/47134733/6229375

Resources