Skip to content
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: 79 additions & 26 deletions src/Xamarin.Android.Build.Tasks/Tasks/D8.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ public class D8 : JavaToolTask
{
public override string TaskPrefix => "DX8";

string? responseFilePath;

[Required]
public string JarPath { get; set; } = "";

Expand Down Expand Up @@ -44,6 +46,17 @@ public class D8 : JavaToolTask

public string? ExtraArguments { get; set; }

public override bool RunTask ()
{
try {
return base.RunTask ();
} finally {
if (!responseFilePath.IsNullOrEmpty () && File.Exists (responseFilePath)) {
File.Delete (responseFilePath);
}
}
}

protected override string GenerateCommandLineCommands ()
{
return GetCommandLineBuilder ().ToString ();
Expand All @@ -57,32 +70,72 @@ protected virtual CommandLineBuilder GetCommandLineBuilder ()
{
var cmd = new CommandLineBuilder ();

if (JavaOptions is { Length: > 0 }) {
// Only JVM arguments go on the command line, everything else goes in the response file
if (!JavaOptions.IsNullOrEmpty ()) {
cmd.AppendSwitch (JavaOptions);
}
cmd.AppendSwitchIfNotNull ("-Xmx", JavaMaximumHeapSize);
cmd.AppendSwitchIfNotNull ("-classpath ", JarPath);
cmd.AppendSwitch (MainClass);

if (ExtraArguments is { Length: > 0 })
cmd.AppendSwitch (ExtraArguments); // it should contain "--dex".
// Create response file with all D8/R8 arguments to avoid command line length limits
responseFilePath = CreateResponseFile ();
cmd.AppendSwitch ($"@{responseFilePath}");

return cmd;
}

/// <summary>
/// Creates a response file containing all D8/R8 arguments.
/// This avoids command line length limits that can occur with many jar libraries.
/// </summary>
protected virtual string CreateResponseFile ()
{
var responseFile = Path.GetTempFileName ();
Log.LogDebugMessage ($"[{MainClass}] response file: {responseFile}");

using var response = new StreamWriter (responseFile, append: false, encoding: Files.UTF8withoutBOM);

// D8/R8 switches
if (!ExtraArguments.IsNullOrEmpty ())
WriteArg (response, ExtraArguments); // it should contain "--dex".
if (Debug)
cmd.AppendSwitch ("--debug");
WriteArg (response, "--debug");
else
cmd.AppendSwitch ("--release");
WriteArg (response, "--release");

//NOTE: if this is blank, we can omit --min-api in this call
if (AndroidManifestFile is { Length: > 0 }) {
if (!AndroidManifestFile.IsNullOrEmpty ()) {
var doc = AndroidAppManifest.Load (AndroidManifestFile, MonoAndroidHelper.SupportedVersions);
if (doc.MinSdkVersion.HasValue) {
MinSdkVersion = doc.MinSdkVersion.Value;
cmd.AppendSwitchIfNotNull ("--min-api ", MinSdkVersion.ToString ());
WriteArg (response, "--min-api");
WriteArg (response, MinSdkVersion.ToString ());
}
}

if (!EnableDesugar)
cmd.AppendSwitch ("--no-desugaring");
WriteArg (response, "--no-desugaring");

if (!OutputDirectory.IsNullOrEmpty ()) {
WriteArg (response, "--output");
WriteArg (response, OutputDirectory);
}

// --map-diagnostics
if (MapDiagnostics != null) {
foreach (var diagnostic in MapDiagnostics) {
var from = diagnostic.ItemSpec;
var to = diagnostic.GetMetadata ("To");
if (from.IsNullOrEmpty () || to.IsNullOrEmpty ())
continue;
WriteArg (response, "--map-diagnostics");
WriteArg (response, from);
WriteArg (response, to);
}
}

// --lib and input jars
var injars = new List<string> ();
var libjars = new List<string> ();
if (AlternativeJarLibrariesToEmbed?.Length > 0) {
Expand All @@ -92,7 +145,7 @@ protected virtual CommandLineBuilder GetCommandLineBuilder ()
}
} else if (JavaLibrariesToEmbed != null) {
Log.LogDebugMessage (" processing ClassesZip, JavaLibrariesToEmbed...");
if (ClassesZip is { Length: > 0 } && File.Exists (ClassesZip)) {
if (!ClassesZip.IsNullOrEmpty () && File.Exists (ClassesZip)) {
injars.Add (ClassesZip);
}
foreach (var jar in JavaLibrariesToEmbed) {
Expand All @@ -106,25 +159,25 @@ protected virtual CommandLineBuilder GetCommandLineBuilder ()
}
}

cmd.AppendSwitchIfNotNull ("--output ", OutputDirectory);
foreach (var jar in libjars)
cmd.AppendSwitchIfNotNull ("--lib ", jar);
foreach (var jar in injars)
cmd.AppendFileNameIfNotNull (jar);

if (MapDiagnostics != null) {
foreach (var diagnostic in MapDiagnostics) {
var from = diagnostic.ItemSpec;
var to = diagnostic.GetMetadata ("To");
if (from is not { Length: > 0 } || to is not { Length: > 0 })
continue;
cmd.AppendSwitch ("--map-diagnostics");
cmd.AppendSwitch (from);
cmd.AppendSwitch (to);
}
foreach (var jar in libjars) {
WriteArg (response, "--lib");
WriteArg (response, jar);
}
foreach (var jar in injars) {
WriteArg (response, jar);
}

return cmd;
return responseFile;
}

/// <summary>
/// Writes a single argument to the response file.
/// R8/D8 response files treat each line as a complete argument, so no quoting is needed.
/// </summary>
protected void WriteArg (StreamWriter writer, string arg)
{
writer.WriteLine (arg);
Log.LogDebugMessage ($" {arg}");
}

// Note: We do not want to call the base.LogEventsFromTextOutput as it will incorrectly identify
Expand Down
38 changes: 26 additions & 12 deletions src/Xamarin.Android.Build.Tasks/Tasks/R8.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,17 @@ public override bool RunTask ()
}
}

protected override CommandLineBuilder GetCommandLineBuilder ()
/// <summary>
/// Override CreateResponseFile to add R8-specific arguments to the response file.
/// This ensures all arguments are passed via response file to avoid command line length limits.
/// </summary>
protected override string CreateResponseFile ()
{
var cmd = base.GetCommandLineBuilder ();
// First, get the base response file path and write base D8 arguments
var responseFile = base.CreateResponseFile ();

// Now append R8-specific arguments to the response file
using var response = new StreamWriter (responseFile, append: true, encoding: Files.UTF8withoutBOM);

if (EnableMultiDex) {
if (MinSdkVersion >= 21) {
Expand All @@ -77,9 +85,12 @@ protected override CommandLineBuilder GetCommandLineBuilder ()
}
File.WriteAllText (temp, string.Concat (content));

cmd.AppendSwitchIfNotNull ("--main-dex-list ", temp);
cmd.AppendSwitchIfNotNull ("--main-dex-rules ", Path.Combine (AndroidSdkBuildToolsPath, "mainDexClasses.rules"));
cmd.AppendSwitchIfNotNull ("--main-dex-list-output ", MultiDexMainDexListFile);
WriteArg (response, "--main-dex-list");
WriteArg (response, temp);
WriteArg (response, "--main-dex-rules");
WriteArg (response, Path.Combine (AndroidSdkBuildToolsPath, "mainDexClasses.rules"));
WriteArg (response, "--main-dex-list-output");
WriteArg (response, MultiDexMainDexListFile);
}
}

Expand Down Expand Up @@ -112,8 +123,8 @@ protected override CommandLineBuilder GetCommandLineBuilder ()
}
} else {
//NOTE: we may be calling r8 *only* for multi-dex, and all shrinking is disabled
cmd.AppendSwitch ("--no-tree-shaking");
cmd.AppendSwitch ("--no-minification");
WriteArg (response, "--no-tree-shaking");
WriteArg (response, "--no-minification");
// Rules to turn off optimizations
var temp = Path.GetTempFileName ();
var lines = new List<string> {
Expand All @@ -131,18 +142,21 @@ protected override CommandLineBuilder GetCommandLineBuilder ()
}
File.WriteAllLines (temp, lines);
tempFiles.Add (temp);
cmd.AppendSwitchIfNotNull ("--pg-conf ", temp);
WriteArg (response, "--pg-conf");
WriteArg (response, temp);
}
if (ProguardConfigurationFiles != null) {
foreach (var file in ProguardConfigurationFiles) {
if (File.Exists (file))
cmd.AppendSwitchIfNotNull ("--pg-conf ", file);
else
if (File.Exists (file)) {
WriteArg (response, "--pg-conf");
WriteArg (response, file);
} else {
Log.LogCodedWarning ("XA4304", file, 0, Properties.Resources.XA4304, file);
}
}
}

return cmd;
return responseFile;
}

// Note: We do not want to call the base.LogEventsFromTextOutput as it will incorrectly identify
Expand Down
Loading