Skip to content

Commit 4ad3d98

Browse files
committed
Add StyleCop rules to repo
1 parent c52a06d commit 4ad3d98

7 files changed

+714
-0
lines changed

Add-StyleCopToProjects.ps1

+229
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
param(
2+
[Parameter(Mandatory=$True)]
3+
[string]$rootPath
4+
)
5+
6+
7+
8+
function Make-RelativeTo {
9+
[CmdletBinding()]
10+
param (
11+
[Parameter(Mandatory=$True)]
12+
[string]$referencePath,
13+
14+
[Parameter(Mandatory=$True, ValueFromPipeline=$True)]
15+
[string[]]$pipePaths,
16+
17+
[Parameter(ValueFromRemainingArguments=$true)]
18+
[string[]]$paths
19+
)
20+
BEGIN {
21+
function _GetAncestry($p, [switch]$skipFile)
22+
{
23+
$p = Get-Item $p;
24+
if(!$p) { return; }
25+
function _List($i)
26+
{
27+
if(!$i.PSIsContainer)
28+
{
29+
if(!$skipFile) { $i; }
30+
$i = $i.Directory;
31+
}
32+
while($i) { $i; $i = $i.Parent; }
33+
}
34+
$l = _List $p;
35+
return $l[$l.Count..0];
36+
}
37+
38+
$referencePathParts = _GetAncestry $referencePath -skipFile;
39+
}
40+
PROCESS {
41+
function _Relativise($path)
42+
{
43+
$pathParts = _GetAncestry $path;
44+
if($pathParts[0].Name -ne $referencePathParts[0].Name)
45+
{
46+
# Absolute.
47+
return (Get-Item $path).FullName.TrimEnd('\');
48+
}
49+
$i = 0;
50+
while($pathParts[$i].Name -eq $referencePathParts[$i].Name)
51+
{
52+
$i++;
53+
if($i -gt $pathParts.Length -and $i -gt $referencePathParts.Length)
54+
{
55+
return "";
56+
}
57+
}
58+
59+
$downPath = @($pathParts[$i..$pathParts.Length] | %{$_.Name}) -join '\';
60+
61+
$upDistance = $referencePathParts.Length - $i;
62+
if($upDistance -gt 0)
63+
{
64+
$upPath = @(1..$upDistance | %{ '..' }) -join '\';
65+
if($downPath) { return $upPath + '\' + $downPath; }
66+
return $upPath;
67+
}
68+
return $downPath;
69+
}
70+
71+
if($pipePaths.Length -gt 0)
72+
{
73+
$paths = $pipePaths;
74+
}
75+
foreach($_ in $paths)
76+
{
77+
if($_)
78+
{
79+
_Relativise $_;
80+
}
81+
}
82+
}
83+
}
84+
85+
function Find-ProjectFiles() # $containers...
86+
{
87+
$containers = $args;
88+
foreach($container in $args)
89+
{
90+
foreach($containerPath in @(Resolve-Path "${rootPath}\${container}"))
91+
{
92+
Get-ChildItem -recurse -filter *.csproj $containerPath | %{ $_.FullName };
93+
}
94+
}
95+
}
96+
97+
function Interpret-PathRelativeToFile {
98+
[CmdletBinding()]
99+
param (
100+
[Parameter(Mandatory=$True)]
101+
[string]$referenceFilePath,
102+
103+
[Parameter(ValueFromRemainingArguments=$true)]
104+
[string[]]$paths
105+
)
106+
BEGIN {
107+
$file = Get-Item $referenceFilePath;
108+
$referenceDir = $file.Directory;
109+
}
110+
PROCESS {
111+
foreach($_ in $paths)
112+
{
113+
114+
$p = "${referenceDir}\$_";
115+
if(Test-Path $p) {
116+
(Get-Item $p).FullName;
117+
}
118+
else { $p; } # Don't canonicalise if nonexistent.
119+
}
120+
}
121+
END {}
122+
}
123+
124+
filter Fixup-ProjectFileImports([string]$globalPropsFile, [string]$globalTargetsFile)
125+
{
126+
$projectFile = Get-Item $_;
127+
if(!$projectFile) { return; } # Not present?!
128+
129+
$projectFileXml = [xml](Get-Content $projectFile);
130+
131+
$imports = @($projectFileXml.Project.Import);
132+
$projectFileXmlDocument = $projectFileXml.Project.OwnerDocument;
133+
134+
$unconditionalImports = $imports | where { -not $_.Condition; };
135+
$msImports = @($unconditionalImports | where { $_.Project -match "\bMicrosoft\b"; });
136+
$xamarinImports = @($unconditionalImports | where { $_.Project -match "\bXamarin\b"; });
137+
$defaultImport = ($msImports + $xamarinImports)[0];
138+
if(!$defaultImport -and !$projectFileXml.Project.Sdk)
139+
{
140+
Write-Host -ForegroundColor Yellow "WARNING: Unable to find default import or Sdk attribute in $(${projectFile}.FullName)";
141+
return;
142+
}
143+
144+
Write-Host "Processing $(${projectFile}.FullName)...";
145+
146+
function _FindImport($fullPath)
147+
{
148+
@($unconditionalImports | where { $fullPath -eq $(Interpret-PathRelativeToFile $projectFile $_.Project) })[0];
149+
}
150+
151+
$projectFileChanged = $false;
152+
153+
function Insert-AroundDefaultImport([string]$before, [string]$after, [ref]$changed)
154+
{
155+
function _CreateImport($path)
156+
{
157+
$importNode = $projectFileXmlDocument.CreateElement("Import", $projectFileXmlDocument.DocumentElement.NamespaceURI);
158+
$importNode.SetAttribute("Project", $path);
159+
return $importNode;
160+
}
161+
162+
if($before -and (Test-Path $before))
163+
{
164+
$existingBefore = _FindImport $before;
165+
if(!$existingBefore)
166+
{
167+
$relativeBefore = Make-RelativeTo $projectFile $before;
168+
$importNode = _CreateImport $relativeBefore;
169+
if ($defaultImport)
170+
{
171+
$defaultImport.ParentNode.InsertBefore($importNode, $defaultImport) | Out-Null;
172+
}
173+
else
174+
{
175+
$projectFileXml.Project.AppendChild($importNode) | Out-Null;
176+
}
177+
Write-Host " Added $relativeBefore";
178+
$changed.Value = $true;
179+
}
180+
}
181+
182+
if($after -and (Test-Path $after))
183+
{
184+
$existingAfter = _FindImport $after;
185+
if(!$existingAfter)
186+
{
187+
$relativeAfter = Make-RelativeTo $projectFile $after;
188+
$importNode = _CreateImport $relativeAfter;
189+
if ($defaultImport)
190+
{
191+
$defaultImport.ParentNode.InsertAfter($importNode, $defaultImport) | Out-Null;
192+
}
193+
else
194+
{
195+
$projectFileXml.Project.AppendChild($importNode) | Out-Null;
196+
}
197+
Write-Host " Added $relativeAfter";
198+
$changed.Value = $true;
199+
}
200+
}
201+
}
202+
203+
Insert-AroundDefaultImport $globalPropsFile $globalTargetsFile ([ref]$projectFileChanged);
204+
205+
foreach($import in $imports)
206+
{
207+
if($import.Project -match "\$\(") { continue; }
208+
if(Test-Path (Interpret-PathRelativeToFile $projectFile $import.Project)) { continue; }
209+
Write-Host -ForegroundColor Yellow " Missing file: $(${import}.Project)";
210+
}
211+
212+
if ($projectFileChanged)
213+
{
214+
$projectFileXml.Save($projectFile.FullName);
215+
}
216+
}
217+
218+
if(!$rootPath)
219+
{
220+
throw "No root path specified.";
221+
}
222+
if(-not (Test-Path $rootPath))
223+
{
224+
throw "Root path does not exist: $rootPath";
225+
}
226+
$rootPath = $(Get-Item $rootPath).FullName;
227+
228+
$allProjects = Find-ProjectFiles .;
229+
$allProjects | Fixup-ProjectFileImports "${rootPath}\StyleCopAnalyzers.props";

StyleCopAnalyzers.props

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<PropertyGroup>
4+
<StyleCopAnalyzersRootPath>$(MSBuildThisFileDirectory)StyleCopAnalyzers\</StyleCopAnalyzersRootPath>
5+
<CodeAnalysisRuleSet>$(StyleCopAnalyzersRootPath)CodeAnalysisRules.ruleset</CodeAnalysisRuleSet>
6+
7+
<!--
8+
* Suppress XML comment warnings completely.
9+
* Ignore duplicate 'using' statements, which might be introduced by merges.
10+
-->
11+
<NoWarn>1591,1592,1573,1574,1571,1570,1572,1711,1587,0105</NoWarn>
12+
<!-- Treat warnings as errors by default: -->
13+
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
14+
<!-- Keep '#warning' and some internally-suppressed warnings as warnings: -->
15+
<WarningsNotAsErrors>1030,1701,1702</WarningsNotAsErrors>
16+
<!-- Do not fail on SemVer 2 compatibility warnings when building packages. -->
17+
<NoWarn>$(NoWarn),NU5105</NoWarn>
18+
19+
</PropertyGroup>
20+
21+
<!-- Enable stylecop analyzers for all non-release builds or a release build that's running a build in visual studio (i.e. Intellisense). -->
22+
<ItemGroup Condition=" '$(Configuration)' != 'Release' Or '$(BuildingInsideVisualStudio)' != 'True' Or '$(BuildingProject)' != 'True' ">
23+
<Analyzer Include="$(StyleCopAnalyzersRootPath)lib\1.0.0\Newtonsoft.Json.dll" />
24+
<Analyzer Include="$(StyleCopAnalyzersRootPath)lib\1.0.0\StyleCop.Analyzers.CodeFixes.dll" />
25+
<Analyzer Include="$(StyleCopAnalyzersRootPath)lib\1.0.0\StyleCop.Analyzers.dll" />
26+
</ItemGroup>
27+
28+
<!-- All our projects share the same style requirements -->
29+
<ItemGroup>
30+
<AdditionalFiles Include="$(StyleCopAnalyzersRootPath)stylecop.json">
31+
<Link>stylecop.json</Link>
32+
<Visible>true</Visible>
33+
</AdditionalFiles>
34+
</ItemGroup>
35+
</Project>

0 commit comments

Comments
 (0)