Exemple #1
0
        public void ParseAndApply(FileFilter result, Stream inputFilterFile, Dictionary <string, Action <FileFilter> > customDirectives)
        {
            var    isSlashed = false;
            Action init      = () =>
            {
                isSlashed = false;
            };
            Func <char, bool> splitter = c =>
            {
                if (c == '\\')
                {
                    isSlashed = true;
                    return(false);
                }

                if (c == ' ' && !isSlashed)
                {
                    return(true);
                }

                isSlashed = false;
                return(false);
            };

            using (var reader = new StreamReader(inputFilterFile))
            {
                while (!reader.EndOfStream)
                {
                    var line = reader.ReadLine();
                    if (line.TrimStart().StartsWith("#") || line.Trim() == "")
                    {
                        continue;
                    }
                    var mode = line.Split(splitter, 2).ToStringArray()[0];
                    switch (mode)
                    {
                    case "include":
                        result.ApplyInclude(line.Init(init).Split(splitter, 2).ToStringArray()[1]);
                        break;

                    case "exclude":
                        result.ApplyExclude(line.Init(init).Split(splitter, 2).ToStringArray()[1]);
                        break;

                    case "rewrite":
                        result.ApplyRewrite(line.Init(init).Split(splitter, 3).ToStringArray()[1], line.Split(splitter, 3).ToStringArray()[2]);
                        break;

                    default:
                        if (customDirectives.ContainsKey(mode))
                        {
                            customDirectives[mode](result);
                        }
                        break;
                    }
                }
            }
            return;
        }
        private void AutomaticallyPackageNormalProject(
            DefinitionInfo[] definitions,
            List <Service> services,
            FileFilter fileFilter,
            string rootPath,
            string platform,
            DefinitionInfo definition)
        {
            var document = XDocument.Load(definition.DefinitionPath);
            var platformSpecificOutputFolderElement = document.XPathSelectElement("/Project/Properties/PlatformSpecificOutputFolder");
            var projectSpecificOutputFolderElement  = document.XPathSelectElement("/Project/Properties/ProjectSpecificOutputFolder");
            var assemblyNameForPlatformElement      = document.XPathSelectElement("/Project/Properties/AssemblyName/Platform[@Name=\"" + platform + "\"]");
            var assemblyNameGlobalElement           = document.XPathSelectElement("/Project/Properties/Property[@Name=\"AssemblyName\"]");
            var platformSpecificOutputFolder        = true;
            var projectSpecificOutputFolder         = false;

            if (platformSpecificOutputFolderElement != null)
            {
                platformSpecificOutputFolder = platformSpecificOutputFolderElement.Value.ToLowerInvariant() != "false";
            }
            if (projectSpecificOutputFolderElement != null)
            {
                projectSpecificOutputFolder = projectSpecificOutputFolderElement.Value.ToLowerInvariant() == "true";
            }

            string assemblyName = null;

            if (assemblyNameForPlatformElement != null)
            {
                assemblyName = assemblyNameForPlatformElement.Value;
            }
            else if (assemblyNameGlobalElement != null)
            {
                assemblyName = assemblyNameGlobalElement.Value;
            }
            else
            {
                assemblyName = definition.Name;
            }

            var assemblyFilesToCopy = new[]
            {
                assemblyName + ".exe",
                assemblyName + ".dll",
                assemblyName + ".dll.config",
                assemblyName + ".dll.mdb",
                assemblyName + ".pdb",
                assemblyName + ".xml",
            };

            var outputMode = OutputPathMode.BinConfiguration;

            if (projectSpecificOutputFolder)
            {
                outputMode = OutputPathMode.BinProjectPlatformArchConfiguration;
            }
            if (platformSpecificOutputFolder)
            {
                outputMode = OutputPathMode.BinPlatformArchConfiguration;
            }

            var externalProjectDocument = new XmlDocument();

            externalProjectDocument.AppendChild(externalProjectDocument.CreateXmlDeclaration("1.0", "UTF-8", null));
            var externalProject = externalProjectDocument.CreateElement("ExternalProject");

            externalProjectDocument.AppendChild(externalProject);
            externalProject.SetAttribute("Name", definition.Name);

            // Copy the assembly itself out to the package.
            switch (outputMode)
            {
            case OutputPathMode.BinConfiguration:
            {
                // In this configuration, we only ship the binaries for
                // the default architecture (because that's all we know
                // about).  We also have to assume the binary folder
                // contains binaries for the desired platform.
                var pathPrefix = definition.Path.Replace('\\', '/').Replace(".", "\\.") + "/bin/([^/]+)/";

                if (definition.Type == "Library")
                {
                    // For libraries, we only copy the assembly (and immediately related files)
                    // into it's directory. Native binaries will be expressed through the ExternalProject.
                    foreach (var assemblyFile in assemblyFilesToCopy)
                    {
                        var includeMatch = fileFilter.ApplyInclude("^" + pathPrefix + Regex.Escape(assemblyFile) + "$");
                        var rewriteMatch = fileFilter.ApplyRewrite("^" + pathPrefix + Regex.Escape(assemblyFile) + "$", definition.Name + "/AnyCPU/" + assemblyFile);
                        if (includeMatch && rewriteMatch)
                        {
                            if (assemblyFile.EndsWith(".dll"))
                            {
                                var binaryEntry = externalProjectDocument.CreateElement("Binary");
                                binaryEntry.SetAttribute("Name", assemblyFile.Substring(0, assemblyFile.Length - 4));
                                binaryEntry.SetAttribute("Path", definition.Name + "\\AnyCPU\\" + assemblyFile);
                                externalProject.AppendChild(binaryEntry);
                            }
                            else if (assemblyFile.EndsWith(".dll.config"))
                            {
                                var configEntry = externalProjectDocument.CreateElement("NativeBinary");
                                configEntry.SetAttribute("Path", definition.Name + "\\AnyCPU\\" + assemblyFile);
                                externalProject.AppendChild(configEntry);
                            }
                        }
                        else if (includeMatch || rewriteMatch)
                        {
                            throw new InvalidOperationException("Automatic filter; only one rule matched.");
                        }
                    }
                }
                else
                {
                    // For executables, we ship everything in the output directory, because we
                    // want the executables to be able to run from the package directory.
                    fileFilter.ApplyInclude("^" + pathPrefix + "(.+)$");
                    fileFilter.ApplyRewrite("^" + pathPrefix + "(.+)$", definition.Name + "/AnyCPU/$2");
                }

                break;
            }

            case OutputPathMode.BinPlatformArchConfiguration:
            {
                // In this configuration, we ship binaries for AnyCPU, iPhone or all .NET architectures
                // depending on whether or not the platform produces multiple architectures.  On Mono,
                // we can't use $(Platform) within a reference's path, so we have to keep this path static
                // for Mono platforms.
                string pathArchMatch, pathArchReplace, pathArchRuntime;
                switch (platform.ToLowerInvariant())
                {
                case "ios":
                {
                    pathArchMatch   = "iPhone";
                    pathArchReplace = "iPhone";
                    pathArchRuntime = "iPhone";
                    break;
                }

                case "windowsphone":
                {
                    pathArchMatch   = "([^/]+)";
                    pathArchReplace = "$1";
                    pathArchRuntime = "$(Platform)";
                    break;
                }

                default:
                {
                    pathArchMatch   = "AnyCPU";
                    pathArchReplace = "AnyCPU";
                    pathArchRuntime = "AnyCPU";
                    break;
                }
                }

                var pathPrefix = definition.Path.Replace('\\', '/').Replace(".", "\\.") + "/bin/" + platform + "/" + pathArchMatch + "/([^/]+)/";

                if (definition.Type == "Library")
                {
                    // For libraries, we only copy the assembly (and immediately related files)
                    // into it's directory. Native binaries will be expressed through the ExternalProject.
                    foreach (var assemblyFile in assemblyFilesToCopy)
                    {
                        var includeMatch = fileFilter.ApplyInclude("^" + pathPrefix + Regex.Escape(assemblyFile) + "$");
                        var rewriteMatch = fileFilter.ApplyRewrite("^" + pathPrefix + Regex.Escape(assemblyFile) + "$", definition.Name + "/" + pathArchReplace + "/" + assemblyFile);
                        if (includeMatch && rewriteMatch)
                        {
                            if (assemblyFile.EndsWith(".dll"))
                            {
                                var binaryEntry = externalProjectDocument.CreateElement("Binary");
                                binaryEntry.SetAttribute("Name", assemblyFile.Substring(0, assemblyFile.Length - 4));
                                binaryEntry.SetAttribute("Path", definition.Name + "\\" + pathArchRuntime + "\\" + assemblyFile);
                                externalProject.AppendChild(binaryEntry);
                            }
                            else if (assemblyFile.EndsWith(".dll.config"))
                            {
                                var configEntry = externalProjectDocument.CreateElement("NativeBinary");
                                configEntry.SetAttribute("Path", definition.Name + "\\" + pathArchRuntime + "\\" + assemblyFile);
                                externalProject.AppendChild(configEntry);
                            }
                        }
                        else if (includeMatch || rewriteMatch)
                        {
                            throw new InvalidOperationException("Automatic filter; only one rule matched.");
                        }
                    }
                }
                else
                {
                    // For executables, we ship everything in the output directory, because we
                    // want the executables to be able to run from the package directory.
                    fileFilter.ApplyInclude("^" + pathPrefix + "(.+)$");

                    if (pathArchMatch == "([^/]+)")
                    {
                        fileFilter.ApplyRewrite("^" + pathPrefix + "(.+)$", definition.Name + "/" + pathArchReplace + "/$3");
                    }
                    else
                    {
                        fileFilter.ApplyRewrite("^" + pathPrefix + "(.+)$", definition.Name + "/" + pathArchReplace + "/$2");
                    }
                }

                break;
            }

            case OutputPathMode.BinProjectPlatformArchConfiguration:
            {
                throw new NotSupportedException();
                break;
            }
            }

            // Convert all of the known references into references within the external project.
            var definitionsByName = definitions.ToDictionary(k => k.Name, v => v);

            foreach (var reference in document.XPathSelectElements("/Project/References/Reference"))
            {
                var includeAttribute = reference.Attribute(XName.Get("Include"));
                if (includeAttribute != null)
                {
                    if (definitionsByName.ContainsKey(includeAttribute.Value))
                    {
                        // This reference will be converted to an external project,
                        // so add a reference to it (in case it contains native binaries
                        // which need to be copied out).
                        var referenceEntry = externalProjectDocument.CreateElement("Reference");
                        referenceEntry.SetAttribute("Include", includeAttribute.Value);
                        externalProject.AppendChild(referenceEntry);
                    }
                }
            }

            // Copy out any files that are marked with copy-on-build flag.
            var detector    = new PlatformAndServiceActiveDetection();
            var xmlDocument = new XmlDocument();

            xmlDocument.Load(definition.DefinitionPath);
            var servicesInput         = this.m_ServiceInputGenerator.Generate(xmlDocument, definition.Name, services);
            var activeServicesElement = servicesInput.ChildNodes.OfType <XmlElement>().FirstOrDefault(x => x.LocalName == "ActiveServicesNames");
            var activeServices        = activeServicesElement.InnerText;

            foreach (var file in document.XPathSelectElements("/Project/Files/*"))
            {
                var copyOnBuild = file.XPathSelectElement("CopyToOutputDirectory");
                if (copyOnBuild == null)
                {
                    continue;
                }

                if (copyOnBuild.Value != "PreserveNewest" && copyOnBuild.Value != "Always")
                {
                    continue;
                }

                var platformsElement        = file.XPathSelectElement("Platforms");
                var includePlatformsElement = file.XPathSelectElement("IncludePlatforms");
                var excludePlatformsElement = file.XPathSelectElement("ExcludePlatforms");
                var servicesElement         = file.XPathSelectElement("Services");
                var includeServicesElement  = file.XPathSelectElement("IncludeServices");
                var excludeServicesElement  = file.XPathSelectElement("ExcludeServices");

                var platformsString        = platformsElement != null ? platformsElement.Value : string.Empty;
                var includePlatformsString = includePlatformsElement != null ? includePlatformsElement.Value : string.Empty;
                var excludePlatformsString = excludePlatformsElement != null ? excludePlatformsElement.Value : string.Empty;
                var servicesString         = servicesElement != null ? servicesElement.Value : string.Empty;
                var includeServicesString  = includeServicesElement != null ? includeServicesElement.Value : string.Empty;
                var excludeServicesString  = excludeServicesElement != null ? excludeServicesElement.Value : string.Empty;

                if (detector.ProjectAndServiceIsActive(
                        platformsString,
                        includePlatformsString,
                        excludePlatformsString,
                        servicesString,
                        includeServicesString,
                        excludeServicesString,
                        platform,
                        activeServices))
                {
                    var include     = file.Attribute(XName.Get("Include"));
                    var linkElement = file.XPathSelectElement("Link");
                    var link        = linkElement != null ? linkElement.Value : include.Value;

                    var fileInfo = new FileInfo(Path.Combine(
                                                    definition.ModulePath.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar),
                                                    definition.Path.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar),
                                                    include.Value.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar)));

                    if (definition.Type == "Library")
                    {
                        // For libraries, we're copying the content so that applications using
                        // the libraries will have the content.  This is most often used to
                        // ship native binaries (although NativeBinary in external projects now
                        // supersedes this).
                        if (link.Contains('/') || link.Contains('\\'))
                        {
                            Console.WriteLine(
                                "WARNING: Copy-on-build file '" + link + "' in library project which " +
                                "does not output to root of project detected.  This is not supported.");
                        }
                        else
                        {
                            if (fileInfo.Name != link)
                            {
                                Console.WriteLine(
                                    "WARNING: Copy-on-build file in library project does not have the same " +
                                    "name when copied to build directory.  This is not supported.");
                            }
                            else
                            {
                                var sourcePath = fileInfo.FullName;
                                sourcePath = sourcePath.Substring(rootPath.Length).Replace('\\', '/').TrimStart('/');
                                var destPath = Path.Combine("_AutomaticExternals", sourcePath);

                                var sourcePathRegex = this.ConvertPathWithMSBuildVariablesFind(sourcePath);
                                var destPathRegex   = this.ConvertPathWithMSBuildVariablesReplace(destPath.Replace('\\', '/'));

                                var includeMatch = fileFilter.ApplyInclude(sourcePathRegex);
                                fileFilter.ApplyRewrite(sourcePathRegex, destPathRegex);
                                if (includeMatch)
                                {
                                    var nativeBinaryEntry = externalProjectDocument.CreateElement("NativeBinary");
                                    nativeBinaryEntry.SetAttribute("Path", destPath);
                                    externalProject.AppendChild(nativeBinaryEntry);
                                }
                                else
                                {
                                    throw new InvalidOperationException(
                                              "File not found at " + sourcePath + " when converting " +
                                              "copy-on-build file in library project.");
                                }
                            }
                        }
                    }
                }
            }

            // Write out the external project to a temporary file and include it.
            var name = Path.GetRandomFileName() + "_" + definition.Name + ".xml";
            var temp = Path.Combine(Path.GetTempPath(), name);

            using (var writer = XmlWriter.Create(temp, new XmlWriterSettings {
                Indent = true, IndentChars = "  "
            }))
            {
                externalProjectDocument.WriteTo(writer);
            }
            fileFilter.AddManualMapping(temp, "Build/Projects/" + definition.Name + ".definition");
        }
        private void TranslateExternalProject(
            DefinitionInfo definition,
            List <Service> services,
            FileFilter fileFilter,
            string rootPath,
            string platform,
            XmlElement fromNode,
            XmlElement toNode,
            bool isNested)
        {
            foreach (var child in fromNode.ChildNodes.OfType <XmlElement>())
            {
                switch (child.LocalName)
                {
                case "Reference":
                {
                    // These are included as-is; either they're references to GAC assemblies
                    // or other Protobuild projects (which are now external projects themselves).
                    var referenceEntry = toNode.OwnerDocument.ImportNode(child, true);
                    toNode.AppendChild(referenceEntry);
                    break;
                }

                case "Binary":
                {
                    // We have to change the path on these files so that the path is nested
                    // underneath the "_AutomaticExternals" folder.  Since these are .NET references, we also
                    // want to copy all of the related files (config, xml, etc.)
                    var sourceFileInfo         = new FileInfo(Path.Combine(definition.ModulePath, child.GetAttribute("Path")));
                    var sourceWithoutExtension = sourceFileInfo.FullName.Substring(
                        0,
                        sourceFileInfo.FullName.Length - sourceFileInfo.Extension.Length);
                    var destWithoutExtension = child.GetAttribute("Path").Substring(
                        0,
                        child.GetAttribute("Path").Length - sourceFileInfo.Extension.Length);

                    var fileExtensionsToCopy = new[]
                    {
                        sourceFileInfo.Extension,
                        sourceFileInfo.Extension + ".config",
                        sourceFileInfo.Extension + ".mdb",
                        ".pdb",
                        ".xml",
                    };

                    foreach (var extension in fileExtensionsToCopy)
                    {
                        var sourcePath = sourceWithoutExtension + extension;
                        sourcePath = sourcePath.Substring(rootPath.Length).Replace('\\', '/').TrimStart('/');
                        var destPath = Path.Combine("_AutomaticExternals", destWithoutExtension + extension);

                        var sourcePathRegex = this.ConvertPathWithMSBuildVariablesFind(sourcePath);
                        var destPathRegex   = this.ConvertPathWithMSBuildVariablesReplace(destPath.Replace('\\', '/'));

                        var includeMatch = fileFilter.ApplyInclude(sourcePathRegex);
                        fileFilter.ApplyRewrite(sourcePathRegex, destPathRegex);
                        if (includeMatch)
                        {
                            if (extension == sourceFileInfo.Extension)
                            {
                                var binaryEntry = toNode.OwnerDocument.CreateElement("Binary");
                                binaryEntry.SetAttribute("Name", child.GetAttribute("Name"));
                                binaryEntry.SetAttribute("Path", destPath);
                                toNode.AppendChild(binaryEntry);
                            }
                            else if (extension == sourceFileInfo.Extension + ".config")
                            {
                                var nativeBinaryEntry = toNode.OwnerDocument.CreateElement("NativeBinary");
                                nativeBinaryEntry.SetAttribute("Path", destPath);
                                toNode.AppendChild(nativeBinaryEntry);
                            }
                        }
                    }

                    break;
                }

                case "NativeBinary":
                {
                    // We have to change the path on these files so that the path is nested
                    // underneath the "_AutomaticExternals" folder.
                    var sourcePath = Path.Combine(definition.ModulePath, child.GetAttribute("Path"));
                    sourcePath = sourcePath.Substring(rootPath.Length).Replace('\\', '/').TrimStart('/');
                    var destPath = Path.Combine("_AutomaticExternals", child.GetAttribute("Path"));

                    var sourcePathRegex = this.ConvertPathWithMSBuildVariablesFind(sourcePath);
                    var destPathRegex   = this.ConvertPathWithMSBuildVariablesReplace(destPath.Replace('\\', '/'));

                    var includeMatch = fileFilter.ApplyInclude(sourcePathRegex);
                    fileFilter.ApplyRewrite(sourcePathRegex, destPathRegex);
                    if (includeMatch)
                    {
                        var nativeBinaryEntry = toNode.OwnerDocument.CreateElement("NativeBinary");
                        nativeBinaryEntry.SetAttribute("Path", destPath);
                        toNode.AppendChild(nativeBinaryEntry);
                    }
                    else
                    {
                        throw new InvalidOperationException("File not found at " + sourcePath + " when converting NativeBinary reference.");
                    }

                    break;
                }

                case "Project":
                {
                    // We can't do anything with these tags, as we don't know how the target C# project
                    // is built (without investing heavily into how MSBuild builds projects).  Instead,
                    // show a warning that this reference will be converted to an external project
                    // reference instead, so that the developer can manually hook this up through
                    // additional directives in the filter file.
                    Console.WriteLine(
                        "WARNING: The 'Project' tag in external projects can not be " +
                        "automatically converted during packaging.  This reference " +
                        "will be converted to refer to an external reference with " +
                        "the name '" + child.GetAttribute("Name") + "' instead, and you'll " +
                        "need to write an ExternalProject definition (and include it via " +
                        "a filter file) to have this reference packaged correctly.");
                    var referenceEntry = toNode.OwnerDocument.CreateElement("Reference");
                    referenceEntry.SetAttribute("Include", child.GetAttribute("Name"));
                    toNode.AppendChild(referenceEntry);
                    break;
                }

                case "Platform":
                {
                    // If the Platform tag matches, copy the contents of it directly into our top-level
                    // external project.  We don't need to copy the Platform tag itself, as packages are
                    // specific to a given platform.
                    if (child.GetAttribute("Type").ToLowerInvariant() == platform.ToLowerInvariant())
                    {
                        this.TranslateExternalProject(definition, services, fileFilter, rootPath, platform, child, toNode, true);
                    }

                    break;
                }

                case "Service":
                {
                    // If the service is enabled, then copy the contents of the service section directly
                    // into our top-level external project.  We don't need to copy the Service tag itself,
                    // as changing services won't have any effect on binary packages.
                    var service = services.FirstOrDefault(x =>
                                                          x.FullName == child.GetAttribute("Name") ||
                                                          x.FullName == definition.Name + "/" + child.GetAttribute("Name"));
                    if (service != null)
                    {
                        // If the service is present in the list, then it is enabled.
                        this.TranslateExternalProject(definition, services, fileFilter, rootPath, platform, child, toNode, true);
                    }

                    break;
                }

                default:
                {
                    // We can't do anything with these tags, because we don't know what services were enabled
                    // when the assemblies were built.  Show a warning instead.
                    Console.WriteLine("WARNING: Unknown tag '" + child.LocalName + "' encountered.");
                    break;
                }
                }
            }
        }
