Skip to content

Commit

Permalink
[Rgen] Use the api definiton for the transformer tests. (#22022)
Browse files Browse the repository at this point in the history
  • Loading branch information
mandel-macaque authored Jan 23, 2025
1 parent 4500b4c commit c8f1ff7
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 90 deletions.
79 changes: 62 additions & 17 deletions tests/common/Configuration.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

using Xamarin.Utils;

#nullable disable // until we get around to fixing this file
Expand Down Expand Up @@ -44,6 +44,7 @@ static partial class Configuration {
public static string DOTNET_DIR;

static Version xcode_version;

public static Version XcodeVersion {
get {
if (xcode_version is null)
Expand All @@ -53,6 +54,7 @@ public static Version XcodeVersion {
}

static bool? use_system; // if the system-installed XI/XM should be used instead of the local one.

public static bool UseSystem {
get {
if (!use_system.HasValue)
Expand All @@ -65,6 +67,7 @@ public static bool UseSystem {
}

static bool? is_vsts; // if the system-installed XI/XM should be used instead of the local one.

public static bool IsVsts {
get {
if (!is_vsts.HasValue)
Expand Down Expand Up @@ -147,14 +150,18 @@ static void ParseConfigFiles ()
tests_dir = file;
break;
}

dir = Path.GetDirectoryName (dir);
}

if (tests_dir is null)
throw new Exception ($"Could not find the directory 'tests'. Please run 'make' in the tests/ directory.");
throw new Exception (
$"Could not find the directory 'tests'. Please run 'make' in the tests/ directory.");
// Run make
ExecutionHelper.Execute ("make", new string [] { "-C", tests_dir, "test.config" });
test_config = FindConfigFiles ("test.config");
}

if (test_config.Any ())
ParseConfigFiles (test_config);
ParseConfigFiles (FindConfigFiles ("configure.inc"));
Expand Down Expand Up @@ -207,10 +214,14 @@ public static string EvaluateVariable (string variable)
return result;

var output = new StringBuilder ();
var rv = ExecutionHelper.Execute ("/usr/bin/make", new string [] { "-C", Path.Combine (SourceRoot, "tools", "devops"), "print-abspath-variable", $"VARIABLE={variable}" }, environmentVariables: null, stdout: output, stderr: output, timeout: TimeSpan.FromSeconds (5));
var rv = ExecutionHelper.Execute ("/usr/bin/make",
new string [] {
"-C", Path.Combine (SourceRoot, "tools", "devops"), "print-abspath-variable", $"VARIABLE={variable}"
}, environmentVariables: null, stdout: output, stderr: output, timeout: TimeSpan.FromSeconds (5));
if (rv != 0)
throw new Exception ($"Failed to evaluate variable '{variable}'. Exit code: {rv}. Output:\n{output}");
result = output.ToString ().Split (new char [] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries).Where (v => v.StartsWith (variable + "=", StringComparison.Ordinal)).SingleOrDefault ();
result = output.ToString ().Split (new char [] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)
.Where (v => v.StartsWith (variable + "=", StringComparison.Ordinal)).SingleOrDefault ();
if (result is null)
throw new Exception ($"Could not find the variable '{variable}' to evaluate.");
return result.Substring (variable.Length + 1);
Expand All @@ -233,7 +244,8 @@ public static string GetPListStringValue (string plist, string key)
using (var fs = new StringReader (ReadPListAsXml (plist))) {
using (var reader = System.Xml.XmlReader.Create (fs, settings)) {
doc.Load (reader);
return doc.DocumentElement.SelectSingleNode ($"//dict/key[text()='{key}']/following-sibling::string[1]/text()").Value;
return doc.DocumentElement
.SelectSingleNode ($"//dict/key[text()='{key}']/following-sibling::string[1]/text()").Value;
}
}
}
Expand Down Expand Up @@ -278,7 +290,8 @@ static Configuration ()
DotNetExecutable = GetVariable ("DOTNET", null);
DotNetTfm = GetVariable ("DOTNET_TFM", null);
EnableXamarin = !string.IsNullOrEmpty (GetVariable ("ENABLE_XAMARIN", ""));
XcodeIsStable = string.Equals (GetVariable ("XCODE_IS_STABLE", ""), "true", StringComparison.OrdinalIgnoreCase);
XcodeIsStable = string.Equals (GetVariable ("XCODE_IS_STABLE", ""), "true",
StringComparison.OrdinalIgnoreCase);
DOTNET_DIR = GetVariable ("DOTNET_DIR", "");

XcodeVersionString = GetVariable ("XCODE_VERSION", GetXcodeVersion (xcode_root));
Expand Down Expand Up @@ -316,19 +329,23 @@ public static string RootPath {
throw new FormatException (".git worktree file is not valid");
}
}

if (Directory.Exists (path))
found = true;

if (!found) {
dir = Path.GetDirectoryName (dir);
if (dir is null)
throw new Exception ($"Could not find the xamarin-macios repo given the test assembly directory {TestAssemblyDirectory}");
throw new Exception (
$"Could not find the xamarin-macios repo given the test assembly directory {TestAssemblyDirectory}");
path = Path.Combine (dir, ".git");
}
}

path = Path.GetDirectoryName (path);
if (!Directory.Exists (path))
throw new Exception ($"Could not find the xamarin-macios repo given the test assembly directory {TestAssemblyDirectory}");
throw new Exception (
$"Could not find the xamarin-macios repo given the test assembly directory {TestAssemblyDirectory}");
return path;
}
}
Expand Down Expand Up @@ -419,9 +436,11 @@ public static string GetNuGetVersionNoMetadata (TargetFramework framework)

