This commit is contained in:
Matthew Leibowitz 2019-12-08 15:21:53 +02:00 коммит произвёл GitHub
Родитель 34093ecb6e
Коммит 6cdf2cc73c
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
30 изменённых файлов: 489 добавлений и 428 удалений

Просмотреть файл

@ -1,9 +1,9 @@
#addin nuget:?package=Cake.Xamarin&version=3.0.2
#addin nuget:?package=Cake.XCode&version=4.2.0
#addin nuget:?package=Cake.FileHelpers&version=3.2.1
#addin nuget:?package=Cake.Json&version=4.0.0&loaddependencies=true
#addin nuget:?package=Cake.Json&version=4.0.0
#addin nuget:?package=SharpCompress&version=0.24.0
#addin nuget:?package=Mono.ApiTools.NuGetDiff&version=1.1.0-preview.1&prerelease&loaddependencies=true
#addin nuget:?package=Mono.ApiTools.NuGetDiff&version=1.3.0&loaddependencies=true
#addin nuget:?package=Xamarin.Nuget.Validator&version=1.1.1
#tool nuget:?package=mdoc&version=5.7.4.10
@ -70,6 +70,7 @@ var TRACKED_NUGETS = new Dictionary<string, Version> {
#load "cake/UtilsManaged.cake"
#load "cake/externals.cake"
#load "cake/UpdateDocs.cake"
#load "cake/samples.cake"
Task ("determine-last-successful-build")
.WithCriteria (string.IsNullOrEmpty (AZURE_BUILD_ID))
@ -136,10 +137,17 @@ Task ("tests")
.IsDependentOn ("externals")
.Does (() =>
{
var RunDesktopTest = new Action<string> (arch => {
var failedTests = 0;
void RunDesktopTest (string arch)
{
RunMSBuild ("./tests/SkiaSharp.Desktop.Tests/SkiaSharp.Desktop.Tests.sln", platform: arch == "AnyCPU" ? "Any CPU" : arch);
RunTests ($"./tests/SkiaSharp.Desktop.Tests/bin/{arch}/{CONFIGURATION}/SkiaSharp.Tests.dll", arch == "x86");
});
try {
RunTests ($"./tests/SkiaSharp.Desktop.Tests/bin/{arch}/{CONFIGURATION}/SkiaSharp.Tests.dll", arch == "x86");
} catch {
failedTests++;
}
}
CleanDirectories ($"{PACKAGE_CACHE_PATH}/skiasharp*");
CleanDirectories ($"{PACKAGE_CACHE_PATH}/harfbuzzsharp*");
@ -156,7 +164,14 @@ Task ("tests")
// .NET Core
RunMSBuild ("./tests/SkiaSharp.NetCore.Tests/SkiaSharp.NetCore.Tests.sln");
RunNetCoreTests ("./tests/SkiaSharp.NetCore.Tests/SkiaSharp.NetCore.Tests.csproj");
try {
RunNetCoreTests ("./tests/SkiaSharp.NetCore.Tests/SkiaSharp.NetCore.Tests.csproj");
} catch {
failedTests++;
}
if (failedTests > 0)
throw new Exception ($"There were {failedTests} failed tests.");
});
////////////////////////////////////////////////////////////////////////////////////////////////////
@ -235,12 +250,30 @@ Task ("samples")
RunMSBuild (sln, platform: buildPlatform);
}
};
}
// build the newly migrated samples
CleanDirectories ($"{PACKAGE_CACHE_PATH}/skiasharp*");
CleanDirectories ($"{PACKAGE_CACHE_PATH}/harfbuzzsharp*");
// TODO: Docker seems to be having issues on the old DevOps agents
// // copy all the packages next to the Dockerfile files
// var dockerfiles = GetFiles ("./output/samples/**/Dockerfile");
// foreach (var dockerfile in dockerfiles) {
// CopyDirectory (OUTPUT_NUGETS_PATH, dockerfile.GetDirectory ().Combine ("packages"));
// }
// // build the run.ps1 (typically for Dockerfiles)
// var runs = GetFiles ("./output/samples/**/run.ps1");
// foreach (var run in runs) {
// RunProcess ("pwsh", new ProcessSettings {
// Arguments = run.FullPath,
// WorkingDirectory = run.GetDirectory (),
// });
// }
// build solutions locally
var solutions = GetFiles ("./output/samples/**/*.sln");
foreach (var sln in solutions) {
var name = sln.GetFilenameWithoutExtension ();
@ -296,7 +329,8 @@ Task ("nuget")
}
}
var removePlatforms = new Action<XDocument> ((xdoc) => {
void RemovePlatforms (XDocument xdoc)
{
var files = xdoc.Root
.Elements ("files")
.Elements ("file");
@ -317,9 +351,10 @@ Task ("nuget")
file.Add (new XAttribute ("target", file.Attribute ("src").Value));
}
}
});
}
var setVersion = new Action<XDocument, string> ((xdoc, suffix) => {
void SetVersion (XDocument xdoc, string suffix)
{
var metadata = xdoc.Root.Element ("metadata");
var id = metadata.Element ("id");
var version = metadata.Element ("version");
@ -351,7 +386,7 @@ Task ("nuget")
}
}
}
});
}
DeleteFiles ("./output/*/nuget/*.nuspec");
foreach (var nuspec in GetFiles ("./nuget/*.nuspec")) {
@ -373,15 +408,15 @@ Task ("nuget")
preview += $".{BUILD_NUMBER}";
}
removePlatforms (xdoc);
RemovePlatforms (xdoc);
var outDir = $"./output/{dir}/nuget";
EnsureDirectoryExists (outDir);
setVersion (xdoc, "");
SetVersion (xdoc, "");
xdoc.Save ($"{outDir}/{id}.nuspec");
setVersion (xdoc, $"{preview}");
SetVersion (xdoc, $"{preview}");
xdoc.Save ($"{outDir}/{id}.prerelease.nuspec");
// the placeholders
@ -392,9 +427,9 @@ Task ("nuget")
CopyFile ("./External-Dependency-Info.txt", $"{outDir}/THIRD-PARTY-NOTICES.txt");
}
DeleteFiles ("output/nugets/*.nupkg");
DeleteFiles ($"{OUTPUT_NUGETS_PATH}/*.nupkg");
foreach (var nuspec in GetFiles ("./output/*/nuget/*.nuspec")) {
PackageNuGet (nuspec, "./output/nugets/");
PackageNuGet (nuspec, OUTPUT_NUGETS_PATH);
}
// setup validation options
@ -408,12 +443,12 @@ Task ("nuget")
ValidPackageNamespace = new [] { "SkiaSharp", "HarfBuzzSharp" },
};
var nupkgFiles = GetFiles ("./output/**/*.nupkg");
var nupkgFiles = GetFiles ($"{OUTPUT_NUGETS_PATH}/*.nupkg");
Information ("Found ({0}) Nuget's to validate", nupkgFiles.Count ());
foreach (var nupkgFile in nupkgFiles) {
Information ("Verifiying Metadata of {0}", nupkgFile.GetFilename ());
Verbose ("Verifiying Metadata of {0}", nupkgFile.GetFilename ());
var result = Xamarin.Nuget.Validator.NugetValidator.Validate(MakeAbsolute(nupkgFile).FullPath, options);
if (!result.Success) {

Просмотреть файл

@ -51,13 +51,13 @@ Task ("docs-expand-build-artifact")
.Does (() =>
{
Unzip ("./output/nuget.zip", "./output");
MoveDirectory ("./output/nuget", "./output/nugets");
MoveDirectory ("./output/nuget", OUTPUT_NUGETS_PATH);
foreach (var id in TRACKED_NUGETS.Keys) {
var version = GetVersion (id);
var name = $"{id}.{version}.nupkg";
CleanDirectories ($"./output/{id}");
Unzip ($"./output/nugets/{name}", $"./output/{id}/nuget");
Unzip ($"{OUTPUT_NUGETS_PATH}/{name}", $"./output/{id}/nuget");
}
});
@ -68,7 +68,7 @@ Task ("docs-download-output")
Task ("docs-api-diff")
.Does (async () =>
{
var baseDir = "./output/nugets/api-diff";
var baseDir = "{OUTPUT_NUGETS_PATH}/api-diff";
CleanDirectories (baseDir);
var comparer = await CreateNuGetDiffAsync ();
@ -91,7 +91,7 @@ Task ("docs-api-diff")
// generate the diff and copy to the changelogs
Debug ($"Running a diff on '{latestVersion}' vs '{version}' of '{id}'...");
var diffRoot = $"{baseDir}/{id}";
using (var reader = new PackageArchiveReader ($"./output/nugets/{id.ToLower ()}.{version}.nupkg")) {
using (var reader = new PackageArchiveReader ($"{OUTPUT_NUGETS_PATH}/{id.ToLower ()}.{version}.nupkg")) {
// run the diff with just the breaking changes
comparer.MarkdownDiffFileExtension = ".breaking.md";
comparer.IgnoreNonBreakingChanges = true;

Просмотреть файл

@ -1,20 +1,16 @@
var MSBuildNS = (XNamespace) "http://schemas.microsoft.com/developer/msbuild/2003";
var PackageNuGet = new Action<FilePath, DirectoryPath> ((nuspecPath, outputPath) =>
void PackageNuGet(FilePath nuspecPath, DirectoryPath outputPath)
{
EnsureDirectoryExists (outputPath);
NuGetPack (nuspecPath, new NuGetPackSettings {
OutputDirectory = outputPath,
BasePath = nuspecPath.GetDirectory (),
EnsureDirectoryExists(outputPath);
NuGetPack(nuspecPath, new NuGetPackSettings {
OutputDirectory = MakeAbsolute(outputPath),
BasePath = nuspecPath.GetDirectory(),
ToolPath = NuGetToolPath,
});
});
}
void RunTests (FilePath testAssembly, bool is32)
void RunTests(FilePath testAssembly, bool is32)
{
var dir = testAssembly.GetDirectory ();
var dir = testAssembly.GetDirectory();
var settings = new XUnit2Settings {
ReportName = "TestResults",
XmlReport = true,
@ -23,18 +19,18 @@ void RunTests (FilePath testAssembly, bool is32)
Parallelism = ParallelismOption.All,
OutputDirectory = dir,
WorkingDirectory = dir,
ArgumentCustomization = args => args.Append ("-verbose"),
ArgumentCustomization = args => args.Append("-verbose"),
};
var traits = CreateTraitsDictionary(UNSUPPORTED_TESTS);
foreach (var trait in traits) {
settings.ExcludeTrait(trait.Name, trait.Value);
}
XUnit2 (new [] { testAssembly }, settings);
XUnit2(new [] { testAssembly }, settings);
}
void RunNetCoreTests (FilePath testAssembly)
void RunNetCoreTests(FilePath testAssembly)
{
var dir = testAssembly.GetDirectory ();
var dir = testAssembly.GetDirectory();
var buildSettings = new DotNetCoreBuildSettings {
Configuration = CONFIGURATION,
WorkingDirectory = dir,
@ -56,7 +52,7 @@ void RunNetCoreTests (FilePath testAssembly)
DotNetCoreTest(testAssembly.GetFilename().ToString(), settings);
}
IEnumerable<(string Name, string Value)> CreateTraitsDictionary (string args)
IEnumerable<(string Name, string Value)> CreateTraitsDictionary(string args)
{
if (!string.IsNullOrEmpty(args)) {
var traits = args.Split(';');
@ -69,183 +65,29 @@ IEnumerable<(string Name, string Value)> CreateTraitsDictionary (string args)
}
}
var DecompressArchive = new Action<FilePath, DirectoryPath> ((archive, outputDir) => {
using (var stream = System.IO.File.OpenRead (archive.FullPath))
using (var reader = ReaderFactory.Open (stream)) {
while (reader.MoveToNextEntry ()) {
void DecompressArchive(FilePath archive, DirectoryPath outputDir)
{
using (var stream = System.IO.File.OpenRead(archive.FullPath))
using (var reader = ReaderFactory.Open(stream)) {
while(reader.MoveToNextEntry()) {
if (!reader.Entry.IsDirectory) {
reader.WriteEntryToDirectory (outputDir.FullPath, new ExtractionOptions {
reader.WriteEntryToDirectory(outputDir.FullPath, new ExtractionOptions {
ExtractFullPath = true,
Overwrite = true
});
}
}
}
});
void CreateSamplesDirectory (DirectoryPath samplesDirPath, DirectoryPath outputDirPath, string versionSuffix = "")
{
samplesDirPath = MakeAbsolute (samplesDirPath);
outputDirPath = MakeAbsolute (outputDirPath);
var solutionProjectRegex = new Regex(@",\s*""(.*?\.\w{2}proj)"", ""(\{.*?\})""");
EnsureDirectoryExists (outputDirPath);
CleanDirectory (outputDirPath);
var ignoreBinObj = new GlobberSettings {
Predicate = fileSystemInfo => {
var segments = fileSystemInfo.Path.Segments;
var keep = segments.All (s =>
!s.Equals ("bin", StringComparison.OrdinalIgnoreCase) &&
!s.Equals ("obj", StringComparison.OrdinalIgnoreCase) &&
!s.Equals ("AppPackages", StringComparison.OrdinalIgnoreCase) &&
!s.Equals (".vs", StringComparison.OrdinalIgnoreCase));
return keep;
}
};
var files = GetFiles ($"{samplesDirPath}/**/*", ignoreBinObj);
foreach (var file in files) {
var rel = samplesDirPath.GetRelativePath (file);
var dest = outputDirPath.CombineWithFilePath (rel);
var ext = file.GetExtension () ?? "";
if (ext.Equals (".sln", StringComparison.OrdinalIgnoreCase)) {
var lines = FileReadLines (file.FullPath).ToList ();
var guids = new List<string> ();
// remove projects that aren't samples
for (var i = 0; i < lines.Count; i++) {
var line = lines [i];
var m = solutionProjectRegex.Match (line);
if (!m.Success)
continue;
// get the path of the project relative to the samples directory
var relProjectPath = (FilePath) m.Groups [1].Value;
var absProjectPath = GetFullPath (file, relProjectPath);
var relSamplesPath = samplesDirPath.GetRelativePath (absProjectPath);
if (!relSamplesPath.FullPath.StartsWith (".."))
continue;
Debug ($"Removing the project '{relProjectPath}' for solution '{rel}'.");
// skip the next line as it is the "EndProject" line
guids.Add (m.Groups [2].Value.ToLower ());
lines.RemoveAt (i--);
lines.RemoveAt (i--);
}
// remove all the other references to this guid
if (guids.Count > 0) {
for (var i = 0; i < lines.Count; i++) {
var line = lines [i];
foreach (var guid in guids) {
if (line.ToLower ().Contains (guid)) {
lines.RemoveAt (i--);
}
}
}
}
// save the solution
EnsureDirectoryExists (dest.GetDirectory ());
FileWriteLines (dest, lines.ToArray ());
} else if (ext.Equals (".csproj", StringComparison.OrdinalIgnoreCase)) {
var xdoc = XDocument.Load (file.FullPath);
// process all the files and project references
var projItems = xdoc.Root
.Elements ().Where (e => e.Name.LocalName == "ItemGroup")
.Elements ().Where (e => !string.IsNullOrWhiteSpace (e.Attribute ("Include")?.Value))
.ToArray ();
foreach (var projItem in projItems) {
// get files in the include
var relFilePath = (FilePath) projItem.Attribute ("Include").Value;
var absFilePath = GetFullPath (file, relFilePath);
// ignore files in the samples directory
var relSamplesPath = samplesDirPath.GetRelativePath (absFilePath);
if (!relSamplesPath.FullPath.StartsWith (".."))
continue;
// substitute <ProjectReference> with <PackageReference>
if (projItem.Name.LocalName == "ProjectReference" && FileExists (absFilePath)) {
var xReference = XDocument.Load (absFilePath.FullPath);
var packagingGroup = xReference.Root
.Elements ().Where (e => e.Name.LocalName == "PropertyGroup")
.Elements ().Where (e => e.Name.LocalName == "PackagingGroup")
.FirstOrDefault ()?.Value;
var version = GetVersion (packagingGroup);
if (!string.IsNullOrWhiteSpace (version)) {
Debug ($"Substituting project reference {relFilePath} for project {rel}.");
var name = projItem.Name.Namespace + "PackageReference";
var suffix = string.IsNullOrEmpty (versionSuffix) ? "" : $"-{versionSuffix}";
projItem.AddAfterSelf (new XElement (name, new object[] {
new XAttribute("Include", packagingGroup),
new XAttribute("Version", version + suffix),
}));
} else {
Warning ($"Unable to find version information for package '{packagingGroup}'.");
}
} else {
Debug ($"Removing the file '{relFilePath}' for project '{rel}'.");
}
// remove the element as it will be outside the sample directory
projItem.Remove ();
}
// process all the imports
var imports = xdoc.Root
.Elements ().Where (e =>
e.Name.LocalName == "Import" &&
!string.IsNullOrWhiteSpace (e.Attribute ("Project")?.Value))
.ToArray ();
foreach (var import in imports) {
var project = import.Attribute ("Project").Value;
// skip files inside the samples directory or do not exist
var absProject = GetFullPath (file, project);
var relSamplesPath = samplesDirPath.GetRelativePath (absProject);
if (!relSamplesPath.FullPath.StartsWith (".."))
continue;
Debug ($"Removing import '{project}' for project '{rel}'.");
// not inside the samples directory, so needs to be removed
import.Remove ();
}
// save the project
EnsureDirectoryExists (dest.GetDirectory ());
xdoc.Save (dest.FullPath);
} else {
EnsureDirectoryExists (dest.GetDirectory ());
CopyFile (file, dest);
}
}
DeleteFiles ($"{outputDirPath}/README.md");
MoveFile ($"{outputDirPath}/README.zip.md", $"{outputDirPath}/README.md");
}
FilePath GetFullPath (FilePath root, FilePath path)
IEnumerable<(DirectoryPath path, string platform)> GetPlatformDirectories(DirectoryPath rootDir)
{
path = path.FullPath.Replace ("*", "_");
path = root.GetDirectory ().CombineWithFilePath (path);
return (FilePath) System.IO.Path.GetFullPath (path.FullPath);
}
IEnumerable<(DirectoryPath path, string platform)> GetPlatformDirectories (DirectoryPath rootDir)
{
var platformDirs = GetDirectories ($"{rootDir}/*");
var platformDirs = GetDirectories($"{rootDir}/*");
// try find any cross-platform frameworks
foreach (var dir in platformDirs) {
var d = dir.GetDirectoryName ().ToLower ();
if (d.StartsWith ("netstandard") || d.StartsWith ("portable")) {
var d = dir.GetDirectoryName().ToLower();
if (d.StartsWith("netstandard") || d.StartsWith("portable")) {
// we just want this single platform
yield return (dir, null);
yield break;
@ -254,83 +96,83 @@ IEnumerable<(DirectoryPath path, string platform)> GetPlatformDirectories (Direc
// there were no cross-platform libraries, so process each platform
foreach (var dir in platformDirs) {
var d = dir.GetDirectoryName ().ToLower ();
if (d.StartsWith ("monoandroid"))
var d = dir.GetDirectoryName().ToLower();
if (d.StartsWith("monoandroid"))
yield return (dir, "android");
else if (d.StartsWith ("net4"))
else if (d.StartsWith("net4"))
yield return (dir, "net");
else if (d.StartsWith ("uap"))
else if (d.StartsWith("uap"))
yield return (dir, "uwp");
else if (d.StartsWith ("xamarinios") || d.StartsWith ("xamarin.ios"))
else if (d.StartsWith("xamarinios") || d.StartsWith("xamarin.ios"))
yield return (dir, "ios");
else if (d.StartsWith ("xamarinmac") || d.StartsWith ("xamarin.mac"))
else if (d.StartsWith("xamarinmac") || d.StartsWith("xamarin.mac"))
yield return (dir, "macos");
else if (d.StartsWith ("xamarintvos") || d.StartsWith ("xamarin.tvos"))
else if (d.StartsWith("xamarintvos") || d.StartsWith("xamarin.tvos"))
yield return (dir, "tvos");
else if (d.StartsWith ("xamarinwatchos") || d.StartsWith ("xamarin.watchos"))
else if (d.StartsWith("xamarinwatchos") || d.StartsWith("xamarin.watchos"))
yield return (dir, "watchos");
else if (d.StartsWith ("tizen"))
else if (d.StartsWith("tizen"))
yield return (dir, "tizen");
else
throw new Exception ($"Unknown platform '{d}' found at '{dir}'.");
throw new Exception($"Unknown platform '{d}' found at '{dir}'.");
}
}
string[] GetReferenceSearchPaths ()
string[] GetReferenceSearchPaths()
{
var refs = new List<string> ();
var refs = new List<string>();
if (IsRunningOnWindows ()) {
var vs = VSWhereLatest (new VSWhereLatestSettings { Requires = "Component.Xamarin" });
if (IsRunningOnWindows()) {
var vs = VSWhereLatest(new VSWhereLatestSettings { Requires = "Component.Xamarin" });
var referenceAssemblies = $"{vs}/Common7/IDE/ReferenceAssemblies/Microsoft/Framework";
var pf = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
refs.AddRange (GetDirectories ("./output/docs/temp/*").Select (d => d.FullPath));
refs.Add ($"{referenceAssemblies}/MonoTouch/v1.0");
refs.Add ($"{referenceAssemblies}/MonoAndroid/v1.0");
refs.Add ($"{referenceAssemblies}/MonoAndroid/v9.0");
refs.Add ($"{referenceAssemblies}/Xamarin.iOS/v1.0");
refs.Add ($"{referenceAssemblies}/Xamarin.TVOS/v1.0");
refs.Add ($"{referenceAssemblies}/Xamarin.WatchOS/v1.0");
refs.Add ($"{referenceAssemblies}/Xamarin.Mac/v2.0");
refs.Add ($"{pf}/Windows Kits/10/UnionMetadata/Facade");
refs.Add ($"{pf}/Windows Kits/10/References/Windows.Foundation.UniversalApiContract/1.0.0.0");
refs.Add ($"{pf}/Windows Kits/10/References/Windows.Foundation.FoundationContract/1.0.0.0");
refs.Add ($"{pf}/GtkSharp/2.12/lib");
refs.Add ($"{vs}/Common7/IDE/PublicAssemblies");
refs.AddRange(GetDirectories("./output/docs/temp/*").Select(d => d.FullPath));
refs.Add($"{referenceAssemblies}/MonoTouch/v1.0");
refs.Add($"{referenceAssemblies}/MonoAndroid/v1.0");
refs.Add($"{referenceAssemblies}/MonoAndroid/v9.0");
refs.Add($"{referenceAssemblies}/Xamarin.iOS/v1.0");
refs.Add($"{referenceAssemblies}/Xamarin.TVOS/v1.0");
refs.Add($"{referenceAssemblies}/Xamarin.WatchOS/v1.0");
refs.Add($"{referenceAssemblies}/Xamarin.Mac/v2.0");
refs.Add($"{pf}/Windows Kits/10/UnionMetadata/Facade");
refs.Add($"{pf}/Windows Kits/10/References/Windows.Foundation.UniversalApiContract/1.0.0.0");
refs.Add($"{pf}/Windows Kits/10/References/Windows.Foundation.FoundationContract/1.0.0.0");
refs.Add($"{pf}/GtkSharp/2.12/lib");
refs.Add($"{vs}/Common7/IDE/PublicAssemblies");
} else {
// TODO
}
return refs.ToArray ();
return refs.ToArray();
}
async Task<NuGetDiff> CreateNuGetDiffAsync ()
async Task<NuGetDiff> CreateNuGetDiffAsync()
{
var comparer = new NuGetDiff ();
comparer.SearchPaths.AddRange (GetReferenceSearchPaths ());
var comparer = new NuGetDiff();
comparer.SearchPaths.AddRange(GetReferenceSearchPaths());
comparer.PackageCache = PACKAGE_CACHE_PATH.FullPath;
await AddDep ("OpenTK.GLControl", "NET40", "reference");
await AddDep ("OpenTK.GLControl", "NET40");
await AddDep ("Tizen.NET", "netstandard2.0");
await AddDep ("Xamarin.Forms", "netstandard2.0");
await AddDep ("Xamarin.Forms", "MonoAndroid90");
await AddDep ("Xamarin.Forms", "Xamarin.iOS10");
await AddDep ("Xamarin.Forms", "Xamarin.Mac");
await AddDep ("Xamarin.Forms", "tizen40");
await AddDep ("Xamarin.Forms", "uap10.0");
await AddDep ("Xamarin.Forms.Platform.WPF", "net45");
await AddDep ("GtkSharp", "netstandard2.0");
await AddDep ("GLibSharp", "netstandard2.0");
await AddDep ("AtkSharp", "netstandard2.0");
await AddDep ("System.Memory", "netstandard2.0");
await AddDep("OpenTK.GLControl", "NET40", "reference");
await AddDep("OpenTK.GLControl", "NET40");
await AddDep("Tizen.NET", "netstandard2.0");
await AddDep("Xamarin.Forms", "netstandard2.0");
await AddDep("Xamarin.Forms", "MonoAndroid90");
await AddDep("Xamarin.Forms", "Xamarin.iOS10");
await AddDep("Xamarin.Forms", "Xamarin.Mac");
await AddDep("Xamarin.Forms", "tizen40");
await AddDep("Xamarin.Forms", "uap10.0");
await AddDep("Xamarin.Forms.Platform.WPF", "net45");
await AddDep("GtkSharp", "netstandard2.0");
await AddDep("GLibSharp", "netstandard2.0");
await AddDep("AtkSharp", "netstandard2.0");
await AddDep("System.Memory", "netstandard2.0");
return comparer;
async Task AddDep(string id, string platform, string type = "release")
{
var version = GetVersion (id, type);
var version = GetVersion(id, type);
var root = await comparer.ExtractCachedPackageAsync(id, version);
comparer.SearchPaths.Add(System.IO.Path.Combine(root, "lib", platform));
}

Просмотреть файл

@ -1,4 +1,5 @@
DirectoryPath PACKAGE_CACHE_PATH = MakeAbsolute(ROOT_PATH.Combine("externals/package_cache"));
DirectoryPath OUTPUT_NUGETS_PATH = MakeAbsolute(ROOT_PATH.Combine("output/nugets"));
void RunMSBuild(
FilePath solution,
@ -7,14 +8,14 @@ void RunMSBuild(
bool restore = true,
bool restoreOnly = false)
{
var nugets = MakeAbsolute(ROOT_PATH.Combine("output/nugets"));
var nugetSources = new [] { nugets.FullPath, "https://api.nuget.org/v3/index.json" };
var nugetSources = new [] { OUTPUT_NUGETS_PATH.FullPath, "https://api.nuget.org/v3/index.json" };
EnsureDirectoryExists(nugets);
EnsureDirectoryExists(OUTPUT_NUGETS_PATH);
MSBuild(solution, c => {
c.Configuration = CONFIGURATION;
c.Verbosity = VERBOSITY;
c.MaxCpuCount = 0;
c.ToolVersion = MSBuildToolVersion.VS2017;
c.NoLogo = VERBOSITY == Verbosity.Minimal;
@ -41,7 +42,7 @@ void RunMSBuild(
c.Properties ["RestoreNoCache"] = new [] { "true" };
c.Properties ["RestorePackagesPath"] = new [] { PACKAGE_CACHE_PATH.FullPath };
// c.Properties ["RestoreSources"] = nugetSources;
var sep = IsRunningOnWindows () ? ";" : "%3B";
c.ArgumentCustomization = args => args.Append ($"/p:RestoreSources=\"{string.Join (sep, nugetSources)}\"");
var sep = IsRunningOnWindows() ? ";" : "%3B";
c.ArgumentCustomization = args => args.Append($"/p:RestoreSources=\"{string.Join(sep, nugetSources)}\"");
});
}

169
cake/samples.cake Normal file
Просмотреть файл

@ -0,0 +1,169 @@
void CreateSamplesDirectory(DirectoryPath samplesDirPath, DirectoryPath outputDirPath, string versionSuffix = "")
{
samplesDirPath = MakeAbsolute(samplesDirPath);
outputDirPath = MakeAbsolute(outputDirPath);
var solutionProjectRegex = new Regex(@",\s*""(.*?\.\w{2}proj)"", ""(\{.*?\})""");
EnsureDirectoryExists(outputDirPath);
CleanDirectory(outputDirPath);
var ignoreBinObj = new GlobberSettings {
Predicate = fileSystemInfo => {
var segments = fileSystemInfo.Path.Segments;
var keep = segments.All(s =>
!s.Equals("bin", StringComparison.OrdinalIgnoreCase) &&
!s.Equals("obj", StringComparison.OrdinalIgnoreCase) &&
!s.Equals("AppPackages", StringComparison.OrdinalIgnoreCase) &&
!s.Equals(".vs", StringComparison.OrdinalIgnoreCase));
return keep;
}
};
var files = GetFiles($"{samplesDirPath}/**/*", ignoreBinObj);
foreach (var file in files) {
var rel = samplesDirPath.GetRelativePath(file);
var dest = outputDirPath.CombineWithFilePath(rel);
var ext = file.GetExtension() ?? "";
if (ext.Equals(".sln", StringComparison.OrdinalIgnoreCase)) {
var lines = FileReadLines(file.FullPath).ToList();
var guids = new List<string>();
// remove projects that aren't samples
for(var i = 0; i < lines.Count; i++) {
var line = lines [i];
var m = solutionProjectRegex.Match(line);
if (!m.Success)
continue;
// get the path of the project relative to the samples directory
var relProjectPath = (FilePath) m.Groups [1].Value;
var absProjectPath = GetFullPath(file, relProjectPath);
var relSamplesPath = samplesDirPath.GetRelativePath(absProjectPath);
if (!relSamplesPath.FullPath.StartsWith(".."))
continue;
Debug($"Removing the project '{relProjectPath}' for solution '{rel}'.");
// skip the next line as it is the "EndProject" line
guids.Add(m.Groups [2].Value.ToLower());
lines.RemoveAt(i--);
lines.RemoveAt(i--);
}
// remove all the other references to this guid
if (guids.Count > 0) {
for(var i = 0; i < lines.Count; i++) {
var line = lines [i];
foreach (var guid in guids) {
if (line.ToLower().Contains(guid)) {
lines.RemoveAt(i--);
}
}
}
}
// save the solution
EnsureDirectoryExists(dest.GetDirectory());
FileWriteLines(dest, lines.ToArray());
} else if (ext.Equals(".csproj", StringComparison.OrdinalIgnoreCase)) {
var xdoc = XDocument.Load(file.FullPath);
// process all the files and project references
var projItems = xdoc.Root
.Elements().Where(e => e.Name.LocalName == "ItemGroup")
.Elements().Where(e => !string.IsNullOrWhiteSpace(e.Attribute("Include")?.Value))
.ToArray();
foreach (var projItem in projItems) {
var suffix = string.IsNullOrEmpty(versionSuffix) ? "" : $"-{versionSuffix}";
// update the <PackageReference> versions
if (projItem.Name.LocalName == "PackageReference") {
var packageId = projItem.Attribute("Include").Value;
var version = GetVersion(packageId);
if (!string.IsNullOrWhiteSpace(version)) {
version += suffix;
Debug($"Substituting package version {packageId} for {version}.");
projItem.Attribute("Version").Value = version;
} else if (packageId.StartsWith("SkiaSharp") || packageId.StartsWith("HarfBuzzSharp")) {
Warning($"Unable to find version information for package '{packageId}'.");
}
continue;
}
// get files in the include
var relFilePath = (FilePath) projItem.Attribute("Include").Value;
var absFilePath = GetFullPath(file, relFilePath);
// ignore files in the samples directory
var relSamplesPath = samplesDirPath.GetRelativePath(absFilePath);
if (!relSamplesPath.FullPath.StartsWith(".."))
continue;
// substitute <ProjectReference> with <PackageReference>
if (projItem.Name.LocalName == "ProjectReference" && FileExists(absFilePath)) {
var xReference = XDocument.Load(absFilePath.FullPath);
var packagingGroup = xReference.Root
.Elements().Where(e => e.Name.LocalName == "PropertyGroup")
.Elements().Where(e => e.Name.LocalName == "PackagingGroup")
.FirstOrDefault()?.Value;
var version = GetVersion(packagingGroup);
if (!string.IsNullOrWhiteSpace(version)) {
Debug($"Substituting project reference {relFilePath} for project {rel}.");
var name = projItem.Name.Namespace + "PackageReference";
projItem.AddAfterSelf(new XElement(name, new object[] {
new XAttribute("Include", packagingGroup),
new XAttribute("Version", version + suffix),
}));
} else {
Warning($"Unable to find version information for package '{packagingGroup}'.");
}
} else {
Debug($"Removing the file '{relFilePath}' for project '{rel}'.");
}
// remove files that are outside
projItem.Remove();
}
// process all the imports
var imports = xdoc.Root
.Elements().Where(e =>
e.Name.LocalName == "Import" &&
!string.IsNullOrWhiteSpace(e.Attribute("Project")?.Value))
.ToArray();
foreach (var import in imports) {
var project = import.Attribute("Project").Value;
// skip files inside the samples directory or do not exist
var absProject = GetFullPath(file, project);
var relSamplesPath = samplesDirPath.GetRelativePath(absProject);
if (!relSamplesPath.FullPath.StartsWith(".."))
continue;
Debug($"Removing import '{project}' for project '{rel}'.");
// not inside the samples directory, so needs to be removed
import.Remove();
}
// save the project
EnsureDirectoryExists(dest.GetDirectory());
xdoc.Save(dest.FullPath);
} else {
EnsureDirectoryExists(dest.GetDirectory());
CopyFile(file, dest);
}
}
DeleteFiles($"{outputDirPath}/README.md");
MoveFile($"{outputDirPath}/README.zip.md", $"{outputDirPath}/README.md");
}
FilePath GetFullPath(FilePath root, FilePath path)
{
path = path.FullPath.Replace("*", "_");
path = root.GetDirectory().CombineWithFilePath(path);
return (FilePath) System.IO.Path.GetFullPath(path.FullPath);
}

Просмотреть файл

@ -3,7 +3,7 @@ DirectoryPath OUTPUT_PATH = MakeAbsolute(ROOT_PATH.Combine("output/native/tizen"
#load "../../cake/native-shared.cake"
DirectoryPath TIZEN_STUDIO_HOME = EnvironmentVariable ("TIZEN_STUDIO_HOME") ?? PROFILE_PATH.Combine ("tizen-studio");
DirectoryPath TIZEN_STUDIO_HOME = EnvironmentVariable("TIZEN_STUDIO_HOME") ?? PROFILE_PATH.Combine("tizen-studio");
var bat = IsRunningOnWindows() ? ".bat" : "";
var tizen = TIZEN_STUDIO_HOME.CombineWithFilePath($"tools/ide/bin/tizen{bat}").FullPath;

Просмотреть файл

@ -0,0 +1,3 @@
.dockerignore
bin
obj

Просмотреть файл

@ -0,0 +1,25 @@
################################################################################
# Build Environment
################################################################################
FROM mcr.microsoft.com/dotnet/core/sdk:2.1 AS build-env
WORKDIR /app
# set up the contents
RUN mkdir packages
COPY . ./
# build and publish
RUN dotnet publish -c Release -o /app/out
################################################################################
# Run Environment
################################################################################
FROM mcr.microsoft.com/dotnet/core/runtime:2.1
WORKDIR /app
# copy from the build environment
COPY --from=build-env /app/out .
# run
ENTRYPOINT [ "dotnet", "SkiaSharpSample.dll" ]

Просмотреть файл

@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26124.0
MinimumVisualStudioVersion = 15.0.26124.0
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkiaSharpSample", "SkiaSharpSample\SkiaSharpSample.csproj", "{F44BD813-7374-4A11-AEBD-C286AF97497F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{F44BD813-7374-4A11-AEBD-C286AF97497F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F44BD813-7374-4A11-AEBD-C286AF97497F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F44BD813-7374-4A11-AEBD-C286AF97497F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F44BD813-7374-4A11-AEBD-C286AF97497F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

Просмотреть файл

@ -0,0 +1,14 @@
using System;
using SkiaSharp;
namespace SkiaSharpSample
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Platform Color Type: " + SKImageInfo.PlatformColorType);
}
}
}

Просмотреть файл

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SkiaSharp" Version="1.0.0" ExcludeAssets="native" />
<PackageReference Include="SkiaSharp.NativeAssets.NanoServer" Version="1.0.0" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="1.0.0" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="packages" value="packages" />
</packageSources>
</configuration>

Просмотреть файл

@ -0,0 +1,2 @@
docker build --tag skiasharp/console .
docker run --rm skiasharp/console

Просмотреть файл

@ -2,7 +2,6 @@
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<DockerDefaultTargetOS>Windows</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
@ -12,9 +11,9 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.1.2" PrivateAssets="All" />
<PackageReference Include="SkiaSharp" Version="1.68.0" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="1.68.0" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
<PackageReference Include="SkiaSharp" Version="1.0.0" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="1.0.0" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,33 @@
################################################################################
# Build Environment
################################################################################
FROM mcr.microsoft.com/dotnet/core/sdk:2.1 AS build-env
WORKDIR /app
# set up the contents
RUN mkdir packages
COPY . ./
# build and publish
RUN dotnet publish -c Release -o /app/out
################################################################################
# Run Environment
################################################################################
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS runtime
# install Fontconfig
RUN apt-get update \
&& apt-get install -y --no-install-recommends libfontconfig1 \
&& rm -rf /var/lib/apt/lists/*
# prepare the container for launch
WORKDIR /app
EXPOSE 80
# copy from the build environment
COPY --from=build-env /app/out .
# run
ENTRYPOINT [ "dotnet", "SkiaSharpSample.dll" ]

Просмотреть файл

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="packages" value="packages" />
</packageSources>
</configuration>

Просмотреть файл

@ -1,21 +1,36 @@
param(
[ValidateSet("linux", "windows")]
[string]$Platform = "linux"
[ValidateSet("linux", "windows", "")]
[string]$Platform = ""
)
$name = "skiasharp-basic-$Platform-sample"
if ($Platform -eq "") {
$Platform = if ($IsLinux -or $IsMacOS) { "linux" } else { "windows" }
}
$name = "skiasharp_webapi"
# we can only do --platform with Experimental=true
$buildPlatform = ""
$isExperimental = docker info -f "{{ json .ExperimentalBuild }}"
if ($isExperimental -eq "true") {
$buildPlatform = "--platform=$Platform"
}
# build the container
docker stop $name
docker rm $name
New-Item "packages" -ItemType Directory -Force | Out-Null
docker build --platform $Platform --tag $name --file "$Platform.Dockerfile" .
docker build $buildPlatform --tag $name --file "$Platform.Dockerfile" .
# run
docker stop $name
docker rm $name
docker run --detach --name $name --publish "80:80" $name
# get the IP so we can talk
$ip = docker inspect -f "{{ .NetworkSettings.Networks.nat.IPAddress }}" $name
if (-not $ip) {
$ip = docker inspect -f "{{ .NetworkSettings.IPAddress }}" $name
}
# get a response (retry on 404 as the server may take a second to start up)
try {
@ -26,6 +41,7 @@ try {
# we were successful on 200 and > 0 content
if ($imageResponse.StatusCode -eq 200) {
if ($imageResponse.Content.Length -gt 0) {
Write-Host "Success!"
break
} else {
throw "Empty response."

Просмотреть файл

@ -0,0 +1,38 @@
################################################################################
# Build Environment
################################################################################
FROM mcr.microsoft.com/dotnet/core/sdk:2.1 AS build-env
WORKDIR /app
# set up the contents
RUN mkdir packages
COPY . ./
# build and publish
RUN dotnet publish -c Release -o /app/out
################################################################################
# Run Environment
################################################################################
FROM microsoft/windowsservercore:1803 AS runtime
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
# install and setup ASP.NET Core Runtime
ENV ASPNETCORE_VERSION 2.1.5
RUN Invoke-WebRequest -OutFile aspnetcore.zip https://dotnetcli.blob.core.windows.net/dotnet/aspnetcore/Runtime/$env:ASPNETCORE_VERSION/aspnetcore-runtime-$env:ASPNETCORE_VERSION-win-x64.zip; \
Expand-Archive aspnetcore.zip -DestinationPath $env:ProgramFiles\dotnet; \
Remove-Item -Force aspnetcore.zip
RUN setx /M PATH $($env:PATH + ';' + $env:ProgramFiles + '\dotnet')
ENV ASPNETCORE_URLS=http://+:80 \
DOTNET_RUNNING_IN_CONTAINERS=true
# prepare the container for launch
WORKDIR /app
EXPOSE 80
# copy from the build environment
COPY --from=build-env /app/out .
# run
ENTRYPOINT [ "dotnet", "SkiaSharpSample.dll" ]

Просмотреть файл

@ -1,51 +0,0 @@
################################################################################
# runtime
#
# Downloads the files that are needed for running an ASP.NET Core app.
################################################################################
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS runtime
RUN apt-get update \
&& apt-get install -y --no-install-recommends libfontconfig1 \
&& rm -rf /var/lib/apt/lists/*
################################################################################
# build
#
# Builds the app.
################################################################################
FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src
COPY SkiaSharpSample/SkiaSharpSample.csproj SkiaSharpSample/
COPY packages/ packages/
RUN dotnet restore SkiaSharpSample/SkiaSharpSample.csproj -s "/src/packages" -s "https://api.nuget.org/v3/index.json"
COPY . .
WORKDIR /src/SkiaSharpSample
RUN dotnet build SkiaSharpSample.csproj -c Release -o /app
################################################################################
# publish
#
# Publishes the app.
################################################################################
FROM build AS publish
RUN dotnet publish SkiaSharpSample.csproj -c Release -o /app
################################################################################
# final
#
# Copies and runs the app.
################################################################################
FROM runtime AS final
WORKDIR /app
EXPOSE 80
COPY --from=publish /app .
ENTRYPOINT dotnet SkiaSharpSample.dll

Просмотреть файл

@ -1 +0,0 @@
. .\sample-base.ps1 -Platform "linux"

Просмотреть файл

@ -1,111 +0,0 @@
# escape=`
################################################################################
# sdk
#
# Downloads the files that are needed for building an ASP.NET Core app.
################################################################################
FROM microsoft/windowsservercore:1803 AS sdk
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
# install .NET Core SDK
ENV DOTNET_SDK_VERSION 2.1.403
RUN Invoke-WebRequest -OutFile dotnet.zip https://dotnetcli.blob.core.windows.net/dotnet/Sdk/$Env:DOTNET_SDK_VERSION/dotnet-sdk-$Env:DOTNET_SDK_VERSION-win-x64.zip; `
$dotnet_sha512 = '52bb1117f170587eaceec1f78cdc41a41d4272154b5535bf61c86bfb75287323cac248434b05eabe4bc7716facabdb0f6475015cbb63f38d91af662618a06720'; `
if ((Get-FileHash dotnet.zip -Algorithm sha512).Hash -ne $dotnet_sha512) { `
Write-Host 'CHECKSUM VERIFICATION FAILED!'; `
exit 1; `
}; `
`
Expand-Archive dotnet.zip -DestinationPath $Env:ProgramFiles\dotnet; `
Remove-Item -Force dotnet.zip
# set PATH
RUN setx /M PATH $($Env:PATH + ';' + $Env:ProgramFiles + '\dotnet')
# Configure Kestrel web server to bind to port 80 when present
ENV ASPNETCORE_URLS=http://+:80 `
# Enable detection of running in a container
DOTNET_RUNNING_IN_CONTAINER=true `
# Enable correct mode for dotnet watch (only mode supported in a container)
DOTNET_USE_POLLING_FILE_WATCHER=true `
# Skip extraction of XML docs - generally not useful within an image/container - helps performance
NUGET_XMLDOC_MODE=skip
# Trigger first run experience by running arbitrary cmd to populate local package cache
RUN dotnet help
################################################################################
# runtime
#
# Downloads the files that are needed for running an ASP.NET Core app.
################################################################################
FROM microsoft/windowsservercore:1803 AS runtime
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
# install ASP.NET Core Runtime
ENV ASPNETCORE_VERSION 2.1.5
RUN Invoke-WebRequest -OutFile aspnetcore.zip https://dotnetcli.blob.core.windows.net/dotnet/aspnetcore/Runtime/$Env:ASPNETCORE_VERSION/aspnetcore-runtime-$Env:ASPNETCORE_VERSION-win-x64.zip; `
$aspnetcore_sha512 = '98224c8646b7eab234b97f52735905bb0219ea2290490e408ff469459ea82116068854e7b9c5869bccef780b4ceac17477f34f23e06a0a6bedca445a3866d73e'; `
if ((Get-FileHash aspnetcore.zip -Algorithm sha512).Hash -ne $aspnetcore_sha512) { `
Write-Host 'CHECKSUM VERIFICATION FAILED!'; `
exit 1; `
}; `
`
Expand-Archive aspnetcore.zip -DestinationPath $Env:ProgramFiles\dotnet; `
Remove-Item -Force aspnetcore.zip
RUN setx /M PATH $($Env:PATH + ';' + $Env:ProgramFiles + '\dotnet')
# install Visual C++ Redistributable
RUN Invoke-WebRequest -OutFile vc_redist.x64.exe https://aka.ms/vs/15/release/vc_redist.x64.exe; `
Start-Process vc_redist.x64.exe -ArgumentList '/install /passive /norestart' -Wait; `
Remove-Item -Force vc_redist.x64.exe
# Configure web servers to bind to port 80 when present
ENV ASPNETCORE_URLS=http://+:80 `
# Enable detection of running in a container
DOTNET_RUNNING_IN_CONTAINERS=true
################################################################################
# build
#
# Builds the app.
################################################################################
FROM sdk AS build
WORKDIR /src
COPY SkiaSharpSample/SkiaSharpSample.csproj SkiaSharpSample/
COPY packages/ packages/
RUN dotnet restore SkiaSharpSample/SkiaSharpSample.csproj -s "/src/packages" -s "https://api.nuget.org/v3/index.json"
COPY . .
WORKDIR /src/SkiaSharpSample
RUN dotnet build SkiaSharpSample.csproj -c Release -o /app
################################################################################
# publish
#
# Publishes the app.
################################################################################
FROM build AS publish
RUN dotnet publish SkiaSharpSample.csproj -c Release -o /app
################################################################################
# final
#
# Copies and runs the app.
################################################################################
FROM runtime AS final
WORKDIR /app
EXPOSE 80
COPY --from=publish /app .
ENTRYPOINT dotnet SkiaSharpSample.dll

Просмотреть файл

@ -1 +0,0 @@
. .\sample-base.ps1 -Platform "windows"