Files
sqltoolsservice/build.cake
Aasim Khan c7d75fa535 Publishing SQL Core library in nuget (#2173)
* Set up CI with Azure Pipelines

[skip ci]

* Update publishSqlCoreProject.yml for Azure Pipelines

* Update publishSqlCoreProject.yml for Azure Pipelines

* Update publishSqlCoreProject.yml for Azure Pipelines

* Update publishSqlCoreProject.yml for Azure Pipelines

* Update publishSqlCoreProject.yml for Azure Pipelines

* Updating json rpc target framework

* Fixing packages to pack

* fixing pattern

* Fixing project pattern

* adding versioning schema

* fixing versioning scheme

* fixing nuget package name

* adding name

* removing var

* Fixing stuff

* Fixing stuff

* Fixing stuff

* Fixing build name

* removing build name

* fixing name

* fixing stuff

* fix pattern

* Fixing stuff

* Fixing feed

* Fix stuff

* Fixing feed

* fixing proj name

* Fixing stuff

* Fixing netframework version

* Fixing target framework

* Adding target framework to dependency

* Fixing frameworks

* adding net7.0

* adding flag

* Fixing target frameworks and making code compatible with lang version

* fixed flag

* Fixing assembly name

* Adding hosting in nuget package

* Fixing tests

* Moving steps to its main pipeline

* only releasing when release is true

* Fixing build scripts

* Adding version to csproj for packaging

* adding version

* Fix dirs

* Adding version number to right prop group

* Adding necessary dis

* Fixing build number

* fixing datatype

* Using different var

* Removing unused yml

* Adding back nullables to hosting

* Adding back more nullables

* adding back more nullables

* moving some props to dir level

* Fixing tests

* Removing dup properties

* Supporting different target frameworks in hosting

* Removing additional setting in csproj

* signing sql core dll in publish

* Fixing version setting

* Adding to build

* Added signing and packaging flag

* Fixing file pattern
2023-08-22 23:15:20 -07:00

810 lines
28 KiB
C#

#addin "nuget:?package=Newtonsoft.Json&version=13.0.2"
#addin "mssql.ResX"
#addin "mssql.XliffParser"
#load "scripts/runhelpers.cake"
#load "scripts/archiving.cake"
#load "scripts/artifacts.cake"
#tool "nuget:?package=Mono.TextTransform"
using System.ComponentModel;
using System.Net;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Cake.Common.IO;
using XliffParser;
// Basic arguments
var target = Argument("target", "Default");
var configuration = Argument("configuration", "Release");
// Optional arguments
var testConfiguration = Argument("test-configuration", "Debug");
var installFolder = Argument("install-path", System.IO.Path.Combine(Environment.GetEnvironmentVariable(IsRunningOnWindows() ? "USERPROFILE" : "HOME"),
".sqltoolsservice", "local"));
var requireArchive = HasArgument("archive");
// Working directory
var workingDirectory = System.IO.Directory.GetCurrentDirectory();
// System specific shell configuration
var shell = IsRunningOnWindows() ? "powershell" : "bash";
var shellArgument = IsRunningOnWindows() ? "-NoProfile /Command" : "-C";
var shellExtension = IsRunningOnWindows() ? "ps1" : "sh";
/// <summary>
/// Class representing build.json
/// </summary>
public class BuildPlan
{
public Project[] FxBuildProjects { get; set; }
public IDictionary<string, string[]> TestProjects { get; set; }
public string BuildToolsFolder { get; set; }
public string ArtifactsFolder { get; set; }
public bool UseSystemDotNetPath { get; set; }
public string DotNetFolder { get; set; }
public string PackageName { get; set; }
public string DotNetInstallScriptURL { get; set; }
public string DotNetChannel { get; set; }
public string DotNetVersion { get; set; }
public string[] Frameworks { get; set; }
public string[] Rids { get; set; }
public string[] MainProjects { get; set; }
// The set of projects that we want to call dotnet pack on directly
public string[] PackageProjects { get; set; }
// The set of projects that we want to call dotnet pack on which require publishing being done first
public string[] DotnetToolProjects { get; set; }
public Project[] Projects{ get; set; }
}
public class Project
{
public string Name { get; set; }
public string[] PackageProjects { get; set; }
public string[] TestProjects { get; set; }
public string[] Frameworks { get; set; }
public bool SkipPack { get; set; }
}
var buildPlan = JsonConvert.DeserializeObject<BuildPlan>(
System.IO.File.ReadAllText(System.IO.Path.Combine(workingDirectory, "build.json")));
// Folders and tools
var dotnetFolder = System.IO.Path.Combine(workingDirectory, buildPlan.DotNetFolder);
var dotnetcli = buildPlan.UseSystemDotNetPath ? "dotnet" : System.IO.Path.Combine(System.IO.Path.GetFullPath(dotnetFolder), "dotnet");
var toolsFolder = System.IO.Path.Combine(workingDirectory, buildPlan.BuildToolsFolder);
var nugetcli = System.IO.Path.Combine(toolsFolder, "nuget.exe");
var sourceFolder = System.IO.Path.Combine(workingDirectory, "src");
var testFolder = System.IO.Path.Combine(workingDirectory, "test");
var packagesFolder = System.IO.Path.Combine(workingDirectory, "packages");
var artifactFolder = System.IO.Path.Combine(workingDirectory, buildPlan.ArtifactsFolder);
var publishFolder = System.IO.Path.Combine(artifactFolder, "publish");
var logFolder = System.IO.Path.Combine(artifactFolder, "logs");
var packageFolder = System.IO.Path.Combine(artifactFolder, "package");
var nugetPackageFolder = System.IO.Path.Combine(artifactFolder, "nugetPackages");
var scriptFolder = System.IO.Path.Combine(artifactFolder, "scripts");
/// <summary>
/// Clean artifacts.
/// </summary>
Task("Cleanup")
.Does(() =>
{
if (System.IO.Directory.Exists(artifactFolder))
{
System.IO.Directory.Delete(artifactFolder, true);
}
System.IO.Directory.CreateDirectory(artifactFolder);
System.IO.Directory.CreateDirectory(logFolder);
System.IO.Directory.CreateDirectory(packageFolder);
System.IO.Directory.CreateDirectory(scriptFolder);
});
/// <summary>
/// Pre-build setup tasks.
/// </summary>
Task("Setup")
.IsDependentOn("InstallDotnet")
.IsDependentOn("InstallXUnit")
.IsDependentOn("PopulateRuntimes")
.Does(() =>
{
});
/// <summary>
/// Populate the RIDs for the specific environment.
/// Use default RID (+ win7-x86 on Windows) for now.
/// </summary>
Task("PopulateRuntimes")
.Does(() =>
{
buildPlan.Rids = new string[]
{
"default", // To allow testing the published artifact
"win-x64",
"win-x86",
"win-arm64",
"ubuntu.14.04-x64",
"ubuntu.16.04-x64",
"centos.7-x64",
"rhel.7.2-x64",
"debian.8-x64",
"fedora.23-x64",
"opensuse.13.2-x64",
"osx.10.11-x64",
"osx-x64",
"osx-arm64",
"linux-x64",
"linux-arm64"
};
});
/// <summary>
/// Install dotnet if it isn't already installed
/// </summary>
Task("InstallDotnet")
.Does(() =>
{
// Determine if `dotnet` is installed
var dotnetInstalled = true;
try
{
Run(dotnetcli, "--info");
Information("dotnet is already installed, will skip download/install");
}
catch(Win32Exception)
{
// If we get this exception, dotnet isn't installed
dotnetInstalled = false;
}
// Install dotnet if it isn't already installed
if (!dotnetInstalled)
{
var installScript = $"dotnet-install.{shellExtension}";
System.IO.Directory.CreateDirectory(dotnetFolder);
var scriptPath = System.IO.Path.Combine(dotnetFolder, installScript);
using (WebClient client = new WebClient())
{
client.DownloadFile($"{buildPlan.DotNetInstallScriptURL}/{installScript}", scriptPath);
}
if (!IsRunningOnWindows())
{
Run("chmod", $"+x '{scriptPath}'");
}
var installArgs = $"-Channel {buildPlan.DotNetChannel}";
if (!String.IsNullOrEmpty(buildPlan.DotNetVersion))
{
installArgs = $"{installArgs} -Version {buildPlan.DotNetVersion}";
}
if (!buildPlan.UseSystemDotNetPath)
{
installArgs = $"{installArgs} -InstallDir {dotnetFolder}";
}
Run(shell, $"{shellArgument} {scriptPath} {installArgs}");
try
{
Run(dotnetcli, "--info");
}
catch (Win32Exception)
{
throw new Exception(".NET CLI failed to be installed");
}
}
});
/// <summary>
/// Installs XUnit nuget package
Task("InstallXUnit")
.Does(() =>
{
// Install the tools
var nugetPath = Environment.GetEnvironmentVariable("NUGET_EXE");
var arguments = $"install xunit.runner.console -ExcludeVersion -NoCache -Prerelease -OutputDirectory \"{toolsFolder}\"";
if (IsRunningOnWindows())
{
Run(nugetPath, arguments);
}
else
{
Run("mono", $"\"{nugetPath}\" {arguments}");
}
});
/// <summary>
/// Restore required NuGet packages.
/// </summary>
Task("Restore")
.IsDependentOn("Setup")
.Does(() =>
{
RunRestore(dotnetcli, "restore", workingDirectory)
.ExceptionOnError("Failed to restore projects under source code folder.");
});
/// <summary>
/// Build Test projects.
/// </summary>
Task("BuildTest")
.IsDependentOn("Setup")
.IsDependentOn("Restore")
.IsDependentOn("BuildFx")
.Does(() =>
{
foreach (var pair in buildPlan.TestProjects)
{
foreach (var framework in pair.Value)
{
var project = pair.Key;
var projectFolder = System.IO.Path.Combine(testFolder, project);
var logPath = System.IO.Path.Combine(logFolder, $"{project}-{framework}-build.log");
using (var logWriter = new StreamWriter(logPath)) {
Run(dotnetcli, $"build --framework {framework} --configuration {testConfiguration} \"{projectFolder}\"",
new RunOptions
{
StandardOutputWriter = logWriter,
StandardErrorWriter = logWriter
})
.ExceptionOnError($"Building test {project} failed for {framework}. See {logPath} for more details.");
}
}
}
});
/// <summary>
/// Build .NET Framework projects.
/// </summary>
Task("BuildFx")
.IsDependentOn("Setup")
.IsDependentOn("Restore")
.Does(() =>
{
foreach (var project in buildPlan.FxBuildProjects)
{
foreach (var framework in project.Frameworks)
{
var projectFolder = System.IO.Path.Combine(sourceFolder, project.Name);
var logPath = System.IO.Path.Combine(logFolder, $"{project.Name}-{framework}-build.log");
using (var logWriter = new StreamWriter(logPath)) {
Run(dotnetcli, $"build --framework {framework} --configuration {configuration} \"{projectFolder}\"",
new RunOptions
{
StandardOutputWriter = logWriter,
StandardErrorWriter = logWriter
})
.ExceptionOnError($"Building test {project.Name} failed for {framework}. See {logPath} for more details.");
}
}
}
});
/// <summary>
/// Packages projects specified in PackageProjects
/// </summary>
Task("DotnetPack")
.IsDependentOn("Cleanup")
.IsDependentOn("Setup")
.IsDependentOn("Restore")
.Does(() =>
{
foreach (var project in buildPlan.PackageProjects)
{
// For now, putting all nugets in the 1 directory
var outputFolder = System.IO.Path.Combine(nugetPackageFolder);
var projectFolder = System.IO.Path.Combine(sourceFolder, project);
DotnetPack(outputFolder, projectFolder, project);
}
});
/// <summary>
/// Packages projects specified in FxBuildProjects using available Nupecs, these projects require that publishing be done first. Note that we
/// don't do the publishing here because we need the binaries to be signed before being packaged up and that is done by the pipeline
/// currently.
/// </summary>
Task("DotnetPackNuspec")
.Does(() =>
{
foreach (var project in buildPlan.FxBuildProjects)
{
if (project.SkipPack != null && project.SkipPack)
{
continue;
}
// For now, putting all nugets in the 1 directory
var outputFolder = System.IO.Path.Combine(nugetPackageFolder);
var projectFolder = System.IO.Path.Combine(packagesFolder, project.Name);
DotnetPackNuspec(outputFolder, projectFolder, project.Name);
}
});
/// <summary>
/// Packages dotnet tool projects specified in DotnetToolProjects.
/// </summary>
Task("DotnetPackServiceTools")
.Does(() =>
{
foreach (var project in buildPlan.DotnetToolProjects)
{
var outputFolder = System.IO.Path.Combine(nugetPackageFolder);
var projectFolder = System.IO.Path.Combine(sourceFolder, project);
DotnetPack(outputFolder, projectFolder, project);
}
});
/// <summary>
/// Run all tests for .NET Desktop and .NET Core
/// </summary>
Task("TestAll")
.IsDependentOn("Test")
.IsDependentOn("TestCore")
.WithCriteria(c => HasArgument("runTests"))
.Does(() =>{});
/// <summary>
/// Run tests for .NET Core (using .NET CLI).
/// </summary>
Task("TestCore")
.IsDependentOn("Setup")
.IsDependentOn("Restore")
.WithCriteria(c => HasArgument("runTests"))
.Does(() =>
{
var testProjects = buildPlan.TestProjects
.Where(pair => pair.Value.Any(framework => framework.Contains("net")))
.Select(pair => pair.Key)
.ToList();
foreach (var testProject in testProjects)
{
var logFile = System.IO.Path.Combine(logFolder, $"{testProject}-core-result.trx");
var testWorkingDir = System.IO.Path.Combine(testFolder, testProject);
Run(dotnetcli, $"test --logger \"trx;LogFileName={logFile}\"", testWorkingDir)
.ExceptionOnError($"Test {testProject} failed for .NET Core.");
}
});
/// <summary>
/// Run tests for other frameworks (using XUnit2).
/// </summary>
Task("Test")
.IsDependentOn("Setup")
.IsDependentOn("SRGen")
.IsDependentOn("CodeGen")
.IsDependentOn("BuildTest")
.WithCriteria(c => HasArgument("runTests"))
.Does(() =>
{
foreach (var pair in buildPlan.TestProjects)
{
foreach (var framework in pair.Value)
{
// Testing against core happens in TestCore
if (framework.Contains("net"))
{
continue;
}
var project = pair.Key;
var frameworkFolder = System.IO.Path.Combine(testFolder, project, "bin", testConfiguration, framework);
var runtime = System.IO.Directory.GetDirectories(frameworkFolder).First();
var instanceFolder = System.IO.Path.Combine(frameworkFolder, runtime);
// Copy xunit executable to test folder to solve path errors
var xunitToolsFolder = System.IO.Path.Combine(toolsFolder, "xunit.runner.console", "tools");
var xunitInstancePath = System.IO.Path.Combine(instanceFolder, "xunit.console.exe");
System.IO.File.Copy(System.IO.Path.Combine(xunitToolsFolder, "xunit.console.exe"), xunitInstancePath, true);
System.IO.File.Copy(System.IO.Path.Combine(xunitToolsFolder, "xunit.runner.utility.desktop.dll"), System.IO.Path.Combine(instanceFolder, "xunit.runner.utility.desktop.dll"), true);
var targetPath = System.IO.Path.Combine(instanceFolder, $"{project}.dll");
var logFile = System.IO.Path.Combine(logFolder, $"{project}-{framework}-result.xml");
var arguments = $"\"{targetPath}\" -parallel none --logger \"trx;LogFileName={logFile}\"";
if (IsRunningOnWindows())
{
Run(xunitInstancePath, arguments, instanceFolder)
.ExceptionOnError($"Test {project} failed for {framework}");
}
else
{
Run("mono", $"\"{xunitInstancePath}\" {arguments}", instanceFolder)
.ExceptionOnError($"Test {project} failed for {framework}");
}
}
}
});
/// <summary>
/// Build, publish and package artifacts.
/// Targets all RIDs specified in build.json unless restricted by RestrictToLocalRuntime.
/// No dependencies on other tasks to support quick builds.
/// </summary>
Task("OnlyPublish")
.IsDependentOn("Setup")
.IsDependentOn("SRGen")
.IsDependentOn("CodeGen")
.Does(() =>
{
PublishProject(buildPlan.PackageName, buildPlan.MainProjects);
});
/// <summary>
/// Build, publish and package artifacts.
/// Targets all RIDs specified in build.json unless restricted by RestrictToLocalRuntime.
/// No dependencies on other tasks to support quick builds.
/// </summary>
Task("PublishExternalProjects")
.IsDependentOn("Setup")
.IsDependentOn("SrGen")
.IsDependentOn("CodeGen")
.Does(() =>
{
foreach(var project in buildPlan.Projects)
{
PublishProject(project.Name, project.PackageProjects);
}
});
void PublishProject(string packageName, string[] projects)
{
foreach (var project in projects)
{
var projectFolder = System.IO.Path.Combine(sourceFolder, project);
foreach (var framework in buildPlan.Frameworks)
{
foreach (var runtime in buildPlan.Rids)
{
var outputFolder = System.IO.Path.Combine(publishFolder, packageName, runtime, framework);
var publishArguments = "publish";
if (!runtime.Equals("default"))
{
publishArguments = $"{publishArguments} --runtime {runtime} --self-contained";
}
publishArguments = $"{publishArguments} --framework {framework} --configuration {configuration}";
publishArguments = $"{publishArguments} --output \"{outputFolder}\" \"{projectFolder}\"";
Run(dotnetcli, publishArguments)
.ExceptionOnError($"Failed to publish {project} / {framework}");
//Setting the rpath for System.Security.Cryptography.Native.dylib library
//Only required for mac. We're assuming the openssl is installed in /usr/local/opt/openssl
//If that's not the case user has to run the command manually
if (!IsRunningOnWindows() && !IsRunningOnUnix() && runtime.Contains("osx"))
{
Run("install_name_tool", "-add_rpath /usr/local/opt/openssl/lib " + outputFolder + "/System.Security.Cryptography.Native.dylib");
}
}
}
if (buildPlan.FxBuildProjects.Any(p => p.Name == project))
{
var projDefinition = buildPlan.FxBuildProjects.First(p => p.Name == project);
foreach(var framework in projDefinition.Frameworks)
{
var outputFolder = System.IO.Path.Combine(publishFolder, packageName, "default", framework);
var publishArguments = "publish";
publishArguments = $"{publishArguments} --framework {framework} --configuration {configuration}";
publishArguments = $"{publishArguments} --output \"{outputFolder}\" \"{projectFolder}\"";
Run(dotnetcli, publishArguments)
.ExceptionOnError($"Failed to publish {project} / {framework}");
}
}
if (requireArchive)
{
foreach (var framework in buildPlan.Frameworks)
{
foreach (var runtime in buildPlan.Rids)
{
var outputFolder = System.IO.Path.Combine(publishFolder, packageName, runtime, framework);
Package(runtime, framework, outputFolder, packageFolder, packageName, workingDirectory);
}
}
}
CreateRunScript(System.IO.Path.Combine(publishFolder, project, "default"), scriptFolder);
}
}
/// <summary>
/// Alias for OnlyPublish.
/// Targets all RIDs as specified in build.json.
/// </summary>
Task("AllPublish")
.IsDependentOn("Restore")
.IsDependentOn("OnlyPublish")
.IsDependentOn("PublishExternalProjects")
.Does(() =>
{
});
/// <summary>
/// Restrict the RIDs for the local default.
/// </summary>
Task("RestrictToLocalRuntime")
.IsDependentOn("Setup")
.Does(() =>
{
buildPlan.Rids = new string[] {"default"};
});
/// <summary>
/// Alias for OnlyPublish.
/// Restricts publishing to local RID.
/// </summary>
Task("LocalPublish")
.IsDependentOn("Restore")
.IsDependentOn("RestrictToLocalRuntime")
.IsDependentOn("OnlyPublish")
.IsDependentOn("PublishExternalProjects")
.Does(() =>
{
});
/// <summary>
/// Test the published binaries if they start up without errors.
/// Uses builds corresponding to local RID.
/// </summary>
Task("TestPublished")
.IsDependentOn("Setup")
.Does(() =>
{
foreach (var project in buildPlan.MainProjects)
{
var projectFolder = System.IO.Path.Combine(sourceFolder, project);
var scriptsToTest = new string[] {"SQLTOOLSSERVICE.Core"};//TODO
foreach (var script in scriptsToTest)
{
var scriptPath = System.IO.Path.Combine(scriptFolder, script);
var didNotExitWithError = Run($"{shell}", $"{shellArgument} \"{scriptPath}\" -s \"{projectFolder}\" --stdio",
new RunOptions
{
TimeOut = 10000
})
.DidTimeOut;
if (!didNotExitWithError)
{
throw new Exception($"Failed to run {script}");
}
}
}
});
/// <summary>
/// Clean install path.
/// </summary>
Task("CleanupInstall")
.Does(() =>
{
if (System.IO.Directory.Exists(installFolder))
{
System.IO.Directory.Delete(installFolder, true);
}
System.IO.Directory.CreateDirectory(installFolder);
});
/// <summary>
/// Quick build.
/// </summary>
Task("Quick")
.IsDependentOn("Cleanup")
.IsDependentOn("LocalPublish")
.Does(() =>
{
});
/// <summary>
/// Quick build + install.
/// </summary>
Task("Install")
.IsDependentOn("Cleanup")
.IsDependentOn("LocalPublish")
.IsDependentOn("CleanupInstall")
.Does(() =>
{
foreach (var project in buildPlan.MainProjects)
{
foreach (var framework in buildPlan.Frameworks)
{
var outputFolder = System.IO.Path.GetFullPath(System.IO.Path.Combine(publishFolder, project, "default", framework));
var targetFolder = System.IO.Path.GetFullPath(System.IO.Path.Combine(installFolder, framework));
// Copy all the folders
foreach (var directory in System.IO.Directory.GetDirectories(outputFolder, "*", SearchOption.AllDirectories))
System.IO.Directory.CreateDirectory(System.IO.Path.Combine(targetFolder, directory.Substring(outputFolder.Length + 1)));
//Copy all the files
foreach (string file in System.IO.Directory.GetFiles(outputFolder, "*", SearchOption.AllDirectories))
System.IO.File.Copy(file, System.IO.Path.Combine(targetFolder, file.Substring(outputFolder.Length + 1)), true);
}
CreateRunScript(installFolder, scriptFolder);
}
});
/// <summary>
/// Full build targeting all RIDs specified in build.json.
/// </summary>
Task("All")
.IsDependentOn("Cleanup")
.IsDependentOn("Restore")
.IsDependentOn("TestAll")
.IsDependentOn("DotnetPack")
.IsDependentOn("AllPublish")
//.IsDependentOn("TestPublished")
.Does(() =>
{
});
/// <summary>
/// Full build targeting local RID.
/// </summary>
Task("Local")
.IsDependentOn("Cleanup")
.IsDependentOn("Restore")
.IsDependentOn("TestAll")
.IsDependentOn("LocalPublish")
// .IsDependentOn("TestPublished")
.Does(() =>
{
});
/// <summary>
/// Update the package versions within project.json files.
/// Uses depversion.json file as input.
/// </summary>
Task("SetPackageVersions")
.Does(() =>
{
var jDepVersion = JObject.Parse(System.IO.File.ReadAllText(System.IO.Path.Combine(workingDirectory, "depversion.json")));
var projects = System.IO.Directory.GetFiles(sourceFolder, "project.json", SearchOption.AllDirectories).ToList();
projects.AddRange(System.IO.Directory.GetFiles(testFolder, "project.json", SearchOption.AllDirectories));
foreach (var project in projects)
{
var jProject = JObject.Parse(System.IO.File.ReadAllText(project));
var dependencies = jProject.SelectTokens("dependencies")
.Union(jProject.SelectTokens("frameworks.*.dependencies"))
.SelectMany(dependencyToken => dependencyToken.Children<JProperty>());
foreach (JProperty dependency in dependencies)
{
if (jDepVersion[dependency.Name] != null)
{
dependency.Value = jDepVersion[dependency.Name];
}
}
System.IO.File.WriteAllText(project, JsonConvert.SerializeObject(jProject, Formatting.Indented));
}
});
/// <summary>
/// Executes SRGen to create a resx file and associated designer C# file
/// </summary>
Task("SRGen")
.Does(() =>
{
// save current working directory to restore at end of task
var taskStartedWorkingDirectory = System.IO.Directory.GetCurrentDirectory();
try
{
var projects = System.IO.Directory.GetFiles(sourceFolder, "*.csproj", SearchOption.AllDirectories).ToList();
var locTemplateDir = System.IO.Path.Combine(sourceFolder, "../localization");
foreach(var project in projects) {
var projectDir = System.IO.Path.GetDirectoryName(project);
// set current directory to this project so relative paths can be reliably
// used. This is to address an issue with quoting differences between Windows
// and MacOS. On MacOS the dotnet command ends up calling SRGen with quotations around
// the arguments stripped off. This causes SRGen to interpret the arg values as options
System.IO.Directory.SetCurrentDirectory(projectDir);
// build remaining paths relative to the project directory
var localizationDir = System.IO.Path.Combine(".", "Localization");
var projectName = (new System.IO.DirectoryInfo(projectDir)).Name;
var projectNameSpace = projectName + ".Localization";
var projectStrings = System.IO.Path.Combine(localizationDir, "sr.strings");
if (!System.IO.File.Exists(projectStrings))
{
Information("Project {0} doesn't contain 'sr.strings' file", projectName);
continue;
}
var srgenPath = System.IO.Path.Combine(toolsFolder, "Microsoft.Data.Tools.StringResourceTool", "lib", "netcoreapp3.1", "srgen.dll");
var outputResx = System.IO.Path.Combine(localizationDir, "sr.resx");
var inputXliff = System.IO.Path.Combine(localizationDir, "transXliff");
var outputXlf = System.IO.Path.Combine(localizationDir, "sr.xlf");
var outputCs = System.IO.Path.Combine(localizationDir, "sr.cs");
// Delete preexisting resx and designer files
if (System.IO.File.Exists(outputResx))
{
System.IO.File.Delete(outputResx);
}
if (System.IO.File.Exists(outputCs))
{
System.IO.File.Delete(outputCs);
}
if (!System.IO.Directory.Exists(inputXliff))
{
System.IO.Directory.CreateDirectory(inputXliff);
}
// Run SRGen
var dotnetArgs = string.Format("--roll-forward LatestMajor {0} -or \"{1}\" -oc \"{2}\" -ns \"{3}\" -an \"{4}\" -cn SR -l CS -dnx \"{5}\"",
srgenPath, outputResx, outputCs, projectName, projectNameSpace, projectStrings);
Information("{0}", dotnetcli);
Information("{0}", dotnetArgs);
Run(dotnetcli, dotnetArgs)
.ExceptionOnError("Failed to run SRGen.");
// Update XLF file from new Resx file
var doc = new XliffParser.XlfDocument(outputXlf);
doc.UpdateFromSource();
var outputXlfFile = doc.Files.Single();
foreach (var unit in outputXlfFile.TransUnits)
{
unit.Target = unit.Source;
}
doc.Save();
// Update ResX files from new xliff files
var xlfDocNames = System.IO.Directory.GetFiles(inputXliff, "*.xlf", SearchOption.AllDirectories).ToList();
foreach(var docName in xlfDocNames)
{
// load our language XLIFF
var xlfDoc = new XliffParser.XlfDocument(docName);
var xlfFile = xlfDoc.Files.Single();
// load a language template
var templateFileLocation = System.IO.Path.Combine(locTemplateDir, System.IO.Path.GetFileName(docName) + ".template");
var templateDoc = new XliffParser.XlfDocument(templateFileLocation);
var templateFile = templateDoc.Files.Single();
// iterate through our tranlation units and prune invalid units
foreach (var unit in xlfFile.TransUnits)
{
// if a unit does not have a target it is invalid
if (unit.Target != null) {
templateFile.AddTransUnit(unit.Id, unit.Source, unit.Target, 0, 0);
}
}
// export modified template to RESX
var newPath = System.IO.Path.Combine(localizationDir, System.IO.Path.GetFileName(docName));
templateDoc.SaveAsResX(newPath.Replace("xlf","resx"));
}
}
}
finally
{
// restore the original working directory
System.IO.Directory.SetCurrentDirectory(taskStartedWorkingDirectory);
}
});
/// <summary>
/// Executes T4Template-based code generators
/// </summary>
Task("CodeGen")
.Does(() =>
{
var t4Files = GetFiles(sourceFolder + "/**/*.tt");
foreach(var t4Template in t4Files)
{
TransformTemplate(t4Template, new TextTransformSettings {});
}
});
/// <summary>
/// Default Task aliases to Local.
/// </summary>
Task("Default")
.IsDependentOn("Local")
.Does(() =>
{
});
/// <summary>
/// Default to Local.
/// </summary>
RunTarget(target);