Exemple #4
0
        public byte[] Transform(string workingDirectory, string url, string gitReference, string platform, string format)
        {
            var urlAndPackageName = url.Split(new[] { '|' }, 2);

            if (urlAndPackageName.Length != 2)
            {
                RedirectableConsole.ErrorWriteLine(
                    "ERROR: Malformed NuGet package reference '" + url +
                    "'.  Make sure you split the NuGet server URL and the package name with a pipe character (|).");
                ExecEnvironment.Exit(1);
            }

            var repoUrl     = urlAndPackageName[0];
            var packageName = urlAndPackageName[1];

            var originalFolder = DownloadOrUseExistingNuGetPackage(repoUrl.TrimEnd('/'), packageName, gitReference);

            if (Directory.Exists(Path.Combine(originalFolder, "protobuild")))
            {
                // This is a Protobuild-aware NuGet package.  In this case, we just use the contents of the
                // "protobuild" folder as the content of our package, and ignore everything else.
                RedirectableConsole.WriteLine("Detected Protobuild-aware package...");

                RedirectableConsole.WriteLine("Converting to a Protobuild package...");

                var target = new MemoryStream();
                var filter = new FileFilter(_getRecursiveUtilitiesInPath.GetRecursiveFilesInPath(originalFolder));

                filter.ApplyInclude("protobuild/(.*)");
                filter.ApplyRewrite("protobuild/(.*)", "$1");

                filter.ImplyDirectories();

                _packageCreator.Create(
                    target,
                    filter,
                    originalFolder,
                    format,
                    platform);

                RedirectableConsole.WriteLine("Package conversion complete.");
                var bytes2 = new byte[target.Position];
                target.Seek(0, SeekOrigin.Begin);
                target.Read(bytes2, 0, bytes2.Length);
                return(bytes2);
            }

            var folder = Path.GetTempFileName();

            File.Delete(folder);
            Directory.CreateDirectory(folder);

            byte[] bytes;
            try
            {
                RedirectableConsole.WriteLine("Copying directory for package transformation...");
                CopyFolder(new DirectoryInfo(originalFolder), new DirectoryInfo(folder));

                RedirectableConsole.WriteLine("Auto-detecting libraries to reference from NuGet package...");

                var packagePath         = new DirectoryInfo(folder).GetFiles("*.nuspec").First().FullName;
                var libraryReferences   = new Dictionary <string, string>();
                var packageDependencies = new Dictionary <string, string>();

                // Use the nuspec file if it exists.
                List <string> references = new List <string>();
                if (File.Exists(packagePath))
                {
                    var packageDoc = new XmlDocument();
                    packageDoc.Load(packagePath);

                    // If the references are explicitly provided in the nuspec, use
                    // those as to what files should be referenced by the projects.
                    if (
                        packageDoc.DocumentElement.FirstChild.ChildNodes.OfType <XmlElement>()
                        .Count(x => x.Name == "references") > 0)
                    {
                        references =
                            packageDoc.DocumentElement.FirstChild.ChildNodes.OfType <XmlElement>()
                            .First(x => x.Name == "references")
                            .ChildNodes.OfType <XmlElement>()
                            .Where(x => x.Name == "reference")
                            .Select(x => x.Attributes["file"].Value)
                            .ToList();
                    }

                    // If there are dependencies specified, store them and convert them to
                    // Protobuild references, and reference them in the Module.xml file.
                    if (
                        packageDoc.DocumentElement.FirstChild.ChildNodes.OfType <XmlElement>()
                        .Count(x => x.Name == "dependencies") > 0)
                    {
                        packageDependencies =
                            packageDoc.DocumentElement.FirstChild.ChildNodes.OfType <XmlElement>()
                            .First(x => x.Name == "dependencies")
                            .ChildNodes.OfType <XmlElement>()
                            .Where(x => x.Name == "dependency")
                            .ToDictionarySafe(
                                k => k.Attributes["id"].Value,
                                v => v.Attributes["version"].Value,
                                (dict, c) =>
                                RedirectableConsole.WriteLine("WARNING: More than one dependency on " + c +
                                                              " in NuGet package."));
                    }
                }

                // Determine the priority of the frameworks that we want to target
                // out of the available versions.
                string[] clrNames = _nuGetPlatformMapping.GetFrameworkNamesForRead(workingDirectory, platform);

                var referenceDirectories = new string[] { "ref", "lib" };

                foreach (var directory in referenceDirectories)
                {
                    // Determine the base path for all references; that is, the lib/ folder.
                    var referenceBasePath = Path.Combine(
                        folder,
                        directory);

                    if (Directory.Exists(referenceBasePath))
                    {
                        // If no references are in nuspec, reference all of the libraries that
                        // are on disk.
                        if (references.Count == 0)
                        {
                            // Search through all of the target frameworks until we find one that
                            // has at least one file in it.
                            foreach (var clrNameOriginal in clrNames)
                            {
                                var clrName  = clrNameOriginal;
                                var foundClr = false;

                                if (clrName[0] == '=')
                                {
                                    // Exact match (strip the equals).
                                    clrName = clrName.Substring(1);

                                    // If this target framework doesn't exist for this library, skip it.
                                    var dirPath = Path.Combine(
                                        referenceBasePath,
                                        clrName);
                                    if (!Directory.Exists(dirPath))
                                    {
                                        continue;
                                    }
                                }
                                else if (clrName[0] == '?')
                                {
                                    // Substring, search the reference base path for any folders
                                    // with a matching substring.
                                    clrName = clrName.Substring(1);

                                    var baseDirPath = referenceBasePath;
                                    var found       = false;
                                    foreach (var subdir in new DirectoryInfo(baseDirPath).GetDirectories())
                                    {
                                        if (subdir.Name.Contains(clrName))
                                        {
                                            clrName = subdir.Name;
                                            found   = true;
                                            break;
                                        }
                                    }

                                    if (!found)
                                    {
                                        continue;
                                    }
                                }
                                else
                                {
                                    throw new InvalidOperationException("Unknown CLR name match type with '" + clrName +
                                                                        "'");
                                }

                                // Otherwise enumerate through all of the libraries in this folder.
                                foreach (var dll in Directory.EnumerateFiles(
                                             Path.Combine(
                                                 referenceBasePath, clrName),
                                             "*.dll"))
                                {
                                    // Determine the relative path to the library.
                                    var packageDll = Path.Combine(
                                        referenceBasePath,
                                        clrName,
                                        Path.GetFileName(dll));

                                    // Confirm again that the file actually exists on disk when
                                    // combined with the root path.
                                    if (File.Exists(
                                            Path.Combine(
                                                packageDll)))
                                    {
                                        // Create the library reference.
                                        if (!libraryReferences.ContainsKey(Path.GetFileNameWithoutExtension(dll)))
                                        {
                                            libraryReferences.Add(
                                                Path.GetFileNameWithoutExtension(dll),
                                                packageDll);
                                        }

                                        // Mark this target framework as having provided at least
                                        // one reference.
                                        foundClr = true;
                                    }
                                }

                                // Break if we have found at least one reference.
                                if (foundClr)
                                {
                                    break;
                                }
                            }
                        }

                        // For all of the references that were found in the original nuspec file,
                        // add those references.
                        foreach (var reference in references)
                        {
                            // Search through all of the target frameworks until we find the one
                            // that has the reference in it.
                            foreach (var clrName in clrNames)
                            {
                                // If this target framework doesn't exist for this library, skip it.
                                var packageDll = Path.Combine(
                                    referenceBasePath,
                                    clrName,
                                    reference);

                                if (File.Exists(
                                        Path.Combine(
                                            packageDll)))
                                {
                                    if (!libraryReferences.ContainsKey(Path.GetFileNameWithoutExtension(packageDll)))
                                    {
                                        libraryReferences.Add(
                                            Path.GetFileNameWithoutExtension(packageDll),
                                            packageDll);
                                    }
                                    break;
                                }
                            }
                        }
                    }
                }

                foreach (var kv in libraryReferences)
                {
                    RedirectableConsole.WriteLine("Found library to reference: " + kv.Key + " (at " + kv.Value + ")");
                }

                RedirectableConsole.WriteLine("Generating external project reference...");
                var document        = new XmlDocument();
                var externalProject = document.CreateElement("ExternalProject");
                externalProject.SetAttribute("Name", packageName);
                document.AppendChild(externalProject);
                foreach (var kv in libraryReferences)
                {
                    var binaryReference = document.CreateElement("Binary");
                    binaryReference.SetAttribute("Name", kv.Key);
                    binaryReference.SetAttribute("Path",
                                                 kv.Value.Substring(folder.Length).TrimStart(new[] { '/', '\\' }).Replace("%2B", "-"));
                    externalProject.AppendChild(binaryReference);
                }
                foreach (var package in packageDependencies)
                {
                    var externalReference = document.CreateElement("Reference");
                    externalReference.SetAttribute("Include", package.Key);
                    externalProject.AppendChild(externalReference);
                }
                document.Save(Path.Combine(folder, "_ProtobuildExternalProject.xml"));

                RedirectableConsole.WriteLine("Generating module...");
                var generatedModule = new ModuleInfo();
                generatedModule.Name     = packageName;
                generatedModule.Packages = new List <PackageRef>();

                foreach (var package in packageDependencies)
                {
                    generatedModule.Packages.Add(new PackageRef
                    {
                        Uri =
                            repoUrl.Replace("http://", "http-nuget://").Replace("https://", "https-nuget://") + "|" +
                            package.Key,
                        GitRef = package.Value.TrimStart('[').TrimEnd(']'),
                        Folder = package.Key
                    });
                }

                generatedModule.Save(Path.Combine(folder, "_ProtobuildModule.xml"));

                RedirectableConsole.WriteLine("Converting to a Protobuild package...");

                var target = new MemoryStream();
                var filter = new FileFilter(_getRecursiveUtilitiesInPath.GetRecursiveFilesInPath(folder));

                foreach (var kv in libraryReferences)
                {
                    filter.ApplyInclude(
                        Regex.Escape(kv.Value.Substring(folder.Length).Replace('\\', '/').TrimStart('/')));
                    filter.ApplyRewrite(
                        Regex.Escape(kv.Value.Substring(folder.Length).Replace('\\', '/').TrimStart('/')),
                        kv.Value.Substring(folder.Length).Replace('\\', '/').TrimStart('/').Replace("%2B", "-"));
                }

                filter.ApplyInclude("_ProtobuildExternalProject\\.xml");
                filter.ApplyRewrite("_ProtobuildExternalProject\\.xml", "Build/Projects/" + packageName + ".definition");
                filter.ApplyInclude("_ProtobuildModule\\.xml");
                filter.ApplyRewrite("_ProtobuildModule\\.xml", "Build/Module.xml");

                filter.ImplyDirectories();

                _packageCreator.Create(
                    target,
                    filter,
                    folder,
                    format,
                    platform);

                RedirectableConsole.WriteLine("Package conversion complete.");
                bytes = new byte[target.Position];
                target.Seek(0, SeekOrigin.Begin);
                target.Read(bytes, 0, bytes.Length);
            }
            finally
            {
                RedirectableConsole.WriteLine("Cleaning up temporary data...");
                PathUtils.AggressiveDirectoryDelete(folder);
            }

            return(bytes);
        }
        private void AutomaticallyPackageNormalProject(
            DefinitionInfo[] definitions,
            List <Service> services,
            FileFilter fileFilter,
            string rootPath,
            string platform,
            DefinitionInfo definition,
            List <string> temporaryFiles)
        {
            var document = XDocument.Load(definition.DefinitionPath);

            var externalProjectDocument = new XmlDocument();

            externalProjectDocument.AppendChild(externalProjectDocument.CreateXmlDeclaration("1.0", "UTF-8", null));
            var externalProject = externalProjectDocument.CreateElement("ExternalProject");

            externalProjectDocument.AppendChild(externalProject);
            externalProject.SetAttribute("Name", definition.Name);

            if (definition.PostBuildHook)
            {
                externalProject.SetAttribute("PostBuildHook", "True");
            }

            var externalProjectServices = externalProjectDocument.CreateElement("Services");

            externalProject.AppendChild(externalProjectServices);

            // Just import all declared services as available, regardless of conflicts or
            // requirements.  We don't have a clean way of automatically translating
            // services for packages (it has to be done manually because services can
            // change the resulting code that's built).  So that things work 95% of time,
            // just declare all services available for projects included via the automatic
            // packaging mechanism.
            var servicesToDeclare = new List <string>();
            var servicesDeclared  = document.Root.Element(XName.Get("Services"));

            if (servicesDeclared != null)
            {
                foreach (var serviceElement in servicesDeclared.Elements().Where(x => x.Name.LocalName == "Service"))
                {
                    servicesToDeclare.Add(serviceElement.Attribute(XName.Get("Name")).Value);
                }
            }

            foreach (var serviceToDeclare in servicesToDeclare)
            {
                var serviceElem = externalProjectDocument.CreateElement("Service");
                serviceElem.SetAttribute("Name", serviceToDeclare);
                var defaultForRoot = externalProjectDocument.CreateElement("DefaultForRoot");
                defaultForRoot.InnerText = "True";
                serviceElem.AppendChild(defaultForRoot);
                externalProjectServices.AppendChild(serviceElem);
            }

            var pathPrefix   = this.m_ProjectOutputPathCalculator.GetProjectOutputPathPrefix(platform, definition, document, true);
            var assemblyName = this.m_ProjectOutputPathCalculator.GetProjectAssemblyName(platform, definition, document);
            var outputMode   = this.m_ProjectOutputPathCalculator.GetProjectOutputMode(document);

            var assemblyFilesToCopy = new[]
            {
                assemblyName + ".exe",
                assemblyName + ".dll",
                assemblyName + ".dll.config",
                assemblyName + ".dll.mdb",
                assemblyName + ".pdb",
                assemblyName + ".xml",
            };

            // Copy the assembly itself out to the package.
            switch (outputMode)
            {
            case OutputPathMode.BinConfiguration:
            {
                // In this configuration, we only ship the binaries for
                // the default architecture (because that's all we know
                // about).  We also have to assume the binary folder
                // contains binaries for the desired platform.
                if (definition.Type == "Library")
                {
                    // For libraries, we only copy the assembly (and immediately related files)
                    // into it's directory. Native binaries will be expressed through the ExternalProject.
                    foreach (var assemblyFile in assemblyFilesToCopy)
                    {
                        var includeMatch = fileFilter.ApplyInclude("^" + pathPrefix + Regex.Escape(assemblyFile) + "$");
                        var rewriteMatch = fileFilter.ApplyRewrite("^" + pathPrefix + Regex.Escape(assemblyFile) + "$", definition.Name + "/AnyCPU/" + assemblyFile);
                        if (includeMatch && rewriteMatch)
                        {
                            if (assemblyFile.EndsWith(".dll"))
                            {
                                var binaryEntry = externalProjectDocument.CreateElement("Binary");
                                binaryEntry.SetAttribute("Name", assemblyFile.Substring(0, assemblyFile.Length - 4));
                                binaryEntry.SetAttribute("Path", definition.Name + "\\AnyCPU\\" + assemblyFile);
                                externalProject.AppendChild(binaryEntry);
                            }
                            else if (assemblyFile.EndsWith(".dll.config"))
                            {
                                var configEntry = externalProjectDocument.CreateElement("NativeBinary");
                                configEntry.SetAttribute("Path", definition.Name + "\\AnyCPU\\" + assemblyFile);
                                externalProject.AppendChild(configEntry);
                            }
                        }
                        else if (includeMatch || rewriteMatch)
                        {
                            throw new InvalidOperationException("Automatic filter; only one rule matched.");
                        }
                    }
                }
                else
                {
                    // For executables, we ship everything in the output directory, because we
                    // want the executables to be able to run from the package directory.
                    fileFilter.ApplyInclude("^" + pathPrefix + "(.+)$");
                    fileFilter.ApplyRewrite("^" + pathPrefix + "(.+)$", definition.Name + "/AnyCPU/$2");

                    // Mark the executable files in the directory as tools that can be executed.
                    foreach (var assemblyFile in assemblyFilesToCopy)
                    {
                        if (assemblyFile.EndsWith(".exe"))
                        {
                            var binaryEntry = externalProjectDocument.CreateElement("Tool");
                            binaryEntry.SetAttribute("Name", assemblyFile.Substring(0, assemblyFile.Length - 4));
                            binaryEntry.SetAttribute("Path", definition.Name + "\\AnyCPU\\" + assemblyFile);
                            externalProject.AppendChild(binaryEntry);
                        }
                    }
                }

                break;
            }

            case OutputPathMode.BinPlatformArchConfiguration:
            {
                // In this configuration, we ship binaries for AnyCPU, iPhoneSimulator or all .NET architectures
                // depending on whether or not the platform produces multiple architectures.  On Mono,
                // we can't use $(Platform) within a reference's path, so we have to keep this path static
                // for Mono platforms.
                string pathArchMatch, pathArchReplace, pathArchRuntime;
                switch (platform.ToLowerInvariant())
                {
                case "ios":
                {
                    pathArchMatch   = "iPhoneSimulator";
                    pathArchReplace = "iPhoneSimulator";
                    pathArchRuntime = "iPhoneSimulator";
                    break;
                }

                case "windowsphone":
                {
                    pathArchMatch   = "([^/]+)";
                    pathArchReplace = "$1";
                    pathArchRuntime = "$(Platform)";
                    break;
                }

                default:
                {
                    pathArchMatch   = "AnyCPU";
                    pathArchReplace = "AnyCPU";
                    pathArchRuntime = "AnyCPU";
                    break;
                }
                }

                if (definition.Type == "Library")
                {
                    // For libraries, we only copy the assembly (and immediately related files)
                    // into it's directory. Native binaries will be expressed through the ExternalProject.
                    foreach (var assemblyFile in assemblyFilesToCopy)
                    {
                        var includeMatch = fileFilter.ApplyInclude("^" + pathPrefix + Regex.Escape(assemblyFile) + "$");
                        var rewriteMatch = fileFilter.ApplyRewrite("^" + pathPrefix + Regex.Escape(assemblyFile) + "$", definition.Name + "/" + pathArchReplace + "/" + assemblyFile);
                        if (includeMatch && rewriteMatch)
                        {
                            if (assemblyFile.EndsWith(".dll"))
                            {
                                var binaryEntry = externalProjectDocument.CreateElement("Binary");
                                binaryEntry.SetAttribute("Name", assemblyFile.Substring(0, assemblyFile.Length - 4));
                                binaryEntry.SetAttribute("Path", definition.Name + "\\" + pathArchRuntime + "\\" + assemblyFile);
                                externalProject.AppendChild(binaryEntry);
                            }
                            else if (assemblyFile.EndsWith(".dll.config"))
                            {
                                var configEntry = externalProjectDocument.CreateElement("NativeBinary");
                                configEntry.SetAttribute("Path", definition.Name + "\\" + pathArchRuntime + "\\" + assemblyFile);
                                externalProject.AppendChild(configEntry);
                            }
                        }
                        else if (includeMatch || rewriteMatch)
                        {
                            throw new InvalidOperationException("Automatic filter; only one rule matched.");
                        }
                    }
                }
                else
                {
                    // For executables, we ship everything in the output directory, because we
                    // want the executables to be able to run from the package directory.
                    fileFilter.ApplyInclude("^" + pathPrefix + "(.+)$");

                    if (pathArchMatch == "([^/]+)")
                    {
                        fileFilter.ApplyRewrite("^" + pathPrefix + "(.+)$", definition.Name + "/" + pathArchReplace + "/$3");
                    }
                    else
                    {
                        fileFilter.ApplyRewrite("^" + pathPrefix + "(.+)$", definition.Name + "/" + pathArchReplace + "/$2");
                    }

                    // Mark the executable files in the directory as tools that can be executed.
                    foreach (var assemblyFile in assemblyFilesToCopy)
                    {
                        if (assemblyFile.EndsWith(".exe"))
                        {
                            var binaryEntry = externalProjectDocument.CreateElement("Tool");
                            binaryEntry.SetAttribute("Name", assemblyFile.Substring(0, assemblyFile.Length - 4));
                            binaryEntry.SetAttribute("Path", definition.Name + "\\" + pathArchRuntime + "\\" + assemblyFile);
                            externalProject.AppendChild(binaryEntry);
                        }
                    }
                }

                break;
            }

            case OutputPathMode.BinProjectPlatformArchConfiguration:
            {
                throw new NotSupportedException();
                break;
            }
            }

            // Convert all of the known references into references within the external project.
            var definitionsByName = definitions.ToDictionarySafe(
                k => k.Name,
                v => v,
                (dict, x) =>
            {
                var existing = dict[x.Name];
                var tried    = x;

                RedirectableConsole.WriteLine("WARNING: There is more than one project with the name " +
                                              x.Name + " (first project loaded from " + tried.AbsolutePath + ", " +
                                              "skipped loading second project from " + existing.AbsolutePath + ")");
            });

            foreach (var reference in document.XPathSelectElements("/Project/References/Reference"))
            {
                var includeAttribute = reference.Attribute(XName.Get("Include"));
                if (includeAttribute != null)
                {
                    if (definitionsByName.ContainsKey(includeAttribute.Value))
                    {
                        var targetDefinition = definitionsByName[includeAttribute.Value];

                        // If the targeted reference is an include project, skip it.
                        if (targetDefinition == null || targetDefinition.Type != "Include")
                        {
                            // This reference will be converted to an external project,
                            // so add a reference to it (in case it contains native binaries
                            // which need to be copied out).
                            var referenceEntry = externalProjectDocument.CreateElement("Reference");
                            referenceEntry.SetAttribute("Include", includeAttribute.Value);
                            externalProject.AppendChild(referenceEntry);
                        }
                    }
                }
            }

            // Copy out any files that are marked with copy-on-build flag.
            var detector    = new PlatformAndServiceActiveDetection();
            var xmlDocument = new XmlDocument();

            xmlDocument.Load(definition.DefinitionPath);
            var servicesInput         = this.m_ServiceInputGenerator.Generate(xmlDocument, definition.Name, services);
            var activeServicesElement = servicesInput.ChildNodes.OfType <XmlElement>().FirstOrDefault(x => x.LocalName == "ActiveServicesNames");
            var activeServices        = activeServicesElement.InnerText;

            foreach (var file in document.XPathSelectElements("/Project/Files/*"))
            {
                var copyOnBuild = file.XPathSelectElement("CopyToOutputDirectory");
                if (copyOnBuild == null)
                {
                    continue;
                }

                if (copyOnBuild.Value != "PreserveNewest" && copyOnBuild.Value != "Always")
                {
                    continue;
                }

                var platformsElement        = file.XPathSelectElement("Platforms");
                var includePlatformsElement = file.XPathSelectElement("IncludePlatforms");
                var excludePlatformsElement = file.XPathSelectElement("ExcludePlatforms");
                var servicesElement         = file.XPathSelectElement("Services");
                var includeServicesElement  = file.XPathSelectElement("IncludeServices");
                var excludeServicesElement  = file.XPathSelectElement("ExcludeServices");

                var platformsString        = platformsElement != null ? platformsElement.Value : string.Empty;
                var includePlatformsString = includePlatformsElement != null ? includePlatformsElement.Value : string.Empty;
                var excludePlatformsString = excludePlatformsElement != null ? excludePlatformsElement.Value : string.Empty;
                var servicesString         = servicesElement != null ? servicesElement.Value : string.Empty;
                var includeServicesString  = includeServicesElement != null ? includeServicesElement.Value : string.Empty;
                var excludeServicesString  = excludeServicesElement != null ? excludeServicesElement.Value : string.Empty;

                if (detector.ProjectAndServiceIsActive(
                        platformsString,
                        includePlatformsString,
                        excludePlatformsString,
                        servicesString,
                        includeServicesString,
                        excludeServicesString,
                        platform,
                        activeServices))
                {
                    var include     = file.Attribute(XName.Get("Include"));
                    var linkElement = file.XPathSelectElement("Link");
                    var link        = linkElement != null ? linkElement.Value : include.Value;

                    var fileInfo = new FileInfo(Path.Combine(
                                                    definition.AbsolutePath.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar),
                                                    include.Value.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar)));

                    if (definition.Type == "Library")
                    {
                        // For libraries, we're copying the content so that applications using
                        // the libraries will have the content.  This is most often used to
                        // ship native binaries (although NativeBinary in external projects now
                        // supersedes this).
                        if (link.Contains('/') || link.Contains('\\'))
                        {
                            RedirectableConsole.WriteLine(
                                "WARNING: Copy-on-build file '" + link + "' in library project which " +
                                "does not output to root of project detected.  This is not supported.");
                        }
                        else
                        {
                            if (fileInfo.Name != link)
                            {
                                RedirectableConsole.WriteLine(
                                    "WARNING: Copy-on-build file in library project does not have the same " +
                                    "name when copied to build directory.  This is not supported.");
                            }
                            else
                            {
                                var sourcePath = fileInfo.FullName;
                                sourcePath = sourcePath.Substring(rootPath.Length).Replace('\\', '/').TrimStart('/');
                                var destPath = Path.Combine("_AutomaticExternals", sourcePath);

                                var sourcePathRegex = this.ConvertPathWithMSBuildVariablesFind(sourcePath);
                                var destPathRegex   = this.ConvertPathWithMSBuildVariablesReplace(destPath.Replace('\\', '/'));

                                var includeMatch = fileFilter.ApplyInclude(sourcePathRegex);
                                fileFilter.ApplyRewrite(sourcePathRegex, destPathRegex);
                                if (includeMatch)
                                {
                                    var nativeBinaryEntry = externalProjectDocument.CreateElement("NativeBinary");
                                    nativeBinaryEntry.SetAttribute("Path", destPath);
                                    externalProject.AppendChild(nativeBinaryEntry);
                                }
                                else
                                {
                                    throw new InvalidOperationException(
                                              "File not found at " + sourcePath + " when converting " +
                                              "copy-on-build file in library project.");
                                }
                            }
                        }
                    }
                }
            }

            // Write out the external project to a temporary file and include it.
            var name = Path.GetRandomFileName() + "_" + definition.Name + ".xml";
            var temp = Path.Combine(Path.GetTempPath(), name);

            temporaryFiles.Add(temp);
            using (var writer = XmlWriter.Create(temp, new XmlWriterSettings {
                Indent = true, IndentChars = "  "
            }))
            {
                externalProjectDocument.WriteTo(writer);
            }
            fileFilter.AddManualMapping(temp, "Build/Projects/" + definition.Name + ".definition");
        }