public static DefinitionInfo Load(string xmlFile) { var def = new DefinitionInfo(); def.DefinitionPath = xmlFile; var doc = XDocument.Load(xmlFile); if (doc.Root.Attributes().Any(x => x.Name == "Name")) def.Name = doc.Root.Attribute(XName.Get("Name")).Value; if (doc.Root.Name == "Project") { if (doc.Root.Attributes().Any(x => x.Name == "Path")) def.Path = doc.Root.Attribute(XName.Get("Path")).Value; if (doc.Root.Attributes().Any(x => x.Name == "Guid")) def.Guid = Guid.Parse(doc.Root.Attribute(XName.Get("Guid")).Value); if (doc.Root.Attributes().Any(x => x.Name == "Type")) def.Type = doc.Root.Attribute(XName.Get("Type")).Value; } else if (doc.Root.Name == "ExternalProject") { def.Type = "External"; } else if (doc.Root.Name == "ContentProject") { def.Type = "Content"; } return def; }
/// <summary> /// Loads all of the project definitions present in the current module. /// </summary> /// <returns>The loaded project definitions.</returns> public DefinitionInfo[] GetDefinitions() { var result = new List <DefinitionInfo>(); var path = System.IO.Path.Combine(this.Path, "Build", "Projects"); if (!Directory.Exists(path)) { return(new DefinitionInfo[0]); } foreach (var file in new DirectoryInfo(path).GetFiles("*.definition")) { result.Add(DefinitionInfo.Load(file.FullName)); } return(result.ToArray()); }
/// <summary> /// Loads a project definition from the specified XML file. /// </summary> /// <param name="xmlFile">The path of the XML file to load.</param> /// <returns>The loaded project definition.</returns> public static DefinitionInfo Load(string xmlFile) { var def = new DefinitionInfo(); def.DefinitionPath = xmlFile; var doc = XDocument.Load(xmlFile); if (doc.Root.Attributes().Any(x => x.Name == "Name")) { def.Name = doc.Root.Attribute(XName.Get("Name")).Value; } if (doc.Root.Name == "Project") { if (doc.Root.Attributes().Any(x => x.Name == "Path")) { def.Path = doc.Root.Attribute(XName.Get("Path")).Value; } if (doc.Root.Attributes().Any(x => x.Name == "Guid")) { def.Guid = Guid.Parse(doc.Root.Attribute(XName.Get("Guid")).Value); } if (doc.Root.Attributes().Any(x => x.Name == "Type")) { def.Type = doc.Root.Attribute(XName.Get("Type")).Value; } } else if (doc.Root.Name == "ExternalProject") { def.Type = "External"; } else if (doc.Root.Name == "ContentProject") { def.Type = "Content"; } if (doc.Root.Attributes().Any(x => x.Name == "SkipAutopackage")) { var skipValue = doc.Root.Attribute(XName.Get("SkipAutopackage")).Value.ToLowerInvariant(); def.SkipAutopackage = skipValue == "true"; } return(def); }
private void AutomaticallyPackageExternalProject( DefinitionInfo[] definitions, List <Service> services, FileFilter fileFilter, string rootPath, string platform, DefinitionInfo definition, List <string> temporaryFiles) { var document = new XmlDocument(); document.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); // Translate all of the references (this is a seperate method so we can call it // again when traversing into Platform nodes). this.TranslateExternalProject( definition, services, fileFilter, rootPath, platform, document.DocumentElement, externalProject, false); // 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 string GetProjectAssemblyName(string platform, DefinitionInfo definition, XDocument document) { var assemblyNameForPlatformElement = document.XPathSelectElement("/Project/Properties/AssemblyName/Platform[@Name=\"" + platform + "\"]"); var assemblyNameGlobalElement = document.XPathSelectElement("/Project/Properties/Property[@Name=\"AssemblyName\"]"); string assemblyName = null; if (assemblyNameForPlatformElement != null) { assemblyName = assemblyNameForPlatformElement.Value; } else if (assemblyNameGlobalElement != null) { assemblyName = assemblyNameGlobalElement.Attribute(XName.Get("Value")).Value; } else { assemblyName = definition.Name; } return assemblyName; }
public string GetProjectAssemblyName(string platform, DefinitionInfo definition, XDocument document) { var assemblyNameForPlatformElement = document.XPathSelectElement("/Project/Properties/AssemblyName/Platform[@Name=\"" + platform + "\"]"); var assemblyNameGlobalElement = document.XPathSelectElement("/Project/Properties/Property[@Name=\"AssemblyName\"]"); string assemblyName = null; if (assemblyNameForPlatformElement != null) { assemblyName = assemblyNameForPlatformElement.Value; } else if (assemblyNameGlobalElement != null) { assemblyName = assemblyNameGlobalElement.Attribute(XName.Get("Value")).Value; } else { assemblyName = definition.Name; } return(assemblyName); }
private void AutomaticallyPackageExternalProject( DefinitionInfo[] definitions, List<Service> services, FileFilter fileFilter, string rootPath, string platform, DefinitionInfo definition, List<string> temporaryFiles) { var document = new XmlDocument(); document.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); // Translate all of the references (this is a seperate method so we can call it // again when traversing into Platform nodes). this.TranslateExternalProject( definition, services, fileFilter, rootPath, platform, document.DocumentElement, externalProject, false); // 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"); }
/// <summary> /// Generates a project at the target path. /// </summary> /// <param name="project">The name of the project to generate.</param> /// <param name="services"></param> /// <param name="packagesFilePath"> /// Either the full path to the packages.config for the /// generated project if it exists, or an empty string. /// </param> /// <param name="onActualGeneration"></param> public void Generate( DefinitionInfo current, List <LoadedDefinitionInfo> definitions, string rootPath, string projectName, string platformName, List <Service> services, out string packagesFilePath, Action onActualGeneration) { packagesFilePath = string.Empty; // Work out what document this is. var projectDoc = definitions.First( x => x.Project.DocumentElement.Attributes["Name"].Value == projectName)?.Project; // Check to see if we have a Project node; if not // then this is an external or other type of project // that we don't process. if (projectDoc == null || projectDoc.DocumentElement.Name != "Project") { return; } // Load the appropriate project transformation XSLT. var languageAttribute = projectDoc.DocumentElement.Attributes["Language"]; var languageText = languageAttribute != null ? languageAttribute.Value : "C#"; var language = this.m_LanguageStringProvider.GetLanguageFromConfigurationName(languageText); var projectTransform = this.m_ResourceProvider.LoadXSLT(ResourceType.GenerateProject, language, platformName); // Work out what platforms this project should be generated for. var platformAttribute = projectDoc.DocumentElement.Attributes["Platforms"]; string[] allowedPlatforms = null; if (platformAttribute != null) { allowedPlatforms = platformAttribute.Value .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) .Select(x => x.Trim()) .ToArray(); } // Filter on allowed platforms. if (allowedPlatforms != null) { var allowed = false; foreach (var platform in allowedPlatforms) { if (string.Compare(platformName, platform, StringComparison.InvariantCultureIgnoreCase) == 0) { allowed = true; break; } } if (!allowed) { return; } } // If the project has a <Services> node, but there are no entries in /Input/Services, // then nothing depends on this service (if there is a <Reference> to this project, it will // use the default service). So for service-aware projects without any services being // referenced, we exclude them from the generation. if (this.m_ExcludedServiceAwareProjectDetector.IsExcludedServiceAwareProject( projectDoc.DocumentElement.Attributes["Name"].Value, projectDoc, services)) { return; } // Inform the user we're generating this project. onActualGeneration(); // Add include projects if they have an AppliesTo tag that matches this project's name. this._includeProjectAppliesToUpdater.UpdateProjectReferences(definitions.Select(x => x.Project).ToList(), projectDoc); // Add references and properties from include projects. _includeProjectMerger.MergeInReferencesAndPropertiesForIncludeProjects(definitions, projectDoc, platformName); // Imply external project references from other external projects. We do // this so that external projects can reference other external projects (which // we can't reasonably handle at the XSLT level since it's recursive). this.m_ExternalProjectReferenceResolver.ResolveExternalProjectReferences(definitions, projectDoc, platformName); // Generate Info.plist files if necessary (for Mac / iOS). this._mPlatformResourcesGenerator.GenerateInfoPListIfNeeded(definitions, current, projectDoc, platformName); // Work out what path to save at. var path = Path.Combine( rootPath, projectDoc.DocumentElement.Attributes["Path"].Value .Replace('\\', Path.DirectorySeparatorChar) .Replace('/', Path.DirectorySeparatorChar), projectDoc.DocumentElement.Attributes["Name"].Value + "." + platformName + "." + this.m_LanguageStringProvider.GetProjectExtension(language)); // Make sure that the directory exists where the file will be stored. var targetFile = new FileInfo(path); if (!targetFile.Directory.Exists) { targetFile.Directory.Create(); } path = targetFile.FullName; // Handle NuGet packages.config early so that it'll be in place // when the generator automatically determined dependencies. this.m_NuGetConfigMover.Move(rootPath, platformName, projectDoc); // Work out what path the NuGet packages.config might be at. var packagesFile = new FileInfo( Path.Combine( rootPath, projectDoc.DocumentElement.Attributes["Path"].Value .Replace('\\', Path.DirectorySeparatorChar) .Replace('/', Path.DirectorySeparatorChar), "packages.config")); // Generate the input document. var input = this.m_ProjectInputGenerator.Generate( definitions.Select(x => x.Project).ToList(), rootPath, projectName, platformName, packagesFile.FullName, projectDoc.DocumentElement.ChildNodes .OfType <XmlElement>() .Where(x => x.Name.ToLower() == "properties") .SelectMany(x => x.ChildNodes .OfType <XmlElement>()), services); // Transform the input document using the XSLT transform. var settings = new XmlWriterSettings(); settings.Indent = true; using (var writer = XmlWriter.Create(path, settings)) { projectTransform.Transform(input, writer); } // Also remove any left over .sln or .userprefs files. var slnPath = Path.Combine( rootPath, projectDoc.DocumentElement.Attributes["Path"].Value, projectDoc.DocumentElement.Attributes["Name"].Value + "." + platformName + ".sln"); var userprefsPath = Path.Combine( rootPath, projectDoc.DocumentElement.Attributes["Path"].Value, projectDoc.DocumentElement.Attributes["Name"].Value + "." + platformName + ".userprefs"); if (File.Exists(slnPath)) { File.Delete(slnPath); } if (File.Exists(userprefsPath)) { File.Delete(userprefsPath); } // Only return the package file path if it exists. if (packagesFile.Exists) { packagesFilePath = packagesFile.FullName; } }
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 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); } } } }
public DefinitionSynchroniser(DefinitionInfo info, CSharpProject project) { this.m_DefinitionInfo = info; this.m_CSharpProject = project; }
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 " + "to '" + child.GetAttribute("Name") + "' will be converted to refer " + "to an external reference with the name " + "'" + child.GetAttribute("Name") + ".External' 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") + ".External"); 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; } case "Tool": { // These are included as-is; they are only explicitly set when an external tool // (that does not use Protobuild) is being packaged up. var referenceEntry = toNode.OwnerDocument.ImportNode(child, true); toNode.AppendChild(referenceEntry); 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; } } } }
/// <summary> /// Loads a project definition from the specified XML file. /// </summary> /// <param name="xmlFile">The path of the XML file to load.</param> /// <returns>The loaded project definition.</returns> public static DefinitionInfo Load(string xmlFile) { var def = new DefinitionInfo(); def.DefinitionPath = xmlFile; try { var doc = XDocument.Load(xmlFile); if (doc.Root.Attributes().Any(x => x.Name == "Name")) { def.Name = doc.Root.Attribute(XName.Get("Name")).Value; } if (doc.Root.Name == "Project") { if (doc.Root.Attributes().Any(x => x.Name == "Path")) { def.RelativePath = doc.Root.Attribute(XName.Get("Path")).Value; } if (doc.Root.Attributes().Any(x => x.Name == "Type")) { def.Type = doc.Root.Attribute(XName.Get("Type")).Value; } } else if (doc.Root.Name == "ExternalProject") { def.Type = "External"; } else if (doc.Root.Name == "IncludeProject") { def.Type = "Include"; if (doc.Root.Attributes().Any(x => x.Name == "Path")) { def.RelativePath = doc.Root.Attribute(XName.Get("Path")).Value; } } else if (doc.Root.Name == "ContentProject") { def.Type = "Content"; } if (doc.Root.Attributes().Any(x => x.Name == "SkipAutopackage")) { var skipValue = doc.Root.Attribute(XName.Get("SkipAutopackage")).Value.ToLowerInvariant(); def.SkipAutopackage = skipValue == "true"; } if (doc.Root.Attributes().Any(x => x.Name == "PostBuildHook")) { var skipValue = doc.Root.Attribute(XName.Get("PostBuildHook")).Value.ToLowerInvariant(); def.PostBuildHook = skipValue == "true"; } return def; } catch (System.Xml.XmlException ex) { throw new InvalidOperationException( "Encountered an XML exception while loading " + xmlFile + " as a project definition. This indicates " + "that the project definition file is badly formed " + "XML, or otherwise has an incorrect structure."); } }
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 GenerateInfoPListIfNeeded(List <LoadedDefinitionInfo> definitions, DefinitionInfo definition, XmlDocument project, string platform) { if (platform == "iOS" || platform == "MacOS") { var type = project.DocumentElement.GetAttribute("Type"); if (type == "Console" || type == "App") { var references = project.DocumentElement.SelectNodes("References/*").OfType <XmlElement>(); foreach (var reference in references) { var lookup = definitions.FirstOrDefault(x => x.Definition.Name == reference.GetAttribute("Include")); if (lookup != null && 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/> </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); } } } }
public virtual void FinalizeNewProject(ModuleInfo module, DefinitionInfo definition) { }
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); } // Copy all existing references that the normal project makes to the external project. var referencesDeclared = document.Root.Element(XName.Get("References")); if (referencesDeclared != null) { foreach (var referenceElement in referencesDeclared.Elements().Where(x => x.Name.LocalName == "Reference")) { var referenceElem = externalProjectDocument.CreateElement("Reference"); referenceElem.SetAttribute("Include", referenceElement.Attribute(XName.Get("Include")).Value); } } 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; Console.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)) { // 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('\\')) { 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); 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 string GetProjectOutputPathPrefix(string platform, DefinitionInfo definition, XDocument document, bool asRegex) { var outputMode = this.GetProjectOutputMode(document); // 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 (asRegex) { return definition.RelativePath.Replace('\\', '/').Replace(".", "\\.") + "/bin/([^/]+)/"; } else { return definition.RelativePath.Replace('\\', '/') + "/bin/"; } } 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; switch (platform.ToLowerInvariant()) { case "ios": { pathArchMatch = "iPhoneSimulator"; break; } case "windowsphone": { pathArchMatch = "([^/]+)"; break; } default: { pathArchMatch = "AnyCPU"; break; } } if (asRegex) { return definition.RelativePath.Replace('\\', '/').Replace(".", "\\.") + "/bin/" + platform + "/" + pathArchMatch + "/([^/]+)/"; } else { return definition.RelativePath.Replace('\\', '/') + "/bin/" + platform + "/" + pathArchMatch + "/"; } } case OutputPathMode.BinProjectPlatformArchConfiguration: { throw new NotSupportedException(); } } throw new NotSupportedException(); }
/// <summary> /// Loads a project definition from the specified XML file. /// </summary> /// <param name="xmlFile">The path of the XML file to load.</param> /// <returns>The loaded project definition.</returns> public static DefinitionInfo Load(string xmlFile) { var def = new DefinitionInfo(); def.DefinitionPath = xmlFile; var doc = XDocument.Load(xmlFile); if (doc.Root.Attributes().Any(x => x.Name == "Name")) { def.Name = doc.Root.Attribute(XName.Get("Name")).Value; } if (doc.Root.Name == "Project") { if (doc.Root.Attributes().Any(x => x.Name == "Path")) { def.RelativePath = doc.Root.Attribute(XName.Get("Path")).Value; } if (doc.Root.Attributes().Any(x => x.Name == "Guid")) { def.Guid = Guid.Parse(doc.Root.Attribute(XName.Get("Guid")).Value); } if (doc.Root.Attributes().Any(x => x.Name == "Type")) { def.Type = doc.Root.Attribute(XName.Get("Type")).Value; } } else if (doc.Root.Name == "ExternalProject") { def.Type = "External"; } else if (doc.Root.Name == "IncludeProject") { def.Type = "Include"; } else if (doc.Root.Name == "ContentProject") { def.Type = "Content"; } if (doc.Root.Attributes().Any(x => x.Name == "SkipAutopackage")) { var skipValue = doc.Root.Attribute(XName.Get("SkipAutopackage")).Value.ToLowerInvariant(); def.SkipAutopackage = skipValue == "true"; } if (doc.Root.Attributes().Any(x => x.Name == "PostBuildHook")) { var skipValue = doc.Root.Attribute(XName.Get("PostBuildHook")).Value.ToLowerInvariant(); def.PostBuildHook = skipValue == "true"; } return def; }
/// <summary> /// Loads a project definition from the specified XML file. /// </summary> /// <param name="xmlFile">The path of the XML file to load.</param> /// <returns>The loaded project definition.</returns> public static DefinitionInfo Load(string xmlFile) { var def = new DefinitionInfo(); def.DefinitionPath = xmlFile; try { var doc = XDocument.Load(xmlFile); if (doc.Root.Attributes().Any(x => x.Name == "Name")) { def.Name = doc.Root.Attribute(XName.Get("Name")).Value; } if (doc.Root.Name == "Project") { if (doc.Root.Attributes().Any(x => x.Name == "Path")) { def.RelativePath = doc.Root.Attribute(XName.Get("Path")).Value; } if (doc.Root.Attributes().Any(x => x.Name == "Type")) { def.Type = doc.Root.Attribute(XName.Get("Type")).Value; } } else if (doc.Root.Name == "ExternalProject") { def.Type = "External"; } else if (doc.Root.Name == "IncludeProject") { def.Type = "Include"; if (doc.Root.Attributes().Any(x => x.Name == "Path")) { def.RelativePath = doc.Root.Attribute(XName.Get("Path")).Value; } } else if (doc.Root.Name == "ContentProject") { def.Type = "Content"; } else if (doc.Root.Name == "CustomProject") { def.Type = "Custom"; if (doc.Root.Attributes().Any(x => x.Name == "Path")) { def.RelativePath = doc.Root.Attribute(XName.Get("Path")).Value; } } if (doc.Root.Attributes().Any(x => x.Name == "SkipAutopackage")) { var skipValue = doc.Root.Attribute(XName.Get("SkipAutopackage")).Value.ToLowerInvariant(); def.SkipAutopackage = skipValue == "true"; } if (doc.Root.Attributes().Any(x => x.Name == "PostBuildHook")) { var skipValue = doc.Root.Attribute(XName.Get("PostBuildHook")).Value.ToLowerInvariant(); def.PostBuildHook = skipValue == "true"; } return(def); } catch (System.Xml.XmlException) { throw new InvalidOperationException( "Encountered an XML exception while loading " + xmlFile + " as a project definition. This indicates " + "that the project definition file is badly formed " + "XML, or otherwise has an incorrect structure."); } }
public LoadedDefinitionInfo Load(string targetPlatform, ModuleInfo module, DefinitionInfo definition) { var doc = new XmlDocument(); doc.Load(definition.DefinitionPath); // If this is a ContentProject, we actually need to generate the // full project node from the files that are in the Source folder. XmlDocument newDoc = null; if (doc.DocumentElement.Name == "ContentProject") newDoc = this.m_ContentProjectGenerator.Generate(targetPlatform, doc, definition.ModulePath); else newDoc = doc; if (module.Path != null && definition.ModulePath != null) { var additionalPath = PathUtils.GetRelativePath( module.Path.TrimEnd(System.IO.Path.DirectorySeparatorChar) + System.IO.Path.DirectorySeparatorChar, definition.ModulePath.TrimEnd(System.IO.Path.DirectorySeparatorChar) + System.IO.Path.DirectorySeparatorChar); if (newDoc.DocumentElement != null && newDoc.DocumentElement.Attributes["Path"] != null && additionalPath != null) { newDoc.DocumentElement.Attributes["Path"].Value = (additionalPath.Trim('\\') + '\\' + newDoc.DocumentElement.Attributes["Path"].Value).Replace('/', '\\').Trim('\\'); } if (newDoc.DocumentElement.Name == "ExternalProject") { // Need to find all descendant Binary and Project tags // and update their paths as well. var xDoc = newDoc.ToXDocument(); var projectsToUpdate = xDoc.Descendants().Where(x => x.Name == "Project"); var binariesToUpdate = xDoc.Descendants().Where(x => x.Name == "Binary"); var nativeBinariesToUpdate = xDoc.Descendants().Where(x => x.Name == "NativeBinary"); var toolsToUpdate = xDoc.Descendants().Where(x => x.Name == "Tool"); foreach (var pathToUpdate in projectsToUpdate.Concat(binariesToUpdate).Concat(nativeBinariesToUpdate).Concat(toolsToUpdate) .Where(x => x.Attribute("Path") != null)) { pathToUpdate.Attribute("Path").Value = (additionalPath.Trim('\\') + '\\' + pathToUpdate.Attribute("Path").Value).Replace('/', '\\').Trim('\\'); } newDoc = xDoc.ToXmlDocument(); } } // If the ProjectGuids element doesn't exist, create it. var projectGuids = doc.DocumentElement.ChildNodes.OfType<XmlElement>().FirstOrDefault(x => x.Name == "ProjectGuids"); if (projectGuids == null) { projectGuids = doc.CreateElement("ProjectGuids"); doc.DocumentElement.AppendChild(projectGuids); } // For all the supported platforms of this project, or all the // supported platforms of the module, generate GUIDs for any platform // that doesn't already exist. var platforms = doc.DocumentElement.GetAttribute("Platforms"); if (string.IsNullOrWhiteSpace(platforms)) { platforms = module.SupportedPlatforms; } if (string.IsNullOrWhiteSpace(platforms)) { platforms = ModuleInfo.GetSupportedPlatformsDefault(); } var platformsList = platforms.Split(',').ToList(); if (!platformsList.Contains(targetPlatform)) { platformsList.Add(targetPlatform); } foreach (var platform in platformsList) { var existing = projectGuids.ChildNodes.OfType<XmlElement>().FirstOrDefault(x => x.Name == "Platform" && x.HasAttribute("Name") && x.GetAttribute("Name") == platform); if (existing == null) { var platformElement = doc.CreateElement("Platform"); platformElement.SetAttribute("Name", platform); var name = doc.DocumentElement.GetAttribute("Name") + "." + platform; var guidBytes = new byte[16]; for (var i = 0; i < guidBytes.Length; i++) guidBytes[i] = (byte)0; var nameBytes = Encoding.ASCII.GetBytes(name); unchecked { for (var i = 0; i < nameBytes.Length; i++) guidBytes[i % 16] += nameBytes[i]; for (var i = nameBytes.Length; i < 16; i++) guidBytes[i] += nameBytes[i % nameBytes.Length]; } var guid = new Guid(guidBytes); platformElement.InnerText = guid.ToString().ToUpper(); projectGuids.AppendChild(platformElement); } } // If the Language property doesn't exist, set it to the default of "C#" if (doc.DocumentElement.Attributes["Language"] == null) { doc.DocumentElement.SetAttribute("Language", "C#"); } // Normalize the project type for backwards compatibility. var projectType = doc.DocumentElement.Attributes["Type"]; if (projectType == null) { doc.DocumentElement.SetAttribute("Type", "Library"); } else { switch (projectType.Value) { case "Library": case "App": case "Console": case "Website": break; case "GUI": case "XNA": case "GTK": doc.DocumentElement.SetAttribute("Type", "App"); break; } } return new LoadedDefinitionInfo { Definition = definition, Project = newDoc, }; }
/// <summary> /// Generates a project at the target path. /// </summary> /// <param name="project">The name of the project to generate.</param> /// <param name="services"></param> /// <param name="packagesFilePath"> /// Either the full path to the packages.config for the /// generated project if it exists, or an empty string. /// </param> /// <param name="onActualGeneration"></param> public void Generate( DefinitionInfo current, List<LoadedDefinitionInfo> definitions, string rootPath, string projectName, string platformName, List<Service> services, out string packagesFilePath, Action onActualGeneration) { packagesFilePath = string.Empty; // Work out what document this is. var projectDoc = definitions.First( x => x.Project.DocumentElement.Attributes["Name"].Value == projectName)?.Project; // Check to see if we have a Project node; if not // then this is an external or other type of project // that we don't process. if (projectDoc == null || projectDoc.DocumentElement.Name != "Project") return; // Load the appropriate project transformation XSLT. var languageAttribute = projectDoc.DocumentElement.Attributes["Language"]; var languageText = languageAttribute != null ? languageAttribute.Value : "C#"; var language = this.m_LanguageStringProvider.GetLanguageFromConfigurationName(languageText); var projectTransform = this.m_ResourceProvider.LoadXSLT(ResourceType.GenerateProject, language, platformName); // Work out what platforms this project should be generated for. var platformAttribute = projectDoc.DocumentElement.Attributes["Platforms"]; string[] allowedPlatforms = null; if (platformAttribute != null) { allowedPlatforms = platformAttribute.Value .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) .Select(x => x.Trim()) .ToArray(); } // Filter on allowed platforms. if (allowedPlatforms != null) { var allowed = false; foreach (var platform in allowedPlatforms) { if (string.Compare(platformName, platform, StringComparison.InvariantCultureIgnoreCase) == 0) { allowed = true; break; } } if (!allowed) { return; } } // If the project has a <Services> node, but there are no entries in /Input/Services, // then nothing depends on this service (if there is a <Reference> to this project, it will // use the default service). So for service-aware projects without any services being // referenced, we exclude them from the generation. if (this.m_ExcludedServiceAwareProjectDetector.IsExcludedServiceAwareProject( projectDoc.DocumentElement.Attributes["Name"].Value, projectDoc, services)) { return; } // Inform the user we're generating this project. onActualGeneration(); // Add include projects if they have an AppliesTo tag that matches this project's name. this._includeProjectAppliesToUpdater.UpdateProjectReferences(definitions.Select(x => x.Project).ToList(), projectDoc); // Add references and properties from include projects. _includeProjectMerger.MergeInReferencesAndPropertiesForIncludeProjects(definitions, projectDoc, platformName); // Imply external project references from other external projects. We do // this so that external projects can reference other external projects (which // we can't reasonably handle at the XSLT level since it's recursive). this.m_ExternalProjectReferenceResolver.ResolveExternalProjectReferences(definitions, projectDoc, platformName); // Generate Info.plist files if necessary (for Mac / iOS). this._mPlatformResourcesGenerator.GenerateInfoPListIfNeeded(definitions, current, projectDoc, platformName); // Work out what path to save at. var path = Path.Combine( rootPath, projectDoc.DocumentElement.Attributes["Path"].Value .Replace('\\', Path.DirectorySeparatorChar) .Replace('/', Path.DirectorySeparatorChar), projectDoc.DocumentElement.Attributes["Name"].Value + "." + platformName + "." + this.m_LanguageStringProvider.GetProjectExtension(language)); // Make sure that the directory exists where the file will be stored. var targetFile = new FileInfo(path); if (!targetFile.Directory.Exists) targetFile.Directory.Create(); path = targetFile.FullName; // Handle NuGet packages.config early so that it'll be in place // when the generator automatically determined dependencies. this.m_NuGetConfigMover.Move(rootPath, platformName, projectDoc); // Work out what path the NuGet packages.config might be at. var packagesFile = new FileInfo( Path.Combine( rootPath, projectDoc.DocumentElement.Attributes["Path"].Value .Replace('\\', Path.DirectorySeparatorChar) .Replace('/', Path.DirectorySeparatorChar), "packages.config")); // Generate the input document. var input = this.m_ProjectInputGenerator.Generate( definitions.Select(x => x.Project).ToList(), rootPath, projectName, platformName, packagesFile.FullName, projectDoc.DocumentElement.ChildNodes .OfType<XmlElement>() .Where(x => x.Name.ToLower() == "properties") .SelectMany(x => x.ChildNodes .OfType<XmlElement>()), services); // Transform the input document using the XSLT transform. var settings = new XmlWriterSettings(); settings.Indent = true; using (var writer = XmlWriter.Create(path, settings)) { projectTransform.Transform(input, writer); } // Also remove any left over .sln or .userprefs files. var slnPath = Path.Combine( rootPath, projectDoc.DocumentElement.Attributes["Path"].Value, projectDoc.DocumentElement.Attributes["Name"].Value + "." + platformName + ".sln"); var userprefsPath = Path.Combine( rootPath, projectDoc.DocumentElement.Attributes["Path"].Value, projectDoc.DocumentElement.Attributes["Name"].Value + "." + platformName + ".userprefs"); if (File.Exists(slnPath)) File.Delete(slnPath); if (File.Exists(userprefsPath)) File.Delete(userprefsPath); // Only return the package file path if it exists. if (packagesFile.Exists) packagesFilePath = packagesFile.FullName; }
public DefinitionSynchroniser(ModuleInfo moduleInfo, DefinitionInfo info, CSharpProject project) { this.m_ModuleInfo = moduleInfo; this.m_DefinitionInfo = info; this.m_CSharpProject = project; }
/// <summary> /// Generates a project at the target path. /// </summary> /// <param name="current">The current project to generate.</param> /// <param name="definitions">A list of all loaded project definitions.</param> /// <param name="workingDirectory"></param> /// <param name="rootPath">The module root path, with directory seperator appended.</param> /// <param name="projectName">The project name.</param> /// <param name="platformName">The platform name.</param> /// <param name="services">A list of services.</param> /// <param name="packagesFilePath"> /// Either the full path to the packages.config for the /// generated project if it exists, or an empty string. /// </param> /// <param name="onActualGeneration"></param> /// <param name="debugProjectGeneration">Whether to emit a .input file for XSLT debugging.</param> public void Generate( DefinitionInfo current, List <LoadedDefinitionInfo> definitions, string workingDirectory, string rootPath, string projectName, string platformName, List <Service> services, out string packagesFilePath, Action onActualGeneration, bool debugProjectGeneration) { packagesFilePath = string.Empty; // Work out what document this is. var projectDoc = definitions.First( x => x.Project.DocumentElement.Attributes["Name"].Value == projectName)?.Project; // Check to see if we have a Project node; don't process it. if (projectDoc?.DocumentElement == null) { return; } // If this is a custom project, run any custom generation commands for // the current host and target platform. if (projectDoc.DocumentElement.Name == "CustomProject") { onActualGeneration(); var onGenerateElements = projectDoc.DocumentElement.SelectNodes("OnGenerate"); if (onGenerateElements != null) { foreach (var onGenerateElement in onGenerateElements.OfType <XmlElement>()) { var matches = true; var hostName = onGenerateElement.GetAttribute("HostName"); var targetName = onGenerateElement.GetAttribute("TargetName"); if (!string.IsNullOrWhiteSpace(hostName)) { if (hostName != _hostPlatformDetector.DetectPlatform()) { matches = false; } } if (!string.IsNullOrWhiteSpace(targetName)) { if (targetName != platformName) { matches = false; } } if (matches) { var command = onGenerateElement.SelectSingleNode("Command")?.InnerText.Trim(); var arguments = onGenerateElement.SelectSingleNode("Arguments")?.InnerText.Trim() ?? string.Empty; if (!string.IsNullOrWhiteSpace(command)) { var workingPath = Path.Combine( rootPath, projectDoc.DocumentElement.Attributes["Path"].Value); var resolvedCommandPath = Path.Combine( workingPath, command); if (File.Exists(resolvedCommandPath)) { var process = Process.Start(new ProcessStartInfo(resolvedCommandPath, arguments) { WorkingDirectory = workingPath, UseShellExecute = false }); if (process == null) { RedirectableConsole.ErrorWriteLine("ERROR: Process did not start when running " + resolvedCommandPath + " " + arguments); ExecEnvironment.Exit(1); return; } process.WaitForExit(); if (process.ExitCode != 0) { RedirectableConsole.ErrorWriteLine( "ERROR: Non-zero exit code " + process.ExitCode); ExecEnvironment.Exit(1); return; } } } } } } } // If this is not a normal project at this point, don't process it. if (projectDoc.DocumentElement.Name != "Project") { return; } // Load the appropriate project transformation XSLT. var languageAttribute = projectDoc.DocumentElement.Attributes["Language"]; var languageText = languageAttribute != null ? languageAttribute.Value : "C#"; var language = this.m_LanguageStringProvider.GetLanguageFromConfigurationName(languageText); var projectTransform = this.m_ResourceProvider.LoadXSLT(workingDirectory, ResourceType.GenerateProject, language, platformName); // Work out what platforms this project should be generated for. var platformAttribute = projectDoc.DocumentElement.Attributes["Platforms"]; string[] allowedPlatforms = null; if (platformAttribute != null) { allowedPlatforms = platformAttribute.Value .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) .Select(x => x.Trim()) .ToArray(); } // Filter on allowed platforms. if (allowedPlatforms != null) { var allowed = false; foreach (var platform in allowedPlatforms) { if (string.Compare(platformName, platform, StringComparison.InvariantCultureIgnoreCase) == 0) { allowed = true; break; } } if (!allowed) { return; } } // If the project has a <Services> node, but there are no entries in /Input/Services, // then nothing depends on this service (if there is a <Reference> to this project, it will // use the default service). So for service-aware projects without any services being // referenced, we exclude them from the generation. if (this.m_ExcludedServiceAwareProjectDetector.IsExcludedServiceAwareProject( projectDoc.DocumentElement.Attributes["Name"].Value, projectDoc, services)) { return; } // Inform the user we're generating this project. onActualGeneration(); // Add include projects if they have an AppliesTo tag that matches this project's name. this._includeProjectAppliesToUpdater.UpdateProjectReferences(definitions.Select(x => x.Project).ToList(), projectDoc); // Add references and properties from include projects. _includeProjectMerger.MergeInReferencesAndPropertiesForIncludeProjects(definitions, projectDoc, platformName); // Imply external project references from other external projects. We do // this so that external projects can reference other external projects (which // we can't reasonably handle at the XSLT level since it's recursive). this.m_ExternalProjectReferenceResolver.ResolveExternalProjectReferences(definitions, projectDoc, platformName); // Generate Info.plist files if necessary (for Mac / iOS). this._mPlatformResourcesGenerator.GenerateInfoPListIfNeeded(definitions, current, projectDoc, platformName); // Work out what path to save at. var path = Path.Combine( rootPath, projectDoc.DocumentElement.Attributes["Path"].Value .Replace('\\', Path.DirectorySeparatorChar) .Replace('/', Path.DirectorySeparatorChar), projectDoc.DocumentElement.Attributes["Name"].Value + "." + platformName + "." + this.m_LanguageStringProvider.GetProjectExtension(language)); // Make sure that the directory exists where the file will be stored. var targetFile = new FileInfo(path); if (!targetFile.Directory.Exists) { targetFile.Directory.Create(); } path = targetFile.FullName; // Handle NuGet packages.config early so that it'll be in place // when the generator automatically determined dependencies. this.m_NuGetConfigMover.Move(rootPath, platformName, projectDoc); // Work out what path the NuGet packages.config might be at. var packagesFile = new FileInfo( Path.Combine( rootPath, projectDoc.DocumentElement.Attributes["Path"].Value .Replace('\\', Path.DirectorySeparatorChar) .Replace('/', Path.DirectorySeparatorChar), "packages.config")); // Generate the input document. var input = this.m_ProjectInputGenerator.Generate( definitions.Select(x => x.Project).ToList(), workingDirectory, rootPath, projectName, platformName, packagesFile.FullName, projectDoc.DocumentElement.ChildNodes .OfType <XmlElement>() .Where(x => x.Name.ToLower() == "properties") .SelectMany(x => x.ChildNodes .OfType <XmlElement>()), services); var settings = new XmlWriterSettings(); settings.Indent = true; if (debugProjectGeneration) { using (var writer = XmlWriter.Create(path + ".input", settings)) { input.Save(writer); } } // Transform the input document using the XSLT transform. using (var writer = XmlWriter.Create(path, settings)) { projectTransform.Transform(input, writer); } // Also remove any left over .sln or .userprefs files. var slnPath = Path.Combine( rootPath, projectDoc.DocumentElement.Attributes["Path"].Value, projectDoc.DocumentElement.Attributes["Name"].Value + "." + platformName + ".sln"); var userprefsPath = Path.Combine( rootPath, projectDoc.DocumentElement.Attributes["Path"].Value, projectDoc.DocumentElement.Attributes["Name"].Value + "." + platformName + ".userprefs"); if (File.Exists(slnPath)) { File.Delete(slnPath); } if (File.Exists(userprefsPath)) { File.Delete(userprefsPath); } // Only return the package file path if it exists. if (packagesFile.Exists) { packagesFilePath = packagesFile.FullName; } }
public string GetProjectOutputPathPrefix(string platform, DefinitionInfo definition, XDocument document, bool asRegex) { var outputMode = this.GetProjectOutputMode(document); // 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 (asRegex) { return(definition.RelativePath.Replace('\\', '/').Replace(".", "\\.") + "/bin/([^/]+)/"); } else { return(definition.RelativePath.Replace('\\', '/') + "/bin/"); } } 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; switch (platform.ToLowerInvariant()) { case "ios": { pathArchMatch = "iPhoneSimulator"; break; } case "windowsphone": { pathArchMatch = "([^/]+)"; break; } default: { pathArchMatch = "AnyCPU"; break; } } if (asRegex) { return(definition.RelativePath.Replace('\\', '/').Replace(".", "\\.") + "/bin/" + platform + "/" + pathArchMatch + "/([^/]+)/"); } else { return(definition.RelativePath.Replace('\\', '/') + "/bin/" + platform + "/" + pathArchMatch + "/"); } } case OutputPathMode.BinProjectPlatformArchConfiguration: { throw new NotSupportedException(); } } throw new NotSupportedException(); }
public LoadedDefinitionInfo Load(string targetPlatform, ModuleInfo module, DefinitionInfo definition) { var doc = new XmlDocument(); doc.Load(definition.DefinitionPath); // If this is a ContentProject, we actually need to generate the // full project node from the files that are in the Source folder. XmlDocument newDoc = null; if (doc.DocumentElement.Name == "ContentProject") { newDoc = this.m_ContentProjectGenerator.Generate(targetPlatform, doc, definition.ModulePath); } else { newDoc = doc; } if (module.Path != null && definition.ModulePath != null) { var additionalPath = PathUtils.GetRelativePath( module.Path.TrimEnd(System.IO.Path.DirectorySeparatorChar) + System.IO.Path.DirectorySeparatorChar, definition.ModulePath.TrimEnd(System.IO.Path.DirectorySeparatorChar) + System.IO.Path.DirectorySeparatorChar); if (newDoc.DocumentElement != null && newDoc.DocumentElement.Attributes["Path"] != null && additionalPath != null) { newDoc.DocumentElement.Attributes["Path"].Value = (additionalPath.Trim('\\') + '\\' + newDoc.DocumentElement.Attributes["Path"].Value).Replace('/', '\\').Trim('\\'); } if (newDoc.DocumentElement.Name == "ExternalProject") { // Need to find all descendant Binary and Project tags // and update their paths as well. var xDoc = newDoc.ToXDocument(); var projectsToUpdate = xDoc.Descendants().Where(x => x.Name == "Project"); var binariesToUpdate = xDoc.Descendants().Where(x => x.Name == "Binary"); var nativeBinariesToUpdate = xDoc.Descendants().Where(x => x.Name == "NativeBinary"); var toolsToUpdate = xDoc.Descendants().Where(x => x.Name == "Tool"); foreach (var pathToUpdate in projectsToUpdate.Concat(binariesToUpdate).Concat(nativeBinariesToUpdate).Concat(toolsToUpdate) .Where(x => x.Attribute("Path") != null)) { pathToUpdate.Attribute("Path").Value = (additionalPath.Trim('\\') + '\\' + pathToUpdate.Attribute("Path").Value).Replace('/', '\\').Trim('\\'); } newDoc = xDoc.ToXmlDocument(); } } // If the ProjectGuids element doesn't exist, create it. var projectGuids = doc.DocumentElement.ChildNodes.OfType <XmlElement>().FirstOrDefault(x => x.Name == "ProjectGuids"); if (projectGuids == null) { projectGuids = doc.CreateElement("ProjectGuids"); doc.DocumentElement.AppendChild(projectGuids); } // For all the supported platforms of this project, or all the // supported platforms of the module, generate GUIDs for any platform // that doesn't already exist. var platforms = doc.DocumentElement.GetAttribute("Platforms"); if (string.IsNullOrWhiteSpace(platforms)) { platforms = module.SupportedPlatforms; } if (string.IsNullOrWhiteSpace(platforms)) { platforms = ModuleInfo.GetSupportedPlatformsDefault(); } var platformsList = platforms.Split(',').ToList(); if (!platformsList.Contains(targetPlatform)) { platformsList.Add(targetPlatform); } foreach (var platform in platformsList) { var existing = projectGuids.ChildNodes.OfType <XmlElement>().FirstOrDefault(x => x.Name == "Platform" && x.HasAttribute("Name") && x.GetAttribute("Name") == platform); if (existing == null) { var platformElement = doc.CreateElement("Platform"); platformElement.SetAttribute("Name", platform); var name = doc.DocumentElement.GetAttribute("Name") + "." + platform; var guidBytes = new byte[16]; for (var i = 0; i < guidBytes.Length; i++) { guidBytes[i] = (byte)0; } var nameBytes = Encoding.ASCII.GetBytes(name); unchecked { for (var i = 0; i < nameBytes.Length; i++) { guidBytes[i % 16] += nameBytes[i]; } for (var i = nameBytes.Length; i < 16; i++) { guidBytes[i] += nameBytes[i % nameBytes.Length]; } } var guid = new Guid(guidBytes); platformElement.InnerText = guid.ToString().ToUpper(); projectGuids.AppendChild(platformElement); } } // If the Language property doesn't exist, set it to the default of "C#" if (doc.DocumentElement.Attributes["Language"] == null) { doc.DocumentElement.SetAttribute("Language", "C#"); } // Normalize the project type for backwards compatibility. var projectType = doc.DocumentElement.Attributes["Type"]; if (projectType == null) { doc.DocumentElement.SetAttribute("Type", "Library"); } else { switch (projectType.Value) { case "Library": case "App": case "Console": case "Website": break; case "GUI": case "XNA": case "GTK": doc.DocumentElement.SetAttribute("Type", "App"); break; } } return(new LoadedDefinitionInfo { Definition = definition, Project = newDoc, }); }
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 void GenerateInfoPListIfNeeded(List<LoadedDefinitionInfo> definitions, DefinitionInfo definition, XmlDocument project, string platform) { if (platform == "iOS" || platform == "MacOS") { var type = project.DocumentElement.GetAttribute("Type"); if (type == "Console" || type == "App") { var references = project.DocumentElement.SelectNodes("References/*").OfType<XmlElement>(); foreach (var reference in references) { var lookup = definitions.FirstOrDefault(x => x.Definition.Name == reference.GetAttribute("Include")); if (lookup != null && 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/> </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); } } } }
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"); }