void Generate(Context context, StreamWriter sw)
        {
            string myPath = Path.Combine(BuildPaths.XAPrepareSourceDir, "Application", "GeneratedMakeRulesFile.cs");

            sw.WriteLine("#");
            sw.WriteLine($"# Generated by {myPath}");
            sw.WriteLine("#");
            sw.WriteLine();

            WriteVariable("export OS_NAME", context.OS.Type);
            WriteVariable("export OS_ARCH", context.OS.Architecture);
            WriteVariable("export OS_ARCH_TRANSLATED", context.OS.ProcessIsTranslated ? "true" : "false");
            WriteVariable("PRODUCT_VERSION", context.ProductVersion);
            WriteVariable("MONO_SOURCE_FULL_PATH", Configurables.Paths.MonoSourceFullPath);

            // These must remain dynamic since the developer may change branches without re-running `prepare`
            string getGitBranchScript = Utilities.GetRelativePath(BuildPaths.XamarinAndroidSourceRoot, Path.Combine(Configurables.Paths.BuildToolsScriptsDir, "get-git-branch.sh"));

            WriteVariable("GIT_BRANCH", $"$(shell LANG=C \"{getGitBranchScript}\" | tr -d '[[:space:]]' | tr -C a-zA-Z0-9- _)");
            WriteVariable("GIT_COMMIT", $"$(shell LANG=C git log --no-color --first-parent -n1 --pretty=format:%h)");
            WriteVariable("-num-commits-since-version-change", $"$(shell LANG=C git log {context.BuildInfo.CommitOfLastVersionChange}..HEAD --oneline 2>/dev/null | wc -l | sed 's/ //g')");

            WriteVariable("ZIP_EXTENSION", context.OS.ZipExtension);
            WriteVariable("ZIP_OUTPUT_BASENAME", GetOutputFileName(context, "xamarin.android-oss"));
            WriteVariable("_TEST_RESULTS_BASENAME", GetOutputFileName(context, "xa-test-results"));
            WriteVariable("_BUILD_STATUS_BASENAME", GetOutputFileName(context, "xa-build-status"));

            WriteVariable("ZIP_OUTPUT", "$(ZIP_OUTPUT_BASENAME).$(ZIP_EXTENSION)");
            WriteVariable("_BUILD_STATUS_ZIP_OUTPUT", "$(_BUILD_STATUS_BASENAME).$(ZIP_EXTENSION)");
            WriteVariable("_TEST_RESULTS_ZIP_OUTPUT", "$(_TEST_RESULTS_BASENAME).$(ZIP_EXTENSION)");

            var allApiLevels     = new List <string> ();
            var allPlatformIDs   = new List <string> ();
            var allFrameworks    = new List <string> ();
            var apiLevels        = new List <string> ();
            var stableApiLevels  = new List <string> ();
            var frameworks       = new List <string> ();
            var stableFrameworks = new List <string> ();
            var platformIds      = new List <string> ();

            foreach (AndroidPlatform ap in BuildAndroidPlatforms.AllPlatforms)
            {
                string api = ap.ApiLevel.ToString();

                allApiLevels.Add(api);
                allPlatformIDs.Add(ap.PlatformID);
                if (!String.IsNullOrEmpty(ap.Framework))
                {
                    allFrameworks.Add(ap.Framework);
                    frameworks.Add(ap.Framework);
                    if (ap.Stable)
                    {
                        stableFrameworks.Add(ap.Framework);
                    }
                }
                else
                {
                    allFrameworks.Add("-");
                }

                if (!ap.Supported)
                {
                    continue;
                }

                apiLevels.Add(api);
                platformIds.Add(ap.PlatformID);
                if (ap.Stable)
                {
                    stableApiLevels.Add(api);
                }
            }

            var enabledJitAbis  = AbiNames.AllJitAbis.Where(a => context.IsTargetJitAbiEnabled(a));
            var enabledHostAbis = AbiNames.AllHostAbis.Where(a => context.IsHostJitAbiEnabled(a));
            var enabledAotAbis  = AbiNames.AllAotAbis.Where(a => context.IsTargetAotAbiEnabled(a));

            WriteVariable("ALL_API_LEVELS", ToValue(allApiLevels));
            WriteVariable("ALL_PLATFORM_IDS", ToValue(allPlatformIDs));
            WriteVariable("ALL_FRAMEWORKS", ToValue(allFrameworks));
            WriteVariable("API_LEVELS", ToValue(apiLevels));
            WriteVariable("STABLE_API_LEVELS", ToValue(stableApiLevels));
            WriteVariable("FRAMEWORKS", ToValue(frameworks));
            WriteVariable("STABLE_FRAMEWORKS", ToValue(stableFrameworks));
            WriteVariable("ALL_JIT_ABIS", ToValue(enabledJitAbis.ToList()));
            WriteVariable("ALL_HOST_ABIS", ToValue(enabledHostAbis.ToList()));
            WriteVariable("ALL_AOT_ABIS", ToValue(enabledAotAbis.ToList()));
            WriteVariable("ANDROID_TOOLCHAIN_DIR", context.Properties.GetRequiredValue(KnownProperties.AndroidToolchainDirectory));
            if (context.MonoOptions != null && context.MonoOptions.Count > 0)
            {
                WriteVariable("MONO_OPTIONS", ToValue(context.MonoOptions));
                sw.WriteLine("export MONO_OPTIONS");
            }

            sw.WriteLine("_MSBUILD_ARGS = \\");
            sw.WriteLine($"\t/p:{KnownProperties.AndroidSupportedTargetJitAbis}={context.Properties.GetRequiredValue (KnownProperties.AndroidSupportedTargetJitAbis)} \\");
            sw.WriteLine($"\t/p:{KnownProperties.AndroidSupportedHostJitAbis}={context.Properties.GetRequiredValue (KnownProperties.AndroidSupportedHostJitAbis)} \\");
            sw.WriteLine($"\t/p:{KnownProperties.AndroidSupportedTargetAotAbis}={context.Properties.GetRequiredValue (KnownProperties.AndroidSupportedTargetAotAbis)}");

            OutputOSVariables(context, sw);

            WriteListVariable("_BUNDLE_ZIPS_INCLUDE", Configurables.Defaults.BundleZipsInclude);
            WriteListVariable("_BUNDLE_ZIPS_EXCLUDE", Configurables.Defaults.BundleZipsExclude);
            WriteListVariable("_TEST_RESULTS_BUNDLE_INCLUDE", Configurables.Defaults.TestResultsBundleInclude);
            WriteListVariable("_TEST_RESULTS_BUNDLE_EXCLUDE", Configurables.Defaults.TestResultsBundleExclude);
            WriteListVariable("_BUILD_STATUS_BUNDLE_INCLUDE", Configurables.Defaults.BuildStatusBundleInclude);
            WriteListVariable("_BUILD_STATUS_BUNDLE_INCLUDE", Configurables.Defaults.BuildStatusBundleIncludeConditional, true);
            WriteListVariable("_BUILD_STATUS_BUNDLE_EXCLUDE", Configurables.Defaults.BuildStatusBundleExclude);

            sw.WriteLine();
            sw.WriteLine(".PHONY: framework-assemblies");
            sw.WriteLine("framework-assemblies:");

            string prevVersion        = "v1.0";
            string monoFrameworksRoot = Path.Combine("bin", "$(CONFIGURATION)", context.Properties.GetRequiredValue(KnownProperties.XABinRelativeInstallPrefix), Configurables.Paths.MonoAndroidFrameworksSubDir);

            for (int i = 0; i < apiLevels.Count; i++)
            {
                string curVersion = frameworks [i];
                string apiLevel   = apiLevels [i];
                string platformId = platformIds [i];
                string redistFile = Path.Combine(monoFrameworksRoot, curVersion, "RedistList", "FrameworkList.xml");
                WriteRuleLine($"grep -q {prevVersion} {redistFile}; \\");
                WriteRuleLine("if [ $$? -ne 0 ] ; then \\");
                WriteRuleLine($"\trm -f {redistFile}; \\");
                WriteRuleLine("fi; \\");
                WriteRuleLine("$(call MSBUILD_BINLOG,Mono.Android,$(_SLN_BUILD)) src/Mono.Android/Mono.Android.csproj \\");
                WriteRuleLine("\t/p:Configuration=$(CONFIGURATION) $(_MSBUILD_ARGS) \\");
                WriteRuleLine($"\t/p:AndroidApiLevel={apiLevel} /p:AndroidPlatformId={platformId} /p:AndroidFrameworkVersion={curVersion} \\");
                WriteRuleLine($"\t/p:AndroidPreviousFrameworkVersion={prevVersion} || exit 1;");

                prevVersion = curVersion;
            }

            string firstApiLevel         = apiLevels [0];
            string firstPlatformId       = platformIds [0];
            string firstFramework        = frameworks [0];
            string latestStableFramework = stableFrameworks [stableFrameworks.Count - 1];

            WriteMSBuildCall(
                fileToRemovePath: Path.Combine(monoFrameworksRoot, "v1.0", "Xamarin.Android.NUnitLite.dll"),
                projectPath: "src/Xamarin.Android.NUnitLite/Xamarin.Android.NUnitLite.csproj"
                );

            WriteMSBuildCall(
                fileToRemovePath: $"{monoFrameworksRoot}/{latestStableFramework}/Mono.Android.Export.*",
                projectPath: "src/Mono.Android.Export/Mono.Android.Export.csproj"
                );

            WriteMSBuildCall(
                fileToRemovePath: $"{monoFrameworksRoot}/{latestStableFramework}/OpenTK-1.0.*",
                projectPath: "src/OpenTK-1.0/OpenTK.csproj"
                );
            sw.WriteLine();

            if (context.RuleGenerators == null || context.RuleGenerators.Count == 0)
            {
                return;
            }

            foreach (RuleGenerator rg in context.RuleGenerators)
            {
                if (rg == null)
                {
                    continue;
                }
                rg(this, sw);
            }

            void WriteMSBuildCall(string fileToRemovePath, string projectPath)
            {
                WriteRuleLine($"rm -f {fileToRemovePath}");
                WriteRuleLine($"$(call MSBUILD_BINLOG,NUnitLite,$(_SLN_BUILD)) $(MSBUILD_FLAGS) {projectPath} \\");
                WriteRuleLine("\t/p:Configuration=$(CONFIGURATION) $(_MSBUILD_ARGS) \\");
                WriteRuleLine($"\t/p:AndroidApiLevel={firstApiLevel} /p:AndroidPlatformId={firstPlatformId} \\");
                WriteRuleLine($"\t/p:AndroidFrameworkVersion={firstFramework} || exit 1;");
            }

            string ToValue(ICollection <string> list, string?separator = null)
            {
                return(String.Join(separator ?? " ", list));
            }

            void WriteRuleLine(string line)
            {
                sw.Write('\t');
                sw.WriteLine(line);
            }

            void WriteVariable(string name, string value)
            {
                sw.WriteLine($"{name} = {value}");
            }

            void WriteListVariable(string name, ICollection <string> list, bool conditional = false)
            {
                if (list.Count == 0)
                {
                    return;
                }

                if (!conditional)
                {
                    sw.Write($"{name} =");
                }

                foreach (string i in list)
                {
                    string item = i.Trim();
                    if (String.IsNullOrEmpty(item))
                    {
                        continue;
                    }

                    if (conditional)
                    {
                        sw.WriteLine($"ifneq ($(wildcard {item}),)");
                        sw.WriteLine($"{name} += {item}");
                        sw.WriteLine("endif");
                        continue;
                    }

                    sw.WriteLine(" \\");
                    sw.Write($"\t{item}");
                }

                if (!conditional)
                {
                    sw.WriteLine();
                }
            }
        }