Build scripts to create publish folders and archive packages

This commit is contained in:
Leila Lali
2016-09-07 12:16:00 -07:00
parent 8ca88992be
commit 9fc32fa74e
12 changed files with 1170 additions and 9 deletions

75
BUILD.md Normal file
View File

@@ -0,0 +1,75 @@
# Usage
Run `build.(ps1|sh)` with the desired set of arguments (see below for options).
The build script itself is `build.cake`, written in C# using the Cake build automation system.
All build related activites should be encapsulated in this file for cross-platform access.
# Arguments
## Primary
`-target=TargetName`: The name of the build task/target to execute (see below for listing and details).
Defaults to `Default`.
`-configuration=(Release|Debug)`: The configuration to build.
Defaults to `Release`.
## Extra
`-test-configuration=(Release|Debug)`: The configuration to use for the unit tests.
Defaults to `Debug`.
`-install-path=Path`: Path used for the **Install** target.
Defaults to `(%USERPROFILE%|$HOME)/.sqltoolsservice/local`
`-archive`: Enable the generation of publishable archives after a build.
# Targets
**Default**: Alias for Local.
**Local**: Full build including testing for the machine-local runtime.
**All**: Same as local, but targeting all runtimes selected by `PopulateRuntimes` in `build.cake`.
Currently configured to also build for a 32-bit Windows runtime on Windows machines.
No additional runtimes are currently selected on non-Windows machines.
**Quick**: Local build which skips all testing.
**Install**: Same as quick, but installs the generated binaries into `install-path`.
**SetPackageVersions**: Updates the dependency versions found within `project.json` files using information from `depversion.json`.
Used for maintainence within the project, not needed for end-users. More information below.
# Configuration files
## build.json
A number of build-related options, including folder names for different entities. Interesting options:
**DotNetInstallScriptURL**: The URL where the .NET SDK install script is located.
Can be used to pin to a specific script version, if a breaking change occurs.
**"DotNetChannel"**: The .NET SDK channel used for retreiving the tools.
**"DotNetVersion"**: The .NET SDK version used for the build. Can be used to pin to a specific version.
Using the string `Latest` will retrieve the latest version.
## depversion.json
A listing of all dependencies (and their desired versions) used by `project.json` files throughout the project.
Allows for quick and automatic updates to the dependency version numbers using the **SetPackageVersions** target.
# Artifacts generated
* Binaries of Microsoft.SqlTools.ServiceLayer and its libraries built for the local machine in `artifacts/publish/Microsoft.SqlTools.ServiceLayer/default/{framework}/`
* Scripts to run Microsoft.SqlTools.ServiceLayer at `scripts/SQLTOOLSSERVICE(.Core)(.cmd)`
* These scripts are updated for every build and every install.
* The scripts point to the installed binary after and install, otherwise just the build folder (reset if a new build occurs without an install).
* Binaries of Microsoft.SqlTools.ServiceLayer and its libraries cross-compiled for other runtimes (if selected in **PopulateRuntimes**) `artifacts/publish/Microsoft.SqlTools.ServiceLayer/{runtime}/{framework}/`
* Test logs in `artifacts/logs`
* Archived binaries in `artifacts/package` (only if `-archive` used on command line)
# Requirements
The build system requires Mono to be installed on non-Windows machines as Cake is not built using .NET Core (yet).

507
build.cake Normal file
View File

@@ -0,0 +1,507 @@
#addin "Newtonsoft.Json"
#load "scripts/runhelpers.cake"
#load "scripts/archiving.cake"
#load "scripts/artifacts.cake"
using System.ComponentModel;
using System.Net;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
// 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 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 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 MainProject { 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 sourceFolder = System.IO.Path.Combine(workingDirectory, "src");
var testFolder = System.IO.Path.Combine(workingDirectory, "test");
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 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("BuildEnvironment")
.IsDependentOn("PopulateRuntimes")
.Does(() =>
{
});
/// <summary>
/// Populate the RIDs for the specific environment.
/// Use default RID (+ win7-x86 on Windows) for now.
/// </summary>
Task("PopulateRuntimes")
.IsDependentOn("BuildEnvironment")
.Does(() =>
{
buildPlan.Rids = new string[]
{
"default", // To allow testing the published artifact
"win7-x64",
"win7-x86",
"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"
};
});
/// <summary>
/// Install/update build environment.
/// </summary>
Task("BuildEnvironment")
.Does(() =>
{
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 binary cannot be found.");
}
System.IO.Directory.CreateDirectory(toolsFolder);
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", sourceFolder)
.ExceptionOnError("Failed to restore projects under source code folder.");
RunRestore(dotnetcli, "restore --infer-runtimes", testFolder)
.ExceptionOnError("Failed to restore projects under test code folder.");
});
/// <summary>
/// Build Test projects.
/// </summary>
Task("BuildTest")
.IsDependentOn("Setup")
.IsDependentOn("Restore")
.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 runLog = new List<string>();
Run(dotnetcli, $"build --framework {framework} --configuration {testConfiguration} \"{projectFolder}\"",
new RunOptions
{
StandardOutputListing = runLog
})
.ExceptionOnError($"Building test {project} failed for {framework}.");
System.IO.File.WriteAllLines(System.IO.Path.Combine(logFolder, $"{project}-{framework}-build.log"), runLog.ToArray());
}
}
});
/// <summary>
/// Run all tests for .NET Desktop and .NET Core
/// </summary>
Task("TestAll")
.IsDependentOn("Test")
.IsDependentOn("TestCore")
.Does(() =>{});
/// <summary>
/// Run all tests for Travis CI .NET Desktop and .NET Core
/// </summary>
Task("TravisTestAll")
.IsDependentOn("Cleanup")
.IsDependentOn("TestAll")
.Does(() =>{});
/// <summary>
/// Run tests for .NET Core (using .NET CLI).
/// </summary>
Task("TestCore")
.IsDependentOn("Setup")
.IsDependentOn("Restore")
.Does(() =>
{
var testProjects = buildPlan.TestProjects
.Where(pair => pair.Value.Any(framework => framework.Contains("netcoreapp")))
.Select(pair => pair.Key)
.ToList();
foreach (var testProject in testProjects)
{
var logFile = System.IO.Path.Combine(logFolder, $"{testProject}-core-result.xml");
var testWorkingDir = System.IO.Path.Combine(testFolder, testProject);
Run(dotnetcli, $"test -f netcoreapp1.0 -xml \"{logFile}\" -notrait category=failing", testWorkingDir)
.ExceptionOnError($"Test {testProject} failed for .NET Core.");
}
});
/// <summary>
/// Run tests for other frameworks (using XUnit2).
/// </summary>
Task("Test")
.IsDependentOn("Setup")
.IsDependentOn("BuildTest")
.Does(() =>
{
foreach (var pair in buildPlan.TestProjects)
{
foreach (var framework in pair.Value)
{
// Testing against core happens in TestCore
if (framework.Contains("netcoreapp"))
{
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 -xml \"{logFile}\" -notrait category=failing";
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")
.Does(() =>
{
var project = buildPlan.MainProject;
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, project, runtime, framework);
var publishArguments = "publish";
if (!runtime.Equals("default"))
{
publishArguments = $"{publishArguments} --runtime {runtime}";
}
publishArguments = $"{publishArguments} --framework {framework} --configuration {configuration}";
publishArguments = $"{publishArguments} --output \"{outputFolder}\" \"{projectFolder}\"";
Run(dotnetcli, publishArguments)
.ExceptionOnError($"Failed to publish {project} / {framework}");
if (requireArchive)
{
Package(runtime, framework, outputFolder, packageFolder, buildPlan.MainProject.ToLower());
}
}
}
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")
.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")
.Does(() =>
{
});
/// <summary>
/// Test the published binaries if they start up without errors.
/// Uses builds corresponding to local RID.
/// </summary>
Task("TestPublished")
.IsDependentOn("Setup")
.Does(() =>
{
var project = buildPlan.MainProject;
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(() =>
{
var project = buildPlan.MainProject;
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("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>
/// Build centered around producing the final artifacts for Travis
///
/// The tests are run as a different task "TestAll"
/// </summary>
Task("Travis")
.IsDependentOn("Cleanup")
.IsDependentOn("Restore")
.IsDependentOn("AllPublish")
// .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>
/// Default Task aliases to Local.
/// </summary>
Task("Default")
.IsDependentOn("Local")
.Does(() =>
{
});
/// <summary>
/// Default to Local.
/// </summary>
RunTarget(target);

18
build.json Normal file
View File

@@ -0,0 +1,18 @@
{
"UseSystemDotNetPath": "true",
"DotNetFolder": ".dotnet",
"DotNetInstallScriptURL": "https://raw.githubusercontent.com/dotnet/cli/rel/1.0.0-preview2/scripts/obtain",
"DotNetChannel": "preview",
"DotNetVersion": "1.0.0-preview2-003121",
"BuildToolsFolder": ".tools",
"ArtifactsFolder": "artifacts",
"TestProjects": {
"Microsoft.SqlTools.ServiceLayer.Test": [
"netcoreapp1.0"
]
},
"Frameworks": [
"netcoreapp1.0"
],
"MainProject": "Microsoft.SqlTools.ServiceLayer"
}

3
build.ps1 Normal file
View File

@@ -0,0 +1,3 @@
$Env:SQLTOOLSSERVICE_PACKAGE_OSNAME = "win-x64"
.\scripts\cake-bootstrap.ps1 -experimental @args
exit $LASTEXITCODE

12
build.sh Normal file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
# Handle to many files on osx
if [ "$TRAVIS_OS_NAME" == "osx" ] || [ `uname` == "Darwin" ]; then
ulimit -n 4096
fi
if [ "$TRAVIS_OS_NAME" == "osx" ] || [ `uname` == "Darwin" ]; then
export SQLTOOLSSERVICE_PACKAGE_OSNAME=osx-x64
else
export SQLTOOLSSERVICE_PACKAGE_OSNAME=linux-x64
fi
bash ./scripts/cake-bootstrap.sh "$@"

104
scripts/archiving.cake Normal file
View File

@@ -0,0 +1,104 @@
#load "runhelpers.cake"
using System.IO.Compression;
using System.Text.RegularExpressions;
/// <summary>
/// Generate the build identifier based on the RID and framework identifier.
/// Special rules when running on Travis (for publishing purposes).
/// </summary>
/// <param name="runtime">The RID</param>
/// <param name="framework">The framework identifier</param>
/// <returns>The designated build identifier</returns>
string GetBuildIdentifier(string runtime, string framework)
{
var runtimeShort = "";
// Default RID uses package name set in build script
if (runtime.Equals("default"))
{
runtimeShort = Environment.GetEnvironmentVariable("SQLTOOLSSERVICE_PACKAGE_OSNAME");
}
else
{
// Remove version number. Note: because there are separate versions for Ubuntu 14 and 16,
// we treat Ubuntu as a special case.
if (runtime.StartsWith("ubuntu.14"))
{
runtimeShort = "ubuntu14-x64";
}
else if (runtime.StartsWith("ubuntu.16"))
{
runtimeShort = "ubuntu16-x64";
}
else
{
runtimeShort = Regex.Replace(runtime, "(\\d|\\.)*-", "-");
}
}
return $"{runtimeShort}-{framework}";
}
/// <summary>
/// Generate an archive out of the given published folder.
/// Use ZIP for Windows runtimes.
/// Use TAR.GZ for non-Windows runtimes.
/// Use 7z to generate TAR.GZ on Windows if available.
/// </summary>
/// <param name="runtime">The RID</param>
/// <param name="contentFolder">The folder containing the files to package</param>
/// <param name="archiveName">The target archive name (without extension)</param>
void DoArchive(string runtime, string contentFolder, string archiveName)
{
// On all platforms use ZIP for Windows runtimes
if (runtime.Contains("win") || (runtime.Equals("default") && IsRunningOnWindows()))
{
var zipFile = $"{archiveName}.zip";
Zip(contentFolder, zipFile);
}
// On all platforms use TAR.GZ for Unix runtimes
else
{
var tarFile = $"{archiveName}.tar.gz";
// Use 7z to create TAR.GZ on Windows
if (IsRunningOnWindows())
{
var tempFile = $"{archiveName}.tar";
try
{
Run("7z", $"a \"{tempFile}\"", contentFolder)
.ExceptionOnError($"Tar-ing failed for {contentFolder} {archiveName}");
Run("7z", $"a \"{tarFile}\" \"{tempFile}\"", contentFolder)
.ExceptionOnError($"Compression failed for {contentFolder} {archiveName}");
System.IO.File.Delete(tempFile);
}
catch(Win32Exception)
{
Information("Warning: 7z not available on PATH to pack tar.gz results");
}
}
// Use tar to create TAR.GZ on Unix
else
{
Run("tar", $"czf \"{tarFile}\" .", contentFolder)
.ExceptionOnError($"Compression failed for {contentFolder} {archiveName}");
}
}
}
/// <summary>
/// Package a given output folder using a build identifier generated from the RID and framework identifier.
/// </summary>
/// <param name="runtime">The RID</param>
/// <param name="framework">The framework identifier</param>
/// <param name="contentFolder">The folder containing the files to package</param>
/// <param name="packageFolder">The destination folder for the archive</param>
/// <param name="projectName">The project name</param>
void Package(string runtime, string framework, string contentFolder, string packageFolder, string projectName)
{
var buildIdentifier = GetBuildIdentifier(runtime, framework);
if (buildIdentifier != null)
{
DoArchive(runtime, contentFolder, $"{packageFolder}/{projectName}-{buildIdentifier}");
}
}

43
scripts/artifacts.cake Normal file
View File

@@ -0,0 +1,43 @@
#load "runhelpers.cake"
/// <summary>
/// Generate the scripts which target the SQLTOOLSSERVICE binaries.
/// </summary>
/// <param name="outputRoot">The root folder where the publised (or installed) binaries are located</param>
void CreateRunScript(string outputRoot, string scriptFolder)
{
if (IsRunningOnWindows())
{
var coreScript = System.IO.Path.Combine(scriptFolder, "SQLTOOLSSERVICE.Core.cmd");
var sqlToolsServicePath = System.IO.Path.Combine(System.IO.Path.GetFullPath(outputRoot), "{0}", "SQLTOOLSSERVICE");
var content = new string[] {
"SETLOCAL",
"",
$"\"{sqlToolsServicePath}\" %*"
};
if (System.IO.File.Exists(coreScript))
{
System.IO.File.Delete(coreScript);
}
content[2] = String.Format(content[2], "netcoreapp1.0");
System.IO.File.WriteAllLines(coreScript, content);
}
else
{
var coreScript = System.IO.Path.Combine(scriptFolder, "SQLTOOLSSERVICE.Core");
var sqlToolsServicePath = System.IO.Path.Combine(System.IO.Path.GetFullPath(outputRoot), "{1}", "SQLTOOLSSERVICE");
var content = new string[] {
"#!/bin/bash",
"",
$"{{0}} \"{sqlToolsServicePath}{{2}}\" \"$@\""
};
if (System.IO.File.Exists(coreScript))
{
System.IO.File.Delete(coreScript);
}
content[2] = String.Format(content[2], "", "netcoreapp1.0", "");
System.IO.File.WriteAllLines(coreScript, content);
Run("chmod", $"+x \"{coreScript}\"");
}
}

110
scripts/cake-bootstrap.ps1 Normal file
View File

@@ -0,0 +1,110 @@
<#
.SYNOPSIS
This is a Powershell script to bootstrap a Cake build.
.DESCRIPTION
This Powershell script will download NuGet if missing, restore NuGet tools (including Cake)
and execute your Cake build script with the parameters you provide.
.PARAMETER Script
The build script to execute.
.PARAMETER Target
The build script target to run.
.PARAMETER Configuration
The build configuration to use.
.PARAMETER Verbosity
Specifies the amount of information to be displayed.
Tells Cake to use the latest Roslyn release.
.PARAMETER WhatIf
Performs a dry run of the build script.
No tasks will be executed.
.PARAMETER Mono
Tells Cake to use the Mono scripting engine.
.LINK
http://cakebuild.net
#>
[CmdletBinding()]
Param(
[string]$Script = "build.cake",
[ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")]
[string]$Verbosity = "Verbose",
[Alias("DryRun","Noop")]
[switch]$WhatIf,
[switch]$Mono,
[switch]$SkipToolPackageRestore,
[Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
[string[]]$ScriptArgs
)
Write-Host "Preparing to run build script..."
$PS_SCRIPT_ROOT = split-path -parent $MyInvocation.MyCommand.Definition;
$TOOLS_DIR = Join-Path $PSScriptRoot "..\.tools"
$NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe"
$NUGET_URL = "https://dist.nuget.org/win-x86-commandline/v3.3.0/nuget.exe"
$CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe"
$PACKAGES_CONFIG = Join-Path $PS_SCRIPT_ROOT "packages.config"
# Should we use mono?
$UseMono = "";
if($Mono.IsPresent) {
Write-Verbose -Message "Using the Mono based scripting engine."
$UseMono = "-mono"
}
# Is this a dry run?
$UseDryRun = "";
if($WhatIf.IsPresent) {
$UseDryRun = "-dryrun"
}
# Make sure tools folder exists
if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) {
Write-Verbose -Message "Creating tools directory..."
New-Item -Path $TOOLS_DIR -Type directory | out-null
}
# Try download NuGet.exe if not exists
if (!(Test-Path $NUGET_EXE)) {
Write-Verbose -Message "Downloading NuGet.exe..."
try {
(New-Object System.Net.WebClient).DownloadFile($NUGET_URL, $NUGET_EXE)
} catch {
Throw "Could not download NuGet.exe."
}
}
# Save nuget.exe path to environment to be available to child processed
$ENV:NUGET_EXE = $NUGET_EXE
# Restore tools from NuGet?
if(-Not $SkipToolPackageRestore.IsPresent)
{
# Restore packages from NuGet.
Push-Location
Set-Location $TOOLS_DIR
Write-Verbose -Message "Restoring tools from NuGet..."
$NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install $PACKAGES_CONFIG -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`""
Write-Verbose -Message ($NuGetOutput | out-string)
Pop-Location
if ($LASTEXITCODE -ne 0)
{
exit $LASTEXITCODE
}
}
# Make sure that Cake has been installed.
if (!(Test-Path $CAKE_EXE)) {
Throw "Could not find Cake.exe at $CAKE_EXE"
}
# Start Cake
Write-Host "Running build script..."
Invoke-Expression "& `"$CAKE_EXE`" `"$Script`" -verbosity=`"$Verbosity`" $UseMono $UseDryRun $ScriptArgs"
exit $LASTEXITCODE

69
scripts/cake-bootstrap.sh Normal file
View File

@@ -0,0 +1,69 @@
#!/usr/bin/env bash
###############################################################
# This is the Cake bootstrapper script that is responsible for
# downloading Cake and all specified tools from NuGet.
###############################################################
# Define directories.
SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
TOOLS_DIR=$SCRIPT_DIR/../.tools
export NUGET_EXE=$TOOLS_DIR/nuget.exe
CAKE_EXE=$TOOLS_DIR/Cake/Cake.exe
PACKAGES_CONFIG=$SCRIPT_DIR/packages.config
# Define default arguments.
SCRIPT="build.cake"
VERBOSITY="verbose"
DRYRUN=
SHOW_VERSION=false
SCRIPT_ARGUMENTS=()
# Parse arguments.
for i in "$@"; do
case $1 in
-s|--script) SCRIPT="$2"; shift ;;
-v|--verbosity) VERBOSITY="$2"; shift ;;
-d|--dryrun) DRYRUN="-dryrun" ;;
--version) SHOW_VERSION=true ;;
--) shift; SCRIPT_ARGUMENTS+=("$@"); break ;;
*) SCRIPT_ARGUMENTS+=("$1") ;;
esac
shift
done
# Make sure the tools folder exist.
if [ ! -d "$TOOLS_DIR" ]; then
mkdir "$TOOLS_DIR"
fi
# Download NuGet if it does not exist.
if [ ! -f "$NUGET_EXE" ]; then
echo "Downloading NuGet..."
curl -Lsfo "$NUGET_EXE" https://dist.nuget.org/win-x86-commandline/v3.3.0/nuget.exe
if [ $? -ne 0 ]; then
echo "An error occured while downloading nuget.exe."
exit 1
fi
fi
# Restore tools from NuGet.
pushd "$TOOLS_DIR" >/dev/null
mono "$NUGET_EXE" install "$PACKAGES_CONFIG" -ExcludeVersion -OutputDirectory "$TOOLS_DIR"
if [ $? -ne 0 ]; then
echo "Could not restore NuGet packages."
exit 1
fi
popd >/dev/null
# Make sure that Cake has been installed.
if [ ! -f "$CAKE_EXE" ]; then
echo "Could not find Cake.exe at '$CAKE_EXE'."
exit 1
fi
# Start Cake
if $SHOW_VERSION; then
exec mono "$CAKE_EXE" -version
else
exec mono "$CAKE_EXE" $SCRIPT -verbosity=$VERBOSITY $DRYRUN "${SCRIPT_ARGUMENTS[@]}"
fi

5
scripts/packages.config Normal file
View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Cake" version="0.10.1" />
<package id="Newtonsoft.Json" version="8.0.3" />
</packages>

204
scripts/runhelpers.cake Normal file
View File

@@ -0,0 +1,204 @@
using System.Collections.Generic;
using System.Diagnostics;
/// <summary>
/// Class encompassing the optional settings for running processes.
/// </summary>
public class RunOptions
{
/// <summary>
/// The working directory of the process.
/// </summary>
public string WorkingDirectory { get; set; }
/// <summary>
/// Container logging the StandardOutput content.
/// </summary>
public IList<string> StandardOutputListing { get; set; }
/// <summary>
/// Desired maximum time-out for the process
/// </summary>
public int TimeOut { get; set; }
}
/// <summary>
/// Wrapper for the exit code and state.
/// Used to query the result of an execution with method calls.
/// </summary>
public struct ExitStatus
{
private int _code;
private bool _timeOut;
/// <summary>
/// Default constructor when the execution finished.
/// </summary>
/// <param name="code">The exit code</param>
public ExitStatus(int code)
{
this._code = code;
this._timeOut = false;
}
/// <summary>
/// Default constructor when the execution potentially timed out.
/// </summary>
/// <param name="code">The exit code</param>
/// <param name="timeOut">True if the execution timed out</param>
public ExitStatus(int code, bool timeOut)
{
this._code = code;
this._timeOut = timeOut;
}
/// <summary>
/// Flag signalling that the execution timed out.
/// </summary>
public bool DidTimeOut { get { return _timeOut; } }
/// <summary>
/// Implicit conversion from ExitStatus to the exit code.
/// </summary>
/// <param name="exitStatus">The exit status</param>
/// <returns>The exit code</returns>
public static implicit operator int(ExitStatus exitStatus)
{
return exitStatus._code;
}
/// <summary>
/// Trigger Exception for non-zero exit code.
/// </summary>
/// <param name="errorMessage">The message to use in the Exception</param>
/// <returns>The exit status for further queries</returns>
public ExitStatus ExceptionOnError(string errorMessage)
{
if (this._code != 0)
{
throw new Exception(errorMessage);
}
return this;
}
}
/// <summary>
/// Run the given executable with the given arguments.
/// </summary>
/// <param name="exec">Executable to run</param>
/// <param name="args">Arguments</param>
/// <returns>The exit status for further queries</returns>
ExitStatus Run(string exec, string args)
{
return Run(exec, args, new RunOptions());
}
/// <summary>
/// Run the given executable with the given arguments.
/// </summary>
/// <param name="exec">Executable to run</param>
/// <param name="args">Arguments</param>
/// <param name="workingDirectory">Working directory</param>
/// <returns>The exit status for further queries</returns>
ExitStatus Run(string exec, string args, string workingDirectory)
{
return Run(exec, args,
new RunOptions()
{
WorkingDirectory = workingDirectory
});
}
/// <summary>
/// Run the given executable with the given arguments.
/// </summary>
/// <param name="exec">Executable to run</param>
/// <param name="args">Arguments</param>
/// <param name="runOptions">Optional settings</param>
/// <returns>The exit status for further queries</returns>
ExitStatus Run(string exec, string args, RunOptions runOptions)
{
var workingDirectory = runOptions.WorkingDirectory ?? System.IO.Directory.GetCurrentDirectory();
var process = System.Diagnostics.Process.Start(
new ProcessStartInfo(exec, args)
{
WorkingDirectory = workingDirectory,
UseShellExecute = false,
RedirectStandardOutput = runOptions.StandardOutputListing != null
});
if (runOptions.StandardOutputListing != null)
{
process.OutputDataReceived += (s, e) =>
{
if (e.Data != null)
{
runOptions.StandardOutputListing.Add(e.Data);
}
};
process.BeginOutputReadLine();
}
if (runOptions.TimeOut == 0)
{
process.WaitForExit();
return new ExitStatus(process.ExitCode);
}
else
{
bool finished = process.WaitForExit(runOptions.TimeOut);
if (finished)
{
return new ExitStatus(process.ExitCode);
}
else
{
KillProcessTree(process);
return new ExitStatus(0, true);
}
}
}
/// <summary>
/// Run restore with the given arguments
/// </summary>
/// <param name="exec">Executable to run</param>
/// <param name="args">Arguments</param>
/// <param name="runOptions">Optional settings</param>
/// <returns>The exit status for further queries</returns>
ExitStatus RunRestore(string exec, string args, string workingDirectory)
{
Information("Restoring packages....");
var p = StartAndReturnProcess(exec,
new ProcessSettings
{
Arguments = args,
RedirectStandardOutput = true,
WorkingDirectory = workingDirectory
});
p.WaitForExit();
var exitCode = p.GetExitCode();
if (exitCode == 0)
{
Information("Package restore successful!");
}
else
{
Error(string.Join("\n", p.GetStandardOutput()));
}
return new ExitStatus(exitCode);
}
/// <summary>
/// Kill the given process and all its child processes.
/// </summary>
/// <param name="process">Root process</param>
public void KillProcessTree(Process process)
{
// Child processes are not killed on Windows by default
// Use TASKKILL to kill the process hierarchy rooted in the process
if (IsRunningOnWindows())
{
StartProcess($"TASKKILL",
new ProcessSettings
{
Arguments = $"/PID {process.Id} /T /F",
});
}
else
{
process.Kill();
}
}

View File

@@ -13,18 +13,29 @@
"Microsoft.SqlServer.Smo": "140.1.5",
"System.Security.SecureString": "4.0.0",
"System.Collections.Specialized": "4.0.1",
"System.ComponentModel.TypeConverter": "4.1.0",
"System.Diagnostics.TraceSource": "4.0.0"
"System.ComponentModel.TypeConverter": "4.1.0",
"System.Diagnostics.TraceSource": "4.0.0",
"NETStandard.Library": "1.6.0",
"Microsoft.NETCore.Runtime.CoreCLR": "1.0.2",
"Microsoft.NETCore.DotNetHostPolicy": "1.0.1",
"System.Diagnostics.Process": "4.1.0",
"System.Threading.Thread": "4.0.0"
},
"frameworks": {
"netcoreapp1.0": {
"dependencies": {
"Microsoft.NETCore.App": {
"type": "platform",
"version": "1.0.0"
}
},
"imports": "dnxcore50"
"imports": "dnxcore50"
}
},
"runtimes": {
"win7-x64": {},
"win7-x86": {},
"osx.10.11-x64": {},
"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": {}
}
}