Skip to content

Provide VsLangProj.ProjectProperties through VSProject export#1842

Merged
basoundr merged 14 commits intodotnet:masterfrom
basoundr:ProjectPropertiesProvider
Mar 28, 2017
Merged

Provide VsLangProj.ProjectProperties through VSProject export#1842
basoundr merged 14 commits intodotnet:masterfrom
basoundr:ProjectPropertiesProvider

Conversation

@basoundr
Copy link
Contributor

@basoundr basoundr commented Mar 24, 2017

This change exports VsLangProjectProperties as VsProject. This VsLangProjectProperties, in addition to VSProject, implements VsLangProj.ProjectProperties as well. This object will now start providing the values in the Project Property pages.

Currently I have implemented just the properties, expected from the CommonProperty, that are used in the Property Pages. We can implement the rest of them as and when required. In the future, this object might also have to implement CSharpProjectProperties7 and VBProjectProperties7, again, since we dont need them now, this change will not handle those cases.

Tagging @dotnet/project-system @davkean @srivatsn for review

Dim throwAwayObject As Decimal = Nothing
If (TypeOf (value) Is UInteger OrElse
(CpsPropertyDescriptorWrapper.IsAnyCpsComponent(m_Objects) AndAlso Decimal.TryParse(DirectCast(value, String), throwAwayObject))) Then
iBaseAddress = CUInt(value)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jviau BaseAddress property is currently backed by a StringProperty in the XAML. I still get a string value, if I change the Property type to IntProperty from the StringProperty. For eg: "123456"

As it turns out, to obtain the value, DynamicType's GetValue is called, which always return a string. Is there a way it could return the actual type that the property is initially backed with?

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think there's a way today to get anything other than a string. There are other places in the prop pages where such code exists. However, for compat, it'll be good if the object returned here was the right data type instead of string.

Copy link
Contributor

Choose a reason for hiding this comment

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

@srivatsn is correct. Right now dynamic type system is always strings, because it is for UI and also msbuild properties are always strings. However, the various implementations of object IProperty.GetValueAsync() does try to return the value as the correct type. In this piece of code we are commenting on, what is the CPS object you are working on? Is it already the value from the DynamicType or something else?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is the value from DynamicType since we get that from DynamicTypeBrowseObject

public object __project => throw new NotImplementedException();

public string StartupObject { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public prjOutputType OutputType
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you group the implemented ones together? Also there are some weird new lines - either have newlines between props or dont

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sure. With CodeLens adornment, it is tough to see them.

_projectProperties = projectProperties;
}


Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: extra whitespace.


namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties
{
internal class VsLangProjectProperties : VSProject, VSLangProj.ProjectProperties
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps make this a partial class, and put the implementation of each interface in a different file? There's a lot of stuff here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Partial class, by roslyn convention, would mean a nested type. So let me try to group the implementation separately without partial class. That way, it does not seem unorganized.

Copy link
Contributor

Choose a reason for hiding this comment

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

No, we have non-nested partial classes.

Copy link
Contributor

Choose a reason for hiding this comment

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

Lots of them, actually: AbstractProject, AbstractLegacyProject, CSharpProjectShim, etc.

get
{
var configurationGeneralBrowseObjectProperties = JoinableTaskFactory.Run(ProjectProperties.GetConfigurationGeneralBrowseObjectPropertiesAsync);
var value = (IEnumValue)JoinableTaskFactory.Run(configurationGeneralBrowseObjectProperties.OutputTypeEx.GetValueAsync);
Copy link
Contributor

Choose a reason for hiding this comment

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

OutputTypeEx [](start = 107, length = 12)

You shouldn't need this, in fact that should be completely killed from the rules once you have the project properties completed. There should only be an OutputType (string) on the project and depending on the property that's getting asked it should convert it back to either projOutputType or prjOutputTypeEx

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OutputType does not support AppContainerExe or WinMDObj. We could use this property to do the conversion but it looks unnecessary given that we can backup that property directly. Also, I dont see why we have to remove OutputTypeEx completely given that it is a VsLangProj property.

Copy link
Contributor

Choose a reason for hiding this comment

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

@srivatsn may have more context here but my understanding is that it was added to the rules page as a workaround to try and provide this property. In the old project system it's the same property (which is usually how "Ex" type properties work). I believe the current provider (which you should be able to delete) is doing that conversion if OutputType is set to either of the UWP values (it converts them to either exe or library).

Copy link
Contributor

Choose a reason for hiding this comment

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

That sounds right but I don't remember the details. You'll have to look at history.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Looking at the history, it does seem to be the case. But we will have to look at what breaks if we remove this. I can create an issue to track this work.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Opened #1867


public prjOriginatorKeyMode AssemblyOriginatorKeyMode { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }

public bool DelaySign { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
Copy link
Contributor

Choose a reason for hiding this comment

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

public bool DelaySign { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } [](start = 8, length = 113)

Property pages use this so leaving it unimplemented will cause issues there (or false results).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

DelaySign property is not from Common properties. It is backed up by DynamicTypeBrowseObject from here. This will not be a problem.

Copy link
Member

Choose a reason for hiding this comment

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

Please file bugs for these unimplemented properties.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Opened #1868

}

[Fact]
public void VsLangProjectProperties_OutputType_Get()
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 was not able to write the tests for set methods because of Moq. I tried the same test below and added the following code but it does not seem to work.

var mockObject = Mock.Get(projectProperties.GetConfigurationGeneralBrowseObjectPropertiesAsync().Result.OutputType); // Get the mock object of IProperty, which is [here](https://github.com/dotnet/roslyn-project-system/blob/master/src/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectPropertiesFactory.cs#L110)
mockObject.Verify(m => m.SetValueAsync(It.IsAny<object>()));

Is this valid? Am I missing something here?

Copy link
Member

Choose a reason for hiding this comment

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

Use SetupProperty

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 want to mock the call SetValueAsync of the OutputType property.

SetupProperty can be used to mock properties that are overrideable. In this case though, the property will be OutputType, from ConfigurationGeneralBrowseObject , which is not overrideable. So it cannot be mocked.

I tried something like this:

        [Fact]
        public void VsLangProjectProperties_AssemblyName_Get()
        {
            var project = UnconfiguredProjectFactory.Create();
            var data = new PropertyPageData()
            {
                Category = ConfigurationGeneral.SchemaName,
                PropertyName = ConfigurationGeneral.AssemblyNameProperty,
                Value = "Blah"
            };

            var projectProperties = ProjectPropertiesFactory.Create(project, data);
            var activeConfiguredProject = ActiveConfiguredProjectFactory.ImplementValue(() => projectProperties);

            var vsLangProjectProperties = new VSProject(Mock.Of<VSLangProj.VSProject>(), IProjectThreadingServiceFactory.Create(), activeConfiguredProject);
            Assert.Equal(vsLangProjectProperties.AssemblyName, "Blah");

            var mockObject = Mock.Get(projectProperties.GetConfigurationGeneralPropertiesAsync().Result.AssemblyName);
            mockObject.Setup(prop => prop.SetValueAsync(It.IsAny<string>()))
                      .Verifiable();

            vsLangProjectProperties.AssemblyName = "Testing";

            mockObject.Verify();
        }

and it results in

       Moq.MockVerificationException : The following setups were not matched:
        IEvaluatedProperty prop => prop.SetValueAsync(It.IsAny<String>())

        Stack Trace:
             at Moq.Mock.Verify()

@basoundr
Copy link
Contributor Author

Tagging @dotnet/project-system again. Tests are in.


public prjOriginatorKeyMode AssemblyOriginatorKeyMode { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }

public bool DelaySign { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
Copy link
Member

Choose a reason for hiding this comment

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

Please file bugs for these unimplemented properties.


set
{
var configurationGeneralBrowseObjectProperties = JoinableTaskFactory.Run(ProjectProperties.GetConfigurationGeneralBrowseObjectPropertiesAsync);
Copy link
Member

@davkean davkean Mar 27, 2017

Choose a reason for hiding this comment

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

Use IProjectThreadingService.ExecuteSynchronously and wrap the whole method instead of running in multiple contexts.

{
get
{
return new VsLangProjectProperties(_vsProject, _threadingService, _projectProperties);
Copy link
Member

Choose a reason for hiding this comment

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

This should be cached - there should be one of these.


[Export(ExportContractNames.VsTypes.VSProject, typeof(VSProject))]
[AppliesTo(ProjectCapability.CSharpOrVisualBasic)]
[Order(10)]
Copy link
Member

Choose a reason for hiding this comment

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

This order looks arbitrary how did you decide it?

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 CPS's implementation is exported with default value(0) . Having an order 10, will give the ability for extensions to export their implementation with an order between 0 &10 and the ability to export with an order greater than 10. We dont want to export our's with MaxValue becauseno one else can have their export ordered higher than ours.

Copy link
Member

Choose a reason for hiding this comment

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

I think we need a pattern for this - I'm going to introduce one.

/// <see cref="VSLangProj.ProjectProperties"/>. This enables us to provide
/// ProjectProperties to the Project Property Pages and maintain Backward Compatibility.
/// </summary>
internal class VsLangProjectPropertiesProvider
Copy link
Member

Choose a reason for hiding this comment

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

Why does this class even exist, why can't we just export the VsLangProjectProperties?

/// <summary>
/// See <see cref="VsLangProjectPropertiesProvider"/> for more info.
/// </summary>
internal partial class VsLangProjectProperties : VSLangProj.ProjectProperties
Copy link
Member

Choose a reason for hiding this comment

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

This is our VSProject implementation and should be named as such.

/// This implementation of VSProject just redirects the VSProject call to the contained
/// VSProject object imported from CPS
/// </summary>
internal partial class VsLangProjectProperties : VSProject
Copy link
Member

Choose a reason for hiding this comment

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

Can you file a bug to implement VSProject2 here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Opened #1869


public VSProjectEvents Events => _vsProject.Events;

// Implementation of VSProject to redirect the call to the actual VSProject object
Copy link
Member

Choose a reason for hiding this comment

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

This comment applies the entire class right? Why is it in the middle of it?

using EnvDTE;
using VSLangProj;

namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties
Copy link
Member

Choose a reason for hiding this comment

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

This should live in the Automation folder/namespace.

}

[Fact]
public void VsLangProjectProperties_OutputType_Get()
Copy link
Member

Choose a reason for hiding this comment

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

Use SetupProperty

basoundr added 12 commits March 27, 2017 11:23
Remove the ExportVsProfferedProjectService export
Export the VSProject with a higher order attribute
This way, this export gets picked up before the CPS export which is exported with Zero ordering

Convert obj string to Decimal for Lib Base address
This property is backed by the rule which says to get the value after context which only returns the string formatted value even if the property is backed by a PageIntProperty
…interfaces VsProject and ProjectProperties

Add doc comments
Add Properties to access ProjectProperties and Threading service easily

Order the methods after the properties
@basoundr basoundr force-pushed the ProjectPropertiesProvider branch from 6a65ab3 to 097e3bf Compare March 27, 2017 18:30
@basoundr basoundr force-pushed the ProjectPropertiesProvider branch from 097e3bf to 403d07f Compare March 27, 2017 18:37
@basoundr
Copy link
Contributor Author

basoundr commented Mar 27, 2017

@davkean Feedback addressed

@basoundr basoundr merged commit e53c25d into dotnet:master Mar 28, 2017
@basoundr basoundr deleted the ProjectPropertiesProvider branch March 28, 2017 16:27
@basoundr
Copy link
Contributor Author

Fixes #1709 #1468 #1378

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants