public XamarinAndroidApplicationProject(string debugConfigurationName = "Debug", string releaseConfigurationName = "Release", [CallerMemberName] string packageName = "") : base(debugConfigurationName, releaseConfigurationName) { if (Builder.UseDotNet) { SetProperty(KnownProperties.OutputType, "Exe"); SetProperty("XamarinAndroidSupportSkipVerifyVersions", "True"); SetProperty("_FastDeploymentDiagnosticLogging", "True"); // Workaround for AndroidX, see: https://github.com/xamarin/AndroidSupportComponents/pull/239 Imports.Add(new Import(() => "Directory.Build.targets") { TextContent = () => @"<Project> <PropertyGroup> <VectorDrawableCheckBuildToolsVersionTaskBeforeTargets /> </PropertyGroup> </Project>" }); } else { SetProperty("_FastDeploymentDiagnosticLogging", "True"); SetProperty("AndroidApplication", "True"); SetProperty("AndroidResgenClass", "Resource"); SetProperty("AndroidResgenFile", () => "Resources\\Resource.designer" + Language.DefaultDesignerExtension); SetProperty("AndroidManifest", "Properties\\AndroidManifest.xml"); SetProperty(DebugProperties, "AndroidLinkMode", "None"); SetProperty(ReleaseProperties, "AndroidLinkMode", "SdkOnly"); SetProperty(DebugProperties, KnownProperties.EmbedAssembliesIntoApk, "False", "'$(EmbedAssembliesIntoApk)' == ''"); SetProperty(ReleaseProperties, KnownProperties.EmbedAssembliesIntoApk, "True", "'$(EmbedAssembliesIntoApk)' == ''"); } AndroidManifest = default_android_manifest; TargetSdkVersion = AndroidSdkResolver.GetMaxInstalledPlatform().ToString(); LayoutMain = default_layout_main; StringsXml = default_strings_xml; PackageName = $"com.xamarin.{(packageName ?? ProjectName).ToLower ()}"; JavaPackageName = JavaPackageName ?? PackageName.ToLowerInvariant(); OtherBuildItems.Add(new BuildItem.NoActionResource("Properties\\AndroidManifest.xml") { TextContent = ProcessManifestTemplate, }); AndroidResources.Add(new AndroidItem.AndroidResource("Resources\\layout\\Main.axml") { TextContent = () => LayoutMain }); AndroidResources.Add(new AndroidItem.AndroidResource("Resources\\values\\Strings.xml") { TextContent = () => StringsXml.Replace("${PROJECT_NAME}", ProjectName) }); Sources.Add(new BuildItem.Source(() => "MainActivity" + Language.DefaultExtension) { TextContent = () => ProcessSourceTemplate(MainActivity ?? DefaultMainActivity) }); }
/// <summary> /// Runs the `dotnet` tool with the specified arguments. /// </summary> /// <param name="args">command arguments</param> /// <returns>Whether or not the command succeeded.</returns> protected bool Execute(params string [] args) { if (string.IsNullOrEmpty(ProcessLogFile)) { ProcessLogFile = Path.Combine(XABuildPaths.TestOutputDirectory, $"dotnet{DateTime.Now.ToString ("yyyyMMddHHmmssff")}-process.log"); } var procOutput = new StringBuilder(); bool succeeded; using (var p = new Process()) { p.StartInfo.FileName = Path.Combine(AndroidSdkResolver.GetDotNetPreviewPath(), "dotnet"); p.StartInfo.Arguments = string.Join(" ", args); p.StartInfo.CreateNoWindow = true; p.StartInfo.UseShellExecute = false; p.StartInfo.RedirectStandardOutput = true; p.StartInfo.RedirectStandardError = true; // Ensure any variable alteration from DotNetXamarinProject.Construct is cleared. if (!Builder.UseDotNet && !TestEnvironment.IsWindows) { p.StartInfo.EnvironmentVariables ["MSBUILD_EXE_PATH"] = null; } p.ErrorDataReceived += (sender, e) => { if (e.Data != null) { procOutput.AppendLine(e.Data); } }; p.ErrorDataReceived += (sender, e) => { if (e.Data != null) { procOutput.AppendLine(e.Data); } }; procOutput.AppendLine($"Running: {p.StartInfo.FileName} {p.StartInfo.Arguments}"); p.Start(); p.BeginOutputReadLine(); p.BeginErrorReadLine(); bool completed = p.WaitForExit((int)new TimeSpan(0, 15, 0).TotalMilliseconds); succeeded = completed && p.ExitCode == 0; procOutput.AppendLine($"Exit Code: {p.ExitCode}"); } File.WriteAllText(ProcessLogFile, procOutput.ToString()); return(succeeded); }
static void DexDump(DataReceivedEventHandler handler, string dexFile, string androidSdkDirectory) { var androidSdk = new AndroidSdkInfo((l, m) => { Console.WriteLine($"{l}: {m}"); if (l == TraceLevel.Error) { throw new Exception(m); } }, androidSdkDirectory, javaSdkPath: AndroidSdkResolver.GetJavaSdkPath()); var buildToolsPath = androidSdk.GetBuildToolsPaths().FirstOrDefault(); if (string.IsNullOrEmpty(buildToolsPath)) { throw new Exception($"Unable to find build-tools in `{androidSdkDirectory}`!"); } var psi = new ProcessStartInfo { FileName = Path.Combine(buildToolsPath, "dexdump"), Arguments = Path.GetFileName(dexFile), CreateNoWindow = true, WindowStyle = ProcessWindowStyle.Hidden, UseShellExecute = false, RedirectStandardError = true, RedirectStandardOutput = true, WorkingDirectory = Path.GetDirectoryName(dexFile), }; using (var p = new Process { StartInfo = psi }) { p.ErrorDataReceived += handler; p.OutputDataReceived += handler; p.Start(); p.BeginErrorReadLine(); p.BeginOutputReadLine(); p.WaitForExit(); if (p.ExitCode != 0) { throw new Exception($"'{psi.FileName} {psi.Arguments}' exited with code: {p.ExitCode}"); } } }
public int GetMaxInstalledPlatform() { string sdkPath = AndroidSdkResolver.GetAndroidSdkPath(); int result = 0; foreach (var dir in Directory.EnumerateDirectories(Path.Combine(sdkPath, "platforms"))) { int version; string v = Path.GetFileName(dir).Replace("android-", ""); if (!int.TryParse(v, out version)) { continue; } if (version < result) { continue; } result = version; } return(result); }
public JarContentBuilder() { Action <TraceLevel, string> logger = (level, value) => { switch (level) { case TraceLevel.Error: throw new Exception($"AndroidSdkInfo {level}: {value}"); default: Console.WriteLine($"AndroidSdkInfo {level}: {value}"); break; } }; var sdkPath = AndroidSdkResolver.GetAndroidSdkPath(); var ndkPath = AndroidSdkResolver.GetAndroidNdkPath(); var androidSdk = new AndroidSdkInfo(logger, androidSdkPath: sdkPath, androidNdkPath: ndkPath); JavacFullPath = Path.Combine(androidSdk.JavaSdkPath, "bin", "javac"); JarFullPath = Path.Combine(androidSdk.JavaSdkPath, "bin", "jar"); }
public XASdkProject(string outputType = "Exe") { Sdk = "Microsoft.NET.Sdk"; TargetFramework = "net5.0-android"; TargetSdkVersion = AndroidSdkResolver.GetMaxInstalledPlatform().ToString(); PackageName = PackageName ?? string.Format("{0}.{0}", ProjectName); JavaPackageName = JavaPackageName ?? PackageName.ToLowerInvariant(); GlobalPackagesFolder = Path.Combine(XABuildPaths.TopDirectory, "packages"); SetProperty(KnownProperties.OutputType, outputType); // Add relevant Android content to our project without writing it to the .csproj file if (outputType == "Exe") { Sources.Add(new BuildItem.Source("Properties\\AndroidManifest.xml") { TextContent = ProcessManifestTemplate }); } Sources.Add(new BuildItem.Source($"MainActivity{Language.DefaultExtension}") { TextContent = () => ProcessSourceTemplate(MainActivity ?? DefaultMainActivity) }); Sources.Add(new BuildItem.Source("Resources\\layout\\Main.axml") { TextContent = () => default_layout_main }); Sources.Add(new BuildItem.Source("Resources\\values\\Strings.xml") { TextContent = () => default_strings_xml.Replace("${PROJECT_NAME}", ProjectName) }); Sources.Add(new BuildItem.Source("Resources\\drawable-mdpi\\Icon.png") { BinaryContent = () => icon_binary_mdpi }); Sources.Add(new BuildItem.Source($"Resources\\Resource.designer{Language.DefaultExtension}") { TextContent = () => string.Empty }); }
protected bool BuildInternal(string projectOrSolution, string target, string [] parameters = null, Dictionary <string, string> environmentVariables = null, bool restore = true) { buildLogFullPath = (!string.IsNullOrEmpty(BuildLogFile)) ? Path.GetFullPath(Path.Combine(XABuildPaths.TestOutputDirectory, Path.GetDirectoryName(projectOrSolution), BuildLogFile)) : null; string processLog = !string.IsNullOrEmpty(BuildLogFile) ? Path.Combine(Path.GetDirectoryName(buildLogFullPath), "process.log") : null; var logger = buildLogFullPath == null ? string.Empty : string.Format("/noconsolelogger \"/flp1:LogFile={0};Encoding=UTF-8;Verbosity={1}\"", buildLogFullPath, Verbosity.ToString().ToLower()); var start = DateTime.UtcNow; var args = new StringBuilder(); var psi = new ProcessStartInfo(BuildTool); var responseFile = Path.Combine(XABuildPaths.TestOutputDirectory, Path.GetDirectoryName(projectOrSolution), "project.rsp"); if (UseDotNet) { args.Append("build "); } args.AppendFormat("{0} /t:{1} {2}", QuoteFileName(Path.Combine(XABuildPaths.TestOutputDirectory, projectOrSolution)), target, logger); if (AutomaticNuGetRestore && restore && !UseDotNet) { args.Append(" /restore"); } args.Append($" @\"{responseFile}\""); using (var sw = new StreamWriter(responseFile, append: false, encoding: Encoding.UTF8)) { sw.WriteLine($" /p:BuildingInsideVisualStudio={BuildingInsideVisualStudio}"); if (BuildingInsideVisualStudio) { sw.WriteLine(" /p:BuildingOutOfProcess=true"); } string sdkPath = AndroidSdkResolver.GetAndroidSdkPath(); if (Directory.Exists(sdkPath)) { sw.WriteLine(" /p:AndroidSdkDirectory=\"{0}\" ", sdkPath); } string ndkPath = AndroidSdkResolver.GetAndroidNdkPath(); if (Directory.Exists(ndkPath)) { sw.WriteLine(" /p:AndroidNdkDirectory=\"{0}\" ", ndkPath); } string jdkPath = AndroidSdkResolver.GetJavaSdkPath(); if (Directory.Exists(jdkPath)) { sw.WriteLine(" /p:JavaSdkDirectory=\"{0}\" ", jdkPath); } if (parameters != null) { foreach (var param in parameters) { sw.WriteLine(" /p:{0}", param); } } var msbuildArgs = Environment.GetEnvironmentVariable("NUNIT_MSBUILD_ARGS"); if (!string.IsNullOrEmpty(msbuildArgs)) { sw.WriteLine(msbuildArgs); } psi.EnvironmentVariables ["MSBUILD"] = "msbuild"; sw.WriteLine($" /bl:\"{Path.GetFullPath (Path.Combine (XABuildPaths.TestOutputDirectory, Path.GetDirectoryName (projectOrSolution), "msbuild.binlog"))}\""); if (environmentVariables != null) { foreach (var kvp in environmentVariables) { psi.EnvironmentVariables [kvp.Key] = kvp.Value; } } } //NOTE: commit messages can "accidentally" cause test failures // Consider if you added an error message in a commit message, then wrote a test asserting the error no longer occurs. // Both Jenkins and VSTS have an environment variable containing the full commit message, which will inexplicably cause your test to fail... // For a Jenkins case, see https://github.com/xamarin/xamarin-android/pull/1049#issuecomment-347625456 // For a VSTS case, see http://build.devdiv.io/1806783 psi.EnvironmentVariables ["ghprbPullLongDescription"] = psi.EnvironmentVariables ["BUILD_SOURCEVERSIONMESSAGE"] = ""; psi.Arguments = args.ToString(); psi.CreateNoWindow = true; psi.UseShellExecute = false; psi.RedirectStandardOutput = true; psi.RedirectStandardError = true; psi.StandardErrorEncoding = Encoding.UTF8; psi.StandardOutputEncoding = Encoding.UTF8; bool nativeCrashDetected = false; bool result = false; bool ranToCompletion = false; int attempts = 1; ManualResetEvent err = new ManualResetEvent(false); ManualResetEvent stdout = new ManualResetEvent(false); for (int attempt = 0; attempt < attempts; attempt++) { if (processLog != null) { File.AppendAllText(processLog, psi.FileName + " " + args.ToString() + Environment.NewLine); } using (var p = new Process()) { p.ErrorDataReceived += (sender, e) => { if (e.Data != null && !string.IsNullOrEmpty(processLog)) { File.AppendAllText(processLog, e.Data + Environment.NewLine); if (e.Data.StartsWith(SigSegvError, StringComparison.OrdinalIgnoreCase)) { nativeCrashDetected = true; } if (e.Data.StartsWith(ConsoleLoggerError, StringComparison.OrdinalIgnoreCase)) { nativeCrashDetected = true; } } if (e.Data == null) { err.Set(); } }; p.OutputDataReceived += (sender, e) => { if (e.Data != null && !string.IsNullOrEmpty(processLog)) { File.AppendAllText(processLog, e.Data + Environment.NewLine); if (e.Data.StartsWith(SigSegvError, StringComparison.OrdinalIgnoreCase)) { nativeCrashDetected = true; } if (e.Data.StartsWith(ConsoleLoggerError, StringComparison.OrdinalIgnoreCase)) { nativeCrashDetected = true; } } if (e.Data == null) { stdout.Set(); } }; p.StartInfo = psi; Console.WriteLine($"{psi.FileName} {psi.Arguments}"); p.Start(); p.BeginOutputReadLine(); p.BeginErrorReadLine(); ranToCompletion = p.WaitForExit((int)new TimeSpan(0, 15, 0).TotalMilliseconds); if (psi.RedirectStandardOutput) { stdout.WaitOne(); } if (psi.RedirectStandardError) { err.WaitOne(); } result = ranToCompletion && p.ExitCode == 0; } LastBuildTime = DateTime.UtcNow - start; if (processLog != null && !ranToCompletion) { File.AppendAllText(processLog, "Build Timed Out!"); } if (buildLogFullPath != null && File.Exists(buildLogFullPath)) { foreach (var line in LastBuildOutput) { if (line.StartsWith("Time Elapsed", StringComparison.OrdinalIgnoreCase)) { var match = timeElapsedRegEx.Match(line); if (match.Success) { LastBuildTime = TimeSpan.Parse(match.Groups ["TimeSpan"].Value); Console.WriteLine($"Found Time Elapsed {LastBuildTime}"); } } } } if (nativeCrashDetected) { Console.WriteLine($"Native crash detected! Running the build for {projectOrSolution} again."); if (attempt == 0) { File.Move(processLog, processLog + ".bak"); } nativeCrashDetected = false; continue; } else { break; } } if (buildLogFullPath != null && processLog != null) { Directory.CreateDirectory(Path.GetDirectoryName(buildLogFullPath)); if (File.Exists(processLog)) { File.AppendAllText(buildLogFullPath, File.ReadAllText(processLog)); } } if (!result && ThrowOnBuildFailure) { string message = "Build failure: " + Path.GetFileName(projectOrSolution) + (BuildLogFile != null && File.Exists(buildLogFullPath) ? "Build log recorded at " + buildLogFullPath : null); //NOTE: enormous logs will lock up IDE's UI. Build result files should be appended to the TestResult on failure. throw new FailedBuildException(message); } return(result); }