public bool IsAlreadyDownloaded(XamarinBuildDownload xbd) { // Skip extraction if the file is already in place var flagFile = xbd.DestinationDir + ".unpacked"; return(File.Exists(flagFile) || File.Exists(xbd.CacheFile)); }
static string GetTarFileName(XamarinBuildDownload xbd) { var tarPath = Path.Combine(xbd.DestinationDir, Path.ChangeExtension(Path.GetFileName(xbd.CacheFile), "tar")); if (File.Exists(tarPath)) { return(tarPath); } var foundFile = Directory.EnumerateFiles(xbd.DestinationDir, "*.tar", SearchOption.TopDirectoryOnly).FirstOrDefault(); return(string.IsNullOrEmpty(foundFile) ? tarPath : foundFile); }
async Task <int> ExtractTarOnWindows(XamarinBuildDownload xbd, StringWriter output, CancellationToken token) { var tarFile = GetTarFileName(xbd); var psi = CreateExtractionArgs(tarFile, xbd.DestinationDir, xbd.Kind, true); var returnCode = await ProcessUtils.StartProcess(psi, output, output, token); if (returnCode == 7) { LogMessage("7Zip command line parse did not work. Trying without -snl-"); psi = CreateExtractionArgs(tarFile, xbd.DestinationDir, xbd.Kind, false); returnCode = await ProcessUtils.StartProcess(psi, output, output, token); } File.Delete(tarFile); return(returnCode); }
async Task <bool> ExtractArchive(XamarinBuildDownload xbd, string flagFile, CancellationToken token) { ProcessStartInfo psi = CreateExtractionArgs(xbd.CacheFile, xbd.DestinationDir, xbd.Kind); try { LogMessage("Extracting {0} to {1}", xbd.CacheFile, xbd.DestinationDir); var output = new StringWriter(); int returnCode = await ProcessUtils.StartProcess(psi, output, output, token); if (returnCode == 0) { //with 7zip, tgz just gets uncompressed to a tar. now extract the tar. if (xbd.Kind == ArchiveKind.Tgz && Platform.IsWindows) { var tarFile = GetTarFileName(xbd); psi = CreateExtractionArgs(tarFile, xbd.DestinationDir, xbd.Kind); returnCode = await ProcessUtils.StartProcess(psi, output, output, token); File.Delete(tarFile); if (returnCode == 0) { return(true); } } else { return(true); } } LogCodedError( ThisOrThat(xbd.CustomErrorCode, ErrorCodes.ExtractionFailed), ThisOrThat(xbd.CustomErrorMessage, () => string.Format("Unpacking failed. Please download '{0}' and extract it to the '{1}' directory " + "and create an empty file called '{2}'.", xbd.Url, xbd.DestinationDir, flagFile))); LogMessage("Unpacking failure reason: " + output.ToString(), MessageImportance.High); } catch (Exception ex) { LogErrorFromException(ex); } //something went wrong, clean up so we try again next time try { Directory.Delete(xbd.DestinationDir, true); } catch (Exception ex) { LogCodedError(ErrorCodes.DirectoryDeleteFailed, "Failed to delete directory '{0}'.", xbd.DestinationDir); LogErrorFromException(ex); } return(false); }
async Task <bool> MakeSureLibraryIsInPlace(XamarinBuildDownload xbd, CancellationToken token) { // Skip extraction if the file is already in place var flagFile = xbd.DestinationDir + ".unpacked"; if (File.Exists(flagFile)) { return(true); } try { Directory.CreateDirectory(xbd.DestinationDir); } catch (Exception ex) { LogCodedError(ThisOrThat(xbd.CustomErrorCode, ErrorCodes.DirectoryCreateFailed), ThisOrThat(xbd.CustomErrorMessage, () => string.Format("Failed to create directory '{0}'.", xbd.DestinationDir))); LogMessage("Directory creation failure reason: " + ex.ToString(), MessageImportance.High); return(false); } var lockFile = xbd.DestinationDir + ".locked"; using (var lockStream = DownloadUtils.ObtainExclusiveFileLock(lockFile, Token, TimeSpan.FromSeconds(xbd.ExclusiveLockTimeout), this)) { if (lockStream == null) { LogCodedError(ErrorCodes.ExclusiveLockTimeout, "Timed out waiting for exclusive file lock on: {0}", lockFile); LogMessage("Timed out waiting for an exclusive file lock on: " + lockFile, MessageImportance.High); return(false); } if (!File.Exists(xbd.CacheFile) || !IsValidDownload(xbd.DestinationDir + ".sha1", xbd.CacheFile, xbd.Sha1)) { try { int progress = -1; DownloadProgressChangedEventHandler downloadHandler = (o, e) => { if (e.ProgressPercentage % 10 != 0 || progress == e.ProgressPercentage) { return; } progress = e.ProgressPercentage; LogMessage( "\t({0}/{1}b), total {2:F1}%", e.BytesReceived, e.TotalBytesToReceive, e.ProgressPercentage ); }; using (var client = new WebClient()) { client.DownloadProgressChanged += downloadHandler; LogMessage(" Downloading {0} to {1}", xbd.Url, xbd.CacheFile); client.DownloadFileTaskAsync(xbd.Url, xbd.CacheFile).Wait(token); LogMessage(" Downloading Complete"); client.DownloadProgressChanged -= downloadHandler; } } catch (Exception e) { LogCodedError(ThisOrThat(xbd.CustomErrorCode, ErrorCodes.DownloadFailed), ThisOrThat(xbd.CustomErrorMessage, () => string.Format("Download failed. Please download {0} to a file called {1}.", xbd.Url, xbd.CacheFile))); LogMessage("Download failure reason: " + e.GetBaseException().Message, MessageImportance.High); File.Delete(xbd.CacheFile); return(false); } } if (!File.Exists(xbd.CacheFile)) { LogCodedError(ThisOrThat(xbd.CustomErrorCode, ErrorCodes.DownloadedFileMissing), ThisOrThat(xbd.CustomErrorMessage, () => string.Format("Downloaded file '{0}' is missing.", xbd.CacheFile))); return(false); } if (xbd.Kind == ArchiveKind.Uncompressed) { var uncompressedCacheFile = xbd.CacheFile; if (!string.IsNullOrEmpty(xbd.ToFile)) { uncompressedCacheFile = xbd.ToFile; } File.Move(xbd.CacheFile, Path.Combine(xbd.DestinationDir, Path.GetFileName(uncompressedCacheFile))); File.WriteAllText(flagFile, "This marks that the extraction completed successfully"); return(true); } else { if (await ExtractArchive(xbd, flagFile, token)) { File.WriteAllText(flagFile, "This marks that the extraction completed successfully"); return(true); } } } // We will attempt to delete the lock file when we're done try { if (File.Exists(lockFile)) { File.Delete(lockFile); } } catch { } return(false); }
public IEnumerable <XamarinBuildDownload> ParseDownloadItems(ITaskItem[] items, bool allowUnsecureUrls) { if (items == null || items.Length <= 0) { return(new List <XamarinBuildDownload> ()); } var results = new List <XamarinBuildDownload> (); foreach (var item in items) { var xbd = new XamarinBuildDownload(); xbd.Id = item.ItemSpec; if (!ValidateId(xbd.Id)) { Log.LogCodedError(ErrorCodes.XbdInvalidItemId, "Invalid item ID {0}", xbd.Id); continue; } xbd.Url = item.GetMetadata("Url"); if (string.IsNullOrEmpty(xbd.Url)) { Log.LogCodedError(ErrorCodes.XbdInvalidUrl, "Missing required Url metadata on item {0}", item.ItemSpec); continue; } xbd.Sha256 = item.GetMetadata("Sha256"); xbd.Kind = GetKind(xbd.Url, item.GetMetadata("Kind")); if (xbd.Kind == ArchiveKind.Unknown) { //TODO we may be able to determine the kind from the server response continue; } if (!EnsureSecureUrl(item, xbd.Url, allowUnsecureUrls)) { continue; } xbd.CustomErrorCode = item.GetMetadata("CustomErrorCode"); xbd.CustomErrorMessage = item.GetMetadata("CustomErrorMessage"); // By default, use the kind (tgz or zip) as the file extension for the cache file var cacheFileExt = xbd.Kind.ToString().ToLower(); // If we have an uncompressed file specified (move file instead of decompressing it) if (xbd.Kind == ArchiveKind.Uncompressed) { // Get the filename xbd.ToFile = item.GetMetadata("ToFile"); // If we have a tofile set, try and grab its extension if (!string.IsNullOrEmpty(xbd.ToFile)) { cacheFileExt = Path.GetExtension(xbd.ToFile)?.ToLower() ?? string.Empty; } } xbd.CacheFile = Path.Combine(CacheDir, item.ItemSpec.TrimEnd('.') + "." + cacheFileExt.TrimStart('.')); xbd.DestinationDir = Path.GetFullPath(Path.Combine(CacheDir, item.ItemSpec)); int lockTimeout = 60; if (int.TryParse(item.GetMetadata("ExclusiveLockTimeout"), out lockTimeout)) { xbd.ExclusiveLockTimeout = lockTimeout; } results.Add(xbd); } // Deduplicate possible results by their Id which should be unique always for the given archive return(results.GroupBy(item => item.Id).Select((kvp) => kvp.FirstOrDefault()).ToArray()); }