Skip to content

Commit

Permalink
Add generate shared framework manifest target, inverse xunit analyzer…
Browse files Browse the repository at this point in the history
…, reference Test SDK correctly (#3139)

* Inverse xunit analyzer, ref vstest correctly

* Add generate framework manifest target

* Code cleanup in uap

* Use a higher version of Newtonsoft.Json

* Hide RemoteExecutor include files in VS
  • Loading branch information
ViktorHofer authored Jun 29, 2019
1 parent e350d32 commit d412d2b
Show file tree
Hide file tree
Showing 9 changed files with 308 additions and 56 deletions.
51 changes: 51 additions & 0 deletions src/Microsoft.DotNet.CoreFxTesting/FileUtilities.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

// Copied from https://github.com/dotnet/core-setup/blob/b73c0af268be449db317a7c0718012027cd8b173/tools-local/tasks/FileUtilities.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;

namespace Microsoft.DotNet.Build.Tasks
{
internal static partial class FileUtilities
{
private static readonly HashSet<string> s_assemblyExtensions = new HashSet<string>(
new[] { ".dll", ".exe", ".winmd" },
StringComparer.OrdinalIgnoreCase);

public static Version GetFileVersion(string sourcePath)
{
var fvi = FileVersionInfo.GetVersionInfo(sourcePath);

if (fvi != null)
{
return new Version(fvi.FileMajorPart, fvi.FileMinorPart, fvi.FileBuildPart, fvi.FilePrivatePart);
}

return null;
}

public static AssemblyName GetAssemblyName(string path)
{
if (!s_assemblyExtensions.Contains(Path.GetExtension(path)))
{
return null;
}

try
{
return AssemblyName.GetAssemblyName(path);
}
catch (BadImageFormatException)
{
// Not a valid assembly.
return null;
}
}
}
}
210 changes: 210 additions & 0 deletions src/Microsoft.DotNet.CoreFxTesting/GenerateFileVersionProps.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

// Copied and slightly modified from https://github.com/dotnet/core-setup/blob/b73c0af268be449db317a7c0718012027cd8b173/tools-local/tasks/GenerateFileVersionProps.cs

using Microsoft.Build.Construction;
using Microsoft.Build.Framework;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace Microsoft.DotNet.Build.Tasks
{
public partial class GenerateFileVersionProps : BuildTask
{
private const string PlatformManifestsItem = "PackageConflictPlatformManifests";
private const string PreferredPackagesProperty = "PackageConflictPreferredPackages";
private static readonly Version ZeroVersion = new Version(0, 0, 0, 0);

[Required]
public ITaskItem[] Files { get; set; }

[Required]
public string PackageId { get; set; }

[Required]
public string PackageVersion { get; set; }

[Required]
public string PlatformManifestFile { get; set; }

public string PropsFile { get; set; }

[Required]
public string PreferredPackages { get; set; }

/// <summary>
/// The task normally enforces that all DLL and EXE files have a non-0.0.0.0 FileVersion.
/// This flag disables the check.
/// </summary>
public bool PermitDllAndExeFilesLackingFileVersion { get; set; }

public override bool Execute()
{
var fileVersions = new Dictionary<string, FileVersionData>(StringComparer.OrdinalIgnoreCase);
foreach(var file in Files)
{
var targetPath = file.GetMetadata("TargetPath");

if (!targetPath.StartsWith("runtimes/"))
{
continue;
}

if (file.GetMetadata("IsSymbolFile").Equals("true", StringComparison.OrdinalIgnoreCase))
{
continue;
}

var fileName = Path.GetFileName(file.ItemSpec);

var current = GetFileVersionData(file);

FileVersionData existing;

if (fileVersions.TryGetValue(fileName, out existing))
{
if (current.AssemblyVersion != null)
{
if (existing.AssemblyVersion == null)
{
fileVersions[fileName] = current;
continue;
}
else if (current.AssemblyVersion != existing.AssemblyVersion)
{
if (current.AssemblyVersion > existing.AssemblyVersion)
{
fileVersions[fileName] = current;
}
continue;
}
}

if (current.FileVersion != null &&
existing.FileVersion != null)
{
if (current.FileVersion > existing.FileVersion)
{
fileVersions[fileName] = current;
}
}
}
else
{
fileVersions[fileName] = current;
}
}

// Check for versionless files after all duplicate filenames are resolved, rather than
// logging errors immediately upon encountering a versionless file. There may be
// duplicate filenames where only one has a version, and this is ok. The highest version
// is used.
if (!PermitDllAndExeFilesLackingFileVersion)
{
var versionlessFiles = fileVersions
.Where(p =>
p.Key.EndsWith(".exe", StringComparison.OrdinalIgnoreCase) ||
p.Key.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
.Where(p => (p.Value.FileVersion ?? ZeroVersion) == ZeroVersion)
.Select(p => p.Value.File.ItemSpec)
.ToArray();

if (versionlessFiles.Any())
{
Log.LogError(
$"Missing FileVersion in {versionlessFiles.Length} shared framework files:" +
string.Concat(versionlessFiles.Select(f => Environment.NewLine + f)));
}
}

bool generatePropsFile = !string.IsNullOrWhiteSpace(PropsFile);
ProjectRootElement props = null;

if (generatePropsFile)
{
props = ProjectRootElement.Create();
var itemGroup = props.AddItemGroup();
// set the platform manifest when the platform is not being published as part of the app
itemGroup.Condition = "'$(RuntimeIdentifier)' == '' or '$(SelfContained)' != 'true'";

var manifestFileName = Path.GetFileName(PlatformManifestFile);
itemGroup.AddItem(PlatformManifestsItem, $"$(MSBuildThisFileDirectory){manifestFileName}");
}

Directory.CreateDirectory(Path.GetDirectoryName(PlatformManifestFile));
using (var manifestWriter = File.CreateText(PlatformManifestFile))
{
foreach (var fileData in fileVersions)
{
var name = fileData.Key;
var versions = fileData.Value;
var assemblyVersion = versions.AssemblyVersion?.ToString() ?? String.Empty;
var fileVersion = versions.FileVersion?.ToString() ?? String.Empty;

manifestWriter.WriteLine($"{name}|{PackageId}|{assemblyVersion}|{fileVersion}");
}
}

if (!string.IsNullOrWhiteSpace(PropsFile))
{
var propertyGroup = props.AddPropertyGroup();
propertyGroup.AddProperty(PreferredPackagesProperty, PreferredPackages);

var versionPropertyName = $"_{PackageId.Replace(".", "_")}_Version";
propertyGroup.AddProperty(versionPropertyName, PackageVersion);

props.Save(PropsFile);
}

return !Log.HasLoggedErrors;
}

FileVersionData GetFileVersionData(ITaskItem file)
{
var filePath = file.GetMetadata("FullPath");

if (File.Exists(filePath))
{
return new FileVersionData()
{
AssemblyVersion = FileUtilities.GetAssemblyName(filePath)?.Version,
FileVersion = FileUtilities.GetFileVersion(filePath),
File = file
};
}
else
{
// allow for the item to specify version directly
Version assemblyVersion, fileVersion;

Version.TryParse(file.GetMetadata("AssemblyVersion"), out assemblyVersion);
Version.TryParse(file.GetMetadata("FileVersion"), out fileVersion);

if (fileVersion == null)
{
// FileVersionInfo will return 0.0.0.0 if a file doesn't have a version.
// match that behavior
fileVersion = ZeroVersion;
}

return new FileVersionData()
{
AssemblyVersion = assemblyVersion,
FileVersion = fileVersion,
File = file
};
}
}

class FileVersionData
{
public Version AssemblyVersion { get; set; }
public Version FileVersion { get; set; }
public ITaskItem File { get; set; }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Build" Version="$(MicrosoftBuildVersion)" />
<PackageReference Include="Microsoft.Build.Framework" Version="$(MicrosoftBuildFrameworkVersion)" />
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="$(MicrosoftBuildUtilitiesCoreVersion)" />
<PackageReference Include="System.Reflection.Metadata" Version="1.6.0" Condition="'$(TargetFramework)' == 'net472'" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<TestRuntimeTemplateConfigPath Condition="'$(TestRuntimeTemplateConfigPath)' == '' and '$(TargetFrameworkIdentifier)' == '.NETFramework'">$(TestAssetsDir)netfx.exe.config</TestRuntimeTemplateConfigPath>

<!-- By default copy the test runtime config file for executable test projects (+ test support projects). -->
<GenerateRuntimeConfigurationFiles Condition="'$(IsTestProject)' == 'true' and ('$(IsTestSupportProject)' != 'true' or '$(OutputType.ToLower())' == 'exe')">true</GenerateRuntimeConfigurationFiles>
<GenerateRuntimeConfigurationFiles Condition="'$(IsTestProject)' == 'true' and ('$(IsTestSupportProject)' != 'true' or '$(OutputType.ToLower())' == 'exe') and '$(TargetFrameworkIdentifier)' != 'UAP'">true</GenerateRuntimeConfigurationFiles>
</PropertyGroup>

<ItemGroup Condition="'$(GenerateRuntimeConfigurationFiles)' == 'true'">
Expand Down Expand Up @@ -77,13 +77,23 @@

</Target>

<UsingTask TaskName="GenerateFileVersionProps" AssemblyFile="$(CoreFxTestingAssemblyPath)"/>
<Target Name="GenerateFileVersionProps">
<GenerateFileVersionProps Files="@(SharedFrameworkRuntimeFiles)"
PackageId="Microsoft.NETCore.App"
PackageVersion="$(ProductVersion)"
PlatformManifestFile="$(PlatformManifestFile)"
PreferredPackages="Microsoft.NetCore.App"
PermitDllAndExeFilesLackingFileVersion="true" />
</Target>

<!--
Shared framework deps file generation.
Produces a test shared-framework deps file.
To use invoke target directly specifying NETCoreAppTestSharedFrameworkPath property.
-->
<UsingTask TaskName="GenerateTestSharedFrameworkDepsFile" AssemblyFile="$(CoreFxTestingAssemblyPath)"/>
<Target Name="GenerateTestSharedFrameworkDepsFile" Condition="'$(GenerateTestSharedFrameworkDepsFile)' == 'true'">
<Target Name="GenerateTestSharedFrameworkDepsFile">
<GenerateTestSharedFrameworkDepsFile SharedFrameworkDirectory="$(NETCoreAppTestSharedFrameworkPath)" />
</Target>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
</PropertyGroup>

<ItemGroup>
<_AllRuntimeDllFiles Include="$(RuntimePath)\*.dll" Exclude="$(RuntimePath)\Microsoft.VisualStudio.TestPlatform.*dll;$(RuntimePath)\Microsoft.TestPlatform.*dll" />
<_ExternalReswFiles Include="$(_ExternalReswOutputPath)*.resw" Exclude="$(RuntimePath)\Microsoft.VisualStudio.TestPlatform.*resw;$(RuntimePath)\Microsoft.TestPlatform.*resw" />
<_AllRuntimeDllFiles Include="$(RuntimePath)*.dll" />
<_ExternalReswFiles Include="$(_ExternalReswOutputPath)*.resw" />

<!-- The first time the CreateReswFilesForExternalDependencies target is executed the itemgroup _ExternalReswFiles will be empty
and that will avoid the target from executing, so we add a dummy file if they are empty so that the target is executed the first time. -->
Expand Down
3 changes: 1 addition & 2 deletions src/Microsoft.DotNet.CoreFxTesting/build/core/Core.targets
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@
</Target>

<UsingTask TaskName="GenerateRunScript" AssemblyFile="$(CoreFxTestingAssemblyPath)"/>
<Target Name="GenerateRunScript"
DependsOnTargets="$(GenerateRunScriptDependsOn)">
<Target Name="GenerateRunScript">

<PropertyGroup>
<!-- RSP file support. -->
Expand Down
40 changes: 11 additions & 29 deletions src/Microsoft.DotNet.CoreFxTesting/build/core/test/Test.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,27 @@
<PropertyGroup>
<TestRunnerConfigPath>$(TestAssetsDir)xunit.runner.json</TestRunnerConfigPath>
<TestResultsName>testResults.xml</TestResultsName>
<GenerateRunScriptDependsOn>ValidateTargetOSTrait;$(GenerateRunScriptDependsOn);</GenerateRunScriptDependsOn>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>

<ItemGroup>
<PackageReference Condition="'$(IncludeRemoteExecutor)' == 'true'" Include="Microsoft.DotNet.RemoteExecutor" Version="$(MicrosoftDotNetRemoteExecutorPackageVersion)" />

<!-- xunit -->
<PackageReference Include="Microsoft.DotNet.XUnitExtensions" Version="$(MicrosoftDotNetXUnitExtensionsPackageVersion)" />
<!-- Excluding build as xunit.core enables deps file generation. -->
<!-- Excluding xunit.core/build as it enables deps file generation. -->
<PackageReference Include="xunit" Version="$(XUnitPackageVersion)" ExcludeAssets="build" />
<PackageReference Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'" Include="Microsoft.DotNet.XUnitConsoleRunner" Version="$(MicrosoftDotNetXUnitConsoleRunnerPackageVersion)" />
<PackageReference Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'" Include="xunit.runner.console" Version="$(XUnitPackageVersion)" />

<PackageReference Condition="'$(ContinuousIntegrationBuild)' != 'true' and '$(OfficialBuildId)' == ''" Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPackageVersion)" IncludeAssets="build" />
<!-- Microsoft.Net.Test.Sdk brings a lot of assemblies with it. To reduce helix payload submission size we disable it on CI. -->
<PackageReference Condition="'$(ArchiveTest)' != 'true'" Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPackageVersion)" />
<PackageReference Condition="'$(ArchiveTest)' != 'true'" Include="xunit.runner.visualstudio" Version="$(XUnitPackageVersion)" />
<!--
Microsoft.Net.Test.Sdk has a dependency on Newtonsoft.Json v9.0.1. We upgrade the dependency version
with the one used in corefx to have a consistent set of dependency versions. Additionally this works
around a dupliate type between System.Runtime.Serialization.Formatters and Newtonsoft.Json.
-->
<PackageReference Condition="'$(ArchiveTest)' != 'true'" Include="Newtonsoft.Json" Version="$(NewtonsoftJsonPackageVersion)" />
</ItemGroup>

<ItemGroup>
Expand All @@ -34,34 +40,10 @@
CopyToOutputDirectory="PreserveNewest"
Visible="false" />
<None Include="$([System.IO.Path]::GetDirectoryName('$(XunitConsoleNetCore21AppPath)'))\*"
Exclude="$([System.IO.Path]::GetDirectoryName('$(XunitConsoleNetCore21AppPath)'))\xunit.console.deps.json"
Exclude="$([System.IO.Path]::GetDirectoryName('$(XunitConsoleNetCore21AppPath)'))\xunit.console.deps.json;$([System.IO.Path]::GetDirectoryName('$(XunitConsoleNetCore21AppPath)'))\xunit.console.runtimeconfig.json"
Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' and '$(XunitConsoleNetCore21AppPath)' != ''"
CopyToOutputDirectory="PreserveNewest"
Visible="false" />
</ItemGroup>

<!--
Only add the Test SDK with assemblies when building locally for .NET Core.
Microsoft.NET.Test.Sdk brings framework assemblies with it: https://github.com/microsoft/vstest/issues/1764
-->
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' and '$(ContinuousIntegrationBuild)' != 'true' and '$(OfficialBuildId)' == ''">
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="$(MicrosoftExtensionsDependencyModelPackageVersion)" />
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonPackageVersion)" />
<PackageReference Include="Microsoft.TestPlatform.TestHost" Version="$(MicrosoftNETTestSdkPackageVersion)" ExcludeAssets="all" PrivateAssets="all" GeneratePathProperty="true" />
<PackageReference Include="Microsoft.TestPlatform.ObjectModel" Version="$(MicrosoftNETTestSdkPackageVersion)" ExcludeAssets="all" PrivateAssets="all" GeneratePathProperty="true" />
<PackageReference Include="xunit.runner.visualstudio" Version="$(XUnitPackageVersion)" PrivateAssets="all" IncludeAssets="build"/>

<Reference Include="$(PkgMicrosoft_TestPlatform_TestHost)\lib\netstandard1.5\Microsoft.TestPlatform.CommunicationUtilities.dll" />
<Reference Include="$(PkgMicrosoft_TestPlatform_TestHost)\lib\netstandard1.5\Microsoft.TestPlatform.CrossPlatEngine.dll" />
<Reference Include="$(PkgMicrosoft_TestPlatform_TestHost)\lib\netstandard1.5\Microsoft.VisualStudio.TestPlatform.Common.dll" />
<None Include="$(PkgMicrosoft_TestPlatform_TestHost)\lib\netstandard1.5\testhost.dll"
CopyToOutputDirectory="PreserveNewest"
Visible="false" />
<None Include="$(PkgMicrosoft_TestPlatform_TestHost)\lib\netstandard1.5\testhost.deps.json"
Condition="'$(GenerateDependencyFile)' == 'true'"
CopyToOutputDirectory="PreserveNewest"
Visible="false" />
<Reference Include="$(PkgMicrosoft_TestPlatform_ObjectModel)\lib\netstandard1.5\*.dll" />
</ItemGroup>

</Project>
Loading

0 comments on commit d412d2b

Please sign in to comment.