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

Verify projects for required properties #75

Open
omidkrad opened this issue Oct 13, 2015 · 8 comments
Open

Verify projects for required properties #75

omidkrad opened this issue Oct 13, 2015 · 8 comments

Comments

@omidkrad
Copy link

I opened a question on StackOverflow for how to check Visual Studio projects (.csproj files) for consistency in their property settings. Apparently there is no straightforward way to do this and you have to either write custom tasks or manually parse .csproj files as XML for testing project properties.

I think this is a good feature to be added to Invoke-MSBuild. Right now, Invoke-MSBuild has a -properties parameter which passes these parameters for build but it does not test projects for having them. For example if you pass -properties @{'WarningLevel'='4'} it will ignore the current WarningLevel property in projects and use the specified value instead. But what if you want the build to fail with proper error message if WarningLevel in any of the building projects is not 4? I've seen different team invent their own stuff to implement this kind of verification in their projects.

I suggest adding a new parameter to Invoke-MSBuild just for this, like -requireProperties that will cause the build to fail if any of the projects does not comply with the specified property requirements.

One thing to notice is, if -configuration Debug is specified together with -requireProperties $hash then required properties will be checked only for the Debug condition. For this we may want to alternatively have the option to pass an MSBuild project with all required properties on different conditions and get Invoke-MSBuild to verify that every project being built follows that.

@sayedihashimi
Copy link
Member

Hi @omidkrad thanks for the comments. I think this is an interesting but I don't see how we can implement something like this in a generic fashion. For example how would we know the available set of value for WarningLevel and other properties. Across all the different project types there are thousands of properties so it's not possible for someone to author a set of possible values list.

In addition to this when Invoke-MSBuild is called without -debug the call is just passed to msbuild.exe so there's no real opportunity to validate any property values. On top of this property values (even ones passed in on the command line) can be overridden inside of a target.

I have a blog post on how to validate specific setting in msbuild at http://sedodream.com/2009/06/30/elementsofreusablemsbuildscriptsvalidation.aspx but I think that's different from what you are looking for.

http://sedodream.com/2009/06/30/elementsofreusablemsbuildscriptsvalidation.aspx

@omidkrad
Copy link
Author

We don't want to validate if properties have valid values, so really don't care what the property name or value is. The whole point is to make sure properties are set consistently across all projects in a solution as we require. Your article is very close, but the main part that is a little bugging is having to manually add that import in every project. And if some new developer adds a new project to the solution then again someone has to manually check if proper import is added to the project.

One way I can think of to avoid that is to use the CustomBeforeMicrosoftCommonTargets property. Something like this:

Invoke-MSBuild Solution.sln -properties @{'CustomBeforeMicrosoftCommonTargets'='Build.Common.targets'}

where Build.Common.targets like in your article does property validations. While this may work, it still is not as straightforward as it should be. I think it would be much easier if we could add our required properties in a file or hashtable and then pass it to the build tool to verify.

@sayedihashimi
Copy link
Member

Your article is very close, but the main part that is a little bugging is having to manually add that import in every project.

Right I was going to suggest CustomBeforeMicrosoftCommonTargets. For those not familiar with it see my blog post at http://sedodream.com/2012/07/28/MSBuildHowToExecuteATargetAfterCoreCompilePart2.aspx.

OK so if I understand your proposal, you'd like to have the following.

  • Add a new parameter to Invoke-MSBuild which would be -requiredProperties
    • -requiredProperties takes a list of property names that have to be passed in as a global property?
    • If any property is missing from -requiredProperties throw an exception

Did I get that right? If not can you elaborate more on exactly what you are proposing.

@sayedihashimi
Copy link
Member

I just read your SO question, now I understand better what you are looking for.

Essentially you want to be able to "analyze" a set of projects to ensure certain properties are declared. I think the right approach is to use the MSBuild construction APIs to read those projects. In psbuild I have PowerShell functions that can help. For example Get-MSBuildProject https://github.com/ligershark/psbuild/blob/master/src/psbuild.psm1#L1411 Find-Property https://github.com/ligershark/psbuild/blob/master/src/psbuild.psm1#L1856 and Test-Property https://github.com/ligershark/psbuild/blob/master/src/psbuild.psm1#L1927.

