提交 17b7347d 编写于 作者: J Jared Parsons

Verification for our props and targets files

上级 6e2e3e10
......@@ -367,6 +367,28 @@
<OptimizationDataFile>$([System.IO.Path]::GetFullPath('$(OptimizationDataFolderPath)\$(TargetName).pgo'))</OptimizationDataFile>
</PropertyGroup>
<Target Name="RestoreToolsetCheck"
Condition="'$(BuildingProject)' == 'true'">
<Error Text="Toolset packages have not been restored, run Restore.cmd before building"
Condition="!Exists('$(ToolsetCompilerPropsFilePath)')" />
</Target>
<Target Name="CheckBootstrapState" Condition="'$(BootstrapBuildPath)' != ''" BeforeTargets="CoreCompile">
<ValidateBootstrap BootstrapPath="$(BootstrapBuildPath)" />
</Target>
<!--
When running our determinism tests we need to copy the diagnostic file from the intermediate directory
to the location of the binary. This ensures .dll and .dll.key are next to each other to be picked up
by our test scripts
-->
<Target Name="CopyDeterministicBuildDiagnosticFile" Condition="'$(DebugDeterminism)' != ''" AfterTargets="CoreCompile">
<Copy
Condition="Exists(@(IntermediateAssembly -> '%(fullpath).key'))"
SourceFiles="@(IntermediateAssembly -> '%(fullpath).key')"
DestinationFolder="$(OutDir)" />
</Target>
<Target Name="PostCompileBinaryModification"
AfterTargets="CoreCompile"
DependsOnTargets="ApplyOptimizations"
......
<Project DefaultTargets="Build" InitialTargets="ValidateMSBuildToolsVersion" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- The UsingTask, UseSharedCompilation, and ToolPath/Exe variables all interact to
choose which compiler path to use and whether or not to use the compiler server.
If UsingTask and UseSharedCompilation are set then the compiler server next to the
task will be used (i.e., the one in this package).
If UseSharedCompilation is false or ToolPath/Exe are set the compiler server will
not be used and the compiler exe at the ToolPath, if set, will be executed, otherwise
the executable in the MSBuild install path will be executed. -->
<Target Name="ValidateMSBuildToolsVersion" Condition="'$(BuildingProject)' == 'true'">
<Error Text="Microsoft.Net.Compilers is only supported on MSBuild v14.0 and above"
Condition="'$(MSBuildToolsVersion)' == '2.0'
OR '$(MSBuildToolsVersion)' == '3.5'
OR '$(MSBuildToolsVersion)' == '4.0'
OR '$(MSBuildToolsVersion)' == '12.0'" />
</Target>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ToolsetCompilerPath>$(NuGetPackageRoot)\Microsoft.Net.Compilers\$(ToolsetCompilerPackageVersion)\tools</ToolsetCompilerPath>
......
......@@ -8,6 +8,20 @@ To accomplish this all of the build logic is contained in our central targets fi
The individual project files contain declaritive information only. They inherit their build logic by importing [Settings.props](Settings.props) at the start and [Imports.targets](Imports.targets) at the conclusion.
## General rules
There are a set of general rules to follow for props and targets files:
- props files
- Do use Import for props files.
- Do not use Import for targets files.
- Do use UsingTask elements.
- Do not use Target elements
- targets files
- Do use Import for targets files.
- Do not use Import for props files.
- Do use Task elements.
## Files
This section describes the purpose and layout of the important files here.
......
......@@ -252,26 +252,4 @@
<!-- NuGet props aren't imported by default on *nix so we do that here -->
<Import Project="$(MSBuildExtensionsPath)\Microsoft\NuGet\Microsoft.NuGet.props" Condition="'$(OS)' != 'Windows_NT'" />
</ImportGroup>
<Target Name="RestoreToolsetCheck"
Condition="'$(BuildingProject)' == 'true'">
<Error Text="Toolset packages have not been restored, run Restore.cmd before building"
Condition="!Exists('$(ToolsetCompilerPropsFilePath)')" />
</Target>
<Target Name="CheckBootstrapState" Condition="'$(BootstrapBuildPath)' != ''" BeforeTargets="CoreCompile">
<ValidateBootstrap BootstrapPath="$(BootstrapBuildPath)" />
</Target>
<!--
When running our determinism tests we need to copy the diagnostic file from the intermediate directory
to the location of the binary. This ensures .dll and .dll.key are next to each other to be picked up
by our test scripts
-->
<Target Name="CopyDeterministicBuildDiagnosticFile" Condition="'$(DebugDeterminism)' != ''" AfterTargets="CoreCompile">
<Copy
Condition="Exists(@(IntermediateAssembly -> '%(fullpath).key'))"
SourceFiles="@(IntermediateAssembly -> '%(fullpath).key')"
DestinationFolder="$(OutDir)" />
</Target>
</Project>
......@@ -120,7 +120,7 @@ echo Running RepoUtil
REM Verify the state of our project.jsons
echo Running BuildBoss
.\Binaries\%BuildConfiguration%\Exes\BuildBoss\BuildBoss.exe Roslyn.sln Compilers.sln src\Samples\Samples.sln CrossPlatform.sln || goto :BuildFailed
.\Binaries\%BuildConfiguration%\Exes\BuildBoss\BuildBoss.exe Roslyn.sln Compilers.sln src\Samples\Samples.sln CrossPlatform.sln build\Targets || goto :BuildFailed
REM Ensure caller sees successful exit.
exit /b 0
......
......@@ -32,6 +32,7 @@
<Compile Include="SharedUtil.cs" />
<Compile Include="ProjectEntry.cs" />
<Compile Include="SolutionUtil.cs" />
<Compile Include="TargetsCheckerUtil.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
......
......@@ -11,18 +11,25 @@ namespace BuildBoss
{
internal static class Program
{
internal static int Main(string[] solutionFilePaths)
internal static int Main(string[] args)
{
if (solutionFilePaths.Length == 0)
if (args.Length == 0)
{
Usage();
return 1;
}
var allGood = true;
foreach (var solutionFilePath in solutionFilePaths)
foreach (var arg in args)
{
allGood &= ProcessSolution(solutionFilePath);
if (SharedUtil.IsSolutionFile(arg))
{
allGood &= ProcessSolution(arg);
}
else
{
allGood &= ProcessTargets(arg);
}
}
return allGood ? 0 : 1;
......@@ -78,6 +85,23 @@ private static bool ProcessProject(string solutionPath, ProjectData projectData,
return true;
}
private static bool ProcessTargets(string targets)
{
var checker = new TargetsCheckerUtil(targets);
var textWriter = new StringWriter();
if (checker.CheckAll(textWriter))
{
Console.WriteLine($"Processing {Path.GetFileName(targets)} passed");
return true;
}
else
{
Console.WriteLine($"Processing {Path.GetFileName(targets)} FAILED");
Console.WriteLine(textWriter.ToString());
return false;
}
}
private static void Usage()
{
Console.WriteLine($"BuildBoss <solution paths>");
......
......@@ -16,6 +16,11 @@ internal class ProjectUtil
private readonly XDocument _document;
private readonly XmlNamespaceManager _manager;
internal ProjectUtil(string filePath) :this(new ProjectKey(filePath), XDocument.Load(filePath))
{
}
internal ProjectUtil(ProjectKey key, XDocument document)
{
_key = key;
......@@ -89,11 +94,23 @@ internal IEnumerable<XElement> GetAllPropertyGroupElements()
}
}
internal IEnumerable<XElement> GetTargets()
{
return _document.XPathSelectElements("//mb:Target", _manager);
}
internal IEnumerable<XElement> GetImports()
{
return _document.XPathSelectElements("//mb:Import", _manager);
}
internal IEnumerable<string> GetImportProjects()
{
return GetImports()
.Select(x => x.Attribute("Project")?.Value)
.Where(x => !string.IsNullOrEmpty(x));
}
internal IEnumerable<XElement> GetItemGroup()
{
return _document.XPathSelectElements("//mb:ItemGroup", _manager);
......
......@@ -31,3 +31,4 @@ Our build process depends on being able to correctly classify our projects. Thi
This could be done using MSBuild targets but the logic is hard to follow and complicates the build. It's easier to verify here.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
......@@ -13,5 +14,10 @@ internal static class SharedUtil
internal static Uri MSBuildNamespaceUri { get; } = new Uri(MSBuildNamespaceUriRaw);
internal static XNamespace MSBuildNamespace { get; } = XNamespace.Get(MSBuildNamespaceUriRaw);
internal static Encoding Encoding { get; } = Encoding.UTF8;
internal static bool IsSolutionFile(string path) => Path.GetExtension(path) == ".sln";
internal static bool IsPropsFile(string path) => Path.GetExtension(path) == ".props";
internal static bool IsTargetsFile(string path) => Path.GetExtension(path) == ".targets";
internal static bool IsXslt(string path) => Path.GetExtension(path) == ".xslt";
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BuildBoss
{
internal sealed class TargetsCheckerUtil
{
private readonly string _targetDir;
internal TargetsCheckerUtil(string targetDir)
{
_targetDir = targetDir;
}
internal bool CheckAll(TextWriter textWriter)
{
var allGood = true;
foreach (var filePath in Directory.GetFiles(_targetDir))
{
var fileName = Path.GetFileName(filePath);
if (fileName == "README.md")
{
continue;
}
textWriter.WriteLine($"Checking {fileName}");
if (SharedUtil.IsPropsFile(filePath))
{
allGood &= CheckProps(new ProjectUtil(filePath), textWriter);
}
else if (SharedUtil.IsTargetsFile(filePath))
{
allGood &= CheckTargets(new ProjectUtil(filePath), textWriter);
}
else if (SharedUtil.IsXslt(filePath))
{
// Nothing to verify
}
else
{
textWriter.WriteLine("Unrecognized file type");
allGood = false;
}
}
return allGood;
}
private bool CheckProps(ProjectUtil util, TextWriter textWriter)
{
var allGood = true;
foreach (var project in GetImportProjects(util))
{
if (!SharedUtil.IsPropsFile(project))
{
textWriter.WriteLine($"Props files should only Import other props files");
allGood = false;
}
}
if (util.GetTargets().Any())
{
textWriter.WriteLine($"Props files should not contain <Target> elements");
allGood = false;
}
return allGood;
}
private bool CheckTargets(ProjectUtil util, TextWriter textWriter)
{
var allGood = true;
foreach (var project in GetImportProjects(util))
{
if (SharedUtil.IsPropsFile(project))
{
textWriter.WriteLine($"Targets files should not Import props files");
allGood = false;
}
}
return allGood;
}
private static IEnumerable<string> GetImportProjects(ProjectUtil util)
{
foreach (var project in util.GetImportProjects())
{
if (IsReplacedValue(project))
{
continue;
}
yield return project;
}
}
/// <summary>
/// Is this a whole replaced MSBuild value like $(Trick)
/// </summary>
private static bool IsReplacedValue(string value)
{
if (value.Length <= 3)
{
return false;
}
return
value[0] == '$' &&
value[1] == '(' &&
value[value.Length - 1] == ')';
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册