-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Produce ref assemblies from command-line and msbuild #17558
Changes from 2 commits
04a5c0d
49a84f5
89aede6
1243e70
86d2473
cdebcc0
43db8ac
41399cd
d0b7133
e062d96
f8139cc
56ba8c9
7258b4b
ef4ee02
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -67,6 +67,8 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable<string> ar | |
string outputDirectory = baseDirectory; | ||
ImmutableArray<KeyValuePair<string, string>> pathMap = ImmutableArray<KeyValuePair<string, string>>.Empty; | ||
string outputFileName = null; | ||
string outputRefFilePath = null; | ||
bool metadataOnly = false; | ||
string documentationPath = null; | ||
string errorLogPath = null; | ||
bool parseDocumentationComments = false; //Don't just null check documentationFileName because we want to do this even if the file name is invalid. | ||
|
@@ -379,6 +381,7 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable<string> ar | |
} | ||
|
||
continue; | ||
|
||
case "out": | ||
if (string.IsNullOrWhiteSpace(value)) | ||
{ | ||
|
@@ -391,6 +394,26 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable<string> ar | |
|
||
continue; | ||
|
||
case "refout": | ||
value = RemoveQuotesAndSlashes(value); | ||
if (string.IsNullOrEmpty(value)) | ||
{ | ||
AddDiagnostic(diagnostics, ErrorCode.ERR_NoFileSpec, arg); | ||
} | ||
else | ||
{ | ||
outputRefFilePath = ParseGenericPathToFile(value, diagnostics, baseDirectory); | ||
} | ||
|
||
continue; | ||
|
||
case "refonly": | ||
if (value != null) | ||
break; | ||
|
||
metadataOnly = true; | ||
continue; | ||
|
||
case "t": | ||
case "target": | ||
if (value == null) | ||
|
@@ -1141,6 +1164,11 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable<string> ar | |
diagnosticOptions[o.Key] = o.Value; | ||
} | ||
|
||
if (metadataOnly && outputRefFilePath != null) | ||
{ | ||
AddDiagnostic(diagnostics, diagnosticOptions, ErrorCode.ERR_NoRefOutWhenRefOnly); | ||
} | ||
|
||
if (!IsScriptRunner && !sourceFilesSpecified && (outputKind.IsNetModule() || !resourcesOrModulesSpecified)) | ||
{ | ||
AddDiagnostic(diagnostics, diagnosticOptions, ErrorCode.WRN_NoSources); | ||
|
@@ -1257,7 +1285,7 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable<string> ar | |
|
||
var emitOptions = new EmitOptions | ||
( | ||
metadataOnly: false, | ||
metadataOnly: metadataOnly, | ||
debugInformationFormat: debugInformationFormat, | ||
pdbFilePath: null, // to be determined later | ||
outputNameOverride: null, // to be determined later | ||
|
@@ -1282,8 +1310,9 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable<string> ar | |
Utf8Output = utf8output, | ||
CompilationName = compilationName, | ||
OutputFileName = outputFileName, | ||
OutputRefFilePath = outputRefFilePath, | ||
PdbPath = pdbPath, | ||
EmitPdb = emitPdb, | ||
EmitPdb = emitPdb && !metadataOnly, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Code above checks the value of emitPdb. So if we are gonna change it here the check above won't take the metadataOnly flag into account. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's correct and that's by design. The idea is that you can just add There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK. I'd then add a comment. |
||
SourceLink = sourceLink, | ||
OutputDirectory = outputDirectory, | ||
DocumentationPath = documentationPath, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2784,6 +2784,16 @@ public void ParseOut() | |
// error CS2005: Missing file specification for '/out:' option | ||
Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("/out:")); | ||
|
||
parsedArgs = DefaultParse(new[] { @"/refout:", "a.cs" }, baseDirectory); | ||
parsedArgs.Errors.Verify( | ||
// error CS2005: Missing file specification for '/refout:' option | ||
Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("/refout:")); | ||
|
||
parsedArgs = DefaultParse(new[] { @"/refout:ref.dll", "/refonly", "a.cs" }, baseDirectory); | ||
parsedArgs.Errors.Verify( | ||
// error CS8301: Do not use refout when using refonly. | ||
Diagnostic(ErrorCode.ERR_NoRefOutWhenRefOnly).WithLocation(1, 1)); | ||
|
||
// Dev11 reports CS2007: Unrecognized option: '/out' | ||
parsedArgs = DefaultParse(new[] { @"/out", "a.cs" }, baseDirectory); | ||
parsedArgs.Errors.Verify( | ||
|
@@ -8845,6 +8855,104 @@ public void Version() | |
} | ||
} | ||
|
||
[Fact] | ||
public void RefOut() | ||
{ | ||
var dir = Temp.CreateDirectory(); | ||
dir.CreateDirectory("ref"); | ||
|
||
var src = dir.CreateFile("a.cs"); | ||
src.WriteAllText(@"class C { public static void Main() { } }"); | ||
|
||
var outWriter = new StringWriter(CultureInfo.InvariantCulture); | ||
var csc = new MockCSharpCompiler(null, dir.Path, new[] { "/nologo", "/out:a.dll", "/refout:ref/a.dll", "/deterministic", "a.cs" }); | ||
|
||
int exitCode = csc.Run(outWriter); | ||
Assert.Equal(0, exitCode); | ||
|
||
var refDll = Path.Combine(dir.Path, "ref\\a.dll"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
Assert.True(File.Exists(refDll)); | ||
var refPeStream = File.OpenRead(refDll); | ||
|
||
using (var refPeReader = new PEReader(refPeStream)) | ||
{ | ||
var metadataReader = refPeReader.GetMetadataReader(); | ||
|
||
// The types and members that are included needs further refinement. | ||
// See issue https://github.com/dotnet/roslyn/issues/17612 | ||
AssertEx.SetEqual(metadataReader.TypeDefinitions.Select(t => metadataReader.Dump(t)), | ||
new[] { "TypeDef:<Module>", "TypeDef:C" }); | ||
|
||
AssertEx.SetEqual(metadataReader.MethodDefinitions.Select(t => metadataReader.Dump(t)), | ||
new[] { "MethodDef: Void Main()", "MethodDef: Void .ctor()" }); | ||
|
||
// ReferenceAssemblyAttribute is missing. | ||
// See issue https://github.com/dotnet/roslyn/issues/17612 | ||
AssertEx.SetEqual( | ||
metadataReader.CustomAttributes.Select(a => metadataReader.GetCustomAttribute(a).Constructor) | ||
.Select(c => metadataReader.GetMemberReference((MemberReferenceHandle)c).Parent) | ||
.Select(p => metadataReader.GetTypeReference((TypeReferenceHandle)p).Name) | ||
.Select(n => metadataReader.GetString(n)), | ||
new[] { "CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute" }); | ||
|
||
// no method implementations | ||
foreach (var typeDef in metadataReader.TypeDefinitions) | ||
{ | ||
Assert.Equal(0, metadataReader.GetTypeDefinition(typeDef).GetMethodImplementations().Count()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This doesn't actually verify that there were no method bodies emitted, which I assume is the intent here. #Closed |
||
} | ||
} | ||
|
||
// Clean up temp files | ||
CleanupAllGeneratedFiles(dir.Path); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
We should verify that the normal assembly is what we expect. #Closed |
||
} | ||
|
||
[Fact] | ||
public void RefOutWithError() | ||
{ | ||
var dir = Temp.CreateDirectory(); | ||
dir.CreateDirectory("ref"); | ||
|
||
var src = dir.CreateFile("a.cs"); | ||
src.WriteAllText(@"class C { public static void Main() { error(); } }"); | ||
|
||
var outWriter = new StringWriter(CultureInfo.InvariantCulture); | ||
var csc = new MockCSharpCompiler(null, dir.Path, new[] { "/nologo", "/out:a.dll", "/refout:ref/a.dll", "/deterministic", "a.cs" }); | ||
int exitCode = csc.Run(outWriter); | ||
Assert.Equal(1, exitCode); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It would be great to examine the diagnostics. #Closed |
||
|
||
var dll = Path.Combine(dir.Path, "a.dll"); | ||
Assert.False(File.Exists(dll)); | ||
|
||
var refDll = Path.Combine(dir.Path, "ref\\a.dll"); | ||
Assert.True(File.Exists(refDll)); | ||
|
||
// Clean up temp files | ||
CleanupAllGeneratedFiles(dir.Path); | ||
} | ||
|
||
[Fact] | ||
public void RefOnly() | ||
{ | ||
var dir = Temp.CreateDirectory(); | ||
|
||
var src = dir.CreateFile("a.cs"); | ||
src.WriteAllText(@"class C { public static void Main() { error(); } }"); // semantic error in method body | ||
|
||
var outWriter = new StringWriter(CultureInfo.InvariantCulture); | ||
var csc = new MockCSharpCompiler(null, dir.Path, new[] { "/nologo", "/out:a.dll", "/refonly", "/debug", "/deterministic", "a.cs" }); | ||
int exitCode = csc.Run(outWriter); | ||
Assert.Equal(0, exitCode); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think we should verify content of a.dll against our expectations. #Closed |
||
|
||
var refDll = Path.Combine(dir.Path, "a.dll"); | ||
Assert.True(File.Exists(refDll)); | ||
|
||
var pdb = Path.Combine(dir.Path, "a.pdb"); | ||
Assert.False(File.Exists(pdb)); | ||
|
||
// Clean up temp files | ||
CleanupAllGeneratedFiles(dir.Path); | ||
} | ||
|
||
public class QuotedArgumentTests | ||
{ | ||
private void VerifyQuotedValid<T>(string name, string value, T expected, Func<CSharpCommandLineArguments, T> getValue) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,7 +40,7 @@ public void Main() | |
EmitResult emitResult; | ||
using (var output = new MemoryStream()) | ||
{ | ||
emitResult = compilation.Emit(output, null, null, null); | ||
emitResult = compilation.Emit(output, pdbStream: null, xmlDocumentationStream: null, win32Resources: null); | ||
} | ||
|
||
emitResult.Diagnostics.Verify( | ||
|
@@ -148,7 +148,7 @@ namespace N.Foo; | |
EmitResult emitResult; | ||
using (var output = new MemoryStream()) | ||
{ | ||
emitResult = comp.Emit(output, null, null, null); | ||
emitResult = comp.Emit(output, pdbStream: null, xmlDocumentationStream: null, win32Resources: null); | ||
} | ||
|
||
Assert.False(emitResult.Success); | ||
|
@@ -236,6 +236,84 @@ public static void Main() | |
} | ||
} | ||
|
||
[Theory, | ||
InlineData("public int M() { return 1; }", "public int M() { return 2; }", true), | ||
InlineData("public int M() { return 1; }", "public int M() { error(); }", true), | ||
InlineData("", "private void M() { }", false), // Should be true. See follow-up issue https://github.com/dotnet/roslyn/issues/17612 | ||
InlineData("internal void M() { }", "", false), | ||
InlineData("public struct S { private int i; }", "public struct S { }", false)] | ||
public void RefAssemblyChanges(string change1, string change2, bool expectMatch) | ||
{ | ||
string sourceTemplate = @" | ||
public class C | ||
{ | ||
CHANGE | ||
} | ||
"; | ||
string name = GetUniqueName(); | ||
string source1 = sourceTemplate.Replace("CHANGE", change1); | ||
CSharpCompilation comp1 = CreateCompilationWithMscorlib(Parse(source1), | ||
options: TestOptions.DebugDll.WithDeterministic(true), assemblyName: name); | ||
|
||
byte[] image1; | ||
|
||
using (var output = new MemoryStream()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
using (var pdbOutput = new MemoryStream()) | ||
{ | ||
var emitResult = comp1.Emit(output, pdbOutput, options: EmitOptions.Default.WithEmitMetadataOnly(true)); | ||
Assert.True(emitResult.Success); | ||
emitResult.Diagnostics.Verify(); | ||
image1 = output.ToArray(); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
var source2 = sourceTemplate.Replace("CHANGE", change2); | ||
Compilation comp2 = CreateCompilationWithMscorlib(Parse(source2), | ||
options: TestOptions.DebugDll.WithDeterministic(true), assemblyName: name); | ||
|
||
byte[] image2; | ||
|
||
using (var output = new MemoryStream()) | ||
using (var pdbOutput = new MemoryStream()) | ||
{ | ||
var emitResult = comp2.Emit(output, pdbOutput, options: EmitOptions.Default.WithEmitMetadataOnly(true)); | ||
Assert.True(emitResult.Success); | ||
emitResult.Diagnostics.Verify(); | ||
image2 = output.ToArray(); | ||
} | ||
|
||
Assert.True(AssertEx.SequenceEqual(image1, image2) == expectMatch); | ||
} | ||
|
||
[Theory, | ||
InlineData("public int M() { error(); }", true), | ||
InlineData("public int M() { error() }", false), // Should be true. See follow-up issue https://github.com/dotnet/roslyn/issues/17612 | ||
InlineData("public Error M() { return null; }", false), // Should be true. See follow-up issue https://github.com/dotnet/roslyn/issues/17612 | ||
] | ||
public void RefAssemblyDiagnostics(string change, bool expectSuccess) | ||
{ | ||
string sourceTemplate = @" | ||
public class C | ||
{ | ||
CHANGE | ||
} | ||
"; | ||
string source = sourceTemplate.Replace("CHANGE", change); | ||
string name = GetUniqueName(); | ||
CSharpCompilation comp1 = CreateCompilationWithMscorlib(Parse(source), | ||
options: TestOptions.DebugDll.WithDeterministic(true), assemblyName: name); | ||
|
||
byte[] image; | ||
|
||
using (var output = new MemoryStream()) | ||
using (var pdbOutput = new MemoryStream()) | ||
{ | ||
var emitResult = comp1.Emit(output, pdbOutput, options: EmitOptions.Default.WithEmitMetadataOnly(true)); | ||
Assert.Equal(expectSuccess, emitResult.Success); | ||
Assert.Equal(!expectSuccess, emitResult.Diagnostics.Any()); | ||
image = output.ToArray(); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Check that when we emit metadata only, we include metadata for | ||
/// compiler generate methods (e.g. the ones for implicit interface | ||
|
@@ -939,7 +1017,7 @@ public class Test | |
EmitResult emitResult; | ||
using (var output = new MemoryStream()) | ||
{ | ||
emitResult = compilation.Emit(output, null, null, null); | ||
emitResult = compilation.Emit(output, pdbStream: null, xmlDocumentationStream: null, win32Resources: null); | ||
} | ||
|
||
Assert.False(emitResult.Success); | ||
|
@@ -972,7 +1050,7 @@ public static void Main() | |
EmitResult emitResult; | ||
using (var output = new MemoryStream()) | ||
{ | ||
emitResult = compilation.Emit(output, null, null, null); | ||
emitResult = compilation.Emit(output, pdbStream: null, xmlDocumentationStream: null, win32Resources: null); | ||
} | ||
|
||
Assert.True(emitResult.Success); | ||
|
@@ -1010,7 +1088,7 @@ public static void Main() | |
EmitResult emitResult; | ||
using (var output = new MemoryStream()) | ||
{ | ||
emitResult = compilation.Emit(output, null, null, null); | ||
emitResult = compilation.Emit(output, pdbStream: null, xmlDocumentationStream: null, win32Resources: null); | ||
} | ||
|
||
Assert.True(emitResult.Success); | ||
|
@@ -2726,7 +2804,7 @@ public void BrokenPDBStream() | |
var output = new MemoryStream(); | ||
var pdb = new BrokenStream(); | ||
pdb.BreakHow = BrokenStream.BreakHowType.ThrowOnSetLength; | ||
var result = compilation.Emit(output, pdb); | ||
var result = compilation.Emit(output, pdbStream: pdb); | ||
|
||
// error CS0041: Unexpected error writing debug information -- 'Exception from HRESULT: 0x806D0004' | ||
var err = result.Diagnostics.Single(); | ||
|
@@ -2737,7 +2815,7 @@ public void BrokenPDBStream() | |
Assert.Equal(ioExceptionMessage, (string)err.Arguments[0]); | ||
|
||
pdb.Dispose(); | ||
result = compilation.Emit(output, pdb); | ||
result = compilation.Emit(output, pdbStream: pdb); | ||
|
||
// error CS0041: Unexpected error writing debug information -- 'Exception from HRESULT: 0x806D0004' | ||
err = result.Diagnostics.Single(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we testing
/refonly:value
?