private T PerformRetryableRequest <T>(string message, Uri baseUri, Func <Uri, T> func) { var exceptions = new List <Exception>(); var backoff = 100; for (var i = 0; i < MaxRequests; i++) { try { RedirectableConsole.WriteLine("(" + (i + 1) + "/" + MaxRequests + ") " + message); return(func(baseUri)); } catch (Exception ex) { exceptions.Add(ex); var webException = ex as WebException; if (webException != null) { switch (webException.Status) { case WebExceptionStatus.ProtocolError: var httpWebResponse = webException.Response as HttpWebResponse; if (httpWebResponse != null) { switch (httpWebResponse.StatusCode) { case HttpStatusCode.NotFound: case HttpStatusCode.Forbidden: // This is a permanent failure. i = MaxRequests; backoff = 0; break; } } break; case WebExceptionStatus.ServerProtocolViolation: case WebExceptionStatus.NameResolutionFailure: // This is a permanent failure. i = MaxRequests; backoff = 0; break; } } if (!SilentOnError || i != MaxRequests || backoff != 0) { RedirectableConsole.ErrorWriteLine("Exception during web request: "); RedirectableConsole.ErrorWriteLine(ex); RedirectableConsole.WriteLine("Backing off web requests for " + backoff + "ms..."); } System.Threading.Thread.Sleep(backoff); backoff *= 2; if (backoff > 20000) { backoff = 20000; } } } throw new AggregateException(exceptions); }
private static void PrintHelp(Dictionary <string, ICommand> commandMappings) { RedirectableConsole.WriteLine("Protobuild.exe [options]"); RedirectableConsole.WriteLine(); RedirectableConsole.WriteLine("By default Protobuild resynchronises or generates projects for"); RedirectableConsole.WriteLine("the current platform, depending on the module configuration."); RedirectableConsole.WriteLine(); foreach (var kv in commandMappings) { if (kv.Value.IsInternal() || !kv.Value.IsRecognised() || kv.Value.IsIgnored()) { continue; } var description = kv.Value.GetDescription(); description = description.Replace("\n", " "); description = description.Replace("\r", ""); var lines = new List <string>(); var wordBuffer = string.Empty; var lineBuffer = string.Empty; var count = 0; var last = false; for (var i = 0; i < description.Length || wordBuffer.Length > 0; i++) { if (i < description.Length) { if (description[i] == ' ') { if (wordBuffer.Length > 0) { lineBuffer += wordBuffer + " "; } wordBuffer = string.Empty; } else { wordBuffer += description[i]; count++; } } else { lineBuffer += wordBuffer + " "; count++; last = true; } if (count >= 74) { lines.Add(lineBuffer); lineBuffer = string.Empty; count = 0; } if (last) { break; } } if (count > 0) { lines.Add(lineBuffer); lineBuffer = string.Empty; } var argDesc = string.Empty; foreach (var arg in kv.Value.GetArgNames()) { if (arg.EndsWith("?")) { argDesc += " [" + arg.TrimEnd('?') + "]"; } else { argDesc += " " + arg; } } RedirectableConsole.WriteLine(" -" + kv.Key + argDesc); RedirectableConsole.WriteLine(); foreach (var line in lines) { RedirectableConsole.WriteLine(" " + line); } RedirectableConsole.WriteLine(); } }
public int Execute(Execution execution) { var hostPlatform = _hostPlatformDetector.DetectPlatform(); string builderPathNativeArch = null; string builderPath64 = null; string builderPath32 = null; var extraArgsNativeArch = string.Empty; var extraArgs64 = string.Empty; var extraArgs32 = string.Empty; var extraArgsGeneral = string.Empty; var targetPlatforms = (execution.Platform ?? hostPlatform).Split(','); var module = ModuleInfo.Load(Path.Combine(execution.WorkingDirectory, "Build", "Module.xml")); if (hostPlatform == "Windows") { // Newer installs of Visual Studio (like 2017) don't create registry entries for MSBuild, so we have to // use a tool called vswhere in order to find MSBuild on these systems. This call will implicitly install // the vswhere package if it's not already installed. var vswhere = _knownToolProvider.GetToolExecutablePath("vswhere"); List <string> installations = null; if (vswhere != null && File.Exists(vswhere)) { try { var processStartInfo = new ProcessStartInfo(); processStartInfo.FileName = vswhere; processStartInfo.Arguments = "-products * -requires Microsoft.Component.MSBuild -property installationPath"; processStartInfo.UseShellExecute = false; processStartInfo.RedirectStandardOutput = true; var process = Process.Start(processStartInfo); var installationsString = process.StandardOutput.ReadToEnd(); installations = installationsString.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).Where(x => !string.IsNullOrWhiteSpace(x)).ToList(); process.WaitForExit(); if (process.ExitCode != 0) { RedirectableConsole.ErrorWriteLine("Unable to locate Visual Studio 2017 and later installations (non-zero exit code from vswhere)"); } } catch (Exception ex) { RedirectableConsole.ErrorWriteLine("Unable to locate Visual Studio 2017 and later installations: " + ex.Message); } } if (installations != null) { // Check if MSBuild is present in any of those installation paths. foreach (var basePath in installations) { var msbuildLocation = Path.Combine(basePath, "MSBuild\\15.0\\Bin\\MSBuild.exe"); if (File.Exists(msbuildLocation)) { builderPathNativeArch = msbuildLocation; extraArgsNativeArch = "/m /nodeReuse:false "; builderPath32 = msbuildLocation; extraArgs32 = "/m /nodeReuse:false "; var x64Location = Path.Combine(basePath, "MSBuild\\15.0\\Bin\\amd64\\MSBuild.exe"); if (File.Exists(x64Location)) { builderPath64 = x64Location; extraArgs64 = "/m /nodeReuse:false "; } break; } } } if (builderPathNativeArch == null) { // Try to find via the registry. foreach (var arch in new[] { RegistryView.Default, RegistryView.Registry32, RegistryView.Registry64 }) { // Find latest version of MSBuild. var registryKey = RegistryKey.OpenBaseKey( RegistryHive.LocalMachine, arch) .OpenSubKey("SOFTWARE")? .OpenSubKey("Microsoft")? .OpenSubKey("MSBuild")? .OpenSubKey("ToolsVersions"); if (registryKey == null) { if (arch == RegistryView.Registry64) { continue; } RedirectableConsole.ErrorWriteLine( "ERROR: No versions of MSBuild were available " + "according to the registry (or they were not readable)."); return(1); } var subkeys = registryKey.GetSubKeyNames(); var orderedVersions = subkeys.OrderByDescending(x => int.Parse(x.Split('.').First(), CultureInfo.InvariantCulture)); var builderPath = (from version in orderedVersions let path = (string)registryKey.OpenSubKey(version)?.GetValue("MSBuildToolsPath") where path != null && Directory.Exists(path) let msbuild = Path.Combine(path, "MSBuild.exe") where File.Exists(msbuild) select msbuild).FirstOrDefault(); if (builderPath == null) { if (arch == RegistryView.Registry64) { continue; } RedirectableConsole.ErrorWriteLine( "ERROR: Unable to find installed MSBuild in any installed tools version."); return(1); } var extraArgs = string.Empty; if (!builderPath.Contains("v2.0.50727")) { extraArgs = "/m /nodeReuse:false "; } switch (arch) { case RegistryView.Default: builderPathNativeArch = builderPath; extraArgsNativeArch = extraArgs; break; case RegistryView.Registry32: builderPath32 = builderPath; extraArgs32 = extraArgs; break; case RegistryView.Registry64: builderPath64 = builderPath; extraArgs64 = extraArgs; break; } } } } else { // Find path to xbuild. var whichPaths = new[] { "/bin/which", "/usr/bin/which" }; // We can only use the new MSBuild tool if no projects are C++ projects on Mac or Linux. var isAnyNativeProject = false; foreach (var def in module.GetDefinitionsRecursively()) { var document = XDocument.Load(def.DefinitionPath); var languageAttr = document?.Root?.Attributes()?.FirstOrDefault(x => x.Name.LocalName == "Language"); if (languageAttr != null && languageAttr.Value == "C++") { isAnyNativeProject = true; break; } } if (!isAnyNativeProject) { foreach (var w in whichPaths) { if (File.Exists(w)) { var whichProcess = Process.Start(new ProcessStartInfo(w, "msbuild") { RedirectStandardOutput = true, UseShellExecute = false }); if (whichProcess == null) { continue; } var result = whichProcess.StandardOutput.ReadToEnd().Trim(); if (!string.IsNullOrWhiteSpace(result) && File.Exists(result)) { builderPathNativeArch = result; break; } } } } if (builderPathNativeArch == null) { foreach (var w in whichPaths) { if (File.Exists(w)) { var whichProcess = Process.Start(new ProcessStartInfo(w, "xbuild") { RedirectStandardOutput = true, UseShellExecute = false }); if (whichProcess == null) { continue; } var result = whichProcess.StandardOutput.ReadToEnd().Trim(); if (!string.IsNullOrWhiteSpace(result) && File.Exists(result)) { builderPathNativeArch = result; break; } } } } if (builderPathNativeArch == null && _hostPlatformDetector.DetectPlatform() == "MacOS" && File.Exists("/usr/local/bin/xbuild")) { // After upgrading to OSX El Capitan, the /usr/local/bin folder is no longer in // the system PATH. If we can't find xbuild with the which tool, manually set the // path here in an attempt to find it. builderPathNativeArch = "/usr/local/bin/xbuild"; } if (builderPathNativeArch == null) { RedirectableConsole.ErrorWriteLine("ERROR: Unable to find msbuild or xbuild on the current PATH."); return(1); } builderPath32 = builderPathNativeArch; builderPath64 = builderPathNativeArch; } if (!string.IsNullOrWhiteSpace(execution.BuildTarget)) { extraArgsGeneral += "/t:\"" + execution.BuildTarget + "\" "; } foreach (var prop in execution.BuildProperties) { extraArgsGeneral += "/p:\"" + prop.Key.Replace("\"", "\\\"") + "\"=\"" + (prop.Value ?? string.Empty).Replace("\"", "\\\"") + "\" "; } switch (execution.BuildProcessArchitecture) { case "x86": RedirectableConsole.WriteLine("INFO: Using " + builderPath32 + " (forced 32-bit) to perform this build."); break; case "x64": RedirectableConsole.WriteLine("INFO: Using " + builderPath64 + " (forced 64-bit) to perform this build."); break; case "Default": default: RedirectableConsole.WriteLine("INFO: Using " + builderPathNativeArch + " (32-bit: " + builderPath32 + ") to perform this build."); break; } foreach (var platform in targetPlatforms) { string builderPath; string extraArgs; switch (execution.BuildProcessArchitecture) { case "x86": builderPath = builderPath32; extraArgs = extraArgs32 + extraArgsGeneral; break; case "x64": builderPath = builderPath64; extraArgs = extraArgs64 + extraArgsGeneral; break; case "Default": default: builderPath = platform == "WindowsPhone" ? builderPath32 : builderPathNativeArch; extraArgs = (platform == "WindowsPhone" ? extraArgs32 : extraArgsNativeArch) + extraArgsGeneral; break; } var fileToBuild = module.Name + "." + platform + ".sln"; RedirectableConsole.WriteLine("INFO: Executing " + builderPath + " with arguments: " + extraArgs + fileToBuild); var process = Process.Start(new ProcessStartInfo(builderPath, extraArgs + fileToBuild) { UseShellExecute = false }); if (process == null) { RedirectableConsole.ErrorWriteLine("ERROR: Build process did not start successfully."); return(1); } process.WaitForExit(); if (process.ExitCode != 0) { return(process.ExitCode); } } return(0); }
public int ExecuteForTemplate(Execution execution) { if (!Directory.Exists(execution.PackageSourceFolder)) { throw new InvalidOperationException("The source folder " + execution.PackageSourceFolder + " does not exist."); } RedirectableConsole.WriteLine("Starting package creation for " + execution.PackagePlatform); var filter = new FileFilter(_getRecursiveUtilitiesInPath.GetRecursiveFilesInPath(execution.PackageSourceFolder)); if (execution.PackageFilterFile != null) { using (var reader = new StreamReader(execution.PackageFilterFile)) { var contents = reader.ReadToEnd(); contents = contents.Replace("%PLATFORM%", execution.PackagePlatform); using (var inputStream = new MemoryStream(Encoding.ASCII.GetBytes(contents))) { this.m_FileFilterParser.ParseAndApply(filter, inputStream, new Dictionary <string, Action <FileFilter> >()); } } } else { filter.ApplyInclude("^.*$"); filter.ApplyExclude("^\\.git/.*$"); filter.ApplyExclude("^\\.hg/.*$"); filter.ApplyExclude("^\\.svn/.*$"); } if (File.Exists(execution.PackageDestinationFile)) { RedirectableConsole.WriteLine("The destination file " + execution.PackageDestinationFile + " already exists; it will be overwritten."); File.Delete(execution.PackageDestinationFile); } filter.ImplyDirectories(); var filterDictionary = filter.ToDictionary(k => k.Key, v => v.Value); if (!filter.ContainsTargetPath("Build/")) { RedirectableConsole.WriteLine("ERROR: The Build directory does not exist in the source folder."); if (execution.PackageFilterFile != null) { this.PrintFilterMappings(filter); } return(1); } if (!filter.ContainsTargetPath("Build/Projects/")) { RedirectableConsole.WriteLine("ERROR: The Build\\Projects directory does not exist in the source folder."); if (execution.PackageFilterFile != null) { this.PrintFilterMappings(filter); } return(1); } if (!filter.ContainsTargetPath("Build/Module.xml")) { RedirectableConsole.WriteLine("ERROR: The Build\\Module.xml file does not exist in the source folder."); if (execution.PackageFilterFile != null) { this.PrintFilterMappings(filter); } return(1); } if (filter.ContainsTargetPath("Protobuild.exe")) { RedirectableConsole.WriteLine("ERROR: The Protobuild.exe file should not be included in the package file."); if (execution.PackageFilterFile != null) { this.PrintFilterMappings(filter); } return(1); } using (var target = new FileStream(execution.PackageDestinationFile, FileMode.CreateNew, FileAccess.Write, FileShare.None)) { _packageCreator.Create( target, filter, execution.PackageSourceFolder, execution.PackageFormat, execution.PackagePlatform, execution.PackageDestinationFile); } RedirectableConsole.WriteLine("\rPackage written to " + execution.PackageDestinationFile + " successfully."); return(0); }
public void Resolve(string workingDirectory, IPackageMetadata metadata, PackageRef reference, string templateName, bool?source, bool forceUpgrade, bool?safeResolve) { if (reference.Folder == null) { if (metadata.PackageType == PACKAGE_TYPE_GLOBAL_TOOL) { } else { throw new InvalidOperationException( "No target folder was provided for package resolution, and the resulting package is not " + "a global tool."); } } else { if (metadata.PackageType == PackageManager.PACKAGE_TYPE_TEMPLATE && templateName == null) { throw new InvalidOperationException( "Template referenced as part of module packages. Templates can only be used " + "with the --start option."); } else if (metadata.PackageType == PackageManager.PACKAGE_TYPE_LIBRARY) { Directory.CreateDirectory(Path.Combine(workingDirectory, reference.Folder)); if (new DirectoryInfo(Path.Combine(workingDirectory, reference.Folder)).GetFiles().Length > 0 || new DirectoryInfo(Path.Combine(workingDirectory, reference.Folder)).GetDirectories().Length > 0) { if (!File.Exists(Path.Combine(workingDirectory, reference.Folder, ".git")) && !Directory.Exists(Path.Combine(workingDirectory, reference.Folder, ".git")) && !File.Exists(Path.Combine(workingDirectory, reference.Folder, ".pkg"))) { bool shouldSafeResolve; if (safeResolve.HasValue) { // If the user specifies it on the command line, use that setting. shouldSafeResolve = safeResolve.Value; } else { if (!_featureManager.IsFeatureEnabled(Feature.SafeResolutionDisabled)) { // If the module doesn't have this feature set enabled, we default // to using safe package resolution. shouldSafeResolve = true; } else { // If the module does have this feature set enabled, or is using the // full feature set, we default to turning safe resolution off. shouldSafeResolve = false; } } if (shouldSafeResolve) { RedirectableConsole.ErrorWriteLine( "WARNING: The package directory '" + reference.Folder + "' already exists and contains " + "files and/or subdirectories, but neither a .pkg file nor a .git file or subdirectory exists. " + "This indicates the package directory contains data that is not been instantiated or managed " + "by Protobuild. Since there is no safe way to initialize the package in this directory " + "without a potential loss of data, Protobuild will not modify the contents of this folder " + "during package resolution. If the folder does not contains the required package " + "dependencies, the project generation or build may unexpectedly fail."); return; } } } if (source == null) { if (File.Exists(Path.Combine(workingDirectory, reference.Folder, ".git")) || Directory.Exists(Path.Combine(workingDirectory, reference.Folder, ".git"))) { RedirectableConsole.WriteLine("Git repository present at " + Path.Combine(workingDirectory, reference.Folder, ".git") + "; leaving as source version."); source = true; } else { RedirectableConsole.WriteLine("Package type not specified (and no file at " + Path.Combine(workingDirectory, reference.Folder, ".git") + "), requesting binary version."); source = false; } } } } metadata.Resolve(workingDirectory, metadata, reference.Folder, templateName, forceUpgrade, source); }
private Stream LoadOverriddableResource(string workingDirectory, ResourceType resourceType, Language language, string platform, out string loadHash) { loadHash = string.Empty; string name = null; var extension = GetResourceExtension(resourceType); var fileSuffix = string.Empty; var replacements = new Dictionary <string, ReplacementInfo>(); bool okayToFail = false; switch (resourceType) { case ResourceType.GenerateProject: name = "GenerateProject"; fileSuffix = "." + this.m_LanguageStringProvider.GetFileSuffix(language); replacements.Add("ADDITIONAL_TRANSFORMS", new ReplacementInfo { ResourceName = ResourceType.AdditionalProjectTransforms }); break; case ResourceType.GenerateSolution: name = "GenerateSolution"; break; case ResourceType.SelectSolution: name = "SelectSolution"; break; case ResourceType.GenerationFunctions: name = "GenerationFunctions"; break; case ResourceType.NuGetPlatformMappings: name = "NuGetPlatformMappings"; break; case ResourceType.AdditionalGenerationFunctions: name = "AdditionalGenerationFunctions"; okayToFail = true; break; case ResourceType.AdditionalProjectTransforms: name = "AdditionalProjectTransforms"; okayToFail = true; break; default: throw new NotSupportedException(); } switch (resourceType) { case ResourceType.GenerateProject: case ResourceType.GenerateSolution: case ResourceType.SelectSolution: replacements.Add("GENERATION_FUNCTIONS", new ReplacementInfo { ResourceName = ResourceType.GenerationFunctions, ReplacementProcessor = x => _generationFunctionsProvider.ConvertGenerationFunctionsToXSLT("user", x) }); replacements.Add("ADDITIONAL_GENERATION_FUNCTIONS", new ReplacementInfo { ResourceName = ResourceType.AdditionalGenerationFunctions, ReplacementProcessor = x => _generationFunctionsProvider.ConvertGenerationFunctionsToXSLT("extra", x) }); break; } var onDiskNames = new List <string>(); onDiskNames.Add(name + fileSuffix + "." + platform + "." + extension); onDiskNames.Add(name + fileSuffix + "." + extension); if (resourceType == ResourceType.GenerateProject && language == Language.CSharp) { onDiskNames.Add(name + "." + extension); } Stream source = null; var globalPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), ".protobuild-xslt"); if (Directory.Exists(globalPath)) { foreach (var filename in onDiskNames) { var path = Path.Combine(globalPath, filename); if (File.Exists(path)) { loadHash = "path:" + path; RedirectableConsole.WriteLine("Loaded XSLT from global path: " + path); source = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read); break; } } } if (source == null) { foreach (var filename in onDiskNames) { var path = Path.Combine(workingDirectory, "Build", filename); if (File.Exists(path)) { loadHash = "path:" + path; RedirectableConsole.WriteLine("Loaded XSLT from Build folder: " + path); source = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read); break; } } } if (source == null) { var embeddedName = name + fileSuffix + "." + extension + ".lzma"; var embeddedStream = Assembly .GetExecutingAssembly() .GetManifestResourceStream(embeddedName); if (embeddedStream == null) { if (okayToFail) { loadHash = "none"; return(new MemoryStream()); } else { throw new InvalidOperationException("No embedded stream with name '" + embeddedName + "'"); } } loadHash = "embedded:" + embeddedName; RedirectableConsole.WriteLine("Loaded XSLT from Protobuild"); source = this.GetTransparentDecompressionStream(embeddedStream); } foreach (var replacement in replacements) { var memory = new MemoryStream(); using (var stream = source) { using (var writer = new StringWriter()) { string outHashTemp; var replacementDataStream = this.LoadOverriddableResource( workingDirectory, replacement.Value.ResourceName, language, platform, out outHashTemp); loadHash += ":" + outHashTemp; string replacementData; using (var reader = new StreamReader(replacementDataStream)) { replacementData = reader.ReadToEnd(); } if (replacement.Value.ReplacementProcessor != null) { replacementData = replacement.Value.ReplacementProcessor(replacementData); } using (var reader = new StreamReader(stream)) { var text = reader.ReadToEnd(); text = text.Replace("<!-- {" + replacement.Key + "} -->", replacementData); writer.Write(text); writer.Flush(); } var resultBytes = Encoding.UTF8.GetBytes(writer.GetStringBuilder().ToString()); memory.Write(resultBytes, 0, resultBytes.Length); memory.Seek(0, SeekOrigin.Begin); } } source = memory; } return(source); }
public void Create(Stream target, FileFilter filter, string basePath, string packageFormat, string platform, string excludedPath) { Stream archive = new MemoryStream(); string cleanUp = null; try { switch (packageFormat) { case PackageManager.ARCHIVE_FORMAT_TAR_GZIP: RedirectableConsole.WriteLine("Writing package in tar/gzip format..."); break; case PackageManager.ARCHIVE_FORMAT_NUGET_ZIP: RedirectableConsole.WriteLine("Writing package in NuGet ZIP format... (this is experimental!)"); break; case PackageManager.ARCHIVE_FORMAT_TAR_LZMA: default: RedirectableConsole.WriteLine("Writing package in tar/lzma format..."); break; } switch (packageFormat) { case PackageManager.ARCHIVE_FORMAT_NUGET_ZIP: { if (platform == "Unified") { // With unified packages, the contents have already been deduplicated in the // individual packages we're combining. Therefore we don't deduplicate again // because we already have the deduplication index calculated as part of the // package contents. Just add all the files to the ZIP instead. Action <ZipStorer> addFilesToZip = zip => { RedirectableConsole.WriteLine("Adding files to package..."); var progressHelper = new AddFilesProgressRenderer(filter.CountTargetPaths()); var current = 0; var uniqueFiles = new HashSet <string>(); var entries = filter.GetExpandedEntries(); foreach (var kv in entries.OrderBy(kv => kv.Value)) { if (kv.Value.EndsWith("/")) { // Directory - do nothing } else { var realFile = Path.Combine(basePath, kv.Key); if (uniqueFiles.Contains(kv.Value)) { // Already exists - do nothing } else { if (excludedPath != null) { var realFileInfo = new FileInfo(realFile); var excludedPathInfo = new FileInfo(excludedPath); if (excludedPathInfo.FullName == realFileInfo.FullName) { // Skip this file because we force it to be excluded. continue; } } zip.AddFile( ZipStorer.Compression.Deflate, realFile, kv.Value, string.Empty); uniqueFiles.Add(kv.Value); } } current++; progressHelper.SetProgress(current); } progressHelper.FinalizeRendering(); }; try { using (var zip = ZipStorer.Create(target, string.Empty, true)) { addFilesToZip(zip); } } catch (OutOfMemoryException) { // It's possible the archive is too large to store in memory. Fall // back to using a temporary file on disk. cleanUp = Path.GetTempFileName(); RedirectableConsole.WriteLine( "WARNING: Out-of-memory while creating ZIP file, falling back to storing " + "temporary file on disk at " + cleanUp + " during package creation."); archive.Dispose(); archive = new FileStream(cleanUp, FileMode.Create, FileAccess.ReadWrite); using (var zip = ZipStorer.Create(archive, string.Empty, true)) { addFilesToZip(zip); } } } else { // Deduplicate the contents of the ZIP package. var state = _deduplicator.CreateState(); RedirectableConsole.Write("Deduplicating files in package..."); var progressHelper = new DedupProgressRenderer(filter.CountTargetPaths()); var current = 0; var entries = filter.GetExpandedEntries(); foreach (var kv in entries.OrderBy(kv => kv.Value)) { if (kv.Value.EndsWith("/")) { // Directory _deduplicator.AddDirectory(state, kv.Value); } else { // File var realFile = Path.Combine(basePath, kv.Key); var realFileInfo = new FileInfo(realFile); if (excludedPath != null) { var excludedPathInfo = new FileInfo(excludedPath); if (excludedPathInfo.FullName == realFileInfo.FullName) { // Skip this file because we force it to be excluded. continue; } } _deduplicator.AddFile(state, realFileInfo, kv.Value); } current++; progressHelper.SetProgress(current); } progressHelper.FinalizeRendering(); RedirectableConsole.WriteLine("Adding files to package..."); try { using (var zip = ZipStorer.Create(target, string.Empty, true)) { _deduplicator.PushToZip(state, zip); } } catch (OutOfMemoryException) { // It's possible the archive is too large to store in memory. Fall // back to using a temporary file on disk. cleanUp = Path.GetTempFileName(); RedirectableConsole.WriteLine( "WARNING: Out-of-memory while creating ZIP file, falling back to storing " + "temporary file on disk at " + cleanUp + " during package creation."); archive.Dispose(); archive = new FileStream(cleanUp, FileMode.Create, FileAccess.ReadWrite); using (var zip = ZipStorer.Create(archive, string.Empty, true)) { _deduplicator.PushToZip(state, zip); } } } break; } case PackageManager.ARCHIVE_FORMAT_TAR_GZIP: case PackageManager.ARCHIVE_FORMAT_TAR_LZMA: default: { var state = _deduplicator.CreateState(); RedirectableConsole.Write("Deduplicating files in package..."); var progressHelper = new DedupProgressRenderer(filter.CountTargetPaths()); var current = 0; var entries = filter.GetExpandedEntries(); foreach (var kv in entries.OrderBy(kv => kv.Value)) { if (kv.Value.EndsWith("/")) { // Directory _deduplicator.AddDirectory(state, kv.Value); } else { // File var realFile = Path.Combine(basePath, kv.Key); var realFileInfo = new FileInfo(realFile); if (excludedPath != null) { var excludedPathInfo = new FileInfo(excludedPath); if (excludedPathInfo.FullName == realFileInfo.FullName) { // Skip this file because we force it to be excluded. continue; } } _deduplicator.AddFile(state, realFileInfo, kv.Value); } current++; progressHelper.SetProgress(current); } progressHelper.FinalizeRendering(); RedirectableConsole.WriteLine("Adding files to package..."); try { using (var writer = new tar_cs.TarWriter(archive)) { _deduplicator.PushToTar(state, writer); } } catch (OutOfMemoryException) { // It's possible the archive is too large to store in memory. Fall // back to using a temporary file on disk. cleanUp = Path.GetTempFileName(); RedirectableConsole.WriteLine( "WARNING: Out-of-memory while creating TAR file, falling back to storing " + "temporary file on disk at " + cleanUp + " during package creation."); archive.Dispose(); archive = new FileStream(cleanUp, FileMode.Create, FileAccess.ReadWrite); using (var writer = new tar_cs.TarWriter(archive)) { _deduplicator.PushToTar(state, writer); } } break; } } archive.Seek(0, SeekOrigin.Begin); switch (packageFormat) { case PackageManager.ARCHIVE_FORMAT_NUGET_ZIP: // No need to compress anything further. break; case PackageManager.ARCHIVE_FORMAT_TAR_GZIP: { RedirectableConsole.WriteLine("Compressing package..."); using (var compress = new GZipStream(target, CompressionMode.Compress)) { archive.CopyTo(compress); } break; } case PackageManager.ARCHIVE_FORMAT_TAR_LZMA: default: { RedirectableConsole.Write("Compressing package..."); var progressHelper = new CompressProgressRenderer(archive.Length); LZMA.LzmaHelper.Compress(archive, target, progressHelper); progressHelper.FinalizeRendering(); break; } } } finally { if (cleanUp != null) { try { File.Delete(cleanUp); } catch { RedirectableConsole.WriteLine("WARNING: Unable to clean up temporary package file at " + cleanUp); } } } }
private int PushToNuGetRepository(Execution execution, bool pushGitVersion, string platform, string commitHash) { if (pushGitVersion) { // We have to patch the package file in memory to include the Git hash // as part of the version. This is required so that Protobuild // can resolve source-equivalent binary packages. RedirectableConsole.WriteLine("Patching package file to include Git version information..."); using (var patchedPackage = new MemoryStream()) { using (var patchedPackageWriter = ZipStorer.Create(patchedPackage, string.Empty, true)) { using (var packageReader = ZipStorer.Open(execution.PackagePushFile, FileAccess.Read)) { var entries = packageReader.ReadCentralDir(); var progressRenderer = new PackagePatchProgressRenderer(entries.Count); var i = 0; foreach (var entry in packageReader.ReadCentralDir()) { if (entry.FilenameInZip.EndsWith(".nuspec") && !entry.FilenameInZip.Contains("/")) { // This is the NuGet specification file in the root of the package // that we need to patch. using (var fileStream = new MemoryStream()) { packageReader.ExtractFile(entry, fileStream); fileStream.Seek(0, SeekOrigin.Begin); string nuspecContent; using ( var reader = new StreamReader(fileStream, Encoding.UTF8, true, 4096, true)) { nuspecContent = reader.ReadToEnd(); var regex = new Regex("version\\>[^\\<]+\\<\\/version"); nuspecContent = regex.Replace(nuspecContent, "version>" + NuGetVersionHelper.CreateNuGetPackageVersion(commitHash, platform) + "</version"); using (var patchedFileStream = new MemoryStream()) { using ( var writer = new StreamWriter(patchedFileStream, Encoding.UTF8, 4096, true)) { writer.Write(nuspecContent); writer.Flush(); patchedFileStream.Seek(0, SeekOrigin.Begin); patchedPackageWriter.AddStream( entry.Method, entry.FilenameInZip, patchedFileStream, entry.ModifyTime, entry.Comment, true); } } } } } else { using (var fileStream = new MemoryStream()) { packageReader.ExtractFile(entry, fileStream); fileStream.Seek(0, SeekOrigin.Begin); patchedPackageWriter.AddStream( entry.Method, entry.FilenameInZip, fileStream, entry.ModifyTime, entry.Comment, true); } } i++; progressRenderer.SetProgress(i); } progressRenderer.FinalizeRendering(); } } patchedPackage.Seek(0, SeekOrigin.Begin); // Push the patched package to the NuGet repository. RedirectableConsole.WriteLine("Uploading package with Git version..."); return(this.PushNuGetBinary(execution.PackagePushUrl, execution.PackagePushApiKey, patchedPackage, execution.PackagePushIgnoreOnExisting)); } } else { RedirectableConsole.WriteLine("Uploading package with semantic version..."); using ( var stream = new FileStream(execution.PackagePushFile, FileMode.Open, FileAccess.Read, FileShare.Read)) { using (var memoryStream = new MemoryStream()) { stream.CopyTo(memoryStream); memoryStream.Seek(0, SeekOrigin.Begin); // Note about the true argument here: We always ignore a conflict for the semantic version, as // the build server may be pushing on every commit. In this scenario, developers will leave // the semantic version as-is until they're ready to release a new semantic version. return(this.PushNuGetBinary(execution.PackagePushUrl, execution.PackagePushApiKey, memoryStream, true)); } } } }
private int PushToProtobuildRepository(Execution execution, string archiveType) { using (var client = new RetryableWebClient()) { RedirectableConsole.WriteLine("Creating new package version..."); if (execution.PackagePushVersion.StartsWith("hash:", StringComparison.InvariantCulture)) { var sha1 = new SHA1Managed(); var hashed = sha1.ComputeHash(Encoding.ASCII.GetBytes(execution.PackagePushVersion.Substring("hash:".Length))); execution.PackagePushVersion = BitConverter.ToString(hashed).ToLowerInvariant().Replace("-", ""); } var uploadParameters = new System.Collections.Specialized.NameValueCollection { { "__apikey__", execution.PackagePushApiKey }, { "version", execution.PackagePushVersion }, { "platform", execution.PackagePushPlatform }, }; byte[] versionData; try { versionData = client.UploadValues(execution.PackagePushUrl + "/version/new/api", uploadParameters); } catch (WebException ex) { var responseData = string.Empty; // Try and get the full response from the server to display in the exception message. try { var stream = ex.Response.GetResponseStream(); if (stream != null) { using (var reader = new StreamReader(stream, Encoding.Default, true, 4096, true)) { responseData = reader.ReadToEnd(); } } } catch (Exception) { } throw new WebException(ex.Message + " Content of response was: " + responseData); } var json = fastJSON.JSON.ToDynamic( System.Text.Encoding.ASCII.GetString( versionData)); if (json.has_error) { RedirectableConsole.WriteLine(json.error); if (execution.PackagePushIgnoreOnExisting && ((string)json.error.ToString()).Contains("Another version already exists with this Git hash and platform")) { return(0); } return(1); } var uploadTarget = (string)json.result.uploadUrl; var finalizeTarget = (string)json.result.finalizeUrl; RedirectableConsole.WriteLine("Uploading package..."); this.PushBinary(uploadTarget, execution.PackagePushFile); RedirectableConsole.WriteLine("Finalizing package version..."); var finalizeParameters = new System.Collections.Specialized.NameValueCollection { { "__apikey__", execution.PackagePushApiKey }, { "archiveType", archiveType }, }; json = fastJSON.JSON.ToDynamic( System.Text.Encoding.ASCII.GetString( client.UploadValues(finalizeTarget, finalizeParameters))); if (json.has_error) { RedirectableConsole.WriteLine(json.error); return(1); } if (execution.PackagePushBranchToUpdate != null) { RedirectableConsole.WriteLine("Updating branch " + execution.PackagePushBranchToUpdate + " to point at new version..."); var branchUpdateParameters = new System.Collections.Specialized.NameValueCollection { { "__apikey__", execution.PackagePushApiKey }, { "name", execution.PackagePushBranchToUpdate }, { "git", execution.PackagePushVersion }, }; json = fastJSON.JSON.ToDynamic( System.Text.Encoding.ASCII.GetString( client.UploadValues( execution.PackagePushUrl + "/branch/edit/" + execution.PackagePushBranchToUpdate + "/api", branchUpdateParameters))); if (json.has_error) { RedirectableConsole.WriteLine(json.error); return(1); } } RedirectableConsole.WriteLine("Package version pushed successfully."); } return(0); }
public void ScanPackageForToolsAndInstall(string toolFolder, IKnownToolProvider knownToolProvider) { var projectsPath = Path.Combine(toolFolder, "Build", "Projects"); var projectsInfo = new DirectoryInfo(projectsPath); foreach (var file in projectsInfo.GetFiles("*.definition")) { var document = XDocument.Load(file.FullName); var tools = document.XPathSelectElements("/ExternalProject/Tool"); foreach (var tool in tools) { var toolPath = Path.Combine(toolFolder, tool.Attribute(XName.Get("Path")).Value); var toolName = tool.Attribute(XName.Get("Name")).Value; if (Path.DirectorySeparatorChar == '\\') { toolPath = toolPath.Replace("/", "\\"); } else if (Path.DirectorySeparatorChar == '/') { toolPath = toolPath.Replace("\\", "/"); } using (var writer = new StreamWriter(Path.Combine(this.GetToolsPath(), toolName + ".tool"))) { writer.WriteLine(toolPath); } RedirectableConsole.WriteLine("Global tool '" + toolName + "' now points to '" + toolPath + "'"); if (_hostPlatformDetector.DetectPlatform() == "Windows") { this.InstallToolIntoWindowsStartMenu(toolName, toolPath); } else if (_hostPlatformDetector.DetectPlatform() == "MacOS") { this.InstallToolIntoUserApplicationFolder(toolName, toolPath); } else if (_hostPlatformDetector.DetectPlatform() == "Linux") { this.InstallToolIntoLinuxApplicationMenu(toolName, toolPath); } } var vsixes = document.XPathSelectElements("/ExternalProject/VSIX"); foreach (var vsix in vsixes) { var vsixPath = Path.Combine(toolFolder, vsix.Attribute(XName.Get("Path")).Value); if (Path.DirectorySeparatorChar == '\\') { vsixPath = vsixPath.Replace("/", "\\"); } else if (Path.DirectorySeparatorChar == '/') { vsixPath = vsixPath.Replace("\\", "/"); } if (_hostPlatformDetector.DetectPlatform() == "Windows") { this.InstallVSIXIntoVisualStudio(vsixPath, knownToolProvider); } } var gacs = document.XPathSelectElements("/ExternalProject/GAC"); foreach (var gac in gacs) { var assemblyPath = Path.Combine(toolFolder, gac.Attribute(XName.Get("Path")).Value); if (Path.DirectorySeparatorChar == '\\') { assemblyPath = assemblyPath.Replace("/", "\\"); } else if (Path.DirectorySeparatorChar == '/') { assemblyPath = assemblyPath.Replace("\\", "/"); } if (_hostPlatformDetector.DetectPlatform() == "Windows") { this.InstallAssemblyIntoGAC(assemblyPath); } } } }
/// <summary> /// Performs a resynchronisation, synchronisation, generation or clean on the specified module. /// </summary> /// <returns><c>true</c>, if the action succeeded, <c>false</c> otherwise.</returns> /// <param name="module">The module to perform the action on.</param> /// <param name="action">The action to perform, either "resync", "sync", "generate" or "clean".</param> /// <param name="platform">The platform to perform the action for.</param> /// <param name="enabledServices">A list of enabled services.</param> /// <param name="disabledServices">A list of disabled services.</param> /// <param name="serviceSpecPath">The service specification path.</param> /// <param name="debugServiceResolution">Whether to enable debugging information during service resolution.</param> /// <param name="disablePackageResolution">Whether to disable package resolution.</param> /// <param name="disableHostPlatformGeneration">Whether to disable generation of the host platform projects.</param> /// <param name="taskParallelisation">Whether to enable or disable task generation, or null for the default behaviour.</param> public bool PerformAction( string workingDirectory, ModuleInfo module, string action, string platform, string[] enabledServices, string[] disabledServices, string serviceSpecPath, bool debugServiceResolution, bool disablePackageResolution, bool disableHostPlatformGeneration, bool?taskParallelisation, bool?safeResolve, bool debugProjectGeneration) { var platformSupplied = !string.IsNullOrWhiteSpace(platform); var hostPlatform = this.m_HostPlatformDetector.DetectPlatform(); if (string.IsNullOrWhiteSpace(platform)) { platform = hostPlatform; } var originalPlatform = platform; string primaryPlatform = null; string multiplePlatforms = null; if (platform.Contains(",")) { // The user requested multiple platforms; the first one // is the primary platform. var platforms = platform.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (platforms.Length == 0) { RedirectableConsole.ErrorWriteLine("You supplied only commas where a list of platforms was expected."); ExecEnvironment.Exit(1); return(false); } else { for (var i = 0; i < platforms.Length; i++) { var newPlatform = _moduleUtilities.NormalizePlatform(module, platforms[i]); if (newPlatform == null) { ShowSupportedPlatformsError(module, platforms[i]); return(false); } platforms[i] = newPlatform; } primaryPlatform = platforms[0]; multiplePlatforms = platforms.Aggregate((a, b) => a + "," + b); } } else { platform = _moduleUtilities.NormalizePlatform(module, platform); if (platform == null && !platformSupplied) { // The current host platform isn't supported, so we shouldn't try to // operate on it. string firstPlatform = null; switch (this.m_HostPlatformDetector.DetectPlatform()) { case "Windows": firstPlatform = module.DefaultWindowsPlatforms.Split(',').FirstOrDefault(); break; case "MacOS": firstPlatform = module.DefaultMacOSPlatforms.Split(',').FirstOrDefault(); break; case "Linux": firstPlatform = module.DefaultLinuxPlatforms.Split(',').FirstOrDefault(); break; } if (firstPlatform != null) { // This will end up null if the first platform isn't supported // either and hence throw the right message. platform = _moduleUtilities.NormalizePlatform(module, firstPlatform); } } if (platform == null) { ShowSupportedPlatformsError(module, originalPlatform); return(false); } primaryPlatform = platform; } // You can generate multiple targets by default by setting the <DefaultWindowsPlatforms> // <DefaultMacOSPlatforms> and <DefaultLinuxPlatforms> tags in Module.xml. Note that // synchronisation will only be done for the primary platform, as there is no correct // synchronisation behaviour when dealing with multiple C# projects. // // We only trigger this behaviour when the platform is omitted; if you explicitly // specify "Windows" on the command line, we'll only generate / resync / sync // the Windows platform. if (!platformSupplied) { switch (platform) { case "Windows": multiplePlatforms = module.DefaultWindowsPlatforms; break; case "MacOS": multiplePlatforms = module.DefaultMacOSPlatforms; break; case "Linux": multiplePlatforms = module.DefaultLinuxPlatforms; break; } } // If no overrides are set, just use the current platform. if (string.IsNullOrEmpty(multiplePlatforms)) { multiplePlatforms = platform; } // If running pure synchronisation or a project clean, we don't need to perform // package resolution. if (action.ToLower() == "sync" || action.ToLower() == "clean") { disablePackageResolution = true; } // Resolve submodules as needed. if (!disablePackageResolution) { this.m_PackageManager.ResolveAll(workingDirectory, module, primaryPlatform, taskParallelisation, false, safeResolve, null); } // Create the list of multiple platforms. var multiplePlatformsList = multiplePlatforms.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToList(); // Remember whether or not we need to implicitly generate the host // platform. var implicitlyGenerateHostPlatform = false; Action requiresHostPlatform = () => implicitlyGenerateHostPlatform = true; // If we are already generating the host platform, then requiring the // host platform is already satisifed. if (platform == hostPlatform || multiplePlatformsList.Contains(hostPlatform)) { requiresHostPlatform = () => {}; } else if (!_featureManager.IsFeatureEnabled(Feature.HostPlatformGeneration)) { requiresHostPlatform = () => { RedirectableConsole.ErrorWriteLine( "WARNING: One or more projects requires host platforms to be generated, " + "but the HostPlatformGeneration feature is not enabled. Expect your " + "build to fail."); }; } // You can configure the default action for Protobuild in their project // with the <DefaultAction> tag in Module.xml. If omitted, default to a resync. // Valid options for this tag are either "Generate", "Resync" or "Sync". // If the actions are "Resync" or "Sync", then we need to perform an initial // step against the primary platform. switch (action.ToLower()) { case "generate": if (!this.GenerateProjectsForPlatform( workingDirectory, module, primaryPlatform, enabledServices, disabledServices, serviceSpecPath, debugServiceResolution, disablePackageResolution, disableHostPlatformGeneration, requiresHostPlatform, debugProjectGeneration)) { return(false); } break; case "resync": if (!this.ResyncProjectsForPlatform( workingDirectory, module, primaryPlatform, enabledServices, disabledServices, serviceSpecPath, debugServiceResolution, disablePackageResolution, disableHostPlatformGeneration, requiresHostPlatform, debugProjectGeneration)) { return(false); } break; case "sync": return(this.SyncProjectsForPlatform(module, primaryPlatform)); case "clean": if (!this.CleanProjectsForPlatform(module, primaryPlatform)) { return(false); } break; default: RedirectableConsole.ErrorWriteLine("Unknown option in <DefaultAction> tag of Module.xml. Defaulting to resync!"); return(this.ResyncProjectsForPlatform( workingDirectory, module, primaryPlatform, enabledServices, disabledServices, serviceSpecPath, debugServiceResolution, disablePackageResolution, disableHostPlatformGeneration, requiresHostPlatform, debugProjectGeneration)); } // Now iterate through the multiple platforms specified. foreach (var platformIter in multiplePlatformsList.Distinct()) { if (platformIter == primaryPlatform) { // Already handled above. continue; } // Resolve submodules as needed. if (!disablePackageResolution) { this.m_PackageManager.ResolveAll(workingDirectory, module, platformIter, taskParallelisation, false, safeResolve, null); } switch (action.ToLower()) { case "generate": case "resync": // We do a generate under resync mode since we only want the primary platform // to have synchronisation done (and it has had above). if (!this.GenerateProjectsForPlatform( workingDirectory, module, platformIter, enabledServices, disabledServices, serviceSpecPath, debugServiceResolution, disablePackageResolution, disableHostPlatformGeneration, requiresHostPlatform, debugProjectGeneration)) { return(false); } break; case "clean": if (!this.CleanProjectsForPlatform(module, platformIter)) { return(false); } break; default: throw new InvalidOperationException("Code should never reach this point"); } } // If we implicitly require the host platform, generate that now (this variable can // only ever be set to true if the host platform is not already in the list of // platforms generated previously). if (implicitlyGenerateHostPlatform) { // Check to see if the host platform is supported. var hostPlatformNormalized = _moduleUtilities.NormalizePlatform(module, hostPlatform); if (hostPlatformNormalized == null) { RedirectableConsole.WriteLine( "WARNING: The current host platform is not a supported platform for the solution. IDE editor " + "projects and post-build hooks will not be available, and this may cause the project to be " + "built incorrectly!"); return(true); } RedirectableConsole.WriteLine( "One or more projects required the presence of host platform " + "projects, implicitly starting generation for " + hostPlatform + "..."); // Resolve submodules as needed. if (!disablePackageResolution) { this.m_PackageManager.ResolveAll(workingDirectory, module, hostPlatform, taskParallelisation, false, safeResolve, null); } switch (action.ToLower()) { case "generate": case "resync": // We do a generate under resync mode since we only want the primary platform // to have synchronisation done (and it has had above). if (!this.GenerateProjectsForPlatform( workingDirectory, module, hostPlatform, enabledServices, disabledServices, serviceSpecPath, debugServiceResolution, disablePackageResolution, disableHostPlatformGeneration, requiresHostPlatform, debugProjectGeneration)) { return(false); } break; case "clean": if (!this.CleanProjectsForPlatform(module, hostPlatform)) { return(false); } break; default: throw new InvalidOperationException("Code should never reach this point"); } } // All the steps succeeded, so return true. return(true); }
private void InstallVSIXIntoVisualStudio(string vsixPath, IKnownToolProvider knownToolProvider) { // This installation technique is for pre-2017 editions of Visual Studio. // We don't list 10.0 and 11.0 because they don't support all editions (you should // use GAC based installation for these editions instead). var vsVersions = new[] { "12.0", "14.0" }; var editionRegistryKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32) ?.OpenSubKey("SOFTWARE") ?.OpenSubKey("Microsoft") ?.OpenSubKey("VisualStudio"); if (editionRegistryKey != null) { foreach (var version in vsVersions) { var installPath = (string)editionRegistryKey?.OpenSubKey(version)?.GetValue("InstallDir"); if (installPath != null) { var vsixInstallerPath = Path.Combine(installPath, "VSIXInstaller.exe"); if (Directory.Exists(installPath)) { if (File.Exists(vsixInstallerPath)) { try { RedirectableConsole.WriteLine("Installing VSIX into Visual Studio " + version + "..."); var processStartInfo = new ProcessStartInfo(); processStartInfo.FileName = vsixInstallerPath; processStartInfo.Arguments = "/q \"" + vsixPath + "\""; processStartInfo.UseShellExecute = false; var process = Process.Start(processStartInfo); process.WaitForExit(); if (process.ExitCode != 0) { RedirectableConsole.ErrorWriteLine("VSIX installation failed for Visual Studio " + version + " (non-zero exit code)"); } else { RedirectableConsole.WriteLine("VSIX installation completed successfully for Visual Studio " + version); } } catch (Exception ex) { RedirectableConsole.ErrorWriteLine("Failed to install VSIX for Visual Studio " + version + ": " + ex.Message); } } else { RedirectableConsole.WriteLine("Visual Studio " + version + " does not provide VSIXInstaller.exe (checked for existance of " + vsixInstallerPath + ")."); } } else { RedirectableConsole.WriteLine("Visual Studio " + version + " is not installed (checked for existance of " + installPath + ")."); } } } } // Now try and install in all editions of Visual Studio 2017 and later. This // may install the vswhere global tool. var vswhere = knownToolProvider.GetToolExecutablePath("vswhere"); List <string> installations = null; RedirectableConsole.WriteLine("Locating installations of Visual Studio 2017 and later..."); try { var processStartInfo = new ProcessStartInfo(); processStartInfo.FileName = vswhere; processStartInfo.Arguments = "-products * -property installationPath"; processStartInfo.UseShellExecute = false; processStartInfo.RedirectStandardOutput = true; var process = Process.Start(processStartInfo); var installationsString = process.StandardOutput.ReadToEnd(); installations = installationsString.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).Where(x => !string.IsNullOrWhiteSpace(x)).ToList(); process.WaitForExit(); if (process.ExitCode != 0) { RedirectableConsole.ErrorWriteLine("Unable to locate Visual Studio 2017 and later installations (non-zero exit code from vswhere)"); } } catch (Exception ex) { RedirectableConsole.ErrorWriteLine("Unable to locate Visual Studio 2017 and later installations: " + ex.Message); } if (installations != null) { foreach (var installPath in installations) { var vsixInstallerPath = Path.Combine(installPath, "Common7", "IDE", "VSIXInstaller.exe"); if (Directory.Exists(installPath)) { if (File.Exists(vsixInstallerPath)) { try { RedirectableConsole.WriteLine("Installing VSIX into " + installPath + "..."); var processStartInfo = new ProcessStartInfo(); processStartInfo.FileName = vsixInstallerPath; processStartInfo.Arguments = "/q \"" + vsixPath + "\""; processStartInfo.UseShellExecute = false; var process = Process.Start(processStartInfo); process.WaitForExit(); if (process.ExitCode != 0) { RedirectableConsole.ErrorWriteLine("VSIX installation failed for " + installPath + " (non-zero exit code)"); } else { RedirectableConsole.WriteLine("VSIX installation completed successfully for " + installPath); } } catch (Exception ex) { RedirectableConsole.ErrorWriteLine("Failed to install VSIX for " + installPath + ": " + ex.Message); } } else { RedirectableConsole.WriteLine("Visual Studio at " + installPath + " does not provide VSIXInstaller.exe (checked for existance of " + vsixInstallerPath + ")."); } } else { RedirectableConsole.WriteLine("Visual Studio at " + installPath + " is not installed (checked for existance of " + installPath + ")."); } } } }
private void InstallToolIntoUserApplicationFolder(string toolName, string toolPath) { var appToolPath = toolPath.Replace(".exe", ".app"); if (!Directory.Exists(appToolPath)) { return; } var basename = Path.GetFileName(appToolPath); var applicationPath = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Applications"); Directory.CreateDirectory(applicationPath); var installPath = Path.Combine(applicationPath, basename); try { var stat = System.Diagnostics.Process.Start(new ProcessStartInfo { FileName = "/usr/bin/stat", Arguments = "-f %T '" + installPath + "'", UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true }); var type = stat.StandardOutput.ReadToEnd().Trim(); if (type == "@") { // The file is a symbolic link. File.Delete(installPath); } else { if (Directory.Exists(installPath)) { // Recursive delete. PathUtils.AggressiveDirectoryDelete(installPath); } else { File.Delete(installPath); } } } catch { } // Make sure we don't create a symbolic link inside a symbolically linked directory. if (!File.Exists(installPath) && !Directory.Exists(installPath)) { var install = System.Diagnostics.Process.Start("ln", "-s '" + appToolPath + "' '" + installPath + "'"); if (install != null) { install.WaitForExit(); RedirectableConsole.WriteLine("Global tool '" + toolName + "' is now available in the application menu"); } else { RedirectableConsole.WriteLine("Unable to install global tool '" + toolName + "' into the application menu (unable to create link)"); } } }
public void MergeInReferencesAndPropertiesForIncludeProjects(List <LoadedDefinitionInfo> documents, XmlDocument projectDoc, string targetPlatform) { // While the <Files> section is handled explicitly in the XSLT (so that // synchronisation behaves correctly), the <References> and <Properties> // sections are handled here, because supporting them via XSLT would // make the XSLT drastically more complex. var documentsByName = documents.ToDictionarySafe( k => k.Definition.Name, v => v, (dict, x) => { var existing = dict[x.Definition.Name]; var tried = x; RedirectableConsole.WriteLine("WARNING: There is more than one project with the name " + x.Definition.Name + " (first project loaded from " + tried.Definition.AbsolutePath + ", " + "skipped loading second project from " + existing.Definition.AbsolutePath + ")"); }) .ToDictionary(k => k.Key, v => v.Value.Project); var currentProjectReferences = projectDoc.SelectNodes("/Project/References/Reference").OfType <XmlElement>() .Select(k => k.GetAttribute("Include")).ToList(); var referencesToAdd = new List <XmlNode>(); var propertiesToAdd = new List <XmlNode>(); foreach (var reference in currentProjectReferences) { if (!documentsByName.ContainsKey(reference)) { continue; } var referencedDocument = documentsByName[reference]; if (referencedDocument.DocumentElement.LocalName == "IncludeProject") { // Find references and copy them in. var includeProjectReferences = referencedDocument.SelectNodes("/IncludeProject/References/Reference").OfType <XmlElement>().ToList(); foreach (var @ref in includeProjectReferences) { referencesToAdd.Add(projectDoc.ImportNode(@ref, true)); } // Find all nodes under the <Properties> section and deep-copy them in. var includeProjectProperties = referencedDocument.SelectNodes("/IncludeProject/Properties/*").OfType <XmlElement>().ToList(); foreach (var node in includeProjectProperties) { propertiesToAdd.Add(projectDoc.ImportNode(node, true)); } } } var references = projectDoc.DocumentElement.SelectSingleNode("References"); if (references == null) { references = projectDoc.CreateElement("References"); projectDoc.DocumentElement.AppendChild(references); } foreach (var @ref in referencesToAdd) { references.AppendChild(@ref); } var properties = projectDoc.DocumentElement.SelectSingleNode("Properties"); if (properties == null) { properties = projectDoc.CreateElement("Properties"); projectDoc.DocumentElement.AppendChild(properties); } foreach (var node in propertiesToAdd) { properties.AppendChild(node); } }
public XmlDocument Generate( List <XmlDocument> definitions, string workingDirectory, string rootPath, string projectName, string platformName, string packagesPath, IEnumerable <XmlElement> properties, List <Service> services) { var doc = new XmlDocument(); doc.AppendChild(doc.CreateXmlDeclaration("1.0", "UTF-8", null)); var input = doc.CreateElement("Input"); doc.AppendChild(input); input.AppendChild(this.m_ServiceInputGenerator.Generate(doc, projectName, services)); var generation = doc.CreateElement("Generation"); var projectNameNode = doc.CreateElement("ProjectName"); projectNameNode.AppendChild(doc.CreateTextNode(projectName)); var platformNameNode = doc.CreateElement("Platform"); platformNameNode.AppendChild(doc.CreateTextNode(platformName)); var hostPlatformName = doc.CreateElement("HostPlatform"); hostPlatformName.AppendChild(doc.CreateTextNode(this.m_HostPlatformDetector.DetectPlatform())); var workingDirectoryNode = doc.CreateElement("WorkingDirectory"); workingDirectoryNode.AppendChild(doc.CreateTextNode(workingDirectory)); if (string.Compare(platformName, "Web", StringComparison.InvariantCultureIgnoreCase) == 0) { // Add JSIL properties string jsilDirectory, jsilCompilerFile; if (!this.m_JSILProvider.GetJSIL(out jsilDirectory, out jsilCompilerFile)) { throw new InvalidOperationException("JSIL not found, but previous check passed."); } var jsilDirectoryNode = doc.CreateElement("JSILDirectory"); jsilDirectoryNode.AppendChild(doc.CreateTextNode(jsilDirectory)); generation.AppendChild(jsilDirectoryNode); var jsilCompilerPathNode = doc.CreateElement("JSILCompilerFile"); jsilCompilerPathNode.AppendChild(doc.CreateTextNode(jsilCompilerFile)); generation.AppendChild(jsilCompilerPathNode); var jsilLibrariesNode = doc.CreateElement("JSILLibraries"); foreach (var entry in this.m_JSILProvider.GetJSILLibraries()) { var entryNode = doc.CreateElement("Library"); var pathAttribute = doc.CreateAttribute("Path"); pathAttribute.Value = entry.Key; var nameAttribute = doc.CreateAttribute("Name"); nameAttribute.Value = entry.Value; entryNode.Attributes.Append(pathAttribute); entryNode.Attributes.Append(nameAttribute); jsilLibrariesNode.AppendChild(entryNode); } generation.AppendChild(jsilLibrariesNode); // Automatically extract the JSIL template if not already present. var currentProject = definitions.Select(x => x.DocumentElement) .Where(x => x.Attributes != null) .Where(x => x.Attributes["Name"] != null) .FirstOrDefault(x => x.Attributes["Name"].Value == projectName); if (currentProject != null) { string type = null; string path = null; if (currentProject.Attributes != null && currentProject.Attributes["Type"] != null) { type = currentProject.Attributes["Type"].Value; } if (currentProject.Attributes != null && currentProject.Attributes["Path"] != null) { path = currentProject.Attributes["Path"].Value; } if (string.Compare(type, "App", StringComparison.InvariantCultureIgnoreCase) == 0 || string.Compare(type, "Console", StringComparison.InvariantCultureIgnoreCase) == 0 || string.Compare(type, "GUI", StringComparison.InvariantCultureIgnoreCase) == 0 || string.Compare(type, "GTK", StringComparison.InvariantCultureIgnoreCase) == 0) { if (path != null) { var srcDir = Path.Combine(rootPath, path); if (Directory.Exists(srcDir)) { if (!File.Exists(Path.Combine(srcDir, "index.htm"))) { RedirectableConsole.WriteLine("Extracting JSIL HTML template..."); ResourceExtractor.ExtractJSILTemplate(projectName, Path.Combine(srcDir, "index.htm")); } } } } } } var rootName = doc.CreateElement("RootPath"); rootName.AppendChild(doc.CreateTextNode( new DirectoryInfo(rootPath).FullName)); generation.AppendChild(projectNameNode); generation.AppendChild(platformNameNode); generation.AppendChild(hostPlatformName); generation.AppendChild(workingDirectoryNode); generation.AppendChild(rootName); input.AppendChild(generation); var propertiesNode = doc.CreateElement("Properties"); foreach (var property in properties) { if (property.Name.ToLower() == "property") { var nodeName = doc.CreateElement(property.GetAttribute("Name")); nodeName.AppendChild(doc.CreateTextNode( property.GetAttribute("Value"))); propertiesNode.AppendChild(nodeName); } else { propertiesNode.AppendChild( doc.ImportNode(property, true)); } } input.AppendChild(propertiesNode); var featuresNode = doc.CreateElement("Features"); foreach (var feature in _featureManager.GetAllEnabledFeatures()) { var featureNode = doc.CreateElement(feature.ToString()); featureNode.AppendChild(doc.CreateTextNode("True")); featuresNode.AppendChild(featureNode); } input.AppendChild(featuresNode); var nuget = doc.CreateElement("NuGet"); input.AppendChild(nuget); var projects = doc.CreateElement("Projects"); input.AppendChild(projects); foreach (var projectDoc in definitions) { var importedProject = this.m_ServiceReferenceTranslator.TranslateProjectWithServiceReferences( doc.ImportNode(projectDoc.DocumentElement, true), services); // Convert <Property> tags in projects other than the one we're currently // generating, so that we can lookup properties in other projects in the XSLT. var importedProjectProperties = importedProject.ChildNodes .OfType <XmlElement>() .FirstOrDefault(x => x.Name.ToLower() == "properties"); if (importedProjectProperties != null) { var existingProperties = importedProjectProperties.ChildNodes .OfType <XmlElement>().ToList(); foreach (var property in existingProperties) { if (property.Name.ToLower() == "property") { if (property.GetAttribute("Name") == null) { throw new Exception( "A property is missing the Name attribute in the '" + projectDoc.DocumentElement.GetAttribute("Name") + "' project."); } var nodeName = doc.CreateElement(property.GetAttribute("Name")); nodeName.AppendChild(doc.CreateTextNode( property.GetAttribute("Value"))); importedProjectProperties.AppendChild(nodeName); } } } projects.AppendChild(importedProject); } // Also check if there are NuGet packages.config file for // this project and if there is, include all of the relevant // NuGet package information for referencing the correct DLLs. if (File.Exists(packagesPath)) { this.m_NuGetReferenceDetector.ApplyNuGetReferences( rootPath, packagesPath, doc, nuget); } return(doc); }
private async Task <int> PushNuGetBinaryAsync(string targetUri, string apiKey, MemoryStream file, bool ignoreOnExisting) { byte[] fileBytes; fileBytes = new byte[(int)file.Length]; file.Read(fileBytes, 0, fileBytes.Length); const string ApiKeyHeader = "X-NuGet-ApiKey"; var boundary = Guid.NewGuid().ToString(); byte[] boundaryBytes = Encoding.UTF8.GetBytes("--" + boundary + "\r\n"); byte[] trailer = Encoding.UTF8.GetBytes("\r\n--" + boundary + "--\r\n"); byte[] header = Encoding.UTF8.GetBytes( string.Format( "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\";\r\nContent-Type: {2}\r\n\r\n", "package", "package.nupkg", "application/octet-stream")); var requestLength = boundaryBytes.Length + header.Length + trailer.Length + fileBytes.Length; var combinedContent = new byte[requestLength]; if (combinedContent == null) { throw new InvalidOperationException("Unable to create byte array " + requestLength + " bytes long for upload!"); } boundaryBytes.CopyTo(combinedContent, 0); header.CopyTo(combinedContent, boundaryBytes.Length); fileBytes.CopyTo(combinedContent, boundaryBytes.Length + header.Length); trailer.CopyTo(combinedContent, boundaryBytes.Length + header.Length + fileBytes.Length); using (var client = new RetryableWebClient()) { var done = false; byte[] result = null; Exception ex = null; var uploadProgressRenderer = new UploadProgressRenderer(); client.UploadDataCompleted += (sender, e) => { if (e.Error != null) { ex = e.Error; } try { result = e.Result; } catch { } done = true; }; client.UploadProgressChanged += (sender, e) => { if (!done) { uploadProgressRenderer.Update(e.ProgressPercentage, e.BytesSent / 1024); } }; client.SetHeader("Content-Type", "multipart/form-data; boundary=\"" + boundary + "\""); if (!string.IsNullOrWhiteSpace(apiKey)) { client.SetHeader(ApiKeyHeader, apiKey); } client.SetHeader("X-NuGet-Protocol-Version", "4.1.0"); client.SetHeader("User-Agent", "Protobuild"); client.UploadDataAsync(new Uri(targetUri), "PUT", combinedContent); while (!done) { System.Threading.Thread.Sleep(0); } uploadProgressRenderer.FinalizeRendering(); if (ex != null) { var webException = ex as WebException; if (webException != null) { var httpResponse = webException.Response as HttpWebResponse; if (httpResponse != null) { RedirectableConsole.ErrorWriteLine(httpResponse.StatusDescription); if (httpResponse.StatusCode == HttpStatusCode.Conflict) { if (ignoreOnExisting) { // This is okay - the user doesn't care if there's an existing package with the same version. return(0); } else { return(1); } } return(1); } } else { throw new InvalidOperationException("Upload error", ex); } } if (result != null) { var resultDecoded = Encoding.UTF8.GetString(result); if (!string.IsNullOrWhiteSpace(resultDecoded)) { RedirectableConsole.WriteLine(Encoding.UTF8.GetString(result)); } else { RedirectableConsole.WriteLine("Package uploaded successfully"); } } else { RedirectableConsole.WriteLine("Package uploaded successfully"); } return(0); } }
public int Execute(Execution execution) { var assemblyPath = Assembly.GetEntryAssembly().Location; var fileInfo = new FileInfo(assemblyPath); var modulePath = fileInfo.DirectoryName; if (modulePath == null) { RedirectableConsole.WriteLine( "Unable to determine the location of Protobuild."); return(1); } if (!File.Exists(Path.Combine(modulePath, "Build", "Module.xml"))) { modulePath = execution.WorkingDirectory; } string executablePath = null; if (!File.Exists(Path.Combine(modulePath, "Build", "Module.xml"))) { // We can only run global tools. var globalToolPath = this.m_PackageGlobalTool.ResolveGlobalToolIfPresent(execution.ExecuteProjectName); if (globalToolPath == null) { RedirectableConsole.WriteLine( "There is no global tool registered as '" + execution.ExecuteProjectName + "'"); return(1); } else { executablePath = globalToolPath; } } else { var platform = this.m_HostPlatformDetector.DetectPlatform(); var module = ModuleInfo.Load(Path.Combine(modulePath, "Build", "Module.xml")); var definitions = module.GetDefinitionsRecursively(platform).ToList(); var target = definitions.FirstOrDefault(x => x.Name == execution.ExecuteProjectName); if (target == null) { // Check to see if there is any external project definition that provides the tool. foreach (var definition in definitions) { var document = XDocument.Load(definition.DefinitionPath); if (document.Root.Name.LocalName == "ExternalProject") { foreach (var node in document.Root.Elements().Where(x => x.Name.LocalName == "Tool")) { var name = node.Attribute(XName.Get("Name")).Value; var path = node.Attribute(XName.Get("Path")).Value; if (name == execution.ExecuteProjectName) { executablePath = Path.Combine(definition.ModulePath, path.Replace('\\', Path.DirectorySeparatorChar).Replace('/', Path.DirectorySeparatorChar)); break; } } } } if (executablePath == null) { var globalToolPath = this.m_PackageGlobalTool.ResolveGlobalToolIfPresent(execution.ExecuteProjectName); if (globalToolPath == null) { RedirectableConsole.WriteLine( "There is no project definition with name '" + execution.ExecuteProjectName + "'"); return(1); } else { executablePath = globalToolPath; } } } else { var document = XDocument.Load(target.DefinitionPath); if (document.Root.Name.LocalName == "Project") { var assemblyName = this.m_ProjectOutputPathCalculator.GetProjectAssemblyName(platform, target, document); var prefixPath = this.m_ProjectOutputPathCalculator.GetProjectOutputPathPrefix(platform, target, document, false); var directory = new DirectoryInfo(Path.Combine(modulePath, prefixPath)); var subdirectories = directory.GetDirectories(); var preferredName = (execution.ExecuteProjectConfiguration ?? "Debug").ToLowerInvariant(); var preferredDirectory = subdirectories.FirstOrDefault(x => x.Name.ToLowerInvariant() == preferredName); if (preferredDirectory != null && File.Exists(Path.Combine(preferredDirectory.FullName, assemblyName + ".exe"))) { executablePath = Path.Combine(preferredDirectory.FullName, assemblyName + ".exe"); } else { string tempPath = null; foreach (var subdirectory in subdirectories) { tempPath = Path.Combine(subdirectory.FullName, assemblyName + ".exe"); if (File.Exists(tempPath)) { break; } } if (tempPath != null && File.Exists(tempPath)) { executablePath = tempPath; } } } else if (document.Root.Name.LocalName == "ExternalProject") { foreach (var node in document.Root.Elements().Where(x => x.Name.LocalName == "Tool")) { var name = node.Attribute(XName.Get("Name")).Value; var path = node.Attribute(XName.Get("Path")).Value; if (name == execution.ExecuteProjectName) { executablePath = Path.Combine(target.ModulePath, path.Replace('\\', Path.DirectorySeparatorChar).Replace('/', Path.DirectorySeparatorChar)); break; } } } if (executablePath == null) { var globalToolPath = this.m_PackageGlobalTool.ResolveGlobalToolIfPresent(execution.ExecuteProjectName); if (globalToolPath == null) { RedirectableConsole.WriteLine( "There is no output path for '" + execution.ExecuteProjectName + "'; has the project been built?"); return(1); } else { executablePath = globalToolPath; } } } } if (!File.Exists(executablePath)) { RedirectableConsole.WriteLine( "There is no executable for '" + execution.ExecuteProjectName + "'; has the project been built?"); return(1); } var arguments = string.Empty; if (execution.ExecuteProjectArguments.Length > 0) { arguments = execution.ExecuteProjectArguments .Select(x => "\"" + x.Replace("\"", "\\\"") + "\"") .Aggregate((a, b) => a + " " + b); } ProcessStartInfo startInfo; if (Path.DirectorySeparatorChar == '/') { var mono = "mono"; if (m_HostPlatformDetector.DetectPlatform() == "MacOS" && File.Exists("/usr/local/bin/mono")) { // After upgrading to OSX El Capitan, the /usr/local/bin folder is no longer in // the system PATH. If we can't find Mono with the which tool, manually set the // path here in an attempt to find it. mono = "/usr/local/bin/mono"; } startInfo = new ProcessStartInfo( mono, "--debug \"" + executablePath + "\" " + arguments); } else { startInfo = new ProcessStartInfo( executablePath, arguments); } startInfo.UseShellExecute = false; var process = Process.Start(startInfo); process.WaitForExit(); return(process.ExitCode); }
private void InstallToolIntoLinuxApplicationMenu(string toolName, string toolPath) { RedirectableConsole.WriteLine("Installing global tool '" + toolName + "' into the application menu..."); var menuPath = Path.Combine(GetToolsPath(), ".linux-menus"); Directory.CreateDirectory(menuPath); // Extract the icon from the assembly. string iconPath = null; try { var asm = System.Reflection.Assembly.Load("System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); var iconType = asm.GetType("System.Drawing.Icon"); var extractMethod = iconType.GetMethod("ExtractAssociatedIcon", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public); dynamic iconObject = extractMethod.Invoke(null, new[] { toolPath }); using (var bmp = iconObject.ToBitmap()) { var iconPathTemp = Path.Combine(menuPath, toolName + ".png"); var enumType = asm.GetType("System.Drawing.Imaging.ImageFormat"); var saveMethod = ((Type)bmp.GetType()).GetMethods().First(x => x.Name == "Save" && x.GetParameters().Length == 2); saveMethod.Invoke( bmp, new object[] { iconPathTemp, enumType.GetProperty("Png").GetGetMethod().Invoke(null, null) }); iconPath = iconPathTemp; } } catch (Exception) { // No icon to extract. } var menuItemPath = Path.Combine(menuPath, "protobuild-" + toolName + ".desktop"); using (var writer = new StreamWriter(menuItemPath)) { writer.WriteLine("[Desktop Entry]"); writer.WriteLine("Type=Application"); writer.WriteLine("Name=" + toolName); if (iconPath != null) { writer.WriteLine("Icon=" + iconPath); } writer.WriteLine("Exec=/usr/bin/mono " + toolPath); writer.WriteLine("Categories=Development"); } var install = System.Diagnostics.Process.Start("xdg-desktop-menu", "install '" + menuItemPath + "'"); if (install != null) { install.WaitForExit(); RedirectableConsole.WriteLine("Global tool '" + toolName + "' is now available in the application menu"); } else { RedirectableConsole.WriteLine("Unable to install global tool '" + toolName + "' into the application menu (xdg-desktop-menu not found)"); } }
public Tuple <int, string, string> RunProtobuild(ModuleInfo module, string args, bool capture = false) { var invokeInline = false; var myHash = ExecEnvironment.GetProgramHash(); var targetHash = ExecEnvironment.GetProgramHash(Path.Combine(module.Path, "Protobuild.exe")); if (myHash == targetHash) { invokeInline = true; } if (ExecEnvironment.RunProtobuildInProcess || invokeInline) { var oldBuffer = RedirectableConsole.TargetBuffer; var ourBuffer = new RedirectableBuffer(); RedirectableConsole.TargetBuffer = ourBuffer; var needsEndSelfInvoke = true; try { var exitCode = ExecEnvironment.InvokeSelf(module.Path, args.SplitCommandLine().ToArray()); RedirectableConsole.TargetBuffer = oldBuffer; needsEndSelfInvoke = false; if (capture) { return(new Tuple <int, string, string>( exitCode, ourBuffer.Stdout, ourBuffer.Stderr)); } else { return(new Tuple <int, string, string>( exitCode, string.Empty, string.Empty)); } } finally { if (needsEndSelfInvoke) { RedirectableConsole.TargetBuffer = oldBuffer; } } } var protobuildPath = Path.Combine(module.Path, "Protobuild.exe"); try { var chmodStartInfo = new ProcessStartInfo { FileName = "chmod", Arguments = "a+x Protobuild.exe", WorkingDirectory = module.Path, CreateNoWindow = capture, UseShellExecute = false }; Process.Start(chmodStartInfo); } catch (ExecEnvironment.SelfInvokeExitException) { throw; } catch { } var stdout = string.Empty; var stderr = string.Empty; for (var attempt = 0; attempt < 3; attempt++) { if (File.Exists(protobuildPath)) { var pi = new ProcessStartInfo { FileName = protobuildPath, Arguments = args, WorkingDirectory = module.Path, CreateNoWindow = capture, RedirectStandardError = capture, RedirectStandardInput = capture, RedirectStandardOutput = capture, UseShellExecute = false }; var p = new Process { StartInfo = pi }; if (capture) { p.OutputDataReceived += (sender, eventArgs) => { if (!string.IsNullOrEmpty(eventArgs.Data)) { if (capture) { stdout += eventArgs.Data + "\n"; } else { RedirectableConsole.WriteLine(eventArgs.Data); } } }; p.ErrorDataReceived += (sender, eventArgs) => { if (!string.IsNullOrEmpty(eventArgs.Data)) { if (capture) { stderr += eventArgs.Data + "\n"; } else { RedirectableConsole.ErrorWriteLine(eventArgs.Data); } } }; } try { p.Start(); } catch (System.ComponentModel.Win32Exception ex) { if (ex.Message.Contains("Cannot find the specified file")) { // Mono sometimes throws this error even though the // file does exist on disk. The best guess is there's // a race condition between performing chmod on the // file and Mono actually seeing it as an executable file. // Show a warning and sleep for a bit before retrying. if (attempt != 2) { RedirectableConsole.WriteLine("WARNING: Unable to execute Protobuild.exe, will retry again..."); System.Threading.Thread.Sleep(2000); continue; } else { RedirectableConsole.WriteLine("ERROR: Still unable to execute Protobuild.exe."); throw; } } } if (capture) { p.BeginOutputReadLine(); p.BeginErrorReadLine(); } p.WaitForExit(); return(new Tuple <int, string, string>(p.ExitCode, stdout, stderr)); } } return(new Tuple <int, string, string>(1, string.Empty, string.Empty)); }
public int Execute(Execution execution) { var hostPlatform = _hostPlatformDetector.DetectPlatform(); string builderPathNativeArch = null; string builderPath64 = null; string builderPath32 = null; var extraArgsNativeArch = string.Empty; var extraArgs64 = string.Empty; var extraArgs32 = string.Empty; var extraArgsGeneral = string.Empty; if (hostPlatform == "Windows") { foreach (var arch in new[] { RegistryView.Default, RegistryView.Registry32, RegistryView.Registry64 }) { // Find latest version of MSBuild. var registryKey = RegistryKey.OpenBaseKey( RegistryHive.LocalMachine, arch) .OpenSubKey("SOFTWARE")? .OpenSubKey("Microsoft")? .OpenSubKey("MSBuild")? .OpenSubKey("ToolsVersions"); if (registryKey == null) { if (arch == RegistryView.Registry64) { continue; } RedirectableConsole.ErrorWriteLine( "ERROR: No versions of MSBuild were available " + "according to the registry (or they were not readable)."); return(1); } var subkeys = registryKey.GetSubKeyNames(); var orderedVersions = subkeys.OrderByDescending(x => int.Parse(x.Split('.').First(), CultureInfo.InvariantCulture)); var builderPath = (from version in orderedVersions let path = (string)registryKey.OpenSubKey(version)?.GetValue("MSBuildToolsPath") where path != null && Directory.Exists(path) let msbuild = Path.Combine(path, "MSBuild.exe") where File.Exists(msbuild) select msbuild).FirstOrDefault(); if (builderPath == null) { if (arch == RegistryView.Registry64) { continue; } RedirectableConsole.ErrorWriteLine( "ERROR: Unable to find installed MSBuild in any installed tools version."); return(1); } var extraArgs = string.Empty; if (!builderPath.Contains("v2.0.50727")) { extraArgs = "/m /nodeReuse:false "; } switch (arch) { case RegistryView.Default: builderPathNativeArch = builderPath; extraArgsNativeArch = extraArgs; break; case RegistryView.Registry32: builderPath32 = builderPath; extraArgs32 = extraArgs; break; case RegistryView.Registry64: builderPath64 = builderPath; extraArgs64 = extraArgs; break; } } } else { // Find path to xbuild. var whichPaths = new[] { "/bin/which", "/usr/bin/which" }; foreach (var w in whichPaths) { if (File.Exists(w)) { var whichProcess = Process.Start(new ProcessStartInfo(w, "xbuild") { RedirectStandardOutput = true, UseShellExecute = false }); if (whichProcess == null) { continue; } var result = whichProcess.StandardOutput.ReadToEnd().Trim(); if (!string.IsNullOrWhiteSpace(result) && File.Exists(result)) { builderPathNativeArch = result; break; } } } if (builderPathNativeArch == null && _hostPlatformDetector.DetectPlatform() == "MacOS" && File.Exists("/usr/local/bin/xbuild")) { // After upgrading to OSX El Capitan, the /usr/local/bin folder is no longer in // the system PATH. If we can't find xbuild with the which tool, manually set the // path here in an attempt to find it. builderPathNativeArch = "/usr/local/bin/xbuild"; } if (builderPathNativeArch == null) { RedirectableConsole.ErrorWriteLine("ERROR: Unable to find xbuild on the current PATH."); return(1); } builderPath32 = builderPathNativeArch; builderPath64 = builderPathNativeArch; } if (!string.IsNullOrWhiteSpace(execution.BuildTarget)) { extraArgsGeneral += "/t:\"" + execution.BuildTarget + "\" "; } foreach (var prop in execution.BuildProperties) { extraArgsGeneral += "/p:\"" + prop.Key.Replace("\"", "\\\"") + "\"=\"" + (prop.Value ?? string.Empty).Replace("\"", "\\\"") + "\" "; } switch (execution.BuildProcessArchitecture) { case "x86": RedirectableConsole.WriteLine("INFO: Using " + builderPath32 + " (forced 32-bit) to perform this build."); break; case "x64": RedirectableConsole.WriteLine("INFO: Using " + builderPath64 + " (forced 64-bit) to perform this build."); break; case "Default": default: RedirectableConsole.WriteLine("INFO: Using " + builderPathNativeArch + " (32-bit: " + builderPath32 + ") to perform this build."); break; } var targetPlatforms = (execution.Platform ?? hostPlatform).Split(','); var module = ModuleInfo.Load(Path.Combine(execution.WorkingDirectory, "Build", "Module.xml")); foreach (var platform in targetPlatforms) { string builderPath; string extraArgs; switch (execution.BuildProcessArchitecture) { case "x86": builderPath = builderPath32; extraArgs = extraArgs32 + extraArgsGeneral; break; case "x64": builderPath = builderPath64; extraArgs = extraArgs64 + extraArgsGeneral; break; case "Default": default: builderPath = platform == "WindowsPhone" ? builderPath32 : builderPathNativeArch; extraArgs = (platform == "WindowsPhone" ? extraArgs32 : extraArgsNativeArch) + extraArgsGeneral; break; } var fileToBuild = module.Name + "." + platform + ".sln"; RedirectableConsole.WriteLine("INFO: Executing " + builderPath + " with arguments: " + extraArgs + fileToBuild); var process = Process.Start(new ProcessStartInfo(builderPath, extraArgs + fileToBuild) { UseShellExecute = false }); if (process == null) { RedirectableConsole.ErrorWriteLine("ERROR: Build process did not start successfully."); return(1); } process.WaitForExit(); if (process.ExitCode != 0) { return(process.ExitCode); } } return(0); }
public int Execute(Execution execution) { var assemblyPath = Assembly.GetEntryAssembly().Location; var fileInfo = new FileInfo(assemblyPath); var modulePath = fileInfo.DirectoryName; if (modulePath == null) { RedirectableConsole.WriteLine( "Unable to determine the location of Protobuild."); return(1); } if (!File.Exists(Path.Combine(modulePath, "Build", "Module.xml"))) { modulePath = execution.WorkingDirectory; } string executablePath = null; var executableIsNative = false; if (!File.Exists(Path.Combine(modulePath, "Build", "Module.xml"))) { // We can only run global tools. var globalToolPath = this.m_PackageGlobalTool.ResolveGlobalToolIfPresent(execution.ExecuteProjectName); if (globalToolPath == null) { RedirectableConsole.WriteLine( "There is no global tool registered as '" + execution.ExecuteProjectName + "'"); return(1); } else { executablePath = globalToolPath; executableIsNative = m_HostPlatformDetector.DetectPlatform() == "MacOS" && globalToolPath.EndsWith(".app"); } } else { var platform = this.m_HostPlatformDetector.DetectPlatform(); var module = ModuleInfo.Load(Path.Combine(modulePath, "Build", "Module.xml")); var definitions = module.GetDefinitionsRecursively(platform).ToList(); var target = definitions.FirstOrDefault(x => x.Name == execution.ExecuteProjectName); if (target == null) { // Check to see if there is any external project definition that provides the tool. foreach (var definition in definitions) { var document = XDocument.Load(definition.DefinitionPath); if (document.Root.Name.LocalName == "ExternalProject") { foreach (var node in document.Root.Elements().Where(x => x.Name.LocalName == "Tool")) { var name = node.Attribute(XName.Get("Name")).Value; var path = node.Attribute(XName.Get("Path")).Value; if (name == execution.ExecuteProjectName) { executablePath = Path.Combine(definition.ModulePath, path.Replace('\\', Path.DirectorySeparatorChar).Replace('/', Path.DirectorySeparatorChar)); break; } } } } if (executablePath == null) { var globalToolPath = this.m_PackageGlobalTool.ResolveGlobalToolIfPresent(execution.ExecuteProjectName); if (globalToolPath == null) { RedirectableConsole.WriteLine( "There is no project definition with name '" + execution.ExecuteProjectName + "'"); return(1); } else { executablePath = globalToolPath; executableIsNative = m_HostPlatformDetector.DetectPlatform() == "MacOS" && globalToolPath.EndsWith(".app"); } } } else { var document = XDocument.Load(target.DefinitionPath); if (document.Root.Name.LocalName == "Project") { var assemblyName = this.m_ProjectOutputPathCalculator.GetProjectAssemblyName(platform, target, document); var prefixPath = this.m_ProjectOutputPathCalculator.GetProjectOutputPathPrefix(platform, target, document, false); var directory = new DirectoryInfo(Path.Combine(modulePath, prefixPath)); var subdirectories = directory.GetDirectories(); var preferredName = (execution.ExecuteProjectConfiguration ?? "Debug").ToLowerInvariant(); var preferredDirectory = subdirectories.FirstOrDefault(x => x.Name.ToLowerInvariant() == preferredName); var targetName = assemblyName + ".exe"; string targetAppName = null; if (m_HostPlatformDetector.DetectPlatform() == "MacOS") { targetAppName = assemblyName + ".app/Contents/MacOS/" + assemblyName; } if (preferredDirectory != null && (File.Exists(Path.Combine(preferredDirectory.FullName, targetName)) || (targetAppName != null && File.Exists(Path.Combine(preferredDirectory.FullName, targetAppName))))) { if (targetAppName != null && File.Exists(Path.Combine(preferredDirectory.FullName, targetAppName))) { // We must use the 'defaults' tool on macOS to determine if the target is a // graphical application. We do this by checking for the presence of NSPrincipalClass // in the Info.plist file, which must be present for graphical applications to // work. If it does not exist, we assume it is a command-line application and run // it normally like on any other platform. We have to do this because the native // entry point is the only one that has access to the GUI, while the non-native // entry point is the only one that has the correct working directory set on // startup. if (_graphicalAppDetection.TargetMacOSAppIsGraphical(Path.Combine(preferredDirectory.FullName, assemblyName + ".app"))) { executablePath = Path.Combine(preferredDirectory.FullName, targetAppName); executableIsNative = true; } } if (executablePath == null && File.Exists(Path.Combine(preferredDirectory.FullName, targetName))) { executablePath = Path.Combine(preferredDirectory.FullName, targetName); } } else { foreach (var subdirectory in subdirectories) { var tempPath = Path.Combine(subdirectory.FullName, assemblyName + ".exe"); string tempAppName = null; if (m_HostPlatformDetector.DetectPlatform() == "MacOS") { tempAppName = Path.Combine(subdirectory.FullName, assemblyName + ".app/Contents/MacOS/" + assemblyName); } if (tempAppName != null && File.Exists(tempAppName)) { // We must use the 'defaults' tool on macOS to determine if the target is a // graphical application. We do this by checking for the presence of NSPrincipalClass // in the Info.plist file, which must be present for graphical applications to // work. If it does not exist, we assume it is a command-line application and run // it normally like on any other platform. We have to do this because the native // entry point is the only one that has access to the GUI, while the non-native // entry point is the only one that has the correct working directory set on // startup. if (_graphicalAppDetection.TargetMacOSAppIsGraphical(Path.Combine(subdirectory.FullName, assemblyName + ".app"))) { executablePath = tempAppName; executableIsNative = true; break; } } if (File.Exists(tempPath)) { executablePath = tempPath; break; } } } } else if (document.Root.Name.LocalName == "ExternalProject") { foreach (var node in document.Root.Elements().Where(x => x.Name.LocalName == "Tool")) { var name = node.Attribute(XName.Get("Name")).Value; var path = node.Attribute(XName.Get("Path")).Value; if (name == execution.ExecuteProjectName) { executablePath = Path.Combine(target.ModulePath, path.Replace('\\', Path.DirectorySeparatorChar).Replace('/', Path.DirectorySeparatorChar)); break; } } } if (executablePath == null) { var globalToolPath = this.m_PackageGlobalTool.ResolveGlobalToolIfPresent(execution.ExecuteProjectName); if (globalToolPath == null) { RedirectableConsole.WriteLine( "There is no output path for '" + execution.ExecuteProjectName + "'; has the project been built?"); return(1); } else { executablePath = globalToolPath; executableIsNative = m_HostPlatformDetector.DetectPlatform() == "MacOS" && globalToolPath.EndsWith(".app"); } } } } if (!(File.Exists(executablePath) || (executablePath.EndsWith(".app") && Directory.Exists(executablePath)))) { RedirectableConsole.WriteLine( "There is no executable for '" + execution.ExecuteProjectName + "'; has the project been built?"); return(1); } var arguments = string.Empty; if (execution.ExecuteProjectArguments.Length > 0) { arguments = execution.ExecuteProjectArguments .Select(x => "\"" + x.Replace("\"", "\\\"") + "\"") .Aggregate((a, b) => a + " " + b); } ProcessStartInfo startInfo; if (Path.DirectorySeparatorChar == '/' && !executableIsNative) { var mono = "mono"; if (m_HostPlatformDetector.DetectPlatform() == "MacOS" && File.Exists("/usr/local/bin/mono")) { // After upgrading to OSX El Capitan, the /usr/local/bin folder is no longer in // the system PATH. If we can't find Mono with the which tool, manually set the // path here in an attempt to find it. mono = "/usr/local/bin/mono"; } startInfo = new ProcessStartInfo( mono, "--debug \"" + executablePath + "\" " + arguments); } else { if (m_HostPlatformDetector.DetectPlatform() == "MacOS" && executablePath.EndsWith(".app")) { // We must use 'open -a' to launch this application. arguments = "-a '" + executablePath.Replace("'", "'\"'\"'") + "' " + arguments; executablePath = "/usr/bin/open"; } startInfo = new ProcessStartInfo( executablePath, arguments); } startInfo.UseShellExecute = false; var process = Process.Start(startInfo); process.WaitForExit(); return(process.ExitCode); }
private void ResolveLibraryBinary(string workingDirectory, ICachableBinaryPackageMetadata protobuildMetadata, string folder, bool forceUpgrade, Func <byte[]> getBinaryPackage) { var platformFolder = Path.Combine(folder, protobuildMetadata.Platform); if (File.Exists(Path.Combine(platformFolder, ".pkg"))) { if (!forceUpgrade) { RedirectableConsole.WriteLine("Protobuild binary package already present at " + platformFolder); return; } } RedirectableConsole.WriteLine("Creating and emptying " + platformFolder); if (File.Exists(Path.Combine(folder, ".pkg"))) { if (Directory.Exists(platformFolder)) { // Only clear out the target's folder if the reference folder // already contains binary packages (for other platforms) PathUtils.AggressiveDirectoryDelete(platformFolder); } } else { // The reference folder is holding source code, so clear it // out entirely. PathUtils.AggressiveDirectoryDelete(folder); } Directory.CreateDirectory(platformFolder); RedirectableConsole.WriteLine("Marking " + folder + " as ignored for Git"); GitUtils.MarkIgnored(folder); var package = getBinaryPackage(); if (package == null) { return; } ExtractTo(workingDirectory, protobuildMetadata.PackageName, protobuildMetadata.BinaryFormat, package, platformFolder, protobuildMetadata.Platform); // Only copy ourselves to the binary folder if both "Build/Module.xml" and // "Build/Projects" exist in the binary package's folder. This prevents us // from triggering the "create new module?" logic if the package hasn't been // setup correctly. if (Directory.Exists(Path.Combine(platformFolder, "Build", "Projects")) && File.Exists(Path.Combine(platformFolder, "Build", "Module.xml"))) { var sourceProtobuild = Assembly.GetEntryAssembly().Location; File.Copy(sourceProtobuild, Path.Combine(platformFolder, "Protobuild.exe"), true); try { var chmodStartInfo = new ProcessStartInfo { FileName = "chmod", Arguments = "a+x Protobuild.exe", WorkingDirectory = platformFolder, UseShellExecute = false }; Process.Start(chmodStartInfo); } catch (ExecEnvironment.SelfInvokeExitException) { throw; } catch { } } var file = File.Create(Path.Combine(platformFolder, ".pkg")); file.Close(); file = File.Create(Path.Combine(folder, ".pkg")); file.Close(); RedirectableConsole.WriteLine("Binary resolution complete"); }
public int Execute(Execution execution) { if (!Directory.Exists(execution.PackageSourceFolder)) { throw new InvalidOperationException("The source folder " + execution.PackageSourceFolder + " does not exist."); } if (execution.PackagePlatform == "Template" && execution.PackageFormat != PackageManager.ARCHIVE_FORMAT_NUGET_ZIP) { return(ExecuteForTemplate(execution)); } var allowAutopackage = true; var moduleExpectedPath = Path.Combine(execution.PackageSourceFolder, "Build", "Module.xml"); ModuleInfo rootModule = null; if (!File.Exists(moduleExpectedPath)) { if (execution.PackageFilterFile == null) { RedirectableConsole.WriteLine( "There is no module in the path '" + execution.PackageSourceFolder + "' (expected to " + "find a Build\\Module.xml file within that directory)."); return(1); } else { // We allow this mode if the user has provided a filter file and are constructing // the package manually. allowAutopackage = false; } } else { rootModule = ModuleInfo.Load(moduleExpectedPath); } var temporaryFiles = new List <string>(); try { var customDirectives = new Dictionary <string, Action <FileFilter> >() { { "autopackage", f => { if (allowAutopackage && rootModule != null) { this.m_AutomaticProjectPackager.Autopackage( execution.WorkingDirectory, f, execution, rootModule, execution.PackageSourceFolder, execution.PackagePlatform, execution.PackageFormat, temporaryFiles); } else { RedirectableConsole.WriteLine( "WARNING: There is no module in the path '" + execution.PackageSourceFolder + "' (expected to " + "find a Build\\Module.xml file within that directory). Ignoring the 'autopackage' directive."); } } } }; RedirectableConsole.WriteLine("Starting package creation for " + execution.PackagePlatform); var filter = new FileFilter(_getRecursiveUtilitiesInPath.GetRecursiveFilesInPath(execution.PackageSourceFolder)); if (execution.PackageFilterFile != null) { using (var reader = new StreamReader(execution.PackageFilterFile)) { var contents = reader.ReadToEnd(); contents = contents.Replace("%PLATFORM%", execution.PackagePlatform); using (var inputStream = new MemoryStream(Encoding.ASCII.GetBytes(contents))) { this.m_FileFilterParser.ParseAndApply(filter, inputStream, customDirectives); } } } else { customDirectives["autopackage"](filter); } if (File.Exists(execution.PackageDestinationFile)) { RedirectableConsole.WriteLine("The destination file " + execution.PackageDestinationFile + " already exists; it will be overwritten."); File.Delete(execution.PackageDestinationFile); } filter.ImplyDirectories(); if (execution.PackageFormat != PackageManager.ARCHIVE_FORMAT_NUGET_ZIP) { if (!filter.ContainsTargetPath("Build/")) { RedirectableConsole.WriteLine("ERROR: The Build directory does not exist in the source folder."); if (execution.PackageFilterFile != null) { this.PrintFilterMappings(filter); } return(1); } if (!filter.ContainsTargetPath("Build/Projects/")) { RedirectableConsole.WriteLine("ERROR: The Build\\Projects directory does not exist in the source folder."); if (execution.PackageFilterFile != null) { this.PrintFilterMappings(filter); } return(1); } if (!filter.ContainsTargetPath("Build/Module.xml")) { RedirectableConsole.WriteLine("ERROR: The Build\\Module.xml file does not exist in the source folder."); if (execution.PackageFilterFile != null) { this.PrintFilterMappings(filter); } return(1); } } if (filter.ContainsTargetPath("Protobuild.exe")) { RedirectableConsole.WriteLine("ERROR: The Protobuild.exe file should not be included in the package file."); if (execution.PackageFilterFile != null) { this.PrintFilterMappings(filter); } return(1); } using ( var target = new FileStream(execution.PackageDestinationFile, FileMode.CreateNew, FileAccess.Write, FileShare.None)) { _packageCreator.Create( target, filter, execution.PackageSourceFolder, execution.PackageFormat, execution.PackagePlatform, execution.PackageDestinationFile); } RedirectableConsole.WriteLine("\rPackage written to " + execution.PackageDestinationFile + " successfully."); return(0); } finally { foreach (var file in temporaryFiles) { File.Delete(file); } } }
private byte[] GetBinaryPackage(ICachableBinaryPackageMetadata metadata) { if (metadata.BinaryFormat == null || metadata.BinaryUri == null) { // There is no binary format for this package. return(null); } var localFileExists = false; try { localFileExists = File.Exists(metadata.BinaryUri); } catch { } if (metadata.BinaryFormat != null && localFileExists) { // This is a local package file, read it directly. using (var stream = new FileStream(metadata.BinaryUri, FileMode.Open, FileAccess.Read, FileShare.Read)) { var data = new byte[stream.Length]; stream.Read(data, 0, data.Length); return(data); } } if (this.HasBinaryPackage(metadata)) { // We have it already downloaded in the cache. var file = Path.Combine( _packageCacheConfiguration.GetCacheDirectory(), this.GetPackageName(metadata)); using (var stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read)) { var data = new byte[stream.Length]; stream.Read(data, 0, data.Length); return(data); } } // We must use the package retrieval interface to download a copy // of the package. var tempFile = Path.Combine( _packageCacheConfiguration.GetCacheDirectory(), this.GetPackageName(metadata) + ".tmp"); if (!DownloadBinaryPackage(metadata, tempFile)) { return(null); } var saveFile = Path.Combine( _packageCacheConfiguration.GetCacheDirectory(), this.GetPackageName(metadata)); try { File.Move(tempFile, saveFile); } catch (Exception) { RedirectableConsole.WriteLine("WARNING: Unable to save package to cache."); saveFile = tempFile; } byte[] saveData; using (var stream = new FileStream(saveFile, FileMode.Open, FileAccess.Read, FileShare.Read)) { saveData = new byte[stream.Length]; stream.Read(saveData, 0, saveData.Length); } if (saveFile == tempFile) { File.Delete(tempFile); } return(saveData); }
public void ResolveAll(string workingDirectory, ModuleInfo module, string platform, bool?enableParallelisation, bool forceUpgrade, bool?safeResolve) { if (!_featureManager.IsFeatureEnabled(Feature.PackageManagement)) { return; } RedirectableConsole.WriteLine("Starting resolution of packages for " + platform + "..."); var parallelisation = enableParallelisation ?? _hostPlatformDetector.DetectPlatform() == "Windows"; if (parallelisation) { RedirectableConsole.WriteLine("Enabled parallelisation; use --no-parallel to disable..."); } if (module.Packages != null && module.Packages.Count > 0) { var taskList = new List <Task <Tuple <string, Action> > >(); var resultList = new List <Tuple <string, Action> >(); foreach (var submodule in module.Packages) { if (submodule.IsActiveForPlatform(platform)) { RedirectableConsole.WriteLine("Querying: " + submodule.Uri); var submodule1 = submodule; if (parallelisation) { var task = new Func <Task <Tuple <string, Action> > >(async() => { var metadata = await Task.Run(() => Lookup(workingDirectory, module, submodule1, platform, null, null, forceUpgrade, safeResolve)); if (metadata == null) { return(new Tuple <string, Action>(submodule1.Uri, () => { })); } return(new Tuple <string, Action>(submodule1.Uri, () => { this.Resolve(workingDirectory, metadata, submodule1, null, null, forceUpgrade, safeResolve); })); }); taskList.Add(task()); } else { var metadata = Lookup(workingDirectory, module, submodule1, platform, null, null, forceUpgrade, safeResolve); if (metadata == null) { resultList.Add(new Tuple <string, Action>(submodule1.Uri, () => { })); } else { resultList.Add(new Tuple <string, Action>(submodule1.Uri, () => { this.Resolve(workingDirectory, metadata, submodule1, null, null, forceUpgrade, safeResolve); })); } } } else { RedirectableConsole.WriteLine("Skipping resolution for " + submodule.Uri + " because it is not active for this target platform"); } } if (parallelisation) { var taskArray = taskList.ToArray(); Task.WaitAll(taskArray); foreach (var tuple in taskArray) { RedirectableConsole.WriteLine("Resolving: " + tuple.Result.Item1); tuple.Result.Item2(); } } else { foreach (var tuple in resultList) { RedirectableConsole.WriteLine("Resolving: " + tuple.Item1); tuple.Item2(); } } } foreach (var submodule in module.GetSubmodules(platform)) { if (submodule.Packages.Count == 0 && submodule.GetSubmodules(platform).Length == 0) { if (_featureManager.IsFeatureEnabledInSubmodule(module, submodule, Feature.OptimizationSkipResolutionOnNoPackagesOrSubmodules)) { RedirectableConsole.WriteLine( "Skipping package resolution in submodule for " + submodule.Name + " (there are no submodule or packages)"); continue; } } RedirectableConsole.WriteLine( "Invoking package resolution in submodule for " + submodule.Name); string parallelMode = null; if (_featureManager.IsFeatureEnabledInSubmodule(module, submodule, Feature.TaskParallelisation)) { if (parallelisation) { parallelMode += "-parallel "; } else { parallelMode += "-no-parallel "; } } _moduleExecution.RunProtobuild( submodule, _featureManager.GetFeatureArgumentToPassToSubmodule(module, submodule) + parallelMode + "-resolve " + platform + " " + packageRedirector.GetRedirectionArguments()); RedirectableConsole.WriteLine( "Finished submodule package resolution for " + submodule.Name); } RedirectableConsole.WriteLine("Package resolution complete."); }
private void ExtractTo(string workingDirectory, string packageName, string format, byte[] data, string path, string platform) { RedirectableConsole.WriteLine("Unpacking binary package from " + format + " archive"); switch (format) { case PackageManager.ARCHIVE_FORMAT_TAR_GZIP: { using (var memory = new MemoryStream(data)) { using (var decompress = new GZipStream(memory, CompressionMode.Decompress)) { using (var memory2 = new MemoryStream()) { decompress.CopyTo(memory2); memory2.Seek(0, SeekOrigin.Begin); var reader = new tar_cs.TarReader(memory2); var reduplicator = new Reduplicator(); reduplicator.UnpackTarToFolder(reader, path); } } } break; } case PackageManager.ARCHIVE_FORMAT_TAR_LZMA: { using (var inMemory = new MemoryStream(data)) { using (var outMemory = new MemoryStream()) { LZMA.LzmaHelper.Decompress(inMemory, outMemory); outMemory.Seek(0, SeekOrigin.Begin); var reader = new tar_cs.TarReader(outMemory); var reduplicator = new Reduplicator(); reduplicator.UnpackTarToFolder(reader, path); } } break; } case PackageManager.ARCHIVE_FORMAT_NUGET_ZIP: { using (var inMemory = new MemoryStream(data)) { using (var zipStorer = ZipStorer.Open(inMemory, FileAccess.Read, true)) { var reduplicator = new Reduplicator(); var extractedFiles = reduplicator.UnpackZipToFolder( zipStorer, path, candidatePath => candidatePath.Replace('\\', '/').StartsWith("protobuild/" + platform + "/"), outputPath => outputPath.Replace('\\', '/').Substring(("protobuild/" + platform + "/").Length)); if (extractedFiles.Count == 0) { // There were no files that matched protobuild/ which means this is // not a Protobuild-aware NuGet package. We need to convert it on-the-fly // to a compatible Protobuild format. ConvertNuGetOnlyPackage(reduplicator, zipStorer, path, packageName, workingDirectory, platform); } } } break; } default: throw new InvalidOperationException( "This version of Protobuild does not support the " + format + " package format."); } }
/// <summary> /// The entry point for Protobuild. /// </summary> /// <param name="args">The arguments passed in on the command line.</param> public static void Main(string workingDirectory, string[] args) { // Ensure we always use the invariant culture in Protobuild. Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; // Set our SSL trust policy. Because Mono doesn't ship with root certificates // on most Linux distributions, we have to be a little more insecure here than // I'd like. For protobuild.org we always verify that the root of the certificate // chain matches what we expect (so people can't forge a certificate from a // *different CA*), but for other domains we just have to implicitly trust them // on Linux since we have no root store. if (Path.DirectorySeparatorChar == '/' && !Directory.Exists("/Library")) { ServicePointManager.ServerCertificateValidationCallback = SSLValidationForLinux; } var kernel = new LightweightKernel(); kernel.BindCore(); kernel.BindBuildResources(); kernel.BindGeneration(); kernel.BindJSIL(); kernel.BindTargets(); kernel.BindFileFilter(); kernel.BindPackages(); kernel.BindAutomatedBuild(); var featureManager = kernel.Get <IFeatureManager>(); featureManager.LoadFeaturesFromDirectory(workingDirectory); var commandMappings = new Dictionary <string, ICommand> { { "sync", kernel.Get <SyncCommand>() }, { "resync", kernel.Get <ResyncCommand>() }, { "generate", kernel.Get <GenerateCommand>() }, { "build", kernel.Get <BuildCommand>() }, { "build-target", kernel.Get <BuildTargetCommand>() }, { "build-property", kernel.Get <BuildPropertyCommand>() }, { "build-process-arch", kernel.Get <BuildProcessArchCommand>() }, { "clean", kernel.Get <CleanCommand>() }, { "automated-build", kernel.Get <AutomatedBuildCommand>() }, { "extract-xslt", kernel.Get <ExtractXSLTCommand>() }, { "enable", kernel.Get <EnableServiceCommand>() }, { "disable", kernel.Get <DisableServiceCommand>() }, { "debug-service-resolution", kernel.Get <DebugServiceResolutionCommand>() }, { "debug-project-generation", kernel.Get <DebugProjectGenerationCommand>() }, { "simulate-host-platform", kernel.Get <SimulateHostPlatformCommand>() }, { "spec", kernel.Get <ServiceSpecificationCommand>() }, { "query-features", kernel.Get <QueryFeaturesCommand>() }, { "features", kernel.Get <FeaturesCommand>() }, { "add", kernel.Get <AddPackageCommand>() }, { "remove", kernel.Get <RemovePackageCommand>() }, { "list", kernel.Get <ListPackagesCommand>() }, { "install", kernel.Get <InstallPackageCommand>() }, { "upgrade", kernel.Get <UpgradePackageCommand>() }, { "upgrade-all", kernel.Get <UpgradeAllPackagesCommand>() }, { "pack", kernel.Get <PackPackageCommand>() }, { "format", kernel.Get <FormatPackageCommand>() }, { "push", kernel.Get <PushPackageCommand>() }, { "ignore-on-existing", kernel.Get <IgnoreOnExistingPackageCommand>() }, { "repush", kernel.Get <RepushPackageCommand>() }, { "resolve", kernel.Get <ResolveCommand>() }, { "no-resolve", kernel.Get <NoResolveCommand>() }, { "safe-resolve", kernel.Get <SafeResolveCommand>() }, { "parallel", kernel.Get <ParallelCommand>() }, { "no-parallel", kernel.Get <NoParallelCommand>() }, { "redirect", kernel.Get <RedirectPackageCommand>() }, { "swap-to-source", kernel.Get <SwapToSourceCommand>() }, { "swap-to-binary", kernel.Get <SwapToBinaryCommand>() }, { "start", kernel.Get <StartCommand>() }, { "no-generate", kernel.Get <NoGenerateCommand>() }, { "no-host-generate", kernel.Get <NoHostGenerateCommand>() }, { "execute", kernel.Get <ExecuteCommand>() }, { "execute-configuration", kernel.Get <ExecuteConfigurationCommand>() }, { "package-id", kernel.Get <PackageIdCommand>() }, { "package-git-repo", kernel.Get <PackageGitRepoCommand>() }, { "package-git-commit", kernel.Get <PackageGitCommitCommand>() }, { "package-type", kernel.Get <PackageTypeCommand>() }, }; var execution = new Execution(); execution.WorkingDirectory = workingDirectory; execution.CommandToExecute = kernel.Get <DefaultCommand>(); var options = new Options(); foreach (var kv in commandMappings) { var key = kv.Key; var value = kv.Value; Action <string[]> handle = x => { if (value.IsRecognised()) { value.Encounter(execution, x); } else if (value.IsIgnored()) { } else { throw new InvalidOperationException("Unknown argument '" + key + "'"); } }; if (value.GetArgCount() == 0) { options[key] = handle; } else { options[key + "@" + value.GetArgCount()] = handle; } } Action <string[]> helpAction = x => { PrintHelp(commandMappings); ExecEnvironment.Exit(0); }; options["help"] = helpAction; options["?"] = helpAction; if (ExecEnvironment.DoNotWrapExecutionInTry) { options.Parse(args); } else { try { options.Parse(args); } catch (InvalidOperationException ex) { RedirectableConsole.WriteLine(ex.Message); PrintHelp(commandMappings); ExecEnvironment.Exit(1); } } featureManager.ValidateEnabledFeatures(); if (ExecEnvironment.DoNotWrapExecutionInTry) { var exitCode = execution.CommandToExecute.Execute(execution); ExecEnvironment.Exit(exitCode); } else { try { var exitCode = execution.CommandToExecute.Execute(execution); ExecEnvironment.Exit(exitCode); } catch (ExecEnvironment.SelfInvokeExitException) { throw; } catch (Exception ex) { RedirectableConsole.WriteLine(ex); ExecEnvironment.Exit(1); } } }
private void ConvertNuGetOnlyPackage(Reduplicator reduplicator, ZipStorer zipStorer, string path, string packageName, string workingDirectory, string platform) { var folder = Path.GetTempFileName(); File.Delete(folder); Directory.CreateDirectory(folder); try { reduplicator.UnpackZipToFolder( zipStorer, folder, candidatePath => true, outputPath => outputPath); var references = new List <string>(); var libraryReferences = new Dictionary <string, string>(); var packageDependencies = new Dictionary <string, string>(); // Load NuGet specification file. var specFile = Directory.GetFiles(folder, "*.nuspec").FirstOrDefault(); if (specFile != null) { specFile = Path.Combine(folder, specFile); if (File.Exists(specFile)) { var packageDoc = new XmlDocument(); packageDoc.Load(specFile); if (packageDoc?.DocumentElement != null) { // If we have an id in the package, that forms the package name. if (packageDoc.DocumentElement.FirstChild.ChildNodes.OfType <XmlElement>() .Count(x => x.Name == "id") > 0) { var newName = packageDoc.DocumentElement.FirstChild.ChildNodes.OfType <XmlElement>() .First(x => x.Name == "id").InnerText.Trim(); if (!string.IsNullOrWhiteSpace(newName)) { packageName = newName; } } // If the references are explicitly provided in the nuspec, use // those as to what files should be referenced by the projects. if (packageDoc.DocumentElement.FirstChild.ChildNodes.OfType <XmlElement>() .Count(x => x.Name == "references") > 0) { references = packageDoc.DocumentElement.FirstChild.ChildNodes.OfType <XmlElement>() .First(x => x.Name == "references") .ChildNodes.OfType <XmlElement>() .Where(x => x.Name == "reference") .Select(x => x.Attributes["file"].Value) .ToList(); } // If there are dependencies specified, store them and convert them to // Protobuild references, and reference them in the Module.xml file. if (packageDoc.DocumentElement.FirstChild.ChildNodes.OfType <XmlElement>() .Count(x => x.Name == "dependencies") > 0) { packageDependencies = packageDoc.DocumentElement.FirstChild.ChildNodes.OfType <XmlElement>() .First(x => x.Name == "dependencies") .ChildNodes.OfType <XmlElement>() .Where(x => x.Name == "dependency") .ToDictionarySafe( k => k.Attributes["id"].Value, v => v.Attributes["version"].Value, (dict, c) => RedirectableConsole.WriteLine("WARNING: More than one dependency on " + c + " in NuGet package.")); } } } } if (string.IsNullOrWhiteSpace(packageName)) { throw new InvalidOperationException("Expected package name when converting NuGet-only package!"); } // Determine the priority of the frameworks that we want to target // out of the available versions. string[] clrNames = _nugetPlatformMapping.GetFrameworkNamesForRead(workingDirectory, platform); var referenceDirectories = new string[] { "ref", "lib" }; foreach (var directory in referenceDirectories) { // Determine the base path for all references; that is, the lib/ folder. var referenceBasePath = Path.Combine( folder, directory); if (Directory.Exists(referenceBasePath)) { // If no references are in nuspec, reference all of the libraries that // are on disk. if (references.Count == 0) { // Search through all of the target frameworks until we find one that // has at least one file in it. foreach (var clrNameOriginal in clrNames) { var clrName = clrNameOriginal; var foundClr = false; if (clrName[0] == '=') { // Exact match (strip the equals). clrName = clrName.Substring(1); // If this target framework doesn't exist for this library, skip it. var dirPath = Path.Combine( referenceBasePath, clrName); if (!Directory.Exists(dirPath)) { continue; } } else if (clrName[0] == '?') { // Substring, search the reference base path for any folders // with a matching substring. clrName = clrName.Substring(1); var baseDirPath = referenceBasePath; var found = false; foreach (var subdir in new DirectoryInfo(baseDirPath).GetDirectories()) { if (subdir.Name.Contains(clrName)) { clrName = subdir.Name; found = true; break; } } if (!found) { continue; } } else { throw new InvalidOperationException("Unknown CLR name match type with '" + clrName + "'"); } // Otherwise enumerate through all of the libraries in this folder. foreach (var dll in Directory.EnumerateFiles( Path.Combine( referenceBasePath, clrName), "*.dll")) { // Determine the relative path to the library. var packageDll = Path.Combine( referenceBasePath, clrName, Path.GetFileName(dll)); // Confirm again that the file actually exists on disk when // combined with the root path. if (File.Exists( Path.Combine( packageDll))) { // Create the library reference. if (!libraryReferences.ContainsKey(Path.GetFileNameWithoutExtension(dll))) { libraryReferences.Add( Path.GetFileNameWithoutExtension(dll), packageDll); } // Mark this target framework as having provided at least // one reference. foundClr = true; } } // Break if we have found at least one reference. if (foundClr) { break; } } } // For all of the references that were found in the original nuspec file, // add those references. foreach (var reference in references) { // Search through all of the target frameworks until we find the one // that has the reference in it. foreach (var clrName in clrNames) { // If this target framework doesn't exist for this library, skip it. var packageDll = Path.Combine( referenceBasePath, clrName, reference); if (File.Exists( Path.Combine( packageDll))) { if (!libraryReferences.ContainsKey(Path.GetFileNameWithoutExtension(packageDll))) { libraryReferences.Add( Path.GetFileNameWithoutExtension(packageDll), packageDll); } break; } } } } } foreach (var kv in libraryReferences) { RedirectableConsole.WriteLine("Found library to reference: " + kv.Key + " (at " + kv.Value + ")"); } RedirectableConsole.WriteLine("Generating external project reference..."); var document = new XmlDocument(); var externalProject = document.CreateElement("ExternalProject"); externalProject.SetAttribute("Name", packageName); document.AppendChild(externalProject); foreach (var kv in libraryReferences) { var binaryReference = document.CreateElement("Binary"); binaryReference.SetAttribute("Name", kv.Key); binaryReference.SetAttribute("Path", kv.Value.Substring(folder.Length).TrimStart(new[] { '/', '\\' }).Replace("%2B", "-")); externalProject.AppendChild(binaryReference); } foreach (var package in packageDependencies) { var externalReference = document.CreateElement("Reference"); externalReference.SetAttribute("Include", package.Key); externalProject.AppendChild(externalReference); } Directory.CreateDirectory(Path.Combine(path, "Build", "Projects")); document.Save(Path.Combine(path, "Build", "Projects", packageName + ".definition")); RedirectableConsole.WriteLine("Generating module..."); var generatedModule = new ModuleInfo(); generatedModule.Name = packageName; generatedModule.Packages = new List <PackageRef>(); foreach (var package in packageDependencies) { generatedModule.Packages.Add(new PackageRef { Uri = "https-nuget-v3://api.nuget.org/v3/index.json|" + package.Key, GitRef = package.Value.TrimStart('[').TrimEnd(']'), Folder = package.Key }); } generatedModule.Save(Path.Combine(path, "Build", "Module.xml")); foreach (var kv in libraryReferences) { var targetFile = new FileInfo(Path.Combine(path, kv.Value.Substring(folder.Length).Replace('\\', '/').TrimStart('/').Replace("%2B", "-"))); targetFile.Directory.Create(); File.Copy(kv.Value, targetFile.FullName); } } finally { PathUtils.AggressiveDirectoryDelete(folder); } }
private static void PrintHelp(Dictionary <string, ICommand> commandMappings, bool extendedHelp = false) { RedirectableConsole.WriteLine("Protobuild v" + ProtobuildVersion.SemanticVersion); RedirectableConsole.WriteLine(" built from " + ProtobuildVersion.BuildCommit); RedirectableConsole.WriteLine(" built at " + ProtobuildVersion.BuildDate); if (ProtobuildVersion.BuiltWithPendingChanges) { RedirectableConsole.WriteLine(" working directory had pending changes when built!"); } RedirectableConsole.WriteLine(); RedirectableConsole.WriteLine("Usage: Protobuild.exe [options]"); RedirectableConsole.WriteLine(); RedirectableConsole.WriteLine("By default Protobuild resynchronises or generates projects for the current platform, depending on the module configuration."); var shortDescs = new List <Tuple <string, string, string> >(); foreach (var kv in commandMappings) { if (kv.Value.IsInternal() || !kv.Value.IsRecognised() || kv.Value.IsIgnored()) { continue; } if (!extendedHelp) { if (kv.Value.GetShortCategory() == "Internal use") { continue; } var shortDescription = kv.Value.GetShortDescription(); var argDesc = string.Empty; foreach (var arg in kv.Value.GetShortArgNames()) { if (arg.EndsWith("?")) { argDesc += " [" + arg.TrimEnd('?') + "]"; } else { argDesc += " " + arg; } } shortDescs.Add(new Tuple <string, string, string>(kv.Value.GetShortCategory(), "--" + kv.Key + argDesc, shortDescription)); } else { var description = kv.Value.GetDescription(); description = description.Replace("\n", " "); description = description.Replace("\r", ""); var lines = new List <string>(); var wordBuffer = string.Empty; var lineBuffer = string.Empty; var count = 0; var last = false; for (var i = 0; i < description.Length || wordBuffer.Length > 0; i++) { if (i < description.Length) { if (description[i] == ' ') { if (wordBuffer.Length > 0) { lineBuffer += wordBuffer + " "; } wordBuffer = string.Empty; } else { wordBuffer += description[i]; count++; } } else { lineBuffer += wordBuffer + " "; count++; last = true; } if (count >= 74) { lines.Add(lineBuffer); lineBuffer = string.Empty; count = 0; } if (last) { break; } } if (count > 0) { lines.Add(lineBuffer); lineBuffer = string.Empty; } var argDesc = string.Empty; foreach (var arg in kv.Value.GetArgNames()) { if (arg.EndsWith("?")) { argDesc += " [" + arg.TrimEnd('?') + "]"; } else { argDesc += " " + arg; } } RedirectableConsole.WriteLine(" -" + kv.Key + argDesc); RedirectableConsole.WriteLine(); foreach (var line in lines) { RedirectableConsole.WriteLine(" " + line); } RedirectableConsole.WriteLine(); } } if (!extendedHelp) { var bufferWidth = 120; try { // This won't work if input is redirected or running under Git Bash on Windows. bufferWidth = Console.BufferWidth; } catch { } var maxArgs = shortDescs.Max(x => x.Item2.Length); if (bufferWidth - (maxArgs + 6) - 6 <= 43) { // Use the format where the description is underneath each command. RedirectableConsole.WriteLine(); foreach (var kvg in shortDescs.GroupBy(x => x.Item1)) { RedirectableConsole.WriteLine(("____ " + kvg.Key + " ").PadRight(bufferWidth - 4, '_')); RedirectableConsole.WriteLine(); foreach (var kv in kvg) { var description = kv.Item3; var lines = new List <string>(); var wordBuffer = string.Empty; var lineBuffer = string.Empty; var last = false; for (var i = 0; i < description.Length || wordBuffer.Length > 0; i++) { if (i < description.Length) { if (description[i] == ' ') { if (wordBuffer.Length > 0) { lineBuffer += wordBuffer + " "; } wordBuffer = string.Empty; } else { wordBuffer += description[i]; } } else { lineBuffer += wordBuffer + " "; last = true; } if (lineBuffer.Length + 1 + wordBuffer.Length >= bufferWidth - 8) { lines.Add(lineBuffer); lineBuffer = string.Empty; } if (last) { break; } } if (lineBuffer.Length > 0) { lines.Add(lineBuffer); lineBuffer = string.Empty; } RedirectableConsole.WriteLine(" " + kv.Item2.PadRight(maxArgs + 4)); RedirectableConsole.WriteLine(); for (var i = 0; i < lines.Count; i++) { RedirectableConsole.WriteLine(" " + lines[i]); } RedirectableConsole.WriteLine(); RedirectableConsole.WriteLine(); } } } else { // Use the format where the description is side-by-side with each command. foreach (var kvg in shortDescs.GroupBy(x => x.Item1)) { RedirectableConsole.WriteLine(); RedirectableConsole.WriteLine(kvg.Key + ":"); foreach (var kv in kvg) { var description = kv.Item3; var lines = new List <string>(); var wordBuffer = string.Empty; var lineBuffer = string.Empty; var last = false; for (var i = 0; i < description.Length || wordBuffer.Length > 0; i++) { if (i < description.Length) { if (description[i] == ' ') { if (wordBuffer.Length > 0) { lineBuffer += wordBuffer + " "; } wordBuffer = string.Empty; } else { wordBuffer += description[i]; } } else { lineBuffer += wordBuffer + " "; last = true; } if (lineBuffer.Length + 1 + wordBuffer.Length >= bufferWidth - (maxArgs + 6) - 6) { lines.Add(lineBuffer); lineBuffer = string.Empty; } if (last) { break; } } if (lineBuffer.Length > 0) { lines.Add(lineBuffer); lineBuffer = string.Empty; } RedirectableConsole.WriteLine(" " + kv.Item2.PadRight(maxArgs + 4) + (lines.Count > 0 ? lines[0] : "")); for (var i = 1; i < lines.Count; i++) { RedirectableConsole.WriteLine(" " + "".PadRight(maxArgs + 4) + lines[i]); } } } } } }
public void GenerateInfoPListIfNeeded(List <LoadedDefinitionInfo> definitions, DefinitionInfo definition, XmlDocument project, string platform) { if (platform == "iOS" || platform == "MacOS") { var documentsByName = definitions.ToDictionarySafe( k => k.Definition.Name, v => v, (dict, x) => { var existing = dict[x.Definition.Name]; var tried = x; RedirectableConsole.WriteLine("WARNING: There is more than one project with the name " + x.Definition.Name + " (first project loaded from " + tried.Definition.AbsolutePath + ", " + "skipped loading second project from " + existing.Definition.AbsolutePath + ")"); }) .ToDictionary(k => k.Key, v => v.Value.Project); var type = project.DocumentElement.GetAttribute("Type"); if (type == "Console" || type == "App") { var references = project.DocumentElement.SelectNodes("References/*").OfType <XmlElement>(); var referencesArray = references.Select(x => x.GetAttribute("Include")).ToArray(); foreach (var reference in references) { var lookup = definitions.FirstOrDefault(x => x.Definition.Name == reference.GetAttribute("Include")); if (lookup != null) { if (lookup.Definition.Type == "Include") { if (project.DocumentElement.SelectSingleNode("Files/*[@Include='Info.plist']") == null) { return; } } else if (lookup.Definition.Type == "External") { var referencedDocument = documentsByName[lookup.Definition.Name]; if (referencedDocument.DocumentElement.LocalName == "ExternalProject") { // Find all top-level references in the external project. var externalDocumentReferences = referencedDocument.SelectNodes("/ExternalProject/Reference").OfType <XmlElement>().Concat( referencedDocument.SelectNodes("/ExternalProject/Platform[@Type='" + platform + "']/Reference").OfType <XmlElement>()).ToList(); foreach (var externalReference in externalDocumentReferences) { lookup = definitions.FirstOrDefault(x => x.Definition.Name == externalReference.GetAttribute("Include")); if (lookup != null) { if (lookup.Definition.Type == "Include") { if (project.DocumentElement.SelectSingleNode("Files/*[@Include='Info.plist']") == null) { return; } } } } } } } } if (project.DocumentElement.SelectSingleNode("Files/*[@Include='Info.plist']") == null) { // We need to generate an Info.plist file for iOS and Mac; we do this // just with a default Info.plist file which is enough for projects // to compile. var infoPListPath = Path.Combine(definition.AbsolutePath, "Info.plist").Replace(Path.DirectorySeparatorChar == '/' ? '\\' : '/', Path.DirectorySeparatorChar); if (!File.Exists(infoPListPath)) { var contents = @" <?xml version=""1.0"" encoding=""UTF-8""?> <!DOCTYPE plist PUBLIC ""-//Apple//DTD PLIST 1.0//EN"" ""http://www.apple.com/DTDs/PropertyList-1.0.dtd""> <plist version=""1.0""> <dict> <key>CFBundleDisplayName</key> <string></string> <key>CFBundleIdentifier</key> <string></string> <key>MinimumOSVersion</key> <string>10.2</string> <key>UIDeviceFamily</key> <array> <integer>1</integer> </array> <key>CFBundleShortVersionString</key> <string></string> <key>CFBundleVersion</key> <string></string> <key>UISupportedInterfaceOrientations</key> <array/> </dict> </plist> ".Trim(); using (var writer = new StreamWriter(infoPListPath)) { writer.Write(contents); } } var files = project.DocumentElement.SelectSingleNode("Files"); var infoPList = project.CreateElement("None"); infoPList.SetAttribute("Include", "Info.plist"); var platforms = project.CreateElement("Platforms"); platforms.InnerText = "iOS,MacOS"; infoPList.AppendChild(platforms); files.AppendChild(infoPList); } } } else if (platform == "Android" || platform == "Ouya") { var type = project.DocumentElement.GetAttribute("Type"); var files = project.DocumentElement.SelectSingleNode("Files"); Directory.CreateDirectory( Path.Combine(definition.AbsolutePath, "Resources") .Replace(Path.DirectorySeparatorChar == '/' ? '\\' : '/', Path.DirectorySeparatorChar)); if (type == "Console" || type == "App") { Directory.CreateDirectory( Path.Combine(definition.AbsolutePath, "Properties") .Replace(Path.DirectorySeparatorChar == '/' ? '\\' : '/', Path.DirectorySeparatorChar)); // We need to generate an AndroidManifest.xml file; we do this just with // a default AndroidManifest file which is enough for projects to compile. var manifestPath = Path.Combine(definition.AbsolutePath, "Properties\\AndroidManifest.xml") .Replace(Path.DirectorySeparatorChar == '/' ? '\\' : '/', Path.DirectorySeparatorChar); if (!File.Exists(manifestPath)) { var contents = (@" <?xml version=""1.0"" encoding=""utf-8""?> <manifest xmlns:android=""http://schemas.android.com/apk/res/android"" package=""" + definition.Name + @""" android:versionCode=""1"" android:versionName=""1.0""> <uses-sdk /> <application android:label=""" + definition.Name + @"""></application> </manifest> ").Trim(); using (var writer = new StreamWriter(manifestPath)) { writer.Write(contents); } } if (files != null) { var manifestNode = project.DocumentElement.SelectSingleNode( "Files/*[@Include='Properties\\AndroidManifest.xml']"); if (manifestNode != null && manifestNode.Name != "Content" && manifestNode.ParentNode != null) { manifestNode.ParentNode.RemoveChild(manifestNode); manifestNode = null; } if (manifestNode == null) { var manifest = project.CreateElement("Content"); manifest.SetAttribute("Include", "Properties\\AndroidManifest.xml"); var platforms = project.CreateElement("Platforms"); platforms.InnerText = "Android,Ouya"; manifest.AppendChild(platforms); files.AppendChild(manifest); } } } // We need to generate an empty Resources\Resource.Designer.cs file; we do this just with // a default Resource.Designer.cs file which is enough for projects to compile. var resourcePath = Path.Combine(definition.AbsolutePath, "Resources\\Resource.Designer.cs").Replace(Path.DirectorySeparatorChar == '/' ? '\\' : '/', Path.DirectorySeparatorChar); if (!File.Exists(resourcePath)) { var contents = string.Empty; using (var writer = new StreamWriter(resourcePath)) { writer.Write(contents); } } if (files != null) { var resourceNode = project.DocumentElement.SelectSingleNode( "Files/*[@Include='Resources\\Resource.Designer.cs']"); if (resourceNode != null && resourceNode.Name != "Compile" && resourceNode.ParentNode != null) { resourceNode.ParentNode.RemoveChild(resourceNode); resourceNode = null; } if (resourceNode == null) { var resource = project.CreateElement("Compile"); resource.SetAttribute("Include", "Resources\\Resource.Designer.cs"); var platforms = project.CreateElement("Platforms"); platforms.InnerText = "Android,Ouya"; resource.AppendChild(platforms); files.AppendChild(resource); } } } }