@omidkrad
Copy link
Author

Exactly. Using MSBuild APIs is the right way to go. I absolutely don't want to get into parsing XML for this. So here's my proposal: Add a new parameter -requiredProperties (or -requireProperties in its imperative form) that takes a hashtable with property name/values.

Invoke-MSBuild Solution.sln -requireProperties @{
    'TargetFrameworkVersion' = 'v4.5'
    'Platform' = 'AnyCPU'
    'WarningLevel' = 4
    'TreatWarningsAsErrors' = true
    'OutputPath' = '$(SolutionDir)bin'
    'SignAssembly' = true
    'AssemblyName' = '$(ProjectFolderName)'
}

During compile, if any of the projects is not explicitly setting these properties then the build should fail with a descriptive error message.

This will be very useful. In all teams I've worked with they put together stuff to accomplish this in some way but I never really liked any of the workarounds.

@sayedihashimi
Copy link
Member

Thanks for the reply. In your SO question you stated

How would you verify that all the projects follow certain rules in their property settings, and enforce these rules if a new project is added

So I understood that to mean that you wanted to ensure that project files are authored in a consistent manner and having the property values you listed included. And the reason why you wanted to make sure those properties were included in the project file is to ensure that the projects are built in a consistent manner no matter where they are built. That I totally get.

Now the specific thing that you are proposing is to have a new parameter on Invoke-MSBuild to enforce that specific property values are present during the build. Instead of that when calling Invoke-MSBuild you can pass in -properties and those will be passed in as command line args to msbuild.exe making them global properties to all projects being built. If you are already calling Invoke-MSBuild to build the project then just pass -properties, no need to verify if you are setting it.

About checking property values, checking values of properties at build time will not ensure that properties are declared in the project. Property values can come from a lot of different places, the project file is just one.

In addition to this I don't think having a property name/value pair is good enough. For example perhaps you want to ensure that SignAssembly is set to true only for Release configuration. Or you want to check that AssemblyName matches a certain regex. So we could figure out a way to support a variety of these combinations but in the end it would still be a limited implementation. Instead of this I think it's better to use the more fine grained functions like Find-Property to get the declaration and then check whatever conditions are needed. This is also more inline with how PowerShell works. For example in your case you could do something like the following.

$proj = Get-MSBuildProject -projectFile .\temp.proj
$tfv = Find-Property -propertyContainer $proj -name TargetFrameworkVersion
if($tfv.Value -ne 'v4.5'){throw ('invalid value for TargetFrameworkVersion [{0}]' -f $tfv.Value)}

Thanks for the comments I appreciate your interest in this project.

@omidkrad
Copy link
Author

Yeah, as long as we can ensure consistency in properties and even more I'm happy with the solution. This also works well with psake which is great.

Do you have helper methods to get certain project types from a solution file? Maybe we can create a psake plugin for testing Visual Studio projects/solutions like VSUnitTest that works with PowerShell. I like these verifications done during build than test to get early errors.

Basically using helper functions you specify the solution file and project types you're looking in it along with a list of required properties and patterns for their values in each configuration. Checking if a project AssemblyName is the same as project FolderName is a good example. What do you think?

@sayedihashimi
Copy link
Member

Do you have helper methods to get certain project types from a solution file?

I'm not sure if there is an OSS parser for .sln files. There may be one in the MSBuild repo but I'm not sure. One way to do this could be to call msbuild.exe with /pp which will produce an MSBuild file for the solution, and then use the psbuild functions to find projects and then check conditions.

If you get that figured out, it may be a good addition of a new function, Get-SolutionProjects, to psbuild.

About the verification part, psbuild already exposes all the Lego blocks for users to compose those checks. I'm not sure if it would make sense to add a verify function because we'd be limited in what we can check as I discussed above.

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

No branches or pull requests

2 participants