/// <summary> /// Executes nuget restore for project /// </summary> /// <param name="project">Project to restore</param> private static void RestoreProjectDependencies(ProjectDescription project) { // restoring packages for project with respec to global configuration file Func<Unit, Unit> failFunc = u => u; var nugetPath = RestorePackageHelper.findNuget("./"); RestorePackageHelper.runNuGet( nugetPath, TimeSpan.FromMinutes(10), $"restore {project.ProjectFileName} -ConfigFile ./nuget.config", failFunc.ToFSharpFunc()); }
/// <summary> /// Extracts project uid from project file /// </summary> /// <param name="project">The project description</param> /// <returns>The project's uid</returns> private static string GetProjectUid(ProjectDescription project) { var doc = new XmlDocument(); doc.Load(project.ProjectFileName); XmlNamespaceManager namespaceManager = new XmlNamespaceManager(doc.NameTable); namespaceManager.AddNamespace("def", "http://schemas.microsoft.com/developer/msbuild/2003"); var uid = doc.DocumentElement?.SelectSingleNode("/def:Project/def:PropertyGroup/def:ProjectGuid", namespaceManager)? .InnerText; return uid; }
/// <summary> /// Runs defined unit tests on all projects /// </summary> /// <param name="project">The project to test</param> public static void RunXUnitTest(ProjectDescription project) { if (!project.ProjectType.HasFlag(ProjectDescription.EnProjectType.XUnitTests)) { return; } var testAssembly = Path.Combine(project.TempBuildDirectory, $"{project.ProjectName}.dll"); var runnerLocation = Directory.GetDirectories(Path.Combine(Directory.GetCurrentDirectory(), "packages")) .OrderByDescending(d => d) .First(); Func<XUnit2.XUnit2Params, XUnit2.XUnit2Params> testParameters = p => { p.SetFieldValue("ToolPath", Path.Combine(runnerLocation, "tools", "xunit.console.exe")); p.SetFieldValue("ErrorLevel", UnitTestCommon.TestRunnerErrorLevel.DontFailBuild); p.SetFieldValue("TimeOut", TimeSpan.FromHours(8)); p.SetFieldValue("ForceTeamCity", true); return p; }; XUnit2.xUnit2(testParameters.ToFSharpFunc(), new[] { testAssembly }); }
/// <summary> /// Builds current project /// </summary> /// <param name="project">Project to build</param> public static void Build(ProjectDescription project) { var tempSrc = Path.Combine(Path.GetFullPath(BuildDirectory), "src"); TraceHelper.trace($"Building {project.ProjectName}"); if (Directory.Exists(project.TempBuildDirectory)) { Directory.Delete(project.TempBuildDirectory, true); } if (Directory.Exists(project.CleanBuildDirectory)) { Directory.Delete(project.CleanBuildDirectory, true); } if (Directory.Exists(tempSrc)) { var attempt = 0; while (true) { try { Directory.Delete(tempSrc, true); continue; } catch (IOException) { ConsoleLog("Src directory is blocked. Retrying..."); attempt++; Thread.Sleep(100); if (attempt >= 10) { throw; } } break; } } Directory.CreateDirectory(project.CleanBuildDirectory); Directory.CreateDirectory(project.TempBuildDirectory); RestoreProjectDependencies(project); Func<string, bool> filter = s => true; FileHelper.CopyDir(tempSrc, project.ProjectDirectory, filter.ToFSharpFunc()); // ReSharper disable once AssignNullToNotNullAttribute var projectFileName = Path.Combine(tempSrc, Path.GetFileName(project.ProjectFileName)); // modifying project file to substitute internal nute-through dependencies to currently built files var projDoc = new XmlDocument(); projDoc.Load(projectFileName); XmlNamespaceManager namespaceManager = new XmlNamespaceManager(projDoc.NameTable); namespaceManager.AddNamespace("def", "http://schemas.microsoft.com/developer/msbuild/2003"); var rootNode = projDoc.DocumentElement; if (rootNode == null) { ConsoleLog($"Could not read xml from {projectFileName}"); return; } var refItemGroup = rootNode.SelectSingleNode("//def:ItemGroup[def:Reference]", namespaceManager); foreach (var dependency in project.InternalDependencies) { var refNode = rootNode.SelectNodes("//def:Reference", namespaceManager) ?.Cast<XmlElement>() .FirstOrDefault( e => e.HasAttribute("Include") && Regex.IsMatch(e.Attributes["Include"].Value, $"^{Regex.Escape(dependency)}((, )|$)")) ?? rootNode.SelectNodes("//def:ProjectReference", namespaceManager) ?.Cast<XmlElement>() .FirstOrDefault( e => e.HasAttribute("Include") && Regex.IsMatch(e.Attributes["Include"].Value, $"(^|[\\\\\\/]){Regex.Escape(dependency)}.csproj$")); if (refNode == null) { var projectReference = rootNode.SelectNodes("//def:ProjectReference", namespaceManager)?.Cast<XmlElement>().Where(e => e.HasAttribute("Include")); if (projectReference != null) { foreach (var element in projectReference) { ConsoleLog($"Comparing {element.Attributes["Include"].Value} and {dependency}"); } } } if (refNode != null) { var libPath = Path.Combine(Path.GetFullPath(BuildClean), dependency, $"{dependency}.dll"); refNode.ParentNode?.RemoveChild(refNode); refNode = projDoc.CreateElement("Reference", "http://schemas.microsoft.com/developer/msbuild/2003"); refItemGroup?.AppendChild(refNode); refNode.SetAttribute("Include", dependency); refNode.InnerXml = $"<SpecificVersion>False</SpecificVersion><HintPath>{libPath}</HintPath><Private>True</Private>"; ConsoleLog($"ref linked to {dependency} was updated"); if (!File.Exists(libPath)) { ConsoleLog($"!!!!!{libPath} does not exist!!!!"); } } else { ConsoleLog($"{dependency} ref node was not found"); } } // todo: @kantora update NuGet package references in case of directory structure change ConsoleLog($"Writing modified {projectFileName}"); projDoc.Save(projectFileName); var assemblyInfoPath = Path.Combine( tempSrc, "Properties", "AssemblyInfo.cs"); var assemblyText = File.ReadAllText(assemblyInfoPath); assemblyText += $"\n[assembly: AssemblyMetadata(\"NugetVersion\", \"{Version?.Replace("\"", "\\\"")}\")]\n"; assemblyText += $"\n[assembly: AssemblyMetadata(\"BuildDate\", \"{DateTimeOffset.Now.ToString("s")}\")]\n"; var assemblyVersion = Regex.Replace(Version ?? "1.0.0.0", "((\\d\\.?)+)(.*)", "$1"); assemblyText = Regex.Replace(assemblyText, "AssemblyVersion\\(\"([^\\)]+)\"\\)", $"AssemblyVersion(\"{assemblyVersion}\")"); assemblyText = Regex.Replace(assemblyText, "AssemblyFileVersion\\(\"([^\\)]+)\"\\)", $"AssemblyFileVersion(\"{assemblyVersion}\")"); File.WriteAllText(assemblyInfoPath, assemblyText); MSBuildHelper.MSBuildRelease(project.TempBuildDirectory, "Clean", new[] { projectFileName }); MSBuildHelper.MSBuildRelease(project.TempBuildDirectory, "Build", new[] { projectFileName }); Directory.CreateDirectory(project.CleanBuildDirectory); var extensions = new[] { "dll", "nuspec", "pdb", "xml", "exe", "dll.config", "exe.config", "fsx" }; var buildFiles = Directory.GetFiles(project.TempBuildDirectory) .Where(f => extensions.Any(e => $"{project.ProjectName}.{e}".Equals(Path.GetFileName(f), StringComparison.InvariantCultureIgnoreCase))) .ToList(); foreach (var fileName in buildFiles.Select(Path.GetFileName).Where(fileName => fileName != null)) { ConsoleLog($"Copying {fileName}"); File.Copy( Path.Combine(project.TempBuildDirectory, fileName), Path.Combine(project.CleanBuildDirectory, fileName)); } Func<string, bool> alwaysTrue = f => true; foreach (var directory in Directory.GetDirectories(project.TempBuildDirectory).Select(Path.GetFileName)) { ConsoleLog($"Copying localization {directory}"); FileHelper.CopyDir( Path.Combine(project.CleanBuildDirectory, directory), Path.Combine(project.TempBuildDirectory, directory), alwaysTrue.ToFSharpFunc()); } if (buildFiles.Any(f => f.EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase) || f.EndsWith(".fsx", StringComparison.InvariantCultureIgnoreCase))) { var toolsDir = Path.Combine(project.CleanBuildDirectory, "tools"); Directory.CreateDirectory(toolsDir); FileHelper.CopyDir(toolsDir, project.TempBuildDirectory, alwaysTrue.ToFSharpFunc()); } TraceHelper.trace($"Build {project.ProjectName} finished"); }
/// <summary> /// Creates nuget package /// </summary> /// <param name="project"> /// The project to publish /// </param> public static void CreateNuget(ProjectDescription project) { if (!project.ProjectType.HasFlag(ProjectDescription.EnProjectType.NugetPackage)) { return; } TraceHelper.trace($"Packing {project.ProjectName}"); var nuspecData = new XmlDocument(); var nuspecDataFileName = $"{project.ProjectName}.nuspec"; nuspecData.Load(Path.Combine(project.ProjectDirectory, nuspecDataFileName)); // ReSharper disable PossibleNullReferenceException var metaData = nuspecData.DocumentElement.SelectSingleNode("/package/metadata"); metaData.SelectSingleNode("id").InnerText = project.ProjectName; metaData.SelectSingleNode("version").InnerText = Version; metaData.SelectSingleNode("title").InnerText = project.ProjectName; if ("$author$".Equals(metaData.SelectSingleNode("authors").InnerText)) { metaData.SelectSingleNode("authors").InnerText = "ClusterKit Team"; } if ("$author$".Equals(metaData.SelectSingleNode("owners").InnerText)) { metaData.SelectSingleNode("owners").InnerText = "ClusterKit Team"; } if ("$description$".Equals(metaData.SelectSingleNode("description").InnerText)) { metaData.SelectSingleNode("description").InnerText = "ClusterKit lib"; } var dependenciesRootElement = metaData.AppendChild(nuspecData.CreateElement("dependencies")); var dependenciesDoc = new XmlDocument(); dependenciesDoc.Load(Path.Combine(project.ProjectDirectory, "packages.config")); foreach (XmlElement dependency in dependenciesDoc.DocumentElement.SelectNodes("/packages/package")) { if (project.InternalDependencies.Contains(dependency.Attributes["id"].Value)) { continue; } if (!Directory.Exists(Path.Combine(PackageInputDirectory, $"{dependency.Attributes["id"].Value}.{dependency.Attributes["version"].Value}"))) { continue; } var dependencyElement = dependenciesRootElement.AppendChild(nuspecData.CreateElement("dependency")); dependencyElement.Attributes.Append(nuspecData.CreateAttribute("id")).Value = dependency.Attributes["id"].Value; dependencyElement.Attributes.Append(nuspecData.CreateAttribute("version")).Value = dependency.Attributes["version"].Value; } foreach (var dependency in project.InternalDependencies) { var dependencyElement = dependenciesRootElement.AppendChild(nuspecData.CreateElement("dependency")); dependencyElement.Attributes.Append(nuspecData.CreateAttribute("id")).Value = dependency; dependencyElement.Attributes.Append(nuspecData.CreateAttribute("version")).Value = Version; } var filesRootElement = nuspecData.DocumentElement.SelectSingleNode("/package").AppendChild(nuspecData.CreateElement("files")); foreach (var file in Directory.GetFiles(project.CleanBuildDirectory)) { var fileElement = filesRootElement.AppendChild(nuspecData.CreateElement("file")); fileElement.Attributes.Append(nuspecData.CreateAttribute("src")).Value = Path.GetFileName(file); fileElement.Attributes.Append(nuspecData.CreateAttribute("target")).Value = $"./lib/{Path.GetFileName(file)}"; } foreach (var directory in Directory.GetDirectories(project.CleanBuildDirectory).Select(Path.GetFileName).Where(d => d != "tools")) { foreach (var file in Directory.GetFiles(Path.Combine(project.CleanBuildDirectory, directory))) { var fileElement = filesRootElement.AppendChild(nuspecData.CreateElement("file")); fileElement.Attributes.Append(nuspecData.CreateAttribute("src")).Value = Path.Combine(directory, Path.GetFileName(file)); fileElement.Attributes.Append(nuspecData.CreateAttribute("target")).Value = $"./lib/{directory}/{Path.GetFileName(file)}"; } } var toolsDir = Path.Combine(project.CleanBuildDirectory, "tools"); if (Directory.Exists(toolsDir)) { foreach (var file in Directory.GetFiles(toolsDir)) { var fileElement = filesRootElement.AppendChild(nuspecData.CreateElement("file")); fileElement.Attributes.Append(nuspecData.CreateAttribute("src")).Value = Path.Combine("tools", Path.GetFileName(file)); fileElement.Attributes.Append(nuspecData.CreateAttribute("target")).Value = $"./tools/{Path.GetFileName(file)}"; } } var generatedNuspecFile = Path.Combine(project.CleanBuildDirectory, nuspecDataFileName); nuspecData.Save(generatedNuspecFile); if (!Directory.Exists(PackageOutputDirectory)) { Directory.CreateDirectory(PackageOutputDirectory); } Func<NuGetHelper.NuGetParams, NuGetHelper.NuGetParams> provider = defaults => { defaults.SetFieldValue("Version", Version); defaults.SetFieldValue("WorkingDir", project.CleanBuildDirectory); defaults.SetFieldValue("OutputPath", PackageOutputDirectory); return defaults; }; Fake.NuGetHelper.NuGetPackDirectly(provider.ToFSharpFunc(), generatedNuspecFile); // ReSharper restore PossibleNullReferenceException }