public static string GetNuGetVersionNoMetadata (ApplePlatform platform)
{
var workloadVersion = Environment.GetEnvironmentVariable ($"{platform.AsString ().ToUpper ()}_WORKLOAD_VERSION");
return string.IsNullOrEmpty (workloadVersion) ?
GetVariable ($"{platform.AsString ().ToUpper ()}_NUGET_VERSION_NO_METADATA", string.Empty) : workloadVersion;
var workloadVersion =
Environment.GetEnvironmentVariable ($"{platform.AsString ().ToUpper ()}_WORKLOAD_VERSION");
return string.IsNullOrEmpty (workloadVersion)
? GetVariable ($"{platform.AsString ().ToUpper ()}_NUGET_VERSION_NO_METADATA", string.Empty)
: workloadVersion;
}

// This is only applicable for .NET
Expand Down Expand Up @@ -580,11 +599,14 @@ public static string CloneTestDirectory (string directory)
var testsTemporaryDirectory = Cache.CreateTemporaryDirectory ($"{Path.GetFileName (directory)}");

// Only copy files in git, we want a clean copy
var rv = ExecutionHelper.Execute ("git", new string [] { "ls-files" }, out var ls_files_output, working_directory: directory, timeout: TimeSpan.FromSeconds (15));
var rv = ExecutionHelper.Execute ("git", new string [] { "ls-files" }, out var ls_files_output,
working_directory: directory, timeout: TimeSpan.FromSeconds (15));
if (rv != 0)
throw new Exception ($"Failed to list test files. 'git ls-files' in {directory} failed with exit code {rv}.");
throw new Exception (
$"Failed to list test files. 'git ls-files' in {directory} failed with exit code {rv}.");

