Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make all internal types in Common internal OR public #1759

Merged
merged 12 commits into from
Feb 20, 2022

Conversation

pgrawehr
Copy link
Contributor

@pgrawehr pgrawehr commented Jan 7, 2022

This cleans up a few dependency issues between the bindings and the common project. The classes marked internal (including the Interop classes) are now public when building CommonHelpers.csproj but still internal for the final assembly. This removes the need to link to individual source files outside their binding folders, which causes build problems when they're used from multiple places.

Microsoft Reviewers: Open in CodeFlow

@ghost ghost added the area-device-bindings Device Bindings for audio, sensor, motor, and display hardware that can used with System.Device.Gpio label Jan 7, 2022
@@ -12,6 +12,9 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
<TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage)</TargetsForTfmSpecificContentInPackage>
<!--Partial elements should declare access modifier: Disabled for this project to simplify handling in common project. See also there.
If we disable this here, the warning is still emmited for the individual projects.-->
<NoWarn>$(NoWarn);SA1205</NoWarn>
Copy link
Member

@krwq krwq Jan 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider putting that directly in the cs files which do that, we want to get this warning for any case where we don't have good justification (#pragma warning disable SA1205 // Partial elements should declare access modifier: We use different visibility for interop individual projects and for published package, see XYZ.cs)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would need to disable it in many files in the common project, which would somewhat jeopardize the nice solution with not specifying a visibility for the internal classes.

As the documentation says: Because all files are also compiled using a separate project, the warning does come up for classes not in the common project.

#pragma warning disable SA1300 // Element should begin with upper-case letter
#pragma warning disable SA1307 // Field should begin with upper-case letter

internal partial class InteropVideodev2
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider following class structure:

partial class Interop
{
  public class Videodev2 { /* ... */ }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That does not work here, because the class Interop is public while building the binding and resides in a different assembly. Partial classes cannot be merged from two assemblies.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps then consider moving this file over to the Common project? I do feel like having a central place for all of the Interop code makes sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem here is that these classes depend on public(!) enums in the Media project. I could move them, too, but then they would be a bit on the odd side.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is enum internal? if so it's also interop related so should be moved, if it's public let's leave as is

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, that's what I'm saying: Those enums are public. (I did not test whether they are rightfully so, though)

Copy link
Member

@krwq krwq left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM after addressing comments

<LangVersion>9</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup Condition="'$(MSBuildProjectName)' != '$(CommonProjectName)' AND '$(MSBuildProjectName)' != '$(SystemDeviceModelProjectName)'">
<!-- These projects are included into every binding. They must not depend on each other, or we get a circular dependency -->
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: Indenting on this comment.

<LangVersion>9</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup Condition="'$(MSBuildProjectName)' != '$(CommonProjectName)' AND '$(MSBuildProjectName)' != '$(SystemDeviceModelProjectName)'">
<!-- These projects are included into every binding. They must not depend on each other, or we get a circular dependency -->
<ProjectReference Include="$(SystemDeviceModelPath)" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usually when we we use infrastructure to automatically Opt-in all of the child projects into, it is good practice to provide an Opt-out switch, for example, a property like Condition="'$(OmitCommonProjectReference)' != 'true'" or something like that. If you do that, then instead of having to use the Condition you have above in the ItemGroup, you can just set those properties onto the Common and System.Device.Model projects themselves which would remove the ProjectReference from there. You don't have to make that change now, and I'm fine to keeping this as is, just wanted to share that other point of view.


#pragma warning disable SA1403 // File may only contain a single namespace

public partial class Interop
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly I don't love the fact of separating access modifiers from their implementation using partials. Given you would only have one instace per file, why not instead just add the ifdef on the class implementation itself to select if the class is either public or itnernal? I think having it separate like this will be harder to maintain and prone to errors.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, we are already using that approach with at least three helper classes in common, I think we should use the same with the rest.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was @krwq's suggestion, and I think it's nice. It reduces the boilerplate on many files, and I think it reduces the possibility of errors. One would clearly not by accident change this file, but when one changes one of the other files (and i.e. adds an access specifier there) a build error would result. We could make this a bit more waterproof by anyway adding the explicit internal case.

@joperezr
Copy link
Member

Thanks so much for all of this great cleanup @pgrawehr! I left some minor comments, and the only major one is on how to execute the visibility of the common classes, but other than that this looks great!

@krwq
Copy link
Member

krwq commented Jan 26, 2022

@joperezr I think for this specific case we should vote on the design - I personally prefer cleaner code (less #ifs).

Please vote:

  • 🚀 for less #ifs
  • 🎉 for explicit visibility (with #ifs)

Time until 1/31 EOD or all triage members voting whichever comes sooner.

@krwq
Copy link
Member

krwq commented Jan 26, 2022

Either way I agree with offline comment - if we decide to keep Visibility file we should be always explicit to make sure there is compile error on accidental change (also create Visibiltity.Interal.cs)

Internal when building Iot.Device.dll, public when
building as separate projects. That way, we should
avoid some possible dependency nightmares for the
individual projects and interop classes.
@joperezr joperezr assigned joperezr and unassigned pgrawehr Feb 10, 2022
@pgrawehr pgrawehr merged commit 2db2f02 into dotnet:main Feb 20, 2022
@pgrawehr pgrawehr deleted the CleanCommonProject branch February 20, 2022 16:02
slawek-andra pushed a commit to slawek-andra/iot that referenced this pull request Mar 22, 2022
This cleans up a few dependency issues between the bindings and the common project. The classes marked internal (including the Interop classes) are now public when building Common.csproj but still internal for the final assembly. This removes the need to link to individual source files outside their binding folders, which causes build problems when they're used from multiple places.
@github-actions github-actions bot locked and limited conversation to collaborators Dec 9, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-device-bindings Device Bindings for audio, sensor, motor, and display hardware that can used with System.Device.Gpio
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants