private void AutomaticallyPackageIncludeProject( DefinitionInfo[] definitions, List <Service> services, FileFilter fileFilter, string rootPath, string platform, DefinitionInfo definition) { // Include the include project's definition. fileFilter.ApplyInclude("^" + Regex.Escape("Build/Projects/" + definition.Name + ".definition") + "$"); // Include everything underneath the include project's path. fileFilter.ApplyInclude("^" + Regex.Escape(definition.RelativePath) + ".+$"); }
public void ParseAndApply(FileFilter result, Stream inputFilterFile, Dictionary <string, Action <FileFilter> > customDirectives) { var isSlashed = false; Action init = () => { isSlashed = false; }; Func <char, bool> splitter = c => { if (c == '\\') { isSlashed = true; return(false); } if (c == ' ' && !isSlashed) { return(true); } isSlashed = false; return(false); }; using (var reader = new StreamReader(inputFilterFile)) { while (!reader.EndOfStream) { var line = reader.ReadLine(); if (line.TrimStart().StartsWith("#") || line.Trim() == "") { continue; } var mode = line.Split(splitter, 2).ToStringArray()[0]; switch (mode) { case "include": result.ApplyInclude(line.Init(init).Split(splitter, 2).ToStringArray()[1]); break; case "exclude": result.ApplyExclude(line.Init(init).Split(splitter, 2).ToStringArray()[1]); break; case "rewrite": result.ApplyRewrite(line.Init(init).Split(splitter, 3).ToStringArray()[1], line.Split(splitter, 3).ToStringArray()[2]); break; default: if (customDirectives.ContainsKey(mode)) { customDirectives[mode](result); } break; } } } return; }
private void AutomaticallyPackageNormalProject( DefinitionInfo[] definitions, List <Service> services, FileFilter fileFilter, string rootPath, string platform, DefinitionInfo definition) { var document = XDocument.Load(definition.DefinitionPath); var platformSpecificOutputFolderElement = document.XPathSelectElement("/Project/Properties/PlatformSpecificOutputFolder"); var projectSpecificOutputFolderElement = document.XPathSelectElement("/Project/Properties/ProjectSpecificOutputFolder"); var assemblyNameForPlatformElement = document.XPathSelectElement("/Project/Properties/AssemblyName/Platform[@Name=\"" + platform + "\"]"); var assemblyNameGlobalElement = document.XPathSelectElement("/Project/Properties/Property[@Name=\"AssemblyName\"]"); var platformSpecificOutputFolder = true; var projectSpecificOutputFolder = false; if (platformSpecificOutputFolderElement != null) { platformSpecificOutputFolder = platformSpecificOutputFolderElement.Value.ToLowerInvariant() != "false"; } if (projectSpecificOutputFolderElement != null) { projectSpecificOutputFolder = projectSpecificOutputFolderElement.Value.ToLowerInvariant() == "true"; } string assemblyName = null; if (assemblyNameForPlatformElement != null) { assemblyName = assemblyNameForPlatformElement.Value; } else if (assemblyNameGlobalElement != null) { assemblyName = assemblyNameGlobalElement.Value; } else { assemblyName = definition.Name; } var assemblyFilesToCopy = new[] { assemblyName + ".exe", assemblyName + ".dll", assemblyName + ".dll.config", assemblyName + ".dll.mdb", assemblyName + ".pdb", assemblyName + ".xml", }; var outputMode = OutputPathMode.BinConfiguration; if (projectSpecificOutputFolder) { outputMode = OutputPathMode.BinProjectPlatformArchConfiguration; } if (platformSpecificOutputFolder) { outputMode = OutputPathMode.BinPlatformArchConfiguration; } var externalProjectDocument = new XmlDocument(); externalProjectDocument.AppendChild(externalProjectDocument.CreateXmlDeclaration("1.0", "UTF-8", null)); var externalProject = externalProjectDocument.CreateElement("ExternalProject"); externalProjectDocument.AppendChild(externalProject); externalProject.SetAttribute("Name", definition.Name); // Copy the assembly itself out to the package. switch (outputMode) { case OutputPathMode.BinConfiguration: { // In this configuration, we only ship the binaries for // the default architecture (because that's all we know // about). We also have to assume the binary folder // contains binaries for the desired platform. var pathPrefix = definition.Path.Replace('\\', '/').Replace(".", "\\.") + "/bin/([^/]+)/"; if (definition.Type == "Library") { // For libraries, we only copy the assembly (and immediately related files) // into it's directory. Native binaries will be expressed through the ExternalProject. foreach (var assemblyFile in assemblyFilesToCopy) { var includeMatch = fileFilter.ApplyInclude("^" + pathPrefix + Regex.Escape(assemblyFile) + "$"); var rewriteMatch = fileFilter.ApplyRewrite("^" + pathPrefix + Regex.Escape(assemblyFile) + "$", definition.Name + "/AnyCPU/" + assemblyFile); if (includeMatch && rewriteMatch) { if (assemblyFile.EndsWith(".dll")) { var binaryEntry = externalProjectDocument.CreateElement("Binary"); binaryEntry.SetAttribute("Name", assemblyFile.Substring(0, assemblyFile.Length - 4)); binaryEntry.SetAttribute("Path", definition.Name + "\\AnyCPU\\" + assemblyFile); externalProject.AppendChild(binaryEntry); } else if (assemblyFile.EndsWith(".dll.config")) { var configEntry = externalProjectDocument.CreateElement("NativeBinary"); configEntry.SetAttribute("Path", definition.Name + "\\AnyCPU\\" + assemblyFile); externalProject.AppendChild(configEntry); } } else if (includeMatch || rewriteMatch) { throw new InvalidOperationException("Automatic filter; only one rule matched."); } } } else { // For executables, we ship everything in the output directory, because we // want the executables to be able to run from the package directory. fileFilter.ApplyInclude("^" + pathPrefix + "(.+)$"); fileFilter.ApplyRewrite("^" + pathPrefix + "(.+)$", definition.Name + "/AnyCPU/$2"); } break; } case OutputPathMode.BinPlatformArchConfiguration: { // In this configuration, we ship binaries for AnyCPU, iPhone or all .NET architectures // depending on whether or not the platform produces multiple architectures. On Mono, // we can't use $(Platform) within a reference's path, so we have to keep this path static // for Mono platforms. string pathArchMatch, pathArchReplace, pathArchRuntime; switch (platform.ToLowerInvariant()) { case "ios": { pathArchMatch = "iPhone"; pathArchReplace = "iPhone"; pathArchRuntime = "iPhone"; break; } case "windowsphone": { pathArchMatch = "([^/]+)"; pathArchReplace = "$1"; pathArchRuntime = "$(Platform)"; break; } default: { pathArchMatch = "AnyCPU"; pathArchReplace = "AnyCPU"; pathArchRuntime = "AnyCPU"; break; } } var pathPrefix = definition.Path.Replace('\\', '/').Replace(".", "\\.") + "/bin/" + platform + "/" + pathArchMatch + "/([^/]+)/"; if (definition.Type == "Library") { // For libraries, we only copy the assembly (and immediately related files) // into it's directory. Native binaries will be expressed through the ExternalProject. foreach (var assemblyFile in assemblyFilesToCopy) { var includeMatch = fileFilter.ApplyInclude("^" + pathPrefix + Regex.Escape(assemblyFile) + "$"); var rewriteMatch = fileFilter.ApplyRewrite("^" + pathPrefix + Regex.Escape(assemblyFile) + "$", definition.Name + "/" + pathArchReplace + "/" + assemblyFile); if (includeMatch && rewriteMatch) { if (assemblyFile.EndsWith(".dll")) { var binaryEntry = externalProjectDocument.CreateElement("Binary"); binaryEntry.SetAttribute("Name", assemblyFile.Substring(0, assemblyFile.Length - 4)); binaryEntry.SetAttribute("Path", definition.Name + "\\" + pathArchRuntime + "\\" + assemblyFile); externalProject.AppendChild(binaryEntry); } else if (assemblyFile.EndsWith(".dll.config")) { var configEntry = externalProjectDocument.CreateElement("NativeBinary"); configEntry.SetAttribute("Path", definition.Name + "\\" + pathArchRuntime + "\\" + assemblyFile); externalProject.AppendChild(configEntry); } } else if (includeMatch || rewriteMatch) { throw new InvalidOperationException("Automatic filter; only one rule matched."); } } } else { // For executables, we ship everything in the output directory, because we // want the executables to be able to run from the package directory. fileFilter.ApplyInclude("^" + pathPrefix + "(.+)$"); if (pathArchMatch == "([^/]+)") { fileFilter.ApplyRewrite("^" + pathPrefix + "(.+)$", definition.Name + "/" + pathArchReplace + "/$3"); } else { fileFilter.ApplyRewrite("^" + pathPrefix + "(.+)$", definition.Name + "/" + pathArchReplace + "/$2"); } } break; } case OutputPathMode.BinProjectPlatformArchConfiguration: { throw new NotSupportedException(); break; } } // Convert all of the known references into references within the external project. var definitionsByName = definitions.ToDictionary(k => k.Name, v => v); foreach (var reference in document.XPathSelectElements("/Project/References/Reference")) { var includeAttribute = reference.Attribute(XName.Get("Include")); if (includeAttribute != null) { if (definitionsByName.ContainsKey(includeAttribute.Value)) { // This reference will be converted to an external project, // so add a reference to it (in case it contains native binaries // which need to be copied out). var referenceEntry = externalProjectDocument.CreateElement("Reference"); referenceEntry.SetAttribute("Include", includeAttribute.Value); externalProject.AppendChild(referenceEntry); } } } // Copy out any files that are marked with copy-on-build flag. var detector = new PlatformAndServiceActiveDetection(); var xmlDocument = new XmlDocument(); xmlDocument.Load(definition.DefinitionPath); var servicesInput = this.m_ServiceInputGenerator.Generate(xmlDocument, definition.Name, services); var activeServicesElement = servicesInput.ChildNodes.OfType <XmlElement>().FirstOrDefault(x => x.LocalName == "ActiveServicesNames"); var activeServices = activeServicesElement.InnerText; foreach (var file in document.XPathSelectElements("/Project/Files/*")) { var copyOnBuild = file.XPathSelectElement("CopyToOutputDirectory"); if (copyOnBuild == null) { continue; } if (copyOnBuild.Value != "PreserveNewest" && copyOnBuild.Value != "Always") { continue; } var platformsElement = file.XPathSelectElement("Platforms"); var includePlatformsElement = file.XPathSelectElement("IncludePlatforms"); var excludePlatformsElement = file.XPathSelectElement("ExcludePlatforms"); var servicesElement = file.XPathSelectElement("Services"); var includeServicesElement = file.XPathSelectElement("IncludeServices"); var excludeServicesElement = file.XPathSelectElement("ExcludeServices"); var platformsString = platformsElement != null ? platformsElement.Value : string.Empty; var includePlatformsString = includePlatformsElement != null ? includePlatformsElement.Value : string.Empty; var excludePlatformsString = excludePlatformsElement != null ? excludePlatformsElement.Value : string.Empty; var servicesString = servicesElement != null ? servicesElement.Value : string.Empty; var includeServicesString = includeServicesElement != null ? includeServicesElement.Value : string.Empty; var excludeServicesString = excludeServicesElement != null ? excludeServicesElement.Value : string.Empty; if (detector.ProjectAndServiceIsActive( platformsString, includePlatformsString, excludePlatformsString, servicesString, includeServicesString, excludeServicesString, platform, activeServices)) { var include = file.Attribute(XName.Get("Include")); var linkElement = file.XPathSelectElement("Link"); var link = linkElement != null ? linkElement.Value : include.Value; var fileInfo = new FileInfo(Path.Combine( definition.ModulePath.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar), definition.Path.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar), include.Value.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar))); if (definition.Type == "Library") { // For libraries, we're copying the content so that applications using // the libraries will have the content. This is most often used to // ship native binaries (although NativeBinary in external projects now // supersedes this). if (link.Contains('/') || link.Contains('\\')) { Console.WriteLine( "WARNING: Copy-on-build file '" + link + "' in library project which " + "does not output to root of project detected. This is not supported."); } else { if (fileInfo.Name != link) { Console.WriteLine( "WARNING: Copy-on-build file in library project does not have the same " + "name when copied to build directory. This is not supported."); } else { var sourcePath = fileInfo.FullName; sourcePath = sourcePath.Substring(rootPath.Length).Replace('\\', '/').TrimStart('/'); var destPath = Path.Combine("_AutomaticExternals", sourcePath); var sourcePathRegex = this.ConvertPathWithMSBuildVariablesFind(sourcePath); var destPathRegex = this.ConvertPathWithMSBuildVariablesReplace(destPath.Replace('\\', '/')); var includeMatch = fileFilter.ApplyInclude(sourcePathRegex); fileFilter.ApplyRewrite(sourcePathRegex, destPathRegex); if (includeMatch) { var nativeBinaryEntry = externalProjectDocument.CreateElement("NativeBinary"); nativeBinaryEntry.SetAttribute("Path", destPath); externalProject.AppendChild(nativeBinaryEntry); } else { throw new InvalidOperationException( "File not found at " + sourcePath + " when converting " + "copy-on-build file in library project."); } } } } } } // Write out the external project to a temporary file and include it. var name = Path.GetRandomFileName() + "_" + definition.Name + ".xml"; var temp = Path.Combine(Path.GetTempPath(), name); using (var writer = XmlWriter.Create(temp, new XmlWriterSettings { Indent = true, IndentChars = " " })) { externalProjectDocument.WriteTo(writer); } fileFilter.AddManualMapping(temp, "Build/Projects/" + definition.Name + ".definition"); }
private void TranslateExternalProject( DefinitionInfo definition, List <Service> services, FileFilter fileFilter, string rootPath, string platform, XmlElement fromNode, XmlElement toNode, bool isNested) { foreach (var child in fromNode.ChildNodes.OfType <XmlElement>()) { switch (child.LocalName) { case "Reference": { // These are included as-is; either they're references to GAC assemblies // or other Protobuild projects (which are now external projects themselves). var referenceEntry = toNode.OwnerDocument.ImportNode(child, true); toNode.AppendChild(referenceEntry); break; } case "Binary": { // We have to change the path on these files so that the path is nested // underneath the "_AutomaticExternals" folder. Since these are .NET references, we also // want to copy all of the related files (config, xml, etc.) var sourceFileInfo = new FileInfo(Path.Combine(definition.ModulePath, child.GetAttribute("Path"))); var sourceWithoutExtension = sourceFileInfo.FullName.Substring( 0, sourceFileInfo.FullName.Length - sourceFileInfo.Extension.Length); var destWithoutExtension = child.GetAttribute("Path").Substring( 0, child.GetAttribute("Path").Length - sourceFileInfo.Extension.Length); var fileExtensionsToCopy = new[] { sourceFileInfo.Extension, sourceFileInfo.Extension + ".config", sourceFileInfo.Extension + ".mdb", ".pdb", ".xml", }; foreach (var extension in fileExtensionsToCopy) { var sourcePath = sourceWithoutExtension + extension; sourcePath = sourcePath.Substring(rootPath.Length).Replace('\\', '/').TrimStart('/'); var destPath = Path.Combine("_AutomaticExternals", destWithoutExtension + extension); var sourcePathRegex = this.ConvertPathWithMSBuildVariablesFind(sourcePath); var destPathRegex = this.ConvertPathWithMSBuildVariablesReplace(destPath.Replace('\\', '/')); var includeMatch = fileFilter.ApplyInclude(sourcePathRegex); fileFilter.ApplyRewrite(sourcePathRegex, destPathRegex); if (includeMatch) { if (extension == sourceFileInfo.Extension) { var binaryEntry = toNode.OwnerDocument.CreateElement("Binary"); binaryEntry.SetAttribute("Name", child.GetAttribute("Name")); binaryEntry.SetAttribute("Path", destPath); toNode.AppendChild(binaryEntry); } else if (extension == sourceFileInfo.Extension + ".config") { var nativeBinaryEntry = toNode.OwnerDocument.CreateElement("NativeBinary"); nativeBinaryEntry.SetAttribute("Path", destPath); toNode.AppendChild(nativeBinaryEntry); } } } break; } case "NativeBinary": { // We have to change the path on these files so that the path is nested // underneath the "_AutomaticExternals" folder. var sourcePath = Path.Combine(definition.ModulePath, child.GetAttribute("Path")); sourcePath = sourcePath.Substring(rootPath.Length).Replace('\\', '/').TrimStart('/'); var destPath = Path.Combine("_AutomaticExternals", child.GetAttribute("Path")); var sourcePathRegex = this.ConvertPathWithMSBuildVariablesFind(sourcePath); var destPathRegex = this.ConvertPathWithMSBuildVariablesReplace(destPath.Replace('\\', '/')); var includeMatch = fileFilter.ApplyInclude(sourcePathRegex); fileFilter.ApplyRewrite(sourcePathRegex, destPathRegex); if (includeMatch) { var nativeBinaryEntry = toNode.OwnerDocument.CreateElement("NativeBinary"); nativeBinaryEntry.SetAttribute("Path", destPath); toNode.AppendChild(nativeBinaryEntry); } else { throw new InvalidOperationException("File not found at " + sourcePath + " when converting NativeBinary reference."); } break; } case "Project": { // We can't do anything with these tags, as we don't know how the target C# project // is built (without investing heavily into how MSBuild builds projects). Instead, // show a warning that this reference will be converted to an external project // reference instead, so that the developer can manually hook this up through // additional directives in the filter file. Console.WriteLine( "WARNING: The 'Project' tag in external projects can not be " + "automatically converted during packaging. This reference " + "will be converted to refer to an external reference with " + "the name '" + child.GetAttribute("Name") + "' instead, and you'll " + "need to write an ExternalProject definition (and include it via " + "a filter file) to have this reference packaged correctly."); var referenceEntry = toNode.OwnerDocument.CreateElement("Reference"); referenceEntry.SetAttribute("Include", child.GetAttribute("Name")); toNode.AppendChild(referenceEntry); break; } case "Platform": { // If the Platform tag matches, copy the contents of it directly into our top-level // external project. We don't need to copy the Platform tag itself, as packages are // specific to a given platform. if (child.GetAttribute("Type").ToLowerInvariant() == platform.ToLowerInvariant()) { this.TranslateExternalProject(definition, services, fileFilter, rootPath, platform, child, toNode, true); } break; } case "Service": { // If the service is enabled, then copy the contents of the service section directly // into our top-level external project. We don't need to copy the Service tag itself, // as changing services won't have any effect on binary packages. var service = services.FirstOrDefault(x => x.FullName == child.GetAttribute("Name") || x.FullName == definition.Name + "/" + child.GetAttribute("Name")); if (service != null) { // If the service is present in the list, then it is enabled. this.TranslateExternalProject(definition, services, fileFilter, rootPath, platform, child, toNode, true); } break; } default: { // We can't do anything with these tags, because we don't know what services were enabled // when the assemblies were built. Show a warning instead. Console.WriteLine("WARNING: Unknown tag '" + child.LocalName + "' encountered."); break; } } } }
public byte[] Transform(string workingDirectory, string url, string gitReference, string platform, string format) { var urlAndPackageName = url.Split(new[] { '|' }, 2); if (urlAndPackageName.Length != 2) { RedirectableConsole.ErrorWriteLine( "ERROR: Malformed NuGet package reference '" + url + "'. Make sure you split the NuGet server URL and the package name with a pipe character (|)."); ExecEnvironment.Exit(1); } var repoUrl = urlAndPackageName[0]; var packageName = urlAndPackageName[1]; var originalFolder = DownloadOrUseExistingNuGetPackage(repoUrl.TrimEnd('/'), packageName, gitReference); if (Directory.Exists(Path.Combine(originalFolder, "protobuild"))) { // This is a Protobuild-aware NuGet package. In this case, we just use the contents of the // "protobuild" folder as the content of our package, and ignore everything else. RedirectableConsole.WriteLine("Detected Protobuild-aware package..."); RedirectableConsole.WriteLine("Converting to a Protobuild package..."); var target = new MemoryStream(); var filter = new FileFilter(_getRecursiveUtilitiesInPath.GetRecursiveFilesInPath(originalFolder)); filter.ApplyInclude("protobuild/(.*)"); filter.ApplyRewrite("protobuild/(.*)", "$1"); filter.ImplyDirectories(); _packageCreator.Create( target, filter, originalFolder, format, platform); RedirectableConsole.WriteLine("Package conversion complete."); var bytes2 = new byte[target.Position]; target.Seek(0, SeekOrigin.Begin); target.Read(bytes2, 0, bytes2.Length); return(bytes2); } var folder = Path.GetTempFileName(); File.Delete(folder); Directory.CreateDirectory(folder); byte[] bytes; try { RedirectableConsole.WriteLine("Copying directory for package transformation..."); CopyFolder(new DirectoryInfo(originalFolder), new DirectoryInfo(folder)); RedirectableConsole.WriteLine("Auto-detecting libraries to reference from NuGet package..."); var packagePath = new DirectoryInfo(folder).GetFiles("*.nuspec").First().FullName; var libraryReferences = new Dictionary <string, string>(); var packageDependencies = new Dictionary <string, string>(); // Use the nuspec file if it exists. List <string> references = new List <string>(); if (File.Exists(packagePath)) { var packageDoc = new XmlDocument(); packageDoc.Load(packagePath); // 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.")); } } // 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); } document.Save(Path.Combine(folder, "_ProtobuildExternalProject.xml")); 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 = repoUrl.Replace("http://", "http-nuget://").Replace("https://", "https-nuget://") + "|" + package.Key, GitRef = package.Value.TrimStart('[').TrimEnd(']'), Folder = package.Key }); } generatedModule.Save(Path.Combine(folder, "_ProtobuildModule.xml")); RedirectableConsole.WriteLine("Converting to a Protobuild package..."); var target = new MemoryStream(); var filter = new FileFilter(_getRecursiveUtilitiesInPath.GetRecursiveFilesInPath(folder)); foreach (var kv in libraryReferences) { filter.ApplyInclude( Regex.Escape(kv.Value.Substring(folder.Length).Replace('\\', '/').TrimStart('/'))); filter.ApplyRewrite( Regex.Escape(kv.Value.Substring(folder.Length).Replace('\\', '/').TrimStart('/')), kv.Value.Substring(folder.Length).Replace('\\', '/').TrimStart('/').Replace("%2B", "-")); } filter.ApplyInclude("_ProtobuildExternalProject\\.xml"); filter.ApplyRewrite("_ProtobuildExternalProject\\.xml", "Build/Projects/" + packageName + ".definition"); filter.ApplyInclude("_ProtobuildModule\\.xml"); filter.ApplyRewrite("_ProtobuildModule\\.xml", "Build/Module.xml"); filter.ImplyDirectories(); _packageCreator.Create( target, filter, folder, format, platform); RedirectableConsole.WriteLine("Package conversion complete."); bytes = new byte[target.Position]; target.Seek(0, SeekOrigin.Begin); target.Read(bytes, 0, bytes.Length); } finally { RedirectableConsole.WriteLine("Cleaning up temporary data..."); PathUtils.AggressiveDirectoryDelete(folder); } return(bytes); }
private void AutomaticallyPackageNormalProject( DefinitionInfo[] definitions, List <Service> services, FileFilter fileFilter, string rootPath, string platform, DefinitionInfo definition, List <string> temporaryFiles) { var document = XDocument.Load(definition.DefinitionPath); var externalProjectDocument = new XmlDocument(); externalProjectDocument.AppendChild(externalProjectDocument.CreateXmlDeclaration("1.0", "UTF-8", null)); var externalProject = externalProjectDocument.CreateElement("ExternalProject"); externalProjectDocument.AppendChild(externalProject); externalProject.SetAttribute("Name", definition.Name); if (definition.PostBuildHook) { externalProject.SetAttribute("PostBuildHook", "True"); } var externalProjectServices = externalProjectDocument.CreateElement("Services"); externalProject.AppendChild(externalProjectServices); // Just import all declared services as available, regardless of conflicts or // requirements. We don't have a clean way of automatically translating // services for packages (it has to be done manually because services can // change the resulting code that's built). So that things work 95% of time, // just declare all services available for projects included via the automatic // packaging mechanism. var servicesToDeclare = new List <string>(); var servicesDeclared = document.Root.Element(XName.Get("Services")); if (servicesDeclared != null) { foreach (var serviceElement in servicesDeclared.Elements().Where(x => x.Name.LocalName == "Service")) { servicesToDeclare.Add(serviceElement.Attribute(XName.Get("Name")).Value); } } foreach (var serviceToDeclare in servicesToDeclare) { var serviceElem = externalProjectDocument.CreateElement("Service"); serviceElem.SetAttribute("Name", serviceToDeclare); var defaultForRoot = externalProjectDocument.CreateElement("DefaultForRoot"); defaultForRoot.InnerText = "True"; serviceElem.AppendChild(defaultForRoot); externalProjectServices.AppendChild(serviceElem); } var pathPrefix = this.m_ProjectOutputPathCalculator.GetProjectOutputPathPrefix(platform, definition, document, true); var assemblyName = this.m_ProjectOutputPathCalculator.GetProjectAssemblyName(platform, definition, document); var outputMode = this.m_ProjectOutputPathCalculator.GetProjectOutputMode(document); var assemblyFilesToCopy = new[] { assemblyName + ".exe", assemblyName + ".dll", assemblyName + ".dll.config", assemblyName + ".dll.mdb", assemblyName + ".pdb", assemblyName + ".xml", }; // Copy the assembly itself out to the package. switch (outputMode) { case OutputPathMode.BinConfiguration: { // In this configuration, we only ship the binaries for // the default architecture (because that's all we know // about). We also have to assume the binary folder // contains binaries for the desired platform. if (definition.Type == "Library") { // For libraries, we only copy the assembly (and immediately related files) // into it's directory. Native binaries will be expressed through the ExternalProject. foreach (var assemblyFile in assemblyFilesToCopy) { var includeMatch = fileFilter.ApplyInclude("^" + pathPrefix + Regex.Escape(assemblyFile) + "$"); var rewriteMatch = fileFilter.ApplyRewrite("^" + pathPrefix + Regex.Escape(assemblyFile) + "$", definition.Name + "/AnyCPU/" + assemblyFile); if (includeMatch && rewriteMatch) { if (assemblyFile.EndsWith(".dll")) { var binaryEntry = externalProjectDocument.CreateElement("Binary"); binaryEntry.SetAttribute("Name", assemblyFile.Substring(0, assemblyFile.Length - 4)); binaryEntry.SetAttribute("Path", definition.Name + "\\AnyCPU\\" + assemblyFile); externalProject.AppendChild(binaryEntry); } else if (assemblyFile.EndsWith(".dll.config")) { var configEntry = externalProjectDocument.CreateElement("NativeBinary"); configEntry.SetAttribute("Path", definition.Name + "\\AnyCPU\\" + assemblyFile); externalProject.AppendChild(configEntry); } } else if (includeMatch || rewriteMatch) { throw new InvalidOperationException("Automatic filter; only one rule matched."); } } } else { // For executables, we ship everything in the output directory, because we // want the executables to be able to run from the package directory. fileFilter.ApplyInclude("^" + pathPrefix + "(.+)$"); fileFilter.ApplyRewrite("^" + pathPrefix + "(.+)$", definition.Name + "/AnyCPU/$2"); // Mark the executable files in the directory as tools that can be executed. foreach (var assemblyFile in assemblyFilesToCopy) { if (assemblyFile.EndsWith(".exe")) { var binaryEntry = externalProjectDocument.CreateElement("Tool"); binaryEntry.SetAttribute("Name", assemblyFile.Substring(0, assemblyFile.Length - 4)); binaryEntry.SetAttribute("Path", definition.Name + "\\AnyCPU\\" + assemblyFile); externalProject.AppendChild(binaryEntry); } } } break; } case OutputPathMode.BinPlatformArchConfiguration: { // In this configuration, we ship binaries for AnyCPU, iPhoneSimulator or all .NET architectures // depending on whether or not the platform produces multiple architectures. On Mono, // we can't use $(Platform) within a reference's path, so we have to keep this path static // for Mono platforms. string pathArchMatch, pathArchReplace, pathArchRuntime; switch (platform.ToLowerInvariant()) { case "ios": { pathArchMatch = "iPhoneSimulator"; pathArchReplace = "iPhoneSimulator"; pathArchRuntime = "iPhoneSimulator"; break; } case "windowsphone": { pathArchMatch = "([^/]+)"; pathArchReplace = "$1"; pathArchRuntime = "$(Platform)"; break; } default: { pathArchMatch = "AnyCPU"; pathArchReplace = "AnyCPU"; pathArchRuntime = "AnyCPU"; break; } } if (definition.Type == "Library") { // For libraries, we only copy the assembly (and immediately related files) // into it's directory. Native binaries will be expressed through the ExternalProject. foreach (var assemblyFile in assemblyFilesToCopy) { var includeMatch = fileFilter.ApplyInclude("^" + pathPrefix + Regex.Escape(assemblyFile) + "$"); var rewriteMatch = fileFilter.ApplyRewrite("^" + pathPrefix + Regex.Escape(assemblyFile) + "$", definition.Name + "/" + pathArchReplace + "/" + assemblyFile); if (includeMatch && rewriteMatch) { if (assemblyFile.EndsWith(".dll")) { var binaryEntry = externalProjectDocument.CreateElement("Binary"); binaryEntry.SetAttribute("Name", assemblyFile.Substring(0, assemblyFile.Length - 4)); binaryEntry.SetAttribute("Path", definition.Name + "\\" + pathArchRuntime + "\\" + assemblyFile); externalProject.AppendChild(binaryEntry); } else if (assemblyFile.EndsWith(".dll.config")) { var configEntry = externalProjectDocument.CreateElement("NativeBinary"); configEntry.SetAttribute("Path", definition.Name + "\\" + pathArchRuntime + "\\" + assemblyFile); externalProject.AppendChild(configEntry); } } else if (includeMatch || rewriteMatch) { throw new InvalidOperationException("Automatic filter; only one rule matched."); } } } else { // For executables, we ship everything in the output directory, because we // want the executables to be able to run from the package directory. fileFilter.ApplyInclude("^" + pathPrefix + "(.+)$"); if (pathArchMatch == "([^/]+)") { fileFilter.ApplyRewrite("^" + pathPrefix + "(.+)$", definition.Name + "/" + pathArchReplace + "/$3"); } else { fileFilter.ApplyRewrite("^" + pathPrefix + "(.+)$", definition.Name + "/" + pathArchReplace + "/$2"); } // Mark the executable files in the directory as tools that can be executed. foreach (var assemblyFile in assemblyFilesToCopy) { if (assemblyFile.EndsWith(".exe")) { var binaryEntry = externalProjectDocument.CreateElement("Tool"); binaryEntry.SetAttribute("Name", assemblyFile.Substring(0, assemblyFile.Length - 4)); binaryEntry.SetAttribute("Path", definition.Name + "\\" + pathArchRuntime + "\\" + assemblyFile); externalProject.AppendChild(binaryEntry); } } } break; } case OutputPathMode.BinProjectPlatformArchConfiguration: { throw new NotSupportedException(); break; } } // Convert all of the known references into references within the external project. var definitionsByName = definitions.ToDictionarySafe( k => k.Name, v => v, (dict, x) => { var existing = dict[x.Name]; var tried = x; RedirectableConsole.WriteLine("WARNING: There is more than one project with the name " + x.Name + " (first project loaded from " + tried.AbsolutePath + ", " + "skipped loading second project from " + existing.AbsolutePath + ")"); }); foreach (var reference in document.XPathSelectElements("/Project/References/Reference")) { var includeAttribute = reference.Attribute(XName.Get("Include")); if (includeAttribute != null) { if (definitionsByName.ContainsKey(includeAttribute.Value)) { var targetDefinition = definitionsByName[includeAttribute.Value]; // If the targeted reference is an include project, skip it. if (targetDefinition == null || targetDefinition.Type != "Include") { // This reference will be converted to an external project, // so add a reference to it (in case it contains native binaries // which need to be copied out). var referenceEntry = externalProjectDocument.CreateElement("Reference"); referenceEntry.SetAttribute("Include", includeAttribute.Value); externalProject.AppendChild(referenceEntry); } } } } // Copy out any files that are marked with copy-on-build flag. var detector = new PlatformAndServiceActiveDetection(); var xmlDocument = new XmlDocument(); xmlDocument.Load(definition.DefinitionPath); var servicesInput = this.m_ServiceInputGenerator.Generate(xmlDocument, definition.Name, services); var activeServicesElement = servicesInput.ChildNodes.OfType <XmlElement>().FirstOrDefault(x => x.LocalName == "ActiveServicesNames"); var activeServices = activeServicesElement.InnerText; foreach (var file in document.XPathSelectElements("/Project/Files/*")) { var copyOnBuild = file.XPathSelectElement("CopyToOutputDirectory"); if (copyOnBuild == null) { continue; } if (copyOnBuild.Value != "PreserveNewest" && copyOnBuild.Value != "Always") { continue; } var platformsElement = file.XPathSelectElement("Platforms"); var includePlatformsElement = file.XPathSelectElement("IncludePlatforms"); var excludePlatformsElement = file.XPathSelectElement("ExcludePlatforms"); var servicesElement = file.XPathSelectElement("Services"); var includeServicesElement = file.XPathSelectElement("IncludeServices"); var excludeServicesElement = file.XPathSelectElement("ExcludeServices"); var platformsString = platformsElement != null ? platformsElement.Value : string.Empty; var includePlatformsString = includePlatformsElement != null ? includePlatformsElement.Value : string.Empty; var excludePlatformsString = excludePlatformsElement != null ? excludePlatformsElement.Value : string.Empty; var servicesString = servicesElement != null ? servicesElement.Value : string.Empty; var includeServicesString = includeServicesElement != null ? includeServicesElement.Value : string.Empty; var excludeServicesString = excludeServicesElement != null ? excludeServicesElement.Value : string.Empty; if (detector.ProjectAndServiceIsActive( platformsString, includePlatformsString, excludePlatformsString, servicesString, includeServicesString, excludeServicesString, platform, activeServices)) { var include = file.Attribute(XName.Get("Include")); var linkElement = file.XPathSelectElement("Link"); var link = linkElement != null ? linkElement.Value : include.Value; var fileInfo = new FileInfo(Path.Combine( definition.AbsolutePath.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar), include.Value.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar))); if (definition.Type == "Library") { // For libraries, we're copying the content so that applications using // the libraries will have the content. This is most often used to // ship native binaries (although NativeBinary in external projects now // supersedes this). if (link.Contains('/') || link.Contains('\\')) { RedirectableConsole.WriteLine( "WARNING: Copy-on-build file '" + link + "' in library project which " + "does not output to root of project detected. This is not supported."); } else { if (fileInfo.Name != link) { RedirectableConsole.WriteLine( "WARNING: Copy-on-build file in library project does not have the same " + "name when copied to build directory. This is not supported."); } else { var sourcePath = fileInfo.FullName; sourcePath = sourcePath.Substring(rootPath.Length).Replace('\\', '/').TrimStart('/'); var destPath = Path.Combine("_AutomaticExternals", sourcePath); var sourcePathRegex = this.ConvertPathWithMSBuildVariablesFind(sourcePath); var destPathRegex = this.ConvertPathWithMSBuildVariablesReplace(destPath.Replace('\\', '/')); var includeMatch = fileFilter.ApplyInclude(sourcePathRegex); fileFilter.ApplyRewrite(sourcePathRegex, destPathRegex); if (includeMatch) { var nativeBinaryEntry = externalProjectDocument.CreateElement("NativeBinary"); nativeBinaryEntry.SetAttribute("Path", destPath); externalProject.AppendChild(nativeBinaryEntry); } else { throw new InvalidOperationException( "File not found at " + sourcePath + " when converting " + "copy-on-build file in library project."); } } } } } } // Write out the external project to a temporary file and include it. var name = Path.GetRandomFileName() + "_" + definition.Name + ".xml"; var temp = Path.Combine(Path.GetTempPath(), name); temporaryFiles.Add(temp); using (var writer = XmlWriter.Create(temp, new XmlWriterSettings { Indent = true, IndentChars = " " })) { externalProjectDocument.WriteTo(writer); } fileFilter.AddManualMapping(temp, "Build/Projects/" + definition.Name + ".definition"); }
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); }