var files = ls_files_output.ToString ().Split (new char [] { '\n' }, StringSplitOptions.RemoveEmptyEntries).ToArray ();
var files = ls_files_output.ToString ().Split (new char [] { '\n' }, StringSplitOptions.RemoveEmptyEntries)
.ToArray ();
foreach (var file in files) {
var src = Path.Combine (directory, file);
var tgt = Path.Combine (testsTemporaryDirectory, file);
Expand All @@ -593,7 +615,8 @@ public static string CloneTestDirectory (string directory)
File.Copy (src, tgt);
if (tgt.EndsWith (".csproj", StringComparison.OrdinalIgnoreCase)) {
var initialContents = File.ReadAllText (tgt);
var fixedContents = initialContents.Replace ($"$(MSBuildThisFileDirectory)", Path.GetDirectoryName (src) + Path.DirectorySeparatorChar);
var fixedContents = initialContents.Replace ($"$(MSBuildThisFileDirectory)",
Path.GetDirectoryName (src) + Path.DirectorySeparatorChar);
if (initialContents != fixedContents)
File.WriteAllText (tgt, fixedContents);
}
Expand Down Expand Up @@ -689,16 +712,19 @@ public static void Touch (string file)
}

static bool? is_apfs;

static bool IsAPFS {
get {
if (!is_apfs.HasValue) {
if (Environment.OSVersion.Platform == PlatformID.Win32NT) {
is_apfs = false;
} else {
var exit_code = ExecutionHelper.Execute ("/bin/df", new string [] { "-t", "apfs", "/" }, out var output, TimeSpan.FromSeconds (10));
var exit_code = ExecutionHelper.Execute ("/bin/df", new string [] { "-t", "apfs", "/" },
out var output, TimeSpan.FromSeconds (10));
is_apfs = exit_code == 0 && output.Trim ().Split ('\n').Length >= 2;
}
}

return is_apfs.Value;
}
}
Expand All @@ -714,6 +740,7 @@ static void EnsureFilestampChange ()

// Return true if the current machine can run ARM64 binaries.
static bool? canRunArm64;

public static bool CanRunArm64 {
get {
if (!canRunArm64.HasValue) {
Expand All @@ -725,6 +752,7 @@ public static bool CanRunArm64 {
canRunArm64 = false;
}
}

return canRunArm64.Value;
}
}
Expand All @@ -739,6 +767,7 @@ public static IEnumerable<string> CallNM (string file, string nmArguments, strin
arguments.Add ("-arch");
arguments.Add (arch);
}

