public void Synchronise(string platform) { var serviceCache = Path.Combine( this.m_ModuleInfo.Path, this.m_ModuleInfo.Name + "." + platform + ".speccache"); var ignoreServiceFiles = !File.Exists(serviceCache); var serviceManager = new ServiceManager(null); List<Service> services = null; if (!ignoreServiceFiles) { services = serviceManager.LoadServiceSpec(serviceCache); } var document = new XmlDocument(); document.Load(this.m_DefinitionInfo.DefinitionPath); var projectElement = document.ChildNodes.OfType<XmlElement>() .FirstOrDefault(x => x.Name == "Project"); var elements = projectElement.ChildNodes.OfType<XmlElement>().ToList(); var files = elements.First(x => x.Name == "Files"); // Remove files that either have no Platforms child, or where the // Platforms child contains the current platform that we're synchronising for. // This is because if I generate a platform for Linux, and the definition // has Windows-only files in it, those won't be in the project file. // Also for files that have services in them, check to see if we have the // service cache. If we don't have a service cache, don't remove any entries // that are conditional on services (because we don't know what services // were enabled when the project was generated). Otherwise only allow removal // if the service was active at the time. foreach (var file in files.ChildNodes.OfType<XmlElement>().ToArray()) { var children = file.ChildNodes.OfType<XmlElement>().ToArray(); var platformsTag = children.FirstOrDefault(x => x.LocalName == "Platforms"); var includePlatformsTag = children.FirstOrDefault(x => x.LocalName == "IncludePlatforms"); var excludePlatformsTag = children.FirstOrDefault(x => x.LocalName == "ExcludePlatforms"); var platformsTagString = platformsTag != null ? platformsTag.InnerText : string.Empty; var includePlatformsTagString = includePlatformsTag != null ? includePlatformsTag.InnerText : string.Empty; var excludePlatformsTagString = excludePlatformsTag != null ? excludePlatformsTag.InnerText : string.Empty; var servicesTag = children.FirstOrDefault(x => x.LocalName == "Services"); var includeServicesTag = children.FirstOrDefault(x => x.LocalName == "IncludeServices"); var excludeServicesTag = children.FirstOrDefault(x => x.LocalName == "ExcludeServices"); var servicesTagString = servicesTag != null ? servicesTag.InnerText : string.Empty; var includeServicesTagString = includeServicesTag != null ? includeServicesTag.InnerText : string.Empty; var excludeServicesTagString = excludeServicesTag != null ? excludeServicesTag.InnerText : string.Empty; if (!string.IsNullOrEmpty(servicesTagString) || !string.IsNullOrEmpty(includeServicesTagString) || !string.IsNullOrEmpty(excludeServicesTagString)) { if (ignoreServiceFiles) { // We don't know whether the service was enabled or not during generation, so we can't determine // if the user wanted to remove this entry. continue; } if (!string.IsNullOrEmpty(excludeServicesTagString)) { var excluded = false; foreach (var service in services) { if (excludeServicesTagString.Split(',').Contains(service.FullName, StringComparer.OrdinalIgnoreCase) || (service.ProjectName == this.m_DefinitionInfo.Name && excludeServicesTagString.Split(',').Contains(service.ServiceName, StringComparer.OrdinalIgnoreCase))) { // This file is excluded in the C# project for this platform // and won't be present. excluded = true; } } if (excluded) { // This file is excluded in the C# project for this platform // and won't be present. continue; } } // Fallback to <IncludeServices> if <Services> is not present. if (string.IsNullOrEmpty(servicesTagString)) { servicesTagString = includeServicesTagString; } var allowFallthrough = false; // If both the <IncludeServices> and <Services> tags are not present, then this // file is generated for all services regardless. if (servicesTag == null && includeServicesTag == null) { allowFallthrough = true; } else { var included = false; foreach (var service in services) { // If the included services string contains any enabled service, then we // remove the file (because it will be present in the C# project). if (servicesTagString.Split(',') .Contains(service.FullName, StringComparer.OrdinalIgnoreCase) || (service.ProjectName == this.m_DefinitionInfo.Name && servicesTagString.Split(',') .Contains(service.ServiceName, StringComparer.OrdinalIgnoreCase))) { included = true; break; } } if (included) { // This file is included in the C# project for this service. allowFallthrough = true; } } if (!allowFallthrough) { // We weren't included with <Services> or <IncludeServices> so we won't // be present regardless of platform settings. continue; } // We were included by a service, but we might not have passed the platform check // so continue performing checks. } if (!string.IsNullOrEmpty(excludePlatformsTagString)) { if (excludePlatformsTagString.Split(',').Contains(platform, StringComparer.OrdinalIgnoreCase)) { // This file is excluded in the C# project for this platform // and won't be present. continue; } } // Fallback to <IncludePlatforms> if <Platforms> is not present. if (string.IsNullOrEmpty(platformsTagString)) { platformsTagString = includePlatformsTagString; } // If both the <IncludePlatforms> and <Platforms> tags are not present, then this // file is generated for all platforms regardless. if (platformsTag == null && includePlatformsTag == null) { files.RemoveChild(file); continue; } // If the included platforms string contains the current platform, then we // remove the file (because it will be present in the C# project). if (platformsTagString.Split(',').Contains(platform, StringComparer.OrdinalIgnoreCase)) { files.RemoveChild(file); } } // Add the new files. var uniquePaths = new List<string>(); foreach (var element in this.m_CSharpProject.Elements.OrderBy(x => x.Name).ThenBy(x => this.NormalizePath(x.GetAttribute("Include")))) { // Ignore Content files. if (element.Name == "None" || element.Name == "Content" || element.Name == "AndroidAsset") { var linkElement = element.ChildNodes .OfType<XmlNode>().FirstOrDefault(x => x.Name == "Link"); if (linkElement != null) { if (linkElement.InnerText.Trim().Replace('\\', '/').StartsWith("Content/", StringComparison.Ordinal)) continue; } } // Ignore files included by include projects. if (element.ChildNodes.OfType<XmlNode>().Any(x => x.Name == "FromIncludeProject")) { if (element.ChildNodes.OfType<XmlNode>().First(x => x.Name == "FromIncludeProject").InnerText.Trim() == "True") { continue; } } // Ignore native binaries. if (element.Name == "None") { if (element.ChildNodes.OfType<XmlNode>().Any(x => x.Name == "NativeBinary")) { continue; } } var normalizedPath = this.NormalizePath(element.GetAttribute("Include")); // Ignore files that have already been added to the list. if (uniquePaths.Contains(normalizedPath)) { // Do not include again. continue; } uniquePaths.Add(normalizedPath); // Change the path. element.SetAttribute("Include", normalizedPath); // Append the file element. files.AppendChild(document.ImportNode(element, true)); } // Clean empty elements as well. var cleaned = this.WashNamespaces(document); foreach (var child in cleaned.ChildNodes.OfType<XmlElement>()) { this.CleanNodes(child); } // Load into an XDocument to resort the list of elements by their Include. var xRoot = this.ToXDocument(cleaned); var xFiles = xRoot.Element(XName.Get("Project")).Element(XName.Get("Files")); var xOrderedNodes = xFiles.Elements() .OrderBy(x => x.Name.LocalName) .ThenBy(x => x.Attribute(XName.Get("Include")) == null ? "" : this.NormalizePath(x.Attribute(XName.Get("Include")).Value)).ToArray(); xFiles.RemoveAll(); foreach (var a in xOrderedNodes) { xFiles.Add(a); } cleaned = this.ToXmlDocument(xRoot); var settings = new XmlWriterSettings { Indent = true, IndentChars = " ", NewLineChars = "\n", Encoding = Encoding.UTF8 }; using (var memory = new MemoryStream()) { using (var writer = XmlWriter.Create(memory, settings)) { cleaned.Save(writer); } memory.Seek(0, SeekOrigin.Begin); var reader = new StreamReader(memory); var content = reader.ReadToEnd().Trim() + Environment.NewLine; using (var writer = new StreamWriter(this.m_DefinitionInfo.DefinitionPath, false, Encoding.UTF8)) { writer.Write(content); } } this.HandleNuGetConfig(platform); }
public override bool Execute() { if (string.Compare(this.Platform, "Web", StringComparison.InvariantCultureIgnoreCase) == 0) { // Trigger JSIL provider download if needed. string jsilDirectory, jsilCompilerFile; if (!this.m_JSILProvider.GetJSIL(out jsilDirectory, out jsilCompilerFile)) { return false; } } var module = ModuleInfo.Load(Path.Combine(this.RootPath, "Build", "Module.xml")); this.LogMessage( "Starting generation of projects for " + this.Platform); var definitions = module.GetDefinitionsRecursively(this.Platform).ToArray(); var loadedProjects = new List<XmlDocument>(); foreach (var definition in definitions) { this.LogMessage("Loading: " + definition.Name); loadedProjects.Add( this.m_ProjectLoader.Load( Path.Combine( definition.ModulePath, "Build", "Projects", definition.Name + ".definition"), this.Platform, module.Path, definition.ModulePath)); } var serviceManager = new ServiceManager(this.Platform); List<Service> services; string serviceSpecPath; if (this.ServiceSpecPath == null) { serviceManager.SetRootDefinitions(module.GetDefinitions()); if (this.EnableServices == null) { this.EnableServices = new string[0]; } if (this.DisableServices == null) { this.DisableServices = new string[0]; } foreach (var service in this.EnableServices) { serviceManager.EnableService(service); } foreach (var service in this.DisableServices) { serviceManager.DisableService(service); } if (this.DebugServiceResolution) { serviceManager.EnableDebugInformation(); } try { services = serviceManager.CalculateDependencyGraph(loadedProjects); } catch (InvalidOperationException ex) { Console.WriteLine("Error during service resolution: " + ex.Message); return false; } serviceSpecPath = serviceManager.SaveServiceSpec(services); foreach (var service in services) { if (service.ServiceName != null) { this.LogMessage("Enabled service: " + service.FullName); } } } else { services = serviceManager.LoadServiceSpec(this.ServiceSpecPath); serviceSpecPath = this.ServiceSpecPath; } // Run Protobuild in batch mode in each of the submodules // where it is present. foreach (var submodule in module.GetSubmodules(this.Platform)) { this.LogMessage( "Invoking submodule generation for " + submodule.Name); var noResolve = submodule.HasProtobuildFeature("no-resolve") ? " -no-resolve" : string.Empty; submodule.RunProtobuild( "-generate " + this.Platform + " -spec " + serviceSpecPath + " " + this.m_PackageRedirector.GetRedirectionArguments() + noResolve); this.LogMessage( "Finished submodule generation for " + submodule.Name); } var repositoryPaths = new List<string>(); foreach (var definition in definitions.Where(x => x.ModulePath == module.Path)) { string repositoryPath; var definitionCopy = definition; this.m_ProjectGenerator.Generate( loadedProjects, this.RootPath, definition.Name, this.Platform, services, out repositoryPath, () => this.LogMessage("Generating: " + definitionCopy.Name)); // Only add repository paths if they should be generated. if (module.GenerateNuGetRepositories && !string.IsNullOrEmpty(repositoryPath)) { repositoryPaths.Add(repositoryPath); } } var solution = Path.Combine( this.RootPath, this.ModuleName + "." + this.Platform + ".sln"); this.LogMessage("Generating: (solution)"); this.m_SolutionGenerator.Generate( module, loadedProjects, this.Platform, solution, services, repositoryPaths); // Only save the specification cache if we allow synchronisation if (module.DisableSynchronisation == null || !module.DisableSynchronisation.Value) { var serviceCache = Path.Combine(this.RootPath, this.ModuleName + "." + this.Platform + ".speccache"); this.LogMessage("Saving service specification"); File.Copy(serviceSpecPath, serviceCache, true); } this.LogMessage( "Generation complete."); return true; }
public override bool Execute() { if (string.Compare(Platform, "Web", StringComparison.InvariantCultureIgnoreCase) == 0) { // Trigger JSIL provider download if needed. string jsilDirectory, jsilCompilerFile; if (!m_JSILProvider.GetJSIL(out jsilDirectory, out jsilCompilerFile)) { return false; } } var module = ModuleInfo.Load(Path.Combine(RootPath, "Build", "Module.xml")); LogMessage( "Starting generation of projects for " + Platform); var definitions = module.GetDefinitionsRecursively(Platform).ToArray(); var loadedProjects = new List<LoadedDefinitionInfo>(); foreach (var definition in definitions) { LogMessage("Loading: " + definition.Name); loadedProjects.Add( m_ProjectLoader.Load( Platform, module, definition)); } var serviceManager = new ServiceManager(Platform); List<Service> services; TemporaryServiceSpec serviceSpecPath; if (ServiceSpecPath == null) { serviceManager.SetRootDefinitions(module.GetDefinitions()); if (EnableServices == null) { EnableServices = new string[0]; } if (DisableServices == null) { DisableServices = new string[0]; } foreach (var service in EnableServices) { serviceManager.EnableService(service); } foreach (var service in DisableServices) { serviceManager.DisableService(service); } if (DebugServiceResolution) { serviceManager.EnableDebugInformation(); } try { services = serviceManager.CalculateDependencyGraph(loadedProjects.Select(x => x.Project).ToList()); } catch (InvalidOperationException ex) { Console.WriteLine("Error during service resolution: " + ex.Message); return false; } serviceSpecPath = serviceManager.SaveServiceSpec(services); foreach (var service in services) { if (service.ServiceName != null) { LogMessage("Enabled service: " + service.FullName); } } } else { services = serviceManager.LoadServiceSpec(ServiceSpecPath); serviceSpecPath = new TemporaryServiceSpec(ServiceSpecPath, true); } using (serviceSpecPath) { // Run Protobuild in batch mode in each of the submodules // where it is present. foreach (var submodule in module.GetSubmodules(Platform)) { if (_featureManager.IsFeatureEnabledInSubmodule(module, submodule, Feature.OptimizationSkipInvocationOnNoStandardProjects)) { if (submodule.GetDefinitionsRecursively(Platform).All(x => !x.IsStandardProject)) { // Do not invoke this submodule. LogMessage( "Skipping submodule generation for " + submodule.Name + " (there are no projects to generate)"); continue; } } LogMessage( "Invoking submodule generation for " + submodule.Name); var noResolve = _featureManager.IsFeatureEnabledInSubmodule(module, submodule, Feature.PackageManagementNoResolve) ? " -no-resolve" : string.Empty; var noHostPlatform = DisableHostPlatformGeneration && _featureManager.IsFeatureEnabledInSubmodule(module, submodule, Feature.NoHostGenerate) ? " -no-host-generate" : string.Empty; _moduleExecution.RunProtobuild( submodule, _featureManager.GetFeatureArgumentToPassToSubmodule(module, submodule) + "-generate " + Platform + " -spec " + serviceSpecPath + " " + m_PackageRedirector.GetRedirectionArguments() + noResolve + noHostPlatform); LogMessage( "Finished submodule generation for " + submodule.Name); } var repositoryPaths = new List<string>(); foreach (var definition in definitions.Where(x => x.ModulePath == module.Path)) { if (definition.PostBuildHook && RequiresHostPlatform != null) { // We require the host platform projects at this point. RequiresHostPlatform(); } string repositoryPath; var definitionCopy = definition; m_ProjectGenerator.Generate( definition, loadedProjects, RootPath, definition.Name, Platform, services, out repositoryPath, () => LogMessage("Generating: " + definitionCopy.Name)); // Only add repository paths if they should be generated. if (module.GenerateNuGetRepositories && !string.IsNullOrEmpty(repositoryPath)) { repositoryPaths.Add(repositoryPath); } } var solution = Path.Combine( RootPath, ModuleName + "." + Platform + ".sln"); LogMessage("Generating: (solution)"); m_SolutionGenerator.Generate( module, loadedProjects.Select(x => x.Project).ToList(), Platform, solution, services, repositoryPaths); // Only save the specification cache if we allow synchronisation if (module.DisableSynchronisation == null || !module.DisableSynchronisation.Value) { var serviceCache = Path.Combine(RootPath, ModuleName + "." + Platform + ".speccache"); LogMessage("Saving service specification"); File.Copy(serviceSpecPath.Path, serviceCache, true); } LogMessage( "Generation complete."); } return true; }