diff --git a/eng/Subsets.props b/eng/Subsets.props
index 499e9c3cb645fe..9de503dd5002f3 100644
--- a/eng/Subsets.props
+++ b/eng/Subsets.props
@@ -173,6 +173,8 @@
+
+
@@ -369,6 +371,10 @@
Test="true" Category="clr" Condition="'$(DotNetBuildSourceOnly)' != 'true' and '$(NativeAotSupported)' == 'true'"/>
+
+
+
+
diff --git a/eng/pipelines/common/evaluate-default-paths.yml b/eng/pipelines/common/evaluate-default-paths.yml
index d954a1ddacbb57..975c18eb69d464 100644
--- a/eng/pipelines/common/evaluate-default-paths.yml
+++ b/eng/pipelines/common/evaluate-default-paths.yml
@@ -164,6 +164,10 @@ jobs:
- src/tools/illink/*
- global.json
+ - subset: tools_cdacreader
+ include:
+ - src/native/managed/cdacreader/*
+
- subset: installer
include:
exclude:
diff --git a/eng/pipelines/runtime.yml b/eng/pipelines/runtime.yml
index b4ed2afd252d23..f0591ff810c37a 100644
--- a/eng/pipelines/runtime.yml
+++ b/eng/pipelines/runtime.yml
@@ -713,7 +713,7 @@ extends:
jobParameters:
timeoutInMinutes: 120
nameSuffix: CLR_Tools_Tests
- buildArgs: -s clr.aot+clr.iltools+libs.sfx+clr.toolstests -c $(_BuildConfig) -test
+ buildArgs: -s clr.aot+clr.iltools+libs.sfx+clr.toolstests+tools.cdacreadertests -c $(_BuildConfig) -test
enablePublishTestResults: true
testResultsFormat: 'xunit'
# We want to run AOT tests when illink changes because there's share code and tests from illink which are used by AOT
@@ -721,6 +721,7 @@ extends:
or(
eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_coreclr.containsChange'], true),
eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_tools_illink.containsChange'], true),
+ eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_tools_cdacreader.containsChange'], true),
eq(variables['isRollingBuild'], true))
#
# Build CrossDacs
diff --git a/src/native/managed/cdacreader/src/ContractDescriptorParser.cs b/src/native/managed/cdacreader/src/ContractDescriptorParser.cs
new file mode 100644
index 00000000000000..fbf76cd4e8d439
--- /dev/null
+++ b/src/native/managed/cdacreader/src/ContractDescriptorParser.cs
@@ -0,0 +1,327 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.Contracts;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace Microsoft.Diagnostics.DataContractReader;
+
+///
+/// A parser for the JSON representation of a contract descriptor.
+///
+///
+/// See design doc for the format.
+///
+public partial class ContractDescriptorParser
+{
+ // data_descriptor.md uses a distinguished property name to indicate the size of a type
+ public const string TypeDescriptorSizeSigil = "!";
+
+ ///
+ /// Parses the "compact" representation of a contract descriptor.
+ ///
+ public static ContractDescriptor? ParseCompact(ReadOnlySpan json)
+ {
+ return JsonSerializer.Deserialize(json, ContractDescriptorContext.Default.ContractDescriptor);
+ }
+
+ [JsonSerializable(typeof(ContractDescriptor))]
+ [JsonSerializable(typeof(int))]
+ [JsonSerializable(typeof(string))]
+ [JsonSerializable(typeof(Dictionary))]
+ [JsonSerializable(typeof(Dictionary))]
+ [JsonSerializable(typeof(Dictionary))]
+ [JsonSerializable(typeof(Dictionary))]
+ [JsonSerializable(typeof(TypeDescriptor))]
+ [JsonSerializable(typeof(FieldDescriptor))]
+ [JsonSerializable(typeof(GlobalDescriptor))]
+ [JsonSourceGenerationOptions(AllowTrailingCommas = true,
+ DictionaryKeyPolicy = JsonKnownNamingPolicy.Unspecified, // contracts, types and globals are case sensitive
+ PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
+ NumberHandling = JsonNumberHandling.AllowReadingFromString,
+ ReadCommentHandling = JsonCommentHandling.Skip)]
+ internal sealed partial class ContractDescriptorContext : JsonSerializerContext
+ {
+ }
+
+ public class ContractDescriptor
+ {
+ public int? Version { get; set; }
+ public string? Baseline { get; set; }
+ public Dictionary? Contracts { get; set; }
+
+ public Dictionary? Types { get; set; }
+
+ public Dictionary? Globals { get; set; }
+
+ [JsonExtensionData]
+ public Dictionary? Extras { get; set; }
+ }
+
+ [JsonConverter(typeof(TypeDescriptorConverter))]
+ public class TypeDescriptor
+ {
+ public uint? Size { get; set; }
+ public Dictionary? Fields { get; set; }
+ }
+
+ [JsonConverter(typeof(FieldDescriptorConverter))]
+ public class FieldDescriptor
+ {
+ public string? Type { get; set; }
+ public int Offset { get; set; }
+ }
+
+ [JsonConverter(typeof(GlobalDescriptorConverter))]
+ public class GlobalDescriptor
+ {
+ public string? Type { get; set; }
+ public ulong Value { get; set; }
+ public bool Indirect { get; set; }
+ }
+
+ internal sealed class TypeDescriptorConverter : JsonConverter
+ {
+ // Almost a normal dictionary converter except:
+ // 1. looks for a special key "!" to set the Size property
+ // 2. field names are property names, but treated case-sensitively
+ public override TypeDescriptor Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ if (reader.TokenType != JsonTokenType.StartObject)
+ throw new JsonException();
+ uint? size = null;
+ Dictionary? fields = new();
+ while (reader.Read())
+ {
+ switch (reader.TokenType)
+ {
+ case JsonTokenType.EndObject:
+ return new TypeDescriptor { Size = size, Fields = fields };
+ case JsonTokenType.PropertyName:
+ string? fieldNameOrSizeSigil = reader.GetString();
+ reader.Read(); // read the next value: either a number or a field descriptor
+ if (fieldNameOrSizeSigil == TypeDescriptorSizeSigil)
+ {
+ uint newSize = reader.GetUInt32();
+ if (size is not null)
+ {
+ throw new JsonException($"Size specified multiple times: {size} and {newSize}");
+ }
+ size = newSize;
+ }
+ else
+ {
+ string? fieldName = fieldNameOrSizeSigil;
+ var field = JsonSerializer.Deserialize(ref reader, ContractDescriptorContext.Default.FieldDescriptor);
+ if (fieldName is null || field is null)
+ throw new JsonException();
+ if (!fields.TryAdd(fieldName, field))
+ {
+ throw new JsonException($"Duplicate field name: {fieldName}");
+ }
+ }
+ break;
+ case JsonTokenType.Comment:
+ // unexpected - we specified to skip comments. but let's ignore anyway
+ break;
+ default:
+ throw new JsonException();
+ }
+ }
+ throw new JsonException();
+ }
+
+ public override void Write(Utf8JsonWriter writer, TypeDescriptor value, JsonSerializerOptions options)
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ internal sealed class FieldDescriptorConverter : JsonConverter
+ {
+ // Compact Field descriptors are either a number or a two element array
+ // 1. number - no type, offset is given as the number
+ // 2. [number, string] - offset is given as the number, type name is given as the string
+ public override FieldDescriptor Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ if (TryGetInt32FromToken(ref reader, out int offset))
+ return new FieldDescriptor { Offset = offset };
+ if (reader.TokenType != JsonTokenType.StartArray)
+ throw new JsonException();
+ reader.Read();
+ // [number, string]
+ // ^ we're here
+ if (!TryGetInt32FromToken(ref reader, out offset))
+ throw new JsonException();
+ reader.Read(); // string
+ if (reader.TokenType != JsonTokenType.String)
+ throw new JsonException();
+ string? type = reader.GetString();
+ reader.Read(); // end of array
+ if (reader.TokenType != JsonTokenType.EndArray)
+ throw new JsonException();
+ return new FieldDescriptor { Type = type, Offset = offset };
+ }
+
+ public override void Write(Utf8JsonWriter writer, FieldDescriptor value, JsonSerializerOptions options)
+ {
+ throw new JsonException();
+ }
+ }
+
+ internal sealed class GlobalDescriptorConverter : JsonConverter
+ {
+ public override GlobalDescriptor Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ // four cases:
+ // 1. number - no type, direct value, given value
+ // 2. [number] - no type, indirect value, given aux data ptr
+ // 3. [number, string] - type, direct value, given value
+ // 4. [[number], string] - type, indirect value, given aux data ptr
+
+ // Case 1: number
+ if (TryGetUInt64FromToken(ref reader, out ulong valueCase1))
+ return new GlobalDescriptor { Value = valueCase1 };
+ if (reader.TokenType != JsonTokenType.StartArray)
+ throw new JsonException();
+ reader.Read();
+ // we're in case 2 or 3 or 4:
+ // case 2: [number]
+ // ^ we're here
+ // case 3: [number, string]
+ // ^ we're here
+ // case 4: [[number], string]
+ // ^ we're here
+ if (TryGetUInt64FromToken(ref reader, out ulong valueCase2or3))
+ {
+ // case 2 or 3
+ // case 2: [number]
+ // ^ we're here
+ // case 3: [number, string]
+ // ^ we're here
+ reader.Read(); // end of array (case 2) or string (case 3)
+ if (reader.TokenType == JsonTokenType.EndArray) // it was case 2
+ {
+ return new GlobalDescriptor { Value = valueCase2or3, Indirect = true };
+ }
+ if (reader.TokenType == JsonTokenType.String) // it was case 3
+ {
+ string? type = reader.GetString();
+ reader.Read(); // end of array for case 3
+ if (reader.TokenType != JsonTokenType.EndArray)
+ throw new JsonException();
+ return new GlobalDescriptor { Type = type, Value = valueCase2or3 };
+ }
+ throw new JsonException();
+ }
+ if (reader.TokenType == JsonTokenType.StartArray)
+ {
+ // case 4: [[number], string]
+ // ^ we're here
+ reader.Read(); // number
+ if (!TryGetUInt64FromToken(ref reader, out ulong value))
+ throw new JsonException();
+ reader.Read(); // end of inner array
+ if (reader.TokenType != JsonTokenType.EndArray)
+ throw new JsonException();
+ reader.Read(); // string
+ if (reader.TokenType != JsonTokenType.String)
+ throw new JsonException();
+ string? type = reader.GetString();
+ reader.Read(); // end of outer array
+ if (reader.TokenType != JsonTokenType.EndArray)
+ throw new JsonException();
+ return new GlobalDescriptor { Type = type, Value = value, Indirect = true };
+ }
+ throw new JsonException();
+ }
+
+ public override void Write(Utf8JsonWriter writer, GlobalDescriptor value, JsonSerializerOptions options)
+ {
+ throw new JsonException();
+ }
+ }
+
+ // Somewhat flexible parsing of numbers, allowing json number tokens or strings as decimal or hex, possibly negatated.
+ private static bool TryGetUInt64FromToken(ref Utf8JsonReader reader, out ulong value)
+ {
+ if (reader.TokenType == JsonTokenType.Number)
+ {
+ if (reader.TryGetUInt64(out value))
+ return true;
+ if (reader.TryGetInt64(out long signedValue))
+ {
+ value = (ulong)signedValue;
+ return true;
+ }
+ }
+ if (reader.TokenType == JsonTokenType.String)
+ {
+ var s = reader.GetString();
+ if (s == null)
+ {
+ value = 0u;
+ return false;
+ }
+ if (ulong.TryParse(s, out value))
+ return true;
+ if (long.TryParse(s, out long signedValue))
+ {
+ value = (ulong)signedValue;
+ return true;
+ }
+ if (s.StartsWith("0x", StringComparison.OrdinalIgnoreCase) &&
+ ulong.TryParse(s.AsSpan(2), System.Globalization.NumberStyles.HexNumber, null, out value))
+ {
+ return true;
+ }
+ if (s.StartsWith("-0x", StringComparison.OrdinalIgnoreCase) &&
+ ulong.TryParse(s.AsSpan(3), System.Globalization.NumberStyles.HexNumber, null, out ulong negValue))
+ {
+ value = ~negValue + 1; // two's complement
+ return true;
+ }
+ }
+ value = 0;
+ return false;
+ }
+
+ // Somewhat flexible parsing of numbers, allowing json number tokens or strings as either decimal or hex, possibly negated
+ private static bool TryGetInt32FromToken(ref Utf8JsonReader reader, out int value)
+ {
+ if (reader.TokenType == JsonTokenType.Number)
+ {
+ value = reader.GetInt32();
+ return true;
+ }
+ if (reader.TokenType == JsonTokenType.String)
+ {
+ var s = reader.GetString();
+ if (s == null)
+ {
+ value = 0;
+ return false;
+ }
+ if (int.TryParse(s, out value))
+ {
+ return true;
+ }
+ if (s.StartsWith("0x", StringComparison.OrdinalIgnoreCase) &&
+ int.TryParse(s.AsSpan(2), System.Globalization.NumberStyles.HexNumber, null, out value))
+ {
+ return true;
+ }
+ if (s.StartsWith("-0x", StringComparison.OrdinalIgnoreCase) &&
+ int.TryParse(s.AsSpan(3), System.Globalization.NumberStyles.HexNumber, null, out int negValue))
+ {
+ value = -negValue;
+ return true;
+ }
+ }
+ value = 0;
+ return false;
+ }
+}
diff --git a/src/native/managed/cdacreader/tests/ContractDescriptorParserTests.cs b/src/native/managed/cdacreader/tests/ContractDescriptorParserTests.cs
new file mode 100644
index 00000000000000..eca740870727bb
--- /dev/null
+++ b/src/native/managed/cdacreader/tests/ContractDescriptorParserTests.cs
@@ -0,0 +1,213 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Text.Unicode;
+using Xunit;
+
+namespace Microsoft.Diagnostics.DataContractReader.UnitTests;
+
+public class ContractDescriptorParserTests
+{
+ [Fact]
+ public void ParsesEmptyContract()
+ {
+ ReadOnlySpan json = "{}"u8;
+ ContractDescriptorParser.ContractDescriptor descriptor = ContractDescriptorParser.ParseCompact(json);
+ Assert.Null(descriptor.Version);
+ Assert.Null(descriptor.Baseline);
+ Assert.Null(descriptor.Contracts);
+ Assert.Null(descriptor.Types);
+ Assert.Null(descriptor.Extras);
+ }
+ [Fact]
+ public void ParsesTrivialContract()
+ {
+ ReadOnlySpan json = """
+ {
+ "version": 0,
+ "baseline": "empty",
+ "contracts": {},
+ "types": {},
+ "globals": {}
+ }
+ """u8;
+ ContractDescriptorParser.ContractDescriptor descriptor = ContractDescriptorParser.ParseCompact(json);
+ Assert.Equal(0, descriptor.Version);
+ Assert.Equal("empty", descriptor.Baseline);
+ Assert.Empty(descriptor.Contracts);
+ Assert.Empty(descriptor.Types);
+ Assert.Empty(descriptor.Globals);
+ Assert.Null(descriptor.Extras);
+ }
+
+ [Fact]
+ public void ParseSizedTypes()
+ {
+ ReadOnlySpan json = """
+ {
+ "version": 0,
+ "baseline": "empty",
+ "contracts": {},
+ "types":
+ {
+ "pointer": { "!" : 8},
+ "int": { "!" : 4},
+ "Point": {
+ "x": [ 4, "int"],
+ "y": 8,
+ "!": 12
+ },
+ "Point3D": { // no size
+ "r": [ 0, "double"],
+ "phi": 8,
+ "rho": 16
+ }
+ },
+ "globals": {}
+ }
+ """u8;
+ ContractDescriptorParser.ContractDescriptor descriptor = ContractDescriptorParser.ParseCompact(json);
+ Assert.Equal(0, descriptor.Version);
+ Assert.Equal("empty", descriptor.Baseline);
+ Assert.Empty(descriptor.Contracts);
+ Assert.Empty(descriptor.Globals);
+ Assert.Equal(4, descriptor.Types.Count);
+ Assert.Equal(8u, descriptor.Types["pointer"].Size);
+ Assert.Equal(4u, descriptor.Types["int"].Size);
+ Assert.Equal(2, descriptor.Types["Point"].Fields.Count);
+ Assert.Equal(4, descriptor.Types["Point"].Fields["x"].Offset);
+ Assert.Equal(8, descriptor.Types["Point"].Fields["y"].Offset);
+ Assert.Equal("int", descriptor.Types["Point"].Fields["x"].Type);
+ Assert.Null(descriptor.Types["Point"].Fields["y"].Type);
+ Assert.Equal(12u, descriptor.Types["Point"].Size);
+ Assert.Equal(3, descriptor.Types["Point3D"].Fields.Count);
+ Assert.Equal(0, descriptor.Types["Point3D"].Fields["r"].Offset);
+ Assert.Equal(8, descriptor.Types["Point3D"].Fields["phi"].Offset);
+ Assert.Equal(16, descriptor.Types["Point3D"].Fields["rho"].Offset);
+ Assert.Equal("double", descriptor.Types["Point3D"].Fields["r"].Type);
+ Assert.Null(descriptor.Types["Point3D"].Fields["phi"].Type);
+ Assert.Null(descriptor.Types["Point3D"].Size);
+ }
+
+ [Fact]
+ public void ParseContractsCaseSensitive()
+ {
+ ReadOnlySpan json = """
+ {
+ "version": 0,
+ "baseline": "empty",
+ "contracts": {
+ "foo": 1,
+ "Foo": 2
+ },
+ "types": {},
+ "globals": {}
+ }
+ """u8;
+ ContractDescriptorParser.ContractDescriptor descriptor = ContractDescriptorParser.ParseCompact(json);
+ Assert.Equal(0, descriptor.Version);
+ Assert.Equal("empty", descriptor.Baseline);
+ Assert.Equal(2, descriptor.Contracts.Count);
+ Assert.Equal(1, descriptor.Contracts["foo"]);
+ Assert.Equal(2, descriptor.Contracts["Foo"]);
+ }
+
+ [Fact]
+ public void ParsesGlobals()
+ {
+ ReadOnlySpan json = """
+ {
+ "version": 0,
+ "baseline": "empty",
+ "contracts": {},
+ "types": {},
+ "globals": {
+ "globalInt": 1,
+ "globalPtr": [2],
+ "globalTypedInt": [3, "uint8"],
+ "globalTypedPtr": [[4], "uintptr"],
+ "globalHex": "0x1234",
+ "globalNegative": -2,
+ "globalStringyInt": "17",
+ "globalStringyNegative": "-2",
+ "globalNegativeHex": "-0xff",
+ "globalBigStringyInt": "0x123456789abcdef",
+ "globalStringyPtr": ["0x1234"],
+ "globalTypedStringyInt": ["0x1234", "int"],
+ "globalTypedStringyPtr": [["0x1234"], "int"]
+ }
+ }
+ """u8;
+ ContractDescriptorParser.ContractDescriptor descriptor = ContractDescriptorParser.ParseCompact(json);
+ Assert.Equal(0, descriptor.Version);
+ Assert.Equal("empty", descriptor.Baseline);
+ Assert.Empty(descriptor.Contracts);
+ Assert.Empty(descriptor.Types);
+ Assert.Equal(13, descriptor.Globals.Count);
+ Assert.Equal((ulong)1, descriptor.Globals["globalInt"].Value);
+ Assert.False(descriptor.Globals["globalInt"].Indirect);
+ Assert.Equal((ulong)2, descriptor.Globals["globalPtr"].Value);
+ Assert.True(descriptor.Globals["globalPtr"].Indirect);
+ Assert.Equal((ulong)3, descriptor.Globals["globalTypedInt"].Value);
+ Assert.False(descriptor.Globals["globalTypedInt"].Indirect);
+ Assert.Equal("uint8", descriptor.Globals["globalTypedInt"].Type);
+ Assert.Equal((ulong)4, descriptor.Globals["globalTypedPtr"].Value);
+ Assert.True(descriptor.Globals["globalTypedPtr"].Indirect);
+ Assert.Equal("uintptr", descriptor.Globals["globalTypedPtr"].Type);
+ Assert.Equal((ulong)0x1234, descriptor.Globals["globalHex"].Value);
+ Assert.False(descriptor.Globals["globalHex"].Indirect);
+ Assert.Equal((ulong)0xfffffffffffffffe, descriptor.Globals["globalNegative"].Value);
+ Assert.False(descriptor.Globals["globalNegative"].Indirect);
+ Assert.Equal((ulong)17, descriptor.Globals["globalStringyInt"].Value);
+ Assert.False(descriptor.Globals["globalStringyInt"].Indirect);
+ Assert.Equal((ulong)0xfffffffffffffffe, descriptor.Globals["globalStringyNegative"].Value);
+ Assert.False(descriptor.Globals["globalStringyNegative"].Indirect);
+ Assert.Equal((ulong)0xffffffffffffff01, descriptor.Globals["globalNegativeHex"].Value);
+ Assert.False(descriptor.Globals["globalNegativeHex"].Indirect);
+ Assert.Equal((ulong)0x123456789abcdef, descriptor.Globals["globalBigStringyInt"].Value);
+ Assert.False(descriptor.Globals["globalBigStringyInt"].Indirect);
+ Assert.Equal((ulong)0x1234, descriptor.Globals["globalStringyPtr"].Value);
+ Assert.True(descriptor.Globals["globalStringyPtr"].Indirect);
+ Assert.Equal("int", descriptor.Globals["globalTypedStringyInt"].Type);
+ Assert.Equal((ulong)0x1234, descriptor.Globals["globalTypedStringyInt"].Value);
+ Assert.False(descriptor.Globals["globalTypedStringyInt"].Indirect);
+ Assert.Equal("int", descriptor.Globals["globalTypedStringyPtr"].Type);
+ Assert.Equal((ulong)0x1234, descriptor.Globals["globalTypedStringyPtr"].Value);
+ Assert.True(descriptor.Globals["globalTypedStringyPtr"].Indirect);
+ }
+
+ [Fact]
+ void ParsesExoticOffsets()
+ {
+ ReadOnlySpan json = """
+ {
+ "version": 0,
+ "baseline": "empty",
+ "contracts": {},
+ "types": {
+ "OddStruct": {
+ "a": -12,
+ "b": "0x12",
+ "c": "-0x12",
+ "d": ["0x100", "int"]
+ }
+ },
+ "globals": {
+ }
+ }
+ """u8;
+ ContractDescriptorParser.ContractDescriptor descriptor = ContractDescriptorParser.ParseCompact(json);
+ Assert.Equal(0, descriptor.Version);
+ Assert.Equal("empty", descriptor.Baseline);
+ Assert.Empty(descriptor.Contracts);
+ Assert.Empty(descriptor.Globals);
+ Assert.Equal(1, descriptor.Types.Count);
+ Assert.Equal(4, descriptor.Types["OddStruct"].Fields.Count);
+ Assert.Equal(-12, descriptor.Types["OddStruct"].Fields["a"].Offset);
+ Assert.Equal(0x12, descriptor.Types["OddStruct"].Fields["b"].Offset);
+ Assert.Equal(-0x12, descriptor.Types["OddStruct"].Fields["c"].Offset);
+ Assert.Equal(0x100, descriptor.Types["OddStruct"].Fields["d"].Offset);
+ Assert.Equal("int", descriptor.Types["OddStruct"].Fields["d"].Type);
+ }
+}
diff --git a/src/native/managed/cdacreader/tests/Microsoft.Diagnostics.DataContractReader.Tests.csproj b/src/native/managed/cdacreader/tests/Microsoft.Diagnostics.DataContractReader.Tests.csproj
new file mode 100644
index 00000000000000..22e8d256e01df1
--- /dev/null
+++ b/src/native/managed/cdacreader/tests/Microsoft.Diagnostics.DataContractReader.Tests.csproj
@@ -0,0 +1,15 @@
+
+
+ true
+ $(NetCoreAppToolCurrent)
+
+
+
+
+
+
+
+
+
+
diff --git a/src/native/managed/compile-native.proj b/src/native/managed/compile-native.proj
index d227466bfcebd1..bcda8c5d6b57b5 100644
--- a/src/native/managed/compile-native.proj
+++ b/src/native/managed/compile-native.proj
@@ -35,24 +35,17 @@
--gcc-toolchain=$(ROOTFS_DIR)/usr
-
-
-
-
-
-
-
+
+
-
- @(SubprojectProps->'%(Identity)=%(Value)', ';')
-
-
+
+
+
+
+
+
+
+
+
+
+
+ @(SubprojectProps->'%(Identity)=%(Value)', ';')
+
+