var symbols = ExecutionHelper.Execute ("nm", arguments, hide_output: true).Split ('\n');
return symbols.Where ((v) => {
return !v.EndsWith (": no symbols", StringComparison.Ordinal);
Expand All @@ -759,6 +788,22 @@ public static IEnumerable<string> GetUndefinedNativeSymbols (string file, string
{
return CallNM (file, "-gujA", arch);
}

public static bool TryGetApiDefinitionRsp (TargetFramework framework,
[NotNullWhen (true)] out string rspPath)
{
rspPath = null;
var platform = framework.Platform switch {
ApplePlatform.iOS => "ios",
ApplePlatform.TVOS => "tvos",
ApplePlatform.MacOSX => "macos",
ApplePlatform.MacCatalyst => "maccatalyst",
_ => null,
};
if (platform is null)
return false;
rspPath = Path.Combine (SourceRoot, "src", "build", "dotnet", platform, $"apidefinition-{platform}.rsp");
return true;
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ void TryeCreateTests (ApplePlatform platform, (string Source, string Path) sourc
{
// create a compilation used to create the transformer
var compilation = CreateCompilation (platform, sources: source);
var syntaxTree = compilation.SyntaxTrees.FirstOrDefault ();
var syntaxTree = compilation.SyntaxTrees.ForSource (source);
Assert.NotNull (syntaxTree);

var semanticModel = compilation.GetSemanticModel (syntaxTree);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ void TryCreateTests (ApplePlatform platform, (string Source, string Path) source
{
// create a compilation used to create the transformer
var compilation = CreateCompilation (platform, sources: source);
var syntaxTree = compilation.SyntaxTrees.FirstOrDefault ();
var syntaxTree = compilation.SyntaxTrees.ForSource (source);
Assert.NotNull (syntaxTree);

var semanticModel = compilation.GetSemanticModel (syntaxTree);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ void TryCreateTests (ApplePlatform platform, (string Source, string Path) source
{
// create a compilation used to create the transformer
var compilation = CreateCompilation (platform, sources: source);
var syntaxTree = compilation.SyntaxTrees.FirstOrDefault ();
var syntaxTree = compilation.SyntaxTrees.ForSource (source);
Assert.NotNull (syntaxTree);

var semanticModel = compilation.GetSemanticModel (syntaxTree);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Collections.Immutable;
using System.Runtime.CompilerServices;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.Macios.Transformer.Extensions;
using Xamarin.Tests;
using Xamarin.Utils;

Expand All @@ -26,38 +26,38 @@ public class BaseTransformerTestClass {

protected Compilation CreateCompilation (ApplePlatform platform, [CallerMemberName] string name = "", params (string Source, string Path) [] sources)
{
// get the dotnet bcl and fully load it for the test.
var references = Directory.GetFiles (Configuration.DotNetBclDir, "*.dll")
.Select (assembly => MetadataReference.CreateFromFile (assembly)).ToList ();

// get the dll for the current platform, this is needed because that way we will get the attributes that
// are used in the old dlls that are needed to test the transformer.
var targetFramework = TargetFramework.GetTargetFramework (platform, isDotNet: true);
var platformDll = Configuration.GetBaseLibrary (targetFramework);
if (!string.IsNullOrEmpty (platformDll)) {
references.Add (MetadataReference.CreateFromFile (platformDll));
} else {
throw new InvalidOperationException ($"Could not find platform dll for {platform}");
}
// include the bgen attributes to the compilation, otherwise the transformer will not work.
var sourcesList = sources.ToList ();
if (Configuration.TryGetRootPath (out var rootPath)) {
var oldVersionAttrs = Path.Combine (rootPath, "src", "ObjCRuntime", "PlatformAvailability.cs");
sourcesList.Add ((File.ReadAllText (oldVersionAttrs), oldVersionAttrs));

var oldBgenAttrs = Path.Combine (rootPath, "src", "bgen", "Attributes.cs");
sourcesList.Add ((File.ReadAllText (oldBgenAttrs), oldBgenAttrs));
var workingDirectory = Path.Combine (Configuration.SourceRoot, "src");
if (!Configuration.TryGetApiDefinitionRsp (targetFramework, out var rspFile)) {
Assert.Fail ($"Could not find rsp file for {targetFramework}");
}

var parseOptions = new CSharpParseOptions (LanguageVersion.Latest, DocumentationMode.None, preprocessorSymbols: ["COREBUILD"]);
var trees = sourcesList.Select (
s => CSharpSyntaxTree.ParseText (s.Source, parseOptions, s.Path))
.ToImmutableArray ();
var parseResult = CSharpCommandLineParser.Default.ParseRsp (
rspFile, workingDirectory, Configuration.DotNetBclDir);

// add NET to the preprocessor directives
var preprocessorDirectives = parseResult.ParseOptions.PreprocessorSymbolNames.ToList ();
preprocessorDirectives.Add ("NET");

var options = new CSharpCompilationOptions (OutputKind.NetModule)
.WithAllowUnsafe (true);
// fixing the parsing options, we must have an issue in the rsp
var updatedParseOptions = parseResult.ParseOptions
.WithLanguageVersion (LanguageVersion.Latest)
.WithPreprocessorSymbols (preprocessorDirectives)
.WithDocumentationMode (DocumentationMode.None);

var references = parseResult.GetReferences (workingDirectory, Configuration.DotNetBclDir);
var parsedSource = parseResult.GetSourceFiles (updatedParseOptions).ToList ();
foreach (var (source, path) in sources) {
parsedSource.Add (CSharpSyntaxTree.ParseText (source, updatedParseOptions, path));
}

return CSharpCompilation.Create (name, trees, references, options);
return CSharpCompilation.Create (
assemblyName: name,
syntaxTrees: parsedSource,
references: references,
options: parseResult.CompilationOptions);
}

}
Loading

0 comments on commit c8f1ff7

Please sign in to comment.