Skip to content

Add CommandLineResource API #78679

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

Merged
merged 2 commits into from
Jun 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 38 additions & 67 deletions src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable<string> ar
bool? delaySignSetting = null;
string? keyFileSetting = null;
string? keyContainerSetting = null;
List<ResourceDescription> managedResources = new List<ResourceDescription>();
List<CommandLineResource> managedResources = new List<CommandLineResource>();
List<CommandLineSourceFile> sourceFiles = new List<CommandLineSourceFile>();
List<CommandLineSourceFile> additionalFiles = new List<CommandLineSourceFile>();
var analyzerConfigPaths = ArrayBuilder<string>.GetInstance();
Expand Down Expand Up @@ -742,8 +742,7 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable<string> ar
break; // Dev11 reports unrecognized option
}

var embeddedResource = ParseResourceDescription(arg, valueMemory.Value, baseDirectory, diagnostics, embedded: true);
if (embeddedResource != null)
if (TryParseResourceDescription(arg, valueMemory.Value, baseDirectory, diagnostics, isEmbedded: true, out var embeddedResource))
{
managedResources.Add(embeddedResource);
resourcesOrModulesSpecified = true;
Expand All @@ -758,8 +757,7 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable<string> ar
break; // Dev11 reports unrecognized option
}

var linkedResource = ParseResourceDescription(arg, valueMemory.Value, baseDirectory, diagnostics, embedded: false);
if (linkedResource != null)
if (TryParseResourceDescription(arg, valueMemory.Value, baseDirectory, diagnostics, isEmbedded: false, out var linkedResource))
{
managedResources.Add(linkedResource);
resourcesOrModulesSpecified = true;
Expand Down Expand Up @@ -1580,7 +1578,7 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable<string> ar
DisplayHelp = displayHelp,
DisplayVersion = displayVersion,
DisplayLangVersions = displayLangVersions,
ManifestResources = managedResources.AsImmutable(),
ManifestResourceArguments = managedResources.AsImmutable(),
CompilationOptions = options,
ParseOptions = parseOptions,
EmitOptions = emitOptions,
Expand Down Expand Up @@ -2015,78 +2013,51 @@ private static IEnumerable<InstrumentationKind> ParseInstrumentationKinds(string
}
}

internal static ResourceDescription? ParseResourceDescription(
string arg,
string resourceDescriptor,
string? baseDirectory,
IList<Diagnostic> diagnostics,
bool embedded) =>
ParseResourceDescription(arg, resourceDescriptor.AsMemory(), baseDirectory, diagnostics, embedded);

internal static ResourceDescription? ParseResourceDescription(
string arg,
internal static bool TryParseResourceDescription(
string argName,
ReadOnlyMemory<char> resourceDescriptor,
string? baseDirectory,
IList<Diagnostic> diagnostics,
bool embedded)
bool isEmbedded,
out CommandLineResource resource)
{
string? filePath;
string? fullPath;
string? fileName;
string? resourceName;
string? accessibility;

ParseResourceDescription(
if (!TryParseResourceDescription(
resourceDescriptor,
baseDirectory,
false,
out filePath,
out fullPath,
out fileName,
out resourceName,
out accessibility);

bool isPublic;
if (accessibility == null)
{
// If no accessibility is given, we default to "public".
// NOTE: Dev10 distinguishes between null and empty/whitespace-only.
isPublic = true;
}
else if (string.Equals(accessibility, "public", StringComparison.OrdinalIgnoreCase))
skipLeadingSeparators: false,
allowEmptyAccessibility: false,
out var filePath,
out var fullPath,
out var fileName,
out var resourceName,
out var isPublic,
out var rawAccessibility))
{
isPublic = true;
}
else if (string.Equals(accessibility, "private", StringComparison.OrdinalIgnoreCase))
{
isPublic = false;
}
else
{
AddDiagnostic(diagnostics, ErrorCode.ERR_BadResourceVis, accessibility);
return null;
}
if (isPublic == null)
{
AddDiagnostic(diagnostics, ErrorCode.ERR_BadResourceVis, rawAccessibility ?? "");
}
else if (RoslynString.IsNullOrWhiteSpace(filePath))
{
AddDiagnostic(diagnostics, ErrorCode.ERR_NoFileSpec, argName);
}
else
{
Debug.Assert(!PathUtilities.IsValidFilePath(fullPath));
AddDiagnostic(diagnostics, ErrorCode.FTL_InvalidInputFileName, filePath);
}

if (RoslynString.IsNullOrWhiteSpace(filePath))
{
AddDiagnostic(diagnostics, ErrorCode.ERR_NoFileSpec, arg);
return null;
resource = default;
return false;
}
Debug.Assert(!resourceName.IsEmpty()); // see ParseResourceDescription's check on filePath

if (!PathUtilities.IsValidFilePath(fullPath))
{
AddDiagnostic(diagnostics, ErrorCode.FTL_InvalidInputFileName, filePath);
return null;
}
resource = new CommandLineResource(
resourceName: resourceName,
fullPath: fullPath,
linkedResourceFileName: isEmbedded ? null : fileName,
isPublic.Value);

Func<Stream> dataProvider = () =>
{
// Use FileShare.ReadWrite because the file could be opened by the current process.
// For example, it is an XML doc file produced by the build.
return new FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
};
return new ResourceDescription(resourceName, fileName, dataProvider, isPublic, embedded, checkArgs: false);
return true;
}

private static void ParseWarnings(ReadOnlyMemory<char> value, ArrayBuilder<string> ids)
Expand Down
Loading