Example #1
0
 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;
 }
Example #2
0
        /// <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());
        }
Example #3
0
        /// <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;
        }
Example #6
0
        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");
        }
Example #8
0
        /// <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;
                        }
                }
            }
        }
Example #13
0
        /// <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) + ".+$");
        }
Example #15
0
        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 DefinitionSynchroniser(DefinitionInfo info, CSharpProject project)
 {
     this.m_DefinitionInfo = info;
     this.m_CSharpProject = project;
 }
Example #17
0
 public virtual void FinalizeNewProject(ModuleInfo module, DefinitionInfo definition)
 {
 }
Example #18
0
 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();
        }
Example #21
0
        /// <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;
        }
Example #22
0
        /// <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.");
            }
        }
Example #23
0
        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,
            };
        }
Example #24
0
        /// <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;
        }
Example #25
0
 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;
            }
        }
Example #27
0
        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");
        }
 public DefinitionSynchroniser(ModuleInfo moduleInfo, DefinitionInfo info, CSharpProject project)
 {
     this.m_ModuleInfo = moduleInfo;
     this.m_DefinitionInfo = info;
     this.m_CSharpProject = project;
 }