Some of the colorspaces created are just references (#922)

* Some of the colorspaces created are just references
  - SRGB and SRGB Linear colorspaces are static and should never be disposed
* Fix concurrent disposal and creation
  - In some cases, a new native object is created with the same memory location while the 
    managed instances is still being disposed. In this case, we cannot remove the managed
    instance from the dictionary, since it is not the current object being disposed anymore.
  - SKColorSpace has some members that should not be disposed, since they are static on the
    native side. Just, there is no docs for this, so calling dispose will crash everything.
  - Added the logic to make sure that all static objects are created before anything, so we can 
    get a handle on them and make sure they are static managed instances.
* Make sure the objects are not collected when using spans
This commit is contained in:
Matthew Leibowitz 2019-09-24 01:34:44 +02:00 коммит произвёл GitHub
Родитель d5aa34a787
Коммит c10c7394a2
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
33 изменённых файлов: 585 добавлений и 144 удалений

1
.gitignore поставляемый
Просмотреть файл

@ -3,6 +3,7 @@
# Repository-specific files # Repository-specific files
output/ output/
output-temp/
tools/ tools/
*.VC.db *.VC.db
**/Resources/Resource.designer.cs **/Resources/Resource.designer.cs

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

@ -4,8 +4,8 @@ mdoc.targets release 5.7.4.9
mdoc release 5.7.4.9 mdoc release 5.7.4.9
harfbuzz release 2.5.3 harfbuzz release 2.5.3
skia release m68 skia release m68
xunit release 2.4.0 xunit release 2.4.1
xunit.runner.console release 2.4.0 xunit.runner.console release 2.4.1
Xamarin.Forms release 4.0.0.540366 Xamarin.Forms release 4.0.0.540366
Tizen.NET release 4.0.0 Tizen.NET release 4.0.0
OpenTK release 3.0.1 OpenTK release 3.0.1

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

@ -392,7 +392,13 @@ namespace SkiaSharp
// no-op due to unsupperted action // no-op due to unsupperted action
} }
public byte[] Bytes => GetPixelSpan ().ToArray (); public byte[] Bytes {
get {
var array = GetPixelSpan ().ToArray ();
GC.KeepAlive (this);
return array;
}
}
public SKColor[] Pixels { public SKColor[] Pixels {
get { get {

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

@ -178,6 +178,21 @@ namespace SkiaSharp
public class SKColorSpace : SKObject, ISKReferenceCounted public class SKColorSpace : SKObject, ISKReferenceCounted
{ {
private static readonly SKColorSpace srgb;
private static readonly SKColorSpace srgbLinear;
static SKColorSpace ()
{
srgb = new SKColorSpaceStatic (SkiaApi.sk_colorspace_new_srgb ());
srgbLinear = new SKColorSpaceStatic (SkiaApi.sk_colorspace_new_srgb_linear ());
}
internal static void EnsureStaticInstanceAreInitialized ()
{
// IMPORTANT: do not remove to ensure that the static instances
// are initialized before any access is made to them
}
[Preserve] [Preserve]
internal SKColorSpace (IntPtr handle, bool owns) internal SKColorSpace (IntPtr handle, bool owns)
: base (handle, owns) : base (handle, owns)
@ -208,11 +223,9 @@ namespace SkiaSharp
return SkiaApi.sk_colorspace_equals (left.Handle, right.Handle); return SkiaApi.sk_colorspace_equals (left.Handle, right.Handle);
} }
public static SKColorSpace CreateSrgb () => public static SKColorSpace CreateSrgb () => srgb;
GetObject<SKColorSpace> (SkiaApi.sk_colorspace_new_srgb ());
public static SKColorSpace CreateSrgbLinear () => public static SKColorSpace CreateSrgbLinear () => srgbLinear;
GetObject<SKColorSpace> (SkiaApi.sk_colorspace_new_srgb_linear ());
public static SKColorSpace CreateIcc (IntPtr input, long length) public static SKColorSpace CreateIcc (IntPtr input, long length)
{ {
@ -299,5 +312,19 @@ namespace SkiaSharp
public SKMatrix44 FromXyzD50 () => public SKMatrix44 FromXyzD50 () =>
GetObject<SKMatrix44> (SkiaApi.sk_colorspace_as_from_xyzd50 (Handle), false); GetObject<SKMatrix44> (SkiaApi.sk_colorspace_as_from_xyzd50 (Handle), false);
private sealed class SKColorSpaceStatic : SKColorSpace
{
internal SKColorSpaceStatic (IntPtr x)
: base (x, false)
{
IgnorePublicDispose = true;
}
protected override void Dispose (bool disposing)
{
// do not dispose
}
}
} }
} }

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

@ -13,8 +13,18 @@ namespace SkiaSharp
// improvement in Copy performance. // improvement in Copy performance.
internal const int CopyBufferSize = 81920; internal const int CopyBufferSize = 81920;
private static readonly Lazy<SKData> empty = private static readonly Lazy<SKData> empty;
new Lazy<SKData> (() => new SKDataStatic (SkiaApi.sk_data_new_empty ()));
static SKData()
{
empty = new Lazy<SKData> (() => new SKDataStatic (SkiaApi.sk_data_new_empty ()));
}
internal static void EnsureStaticInstanceAreInitialized ()
{
// IMPORTANT: do not remove to ensure that the static instances
// are initialized before any access is made to them
}
[Preserve] [Preserve]
internal SKData (IntPtr x, bool owns) internal SKData (IntPtr x, bool owns)
@ -174,7 +184,12 @@ namespace SkiaSharp
return GetObject<SKData> (SkiaApi.sk_data_new_subset (Handle, (IntPtr) offset, (IntPtr) length)); return GetObject<SKData> (SkiaApi.sk_data_new_subset (Handle, (IntPtr) offset, (IntPtr) length));
} }
public byte[] ToArray () => AsSpan ().ToArray (); public byte[] ToArray ()
{
var array = AsSpan ().ToArray ();
GC.KeepAlive (this);
return array;
}
public bool IsEmpty => Size == 0; public bool IsEmpty => Size == 0;

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

@ -7,8 +7,18 @@ namespace SkiaSharp
{ {
public class SKFontManager : SKObject, ISKReferenceCounted public class SKFontManager : SKObject, ISKReferenceCounted
{ {
private static readonly Lazy<SKFontManager> defaultManager = private static readonly Lazy<SKFontManager> defaultManager;
new Lazy<SKFontManager> (() => new SKFontManagerStatic (SkiaApi.sk_fontmgr_ref_default ()));
static SKFontManager()
{
defaultManager = new Lazy<SKFontManager> (() => new SKFontManagerStatic (SkiaApi.sk_fontmgr_ref_default ()));
}
internal static void EnsureStaticInstanceAreInitialized ()
{
// IMPORTANT: do not remove to ensure that the static instances
// are initialized before any access is made to them
}
[Preserve] [Preserve]
internal SKFontManager (IntPtr handle, bool owns) internal SKFontManager (IntPtr handle, bool owns)

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

@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading;
namespace SkiaSharp namespace SkiaSharp
{ {
@ -13,11 +14,22 @@ namespace SkiaSharp
internal static readonly ConcurrentBag<Exception> exceptions = new ConcurrentBag<Exception> (); internal static readonly ConcurrentBag<Exception> exceptions = new ConcurrentBag<Exception> ();
#endif #endif
internal static readonly ConcurrentDictionary<Type, ConstructorInfo> constructors = new ConcurrentDictionary<Type, ConstructorInfo> (); internal static readonly ConcurrentDictionary<Type, ConstructorInfo> constructors;
internal static readonly ConcurrentDictionary<IntPtr, WeakReference> instances = new ConcurrentDictionary<IntPtr, WeakReference> (); internal static readonly ConcurrentDictionary<IntPtr, WeakReference> instances;
internal readonly ConcurrentDictionary<IntPtr, SKObject> ownedObjects = new ConcurrentDictionary<IntPtr, SKObject> (); internal readonly ConcurrentDictionary<IntPtr, SKObject> ownedObjects = new ConcurrentDictionary<IntPtr, SKObject> ();
static SKObject ()
{
constructors = new ConcurrentDictionary<Type, ConstructorInfo> ();
instances = new ConcurrentDictionary<IntPtr, WeakReference> ();
SKColorSpace.EnsureStaticInstanceAreInitialized ();
SKData.EnsureStaticInstanceAreInitialized ();
SKFontManager.EnsureStaticInstanceAreInitialized ();
SKTypeface.EnsureStaticInstanceAreInitialized ();
}
[Preserve] [Preserve]
internal SKObject (IntPtr handle, bool owns) internal SKObject (IntPtr handle, bool owns)
: base (handle, owns) : base (handle, owns)
@ -108,7 +120,7 @@ namespace SkiaSharp
WeakReference Update (IntPtr key, WeakReference oldValue) WeakReference Update (IntPtr key, WeakReference oldValue)
{ {
if (oldValue.Target is SKObject obj) { if (oldValue.Target is SKObject obj && !obj.IsDisposed) {
#if THROW_OBJECT_EXCEPTIONS #if THROW_OBJECT_EXCEPTIONS
if (obj.OwnsHandle) if (obj.OwnsHandle)
throw new InvalidOperationException ( throw new InvalidOperationException (
@ -128,14 +140,28 @@ namespace SkiaSharp
if (handle == IntPtr.Zero) if (handle == IntPtr.Zero)
return; return;
var removed = instances.TryRemove (handle, out _); var nonExistent = new WeakReference (null);
var weak = instances.AddOrUpdate (handle, nonExistent, (ptr, old) => {
if (old.Target is SKObject obj && !obj.IsDisposed)
return old;
return new WeakReference (null);
});
#if THROW_OBJECT_EXCEPTIONS #if THROW_OBJECT_EXCEPTIONS
if (!removed) { InvalidOperationException ex = null;
var ex = new InvalidOperationException ( if (weak == nonExistent) {
// the dummy instance was added instead of being removed
ex = new InvalidOperationException (
$"A managed object did not exist for the specified native object. " + $"A managed object did not exist for the specified native object. " +
$"H: {handle.ToString ("x")} Type: {instance.GetType ()}"); $"H: {handle.ToString ("x")} Type: {instance.GetType ()}");
ex.Data.Add ("Handle", handle); }
if (weak.Target is SKObject o && o != instance && !instance.IsDisposed) {
// there was a new living object there, but we are still alive
ex = new InvalidOperationException (
$"Trying to remove a different object with the same native handle. " +
$"H: {handle.ToString ("x")} Type: ({o.GetType ()}, {instance.GetType ()})");
}
if (ex != null) {
if (instance.fromFinalizer) if (instance.fromFinalizer)
exceptions.Add (ex); exceptions.Add (ex);
else else
@ -148,18 +174,26 @@ namespace SkiaSharp
where TSkiaObject : SKObject where TSkiaObject : SKObject
{ {
if (instances.TryGetValue (handle, out var weak)) { if (instances.TryGetValue (handle, out var weak)) {
if (weak.Target is TSkiaObject match) {
if (!match.IsDisposed) {
instance = match;
return true;
}
#if THROW_OBJECT_EXCEPTIONS #if THROW_OBJECT_EXCEPTIONS
if (weak.Target is object existing && !(weak.Target is TSkiaObject)) } else if (weak.Target is SKObject obj) {
if (!obj.IsDisposed) {
throw new InvalidOperationException (
$"A managed object exists for the handle, but is not the expected type. " +
$"H: {handle.ToString ("x")} Type: ({obj.GetType ()}, {typeof (TSkiaObject)})");
}
} else if (weak.Target is object o) {
throw new InvalidOperationException ( throw new InvalidOperationException (
$"A managed object exists for the handle, but is not the expected type. " + $"An unknown object exists for the handle when trying to fetch an instance. " +
$"H: {handle.ToString ("x")} Type: ({existing.GetType ()}, {typeof (TSkiaObject)})"); $"H: {handle.ToString ("x")} Type: ({o.GetType ()}, {typeof (TSkiaObject)})");
#endif #endif
if (weak.Target is TSkiaObject obj && obj.Handle != IntPtr.Zero) {
instance = obj;
return true;
} }
} }
instance = null; instance = null;
return false; return false;
} }
@ -225,7 +259,7 @@ namespace SkiaSharp
internal bool fromFinalizer = false; internal bool fromFinalizer = false;
#endif #endif
private bool isDisposed = false; private int isDisposed = 0;
internal SKNativeObject (IntPtr handle) internal SKNativeObject (IntPtr handle)
: this (handle, true) : this (handle, true)
@ -253,6 +287,8 @@ namespace SkiaSharp
protected internal bool IgnorePublicDispose { get; protected set; } protected internal bool IgnorePublicDispose { get; protected set; }
protected internal bool IsDisposed => isDisposed == 1;
protected virtual void DisposeManaged () protected virtual void DisposeManaged ()
{ {
// dispose of any managed resources // dispose of any managed resources
@ -265,9 +301,8 @@ namespace SkiaSharp
protected virtual void Dispose (bool disposing) protected virtual void Dispose (bool disposing)
{ {
if (isDisposed) if (Interlocked.CompareExchange (ref isDisposed, 1, 0) != 0)
return; return;
isDisposed = true;
if (disposing) { if (disposing) {
DisposeManaged (); DisposeManaged ();

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

@ -15,8 +15,18 @@ namespace SkiaSharp
public class SKTypeface : SKObject, ISKReferenceCounted public class SKTypeface : SKObject, ISKReferenceCounted
{ {
private static readonly Lazy<SKTypeface> defaultTypeface = private static readonly Lazy<SKTypeface> defaultTypeface;
new Lazy<SKTypeface> (() => new SKTypefaceStatic (SkiaApi.sk_typeface_ref_default ()));
static SKTypeface ()
{
defaultTypeface = new Lazy<SKTypeface> (() => new SKTypefaceStatic (SkiaApi.sk_typeface_ref_default ()));
}
internal static void EnsureStaticInstanceAreInitialized ()
{
// IMPORTANT: do not remove to ensure that the static instances
// are initialized before any access is made to them
}
[Preserve] [Preserve]
internal SKTypeface (IntPtr handle, bool owns) internal SKTypeface (IntPtr handle, bool owns)

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

@ -69,9 +69,21 @@ namespace HarfBuzzSharp
set => HarfBuzzApi.hb_buffer_set_unicode_funcs (Handle, value.Handle); set => HarfBuzzApi.hb_buffer_set_unicode_funcs (Handle, value.Handle);
} }
public GlyphInfo[] GlyphInfos => GetGlyphInfoSpan ().ToArray (); public GlyphInfo[] GlyphInfos {
get {
var array = GetGlyphInfoSpan ().ToArray ();
GC.KeepAlive (this);
return array;
}
}
public GlyphPosition[] GlyphPositions => GetGlyphPositionSpan ().ToArray (); public GlyphPosition[] GlyphPositions {
get {
var array = GetGlyphPositionSpan ().ToArray ();
GC.KeepAlive (this);
return array;
}
}
public void Add (uint codepoint, uint cluster) public void Add (uint codepoint, uint cluster)
{ {

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

@ -6,7 +6,7 @@
#addin nuget:?package=Xamarin.Nuget.Validator&version=1.1.1 #addin nuget:?package=Xamarin.Nuget.Validator&version=1.1.1
#tool nuget:?package=mdoc&version=5.7.4.9 #tool nuget:?package=mdoc&version=5.7.4.9
#tool nuget:?package=xunit.runner.console&version=2.4.0 #tool nuget:?package=xunit.runner.console&version=2.4.1
#tool nuget:?package=vswhere&version=2.5.2 #tool nuget:?package=vswhere&version=2.5.2
using System.Linq; using System.Linq;
@ -24,7 +24,7 @@ using NuGet.Versioning;
#load "cake/Utils.cake" #load "cake/Utils.cake"
var TARGET = Argument ("t", Argument ("target", Argument ("Target", "Default"))); var TARGET = Argument ("t", Argument ("target", Argument ("Target", "Default")));
var VERBOSITY = (Verbosity) Enum.Parse (typeof(Verbosity), Argument ("v", Argument ("verbosity", Argument ("Verbosity", "Normal"))), true); var VERBOSITY = Argument ("v", Argument ("verbosity", Argument ("Verbosity", Verbosity.Normal)));
var SKIP_EXTERNALS = Argument ("skipexternals", Argument ("SkipExternals", "")).ToLower ().Split (','); var SKIP_EXTERNALS = Argument ("skipexternals", Argument ("SkipExternals", "")).ToLower ().Split (',');
var PACK_ALL_PLATFORMS = Argument ("packall", Argument ("PackAll", Argument ("PackAllPlatforms", TARGET.ToLower() == "ci" || TARGET.ToLower() == "nuget-only"))); var PACK_ALL_PLATFORMS = Argument ("packall", Argument ("PackAll", Argument ("PackAllPlatforms", TARGET.ToLower() == "ci" || TARGET.ToLower() == "nuget-only")));
var PRINT_ALL_ENV_VARS = Argument ("printAllEnvVars", false); var PRINT_ALL_ENV_VARS = Argument ("printAllEnvVars", false);
@ -137,16 +137,6 @@ Task ("tests-only")
.Does (() => .Does (() =>
{ {
var RunDesktopTest = new Action<string> (arch => { var RunDesktopTest = new Action<string> (arch => {
var platform = "";
if (IsRunningOnWindows ()) {
platform = "windows";
} else if (IsRunningOnMac ()) {
platform = "mac";
} else if (IsRunningOnLinux ()) {
platform = "linux";
}
EnsureDirectoryExists ($"./output/tests/{platform}/{arch}");
RunMSBuild ("./tests/SkiaSharp.Desktop.Tests/SkiaSharp.Desktop.Tests.sln", platform: arch == "AnyCPU" ? "Any CPU" : 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"); RunTests ($"./tests/SkiaSharp.Desktop.Tests/bin/{arch}/{CONFIGURATION}/SkiaSharp.Tests.dll", arch == "x86");
}); });
@ -165,7 +155,6 @@ Task ("tests-only")
} }
// .NET Core // .NET Core
EnsureDirectoryExists ("./output/tests/netcore");
RunMSBuild ("./tests/SkiaSharp.NetCore.Tests/SkiaSharp.NetCore.Tests.sln"); RunMSBuild ("./tests/SkiaSharp.NetCore.Tests/SkiaSharp.NetCore.Tests.sln");
RunNetCoreTests ("./tests/SkiaSharp.NetCore.Tests/SkiaSharp.NetCore.Tests.csproj"); RunNetCoreTests ("./tests/SkiaSharp.NetCore.Tests/SkiaSharp.NetCore.Tests.csproj");
}); });

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

@ -71,6 +71,8 @@ void RunTests (FilePath testAssembly, bool is32)
ReportName = "TestResults", ReportName = "TestResults",
XmlReport = true, XmlReport = true,
UseX86 = is32, UseX86 = is32,
NoAppDomain = true,
Parallelism = ParallelismOption.All,
OutputDirectory = dir, OutputDirectory = dir,
WorkingDirectory = dir, WorkingDirectory = dir,
ArgumentCustomization = args => args.Append ("-verbose"), ArgumentCustomization = args => args.Append ("-verbose"),

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

@ -1,5 +1,5 @@
{ {
"msbuild-sdks": { "sdk": {
"MSBuild.Sdk.Extras": "1.6.65" "version": "2.2.108.1"
} }
} }

Двоичный файл не отображается.

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

@ -17,7 +17,7 @@
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> <ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<PackageCertificateKeyFile>..\..\..\AppStoreCertificates\SkiaSharpSample_TemporaryKey.pfx</PackageCertificateKeyFile> <PackageCertificateKeyFile>..\..\..\AppStoreCertificates\SkiaSharpSample_TemporaryKey.pfx</PackageCertificateKeyFile>
<PackageCertificateThumbprint>8DD3DBEC438E69AF67465BF733A17DBA8397D719</PackageCertificateThumbprint> <PackageCertificateThumbprint>D3578B78019FED6AEE572B23C1723D44951E0BF9</PackageCertificateThumbprint>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle> <RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">

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

@ -18,7 +18,7 @@
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> <ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<PackageCertificateKeyFile>..\..\..\AppStoreCertificates\SkiaSharpSample_TemporaryKey.pfx</PackageCertificateKeyFile> <PackageCertificateKeyFile>..\..\..\AppStoreCertificates\SkiaSharpSample_TemporaryKey.pfx</PackageCertificateKeyFile>
<PackageCertificateThumbprint>8DD3DBEC438E69AF67465BF733A17DBA8397D719</PackageCertificateThumbprint> <PackageCertificateThumbprint>D3578B78019FED6AEE572B23C1723D44951E0BF9</PackageCertificateThumbprint>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle> <RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">

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

@ -17,7 +17,7 @@
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> <ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<PackageCertificateKeyFile>..\..\..\AppStoreCertificates\SkiaSharpSample_TemporaryKey.pfx</PackageCertificateKeyFile> <PackageCertificateKeyFile>..\..\..\AppStoreCertificates\SkiaSharpSample_TemporaryKey.pfx</PackageCertificateKeyFile>
<PackageCertificateThumbprint>8DD3DBEC438E69AF67465BF733A17DBA8397D719</PackageCertificateThumbprint> <PackageCertificateThumbprint>D3578B78019FED6AEE572B23C1723D44951E0BF9</PackageCertificateThumbprint>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle> <RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">

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

@ -18,7 +18,7 @@
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> <ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<PackageCertificateKeyFile>..\..\..\AppStoreCertificates\SkiaSharpSample_TemporaryKey.pfx</PackageCertificateKeyFile> <PackageCertificateKeyFile>..\..\..\AppStoreCertificates\SkiaSharpSample_TemporaryKey.pfx</PackageCertificateKeyFile>
<PackageCertificateThumbprint>8DD3DBEC438E69AF67465BF733A17DBA8397D719</PackageCertificateThumbprint> <PackageCertificateThumbprint>D3578B78019FED6AEE572B23C1723D44951E0BF9</PackageCertificateThumbprint>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle> <RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">

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

@ -2,6 +2,10 @@ trigger:
- master - master
- development - development
pr:
- master
- development
variables: variables:
FEATURE_NAME_PREFIX: 'feature/' FEATURE_NAME_PREFIX: 'feature/'
VERBOSITY: normal VERBOSITY: normal
@ -11,14 +15,20 @@ variables:
FEATURE_NAME: '' FEATURE_NAME: ''
PREVIEW_LABEL: 'rc' PREVIEW_LABEL: 'rc'
NATIVE_LINUX_PACKAGES: curl mono-complete msbuild python git libfontconfig1-dev clang-3.8 make NATIVE_LINUX_PACKAGES: curl mono-complete msbuild python git libfontconfig1-dev clang-3.8 make
TIZEN_LINUX_PACKAGES: libxcb-xfixes0 libxcb-render-util0 libwebkitgtk-1.0-0 libxcb-image0 acl libsdl1.2debian libv4l-0 libxcb-randr0 libxcb-shape0 libxcb-icccm4 libsm6 gettext rpm2cpio cpio bridge-utils openvpn TIZEN_LINUX_PACKAGES: mono-complete msbuild libxcb-xfixes0 libxcb-render-util0 libwebkitgtk-1.0-0 libxcb-image0 acl libsdl1.2debian libv4l-0 libxcb-randr0 libxcb-shape0 libxcb-icccm4 libsm6 gettext rpm2cpio cpio bridge-utils openvpn
MANAGED_LINUX_PACKAGES: ttf-ancient-fonts MANAGED_LINUX_PACKAGES: mono-complete msbuild ttf-ancient-fonts
MONO_VERSION: 5_18_1 MONO_VERSION_MACOS: 5_18_3
XCODE_VERSION: 10.2.1 MONO_VERSION_LINUX: stable-xenial/snapshots/5.20.1.34
# CONFIGURATION: 'Release' XCODE_VERSION: 10.3
VM_IMAGE_WINDOWS: vs2017-win2016 DOTNET_VERSION: 2.2.108
VM_IMAGE_MAC: macos-10.14 CONFIGURATION: 'Release'
VM_IMAGE_LINUX: ubuntu-16.04 VM_IMAGE_WINDOWS: Hosted VS2017
VM_IMAGE_MAC: Hosted macOS
VM_IMAGE_LINUX: Hosted Ubuntu 1604
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
# # To speed up builds when debugging DevOps
# DOWNLOAD_EXTERNALS: ''
resources: resources:
repositories: repositories:
@ -38,12 +48,14 @@ stages:
displayName: Build Native Android (Windows) displayName: Build Native Android (Windows)
vmImage: $(VM_IMAGE_WINDOWS) vmImage: $(VM_IMAGE_WINDOWS)
target: externals-android target: externals-android
buildExternals: $(DOWNLOAD_EXTERNALS)
- template: azure-templates-bootstrapper.yml # Build Native Tizen (Windows) - template: azure-templates-bootstrapper.yml # Build Native Tizen (Windows)
parameters: parameters:
name: native_tizen_windows name: native_tizen_windows
displayName: Build Native Tizen (Windows) displayName: Build Native Tizen (Windows)
vmImage: $(VM_IMAGE_WINDOWS) vmImage: $(VM_IMAGE_WINDOWS)
target: externals-tizen target: externals-tizen
buildExternals: $(DOWNLOAD_EXTERNALS)
condition: false # TODO: TIZEN INSTALL BUGS condition: false # TODO: TIZEN INSTALL BUGS
- template: azure-templates-bootstrapper.yml # Build Native UWP (Windows) - template: azure-templates-bootstrapper.yml # Build Native UWP (Windows)
parameters: parameters:
@ -51,12 +63,14 @@ stages:
displayName: Build Native UWP (Windows) displayName: Build Native UWP (Windows)
vmImage: $(VM_IMAGE_WINDOWS) vmImage: $(VM_IMAGE_WINDOWS)
target: externals-uwp target: externals-uwp
buildExternals: $(DOWNLOAD_EXTERNALS)
- template: azure-templates-bootstrapper.yml # Build Native Win32 (Windows) - template: azure-templates-bootstrapper.yml # Build Native Win32 (Windows)
parameters: parameters:
name: native_win32_windows name: native_win32_windows
displayName: Build Native Win32 (Windows) displayName: Build Native Win32 (Windows)
vmImage: $(VM_IMAGE_WINDOWS) vmImage: $(VM_IMAGE_WINDOWS)
target: externals-windows target: externals-windows
buildExternals: $(DOWNLOAD_EXTERNALS)
# NATIVE JOBS - MAC # NATIVE JOBS - MAC
- template: azure-templates-bootstrapper.yml # Build Native Android (macOS) - template: azure-templates-bootstrapper.yml # Build Native Android (macOS)
parameters: parameters:
@ -64,24 +78,28 @@ stages:
displayName: Build Native Android (macOS) displayName: Build Native Android (macOS)
vmImage: $(VM_IMAGE_MAC) vmImage: $(VM_IMAGE_MAC)
target: externals-android target: externals-android
buildExternals: $(DOWNLOAD_EXTERNALS)
- template: azure-templates-bootstrapper.yml # Build Native iOS (macOS) - template: azure-templates-bootstrapper.yml # Build Native iOS (macOS)
parameters: parameters:
name: native_ios_macos name: native_ios_macos
displayName: Build Native iOS (macOS) displayName: Build Native iOS (macOS)
vmImage: $(VM_IMAGE_MAC) vmImage: $(VM_IMAGE_MAC)
target: externals-ios target: externals-ios
buildExternals: $(DOWNLOAD_EXTERNALS)
- template: azure-templates-bootstrapper.yml # Build Native macOS (macOS) - template: azure-templates-bootstrapper.yml # Build Native macOS (macOS)
parameters: parameters:
name: native_macos_macos name: native_macos_macos
displayName: Build Native macOS (macOS) displayName: Build Native macOS (macOS)
vmImage: $(VM_IMAGE_MAC) vmImage: $(VM_IMAGE_MAC)
target: externals-macos target: externals-macos
buildExternals: $(DOWNLOAD_EXTERNALS)
- template: azure-templates-bootstrapper.yml # Build Native Tizen (macOS) - template: azure-templates-bootstrapper.yml # Build Native Tizen (macOS)
parameters: parameters:
name: native_tizen_macos name: native_tizen_macos
displayName: Build Native Tizen (macOS) displayName: Build Native Tizen (macOS)
vmImage: $(VM_IMAGE_MAC) vmImage: $(VM_IMAGE_MAC)
target: externals-tizen target: externals-tizen
buildExternals: $(DOWNLOAD_EXTERNALS)
condition: false # TODO: TIZEN INSTALL BUGS condition: false # TODO: TIZEN INSTALL BUGS
- template: azure-templates-bootstrapper.yml # Build Native tvOS (macOS) - template: azure-templates-bootstrapper.yml # Build Native tvOS (macOS)
parameters: parameters:
@ -89,25 +107,31 @@ stages:
displayName: Build Native tvOS (macOS) displayName: Build Native tvOS (macOS)
vmImage: $(VM_IMAGE_MAC) vmImage: $(VM_IMAGE_MAC)
target: externals-tvos target: externals-tvos
buildExternals: $(DOWNLOAD_EXTERNALS)
- template: azure-templates-bootstrapper.yml # Build Native watchOS (macOS) - template: azure-templates-bootstrapper.yml # Build Native watchOS (macOS)
parameters: parameters:
name: native_watchos_macos name: native_watchos_macos
displayName: Build Native watchOS (macOS) displayName: Build Native watchOS (macOS)
vmImage: $(VM_IMAGE_MAC) vmImage: $(VM_IMAGE_MAC)
target: externals-watchos target: externals-watchos
buildExternals: $(DOWNLOAD_EXTERNALS)
# NATIVE JOBS - LINUX # NATIVE JOBS - LINUX
- template: azure-templates-native-linux.yml # Build Native Linux (Linux) - template: azure-templates-native-linux.yml # Build Native Linux (Linux)
parameters: parameters:
name: native_linux_linux name: native_linux_linux
displayName: Build Native Linux (Linux) displayName: Build Native Linux (Linux)
vmImage: $(VM_IMAGE_LINUX)
packages: $(NATIVE_LINUX_PACKAGES) packages: $(NATIVE_LINUX_PACKAGES)
target: externals-linux target: externals-linux
buildExternals: $(DOWNLOAD_EXTERNALS)
- template: azure-templates-native-linux.yml # Build Native Linux [No Dependencies] (Linux) - template: azure-templates-native-linux.yml # Build Native Linux [No Dependencies] (Linux)
parameters: parameters:
name: native_linux_nodependencies_linux name: native_linux_nodependencies_linux
displayName: Build Native Linux [No Dependencies] (Linux) displayName: Build Native Linux [No Dependencies] (Linux)
vmImage: $(VM_IMAGE_LINUX)
packages: $(NATIVE_LINUX_PACKAGES) packages: $(NATIVE_LINUX_PACKAGES)
target: externals-linux target: externals-linux
buildExternals: $(DOWNLOAD_EXTERNALS)
additionalArgs: --additionalGnArgs="skia_use_fontconfig=false" additionalArgs: --additionalGnArgs="skia_use_fontconfig=false"
- template: azure-templates-bootstrapper.yml # Build Native Tizen (Linux) - template: azure-templates-bootstrapper.yml # Build Native Tizen (Linux)
parameters: parameters:
@ -116,6 +140,7 @@ stages:
vmImage: $(VM_IMAGE_LINUX) vmImage: $(VM_IMAGE_LINUX)
packages: $(TIZEN_LINUX_PACKAGES) packages: $(TIZEN_LINUX_PACKAGES)
target: externals-tizen target: externals-tizen
buildExternals: $(DOWNLOAD_EXTERNALS)
- stage: managed - stage: managed
displayName: Build Managed displayName: Build Managed
@ -238,7 +263,7 @@ stages:
- package_nodependencies_windows - package_nodependencies_windows
- package_windows - package_windows
pool: pool:
vmImage: $(VM_IMAGE_LINUX) name: $(VM_IMAGE_LINUX)
steps: steps:
- task: DownloadBuildArtifacts@0 - task: DownloadBuildArtifacts@0
displayName: Download the nuget-nodependencies artifacts displayName: Download the nuget-nodependencies artifacts
@ -270,23 +295,9 @@ stages:
dependsOn: package dependsOn: package
condition: eq(variables['System.TeamProject'], 'devdiv') condition: eq(variables['System.TeamProject'], 'devdiv')
jobs: jobs:
- job: signing # Sign NuGets - template: sign-artifacts/jobs/v1.yml@xamarin-templates
displayName: Sign NuGets parameters:
pool: additionalConditions: eq(variables['Build.SourceBranch'], 'refs/heads/master')
name: VSEng-XamarinCustom
demands:
- corpnet
condition: and(succeeded(), or(startsWith(variables['Build.SourceBranch'], 'refs/tags/'), eq(variables['Build.SourceBranch'], 'refs/heads/master')))
steps:
- checkout: none
- template: sign-artifacts.yml@xamarin-templates
parameters:
targetFolder: '$(Build.ArtifactStagingDirectory)/signed'
- task: PublishBuildArtifacts@1
displayName: Publish the nuget-signed artifacts
inputs:
artifactName: nuget-signed
pathToPublish: '$(Build.ArtifactStagingDirectory)/signed'
- stage: tests - stage: tests
displayName: Run Tests displayName: Run Tests
@ -305,11 +316,19 @@ stages:
- native_win32_windows - native_win32_windows
postBuildSteps: postBuildSteps:
- task: PublishTestResults@2 - task: PublishTestResults@2
displayName: Publish the test results displayName: Publish the .NET Framework test results
condition: always() condition: always()
inputs: inputs:
testResultsFormat: xUnit testResultsFormat: xUnit
testResultsFiles: 'tests/**/TestResults.xml' testResultsFiles: 'tests/SkiaSharp.Desktop.Tests/**/TestResults.xml'
testRunTitle: 'Windows .NET Framework Tests'
- task: PublishTestResults@2
displayName: Publish the .NET Core test results
condition: always()
inputs:
testResultsFormat: xUnit
testResultsFiles: 'tests/SkiaSharp.NetCore.Tests/**/TestResults.xml'
testRunTitle: 'Windows .NET Core Tests'
- template: azure-templates-bootstrapper.yml # Tests (macOS) - template: azure-templates-bootstrapper.yml # Tests (macOS)
parameters: parameters:
name: tests_macos name: tests_macos
@ -323,11 +342,19 @@ stages:
- native_macos_macos - native_macos_macos
postBuildSteps: postBuildSteps:
- task: PublishTestResults@2 - task: PublishTestResults@2
displayName: Publish the test results displayName: Publish the Mono test results
condition: always() condition: always()
inputs: inputs:
testResultsFormat: xUnit testResultsFormat: xUnit
testResultsFiles: 'tests/**/TestResults.xml' testResultsFiles: 'tests/SkiaSharp.Desktop.Tests/**/TestResults.xml'
testRunTitle: 'macOS Mono Tests'
- task: PublishTestResults@2
displayName: Publish the .NET Core test results
condition: always()
inputs:
testResultsFormat: xUnit
testResultsFiles: 'tests/SkiaSharp.NetCore.Tests/**/TestResults.xml'
testRunTitle: 'macOS .NET Core Tests'
- template: azure-templates-bootstrapper.yml # Tests (Linux) - template: azure-templates-bootstrapper.yml # Tests (Linux)
parameters: parameters:
name: tests_linux name: tests_linux
@ -342,11 +369,19 @@ stages:
- native_linux_linux - native_linux_linux
postBuildSteps: postBuildSteps:
- task: PublishTestResults@2 - task: PublishTestResults@2
displayName: Publish the test results displayName: Publish the Mono test results
condition: always() condition: always()
inputs: inputs:
testResultsFormat: xUnit testResultsFormat: xUnit
testResultsFiles: 'tests/**/TestResults.xml' testResultsFiles: 'tests/SkiaSharp.Desktop.Tests/**/TestResults.xml'
testRunTitle: 'Linux Mono Tests'
- task: PublishTestResults@2
displayName: Publish the .NET Core test results
condition: always()
inputs:
testResultsFormat: xUnit
testResultsFiles: 'tests/SkiaSharp.NetCore.Tests/**/TestResults.xml'
testRunTitle: 'Linux .NET Core Tests'
- template: azure-templates-bootstrapper.yml # Tests [No Dependencies] (Linux) - template: azure-templates-bootstrapper.yml # Tests [No Dependencies] (Linux)
parameters: parameters:
name: tests_nodependencies_linux name: tests_nodependencies_linux
@ -361,11 +396,19 @@ stages:
- native_linux_nodependencies_linux - native_linux_nodependencies_linux
postBuildSteps: postBuildSteps:
- task: PublishTestResults@2 - task: PublishTestResults@2
displayName: Publish the test results displayName: Publish the Mono test results
condition: always() condition: always()
inputs: inputs:
testResultsFormat: xUnit testResultsFormat: xUnit
testResultsFiles: 'tests/**/TestResults.xml' testResultsFiles: 'tests/SkiaSharp.Desktop.Tests/**/TestResults.xml'
testRunTitle: 'Linux [No Dependencies] Mono Tests'
- task: PublishTestResults@2
displayName: Publish the .NET Core test results
condition: always()
inputs:
testResultsFormat: xUnit
testResultsFiles: 'tests/SkiaSharp.NetCore.Tests/**/TestResults.xml'
testRunTitle: 'Linux [No Dependencies] .NET Core Tests'
- stage: samples - stage: samples
displayName: Build Samples displayName: Build Samples

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

@ -1,25 +1,38 @@
parameters: parameters:
name: '' # in the form type_platform_host name: '' # in the form type_platform_host
displayName: '' # the human name displayName: '' # the human name
vmImage: '' # the VM image vmImage: '' # the VM image
packages: '' # any additional packages packages: '' # any additional packages
target: '' # the bootstrapper target target: '' # the bootstrapper target
dependsOn: [] # the dependiencies dependsOn: [] # the dependiencies
requiredArtifacts: [] # the artifacts that this build needs to download requiredArtifacts: [] # the artifacts that this build needs to download
demands: [] # the demands demands: [] # the demands
preBuildSteps: [] # any steps to run before the build preBuildSteps: [] # any steps to run before the build
postBuildSteps: [] # any additional steps to run after the build postBuildSteps: [] # any additional steps to run after the build
additionalArgs: '' # any additional arguments to pass to the bootstrapper additionalArgs: '' # any additional arguments to pass to the bootstrapper
retryCount: 1 # the number of times to retry the bootstrapper retryCount: 1 # the number of times to retry the bootstrapper
condition: succeeded() # whether or not to run this template condition: succeeded() # whether or not to run this template
shouldPublish: true # whether or not to publish the artifacts shouldPublish: true # whether or not to publish the artifacts
configuration: '$(CONFIGURATION)' # the build configuration
buildExternals: '$(DOWNLOAD_EXTERNALS)' # the build number to download externals from
verbosity: '$(VERBOSITY)' # the level of verbosity to use when building
jobs: jobs:
# - ${{ if and(parameters.buildExternals, startsWith(parameters.name, 'native_')) }}:
# - template: azure-templates-download.yml
# parameters:
# name: ${{ parameters.name }}
# displayName: ${{ parameters.displayName }}
# vmImage: ${{ parameters.vmImage }}
# condition: ${{ parameters.condition }}
# buildExternals: ${{ parameters.buildExternals }}
# - ${{ if or(not(parameters.buildExternals), not(startsWith(parameters.name, 'native_'))) }}:
- job: ${{ parameters.name }} - job: ${{ parameters.name }}
displayName: ${{ parameters.displayName }} displayName: ${{ parameters.displayName }}
timeoutInMinutes: 120 timeoutInMinutes: 120
pool: pool:
vmImage: ${{ parameters.vmImage }} name: ${{ parameters.vmImage }}
demands: ${{ parameters.demands }} demands: ${{ parameters.demands }}
dependsOn: ${{ parameters.dependsOn }} dependsOn: ${{ parameters.dependsOn }}
condition: ${{ parameters.condition }} condition: ${{ parameters.condition }}
@ -35,6 +48,11 @@ jobs:
# install any packages on linux # install any packages on linux
- ${{ if endsWith(parameters.name, '_linux') }}: - ${{ if endsWith(parameters.name, '_linux') }}:
- bash: | - bash: |
sudo apt remove -y mono-complete msbuild
sudo apt autoremove -y
sudo rm /etc/mono/config
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
echo "deb https://download.mono-project.com/repo/ubuntu $(MONO_VERSION_LINUX) main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list
sudo apt update sudo apt update
sudo apt install -y ${{ parameters.packages }} sudo apt install -y ${{ parameters.packages }}
displayName: Install any package dependencies displayName: Install any package dependencies
@ -56,9 +74,17 @@ jobs:
- ${{ if contains(parameters.name, '_android_') }}: - ${{ if contains(parameters.name, '_android_') }}:
- powershell: .\scripts\install-android-ndk.ps1 - powershell: .\scripts\install-android-ndk.ps1
displayName: Install the Android NDK displayName: Install the Android NDK
# install extra bits for the manged builds
- ${{ if not(startsWith(parameters.name, 'native_')) }}:
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: $(DOTNET_VERSION)
performMultiLevelLookup: true
displayName: Install the correct version of .NET Core
# switch to the correct mono version on mac # switch to the correct mono version on mac
- ${{ if endsWith(parameters.name, '_macos') }}: - ${{ if endsWith(parameters.name, '_macos') }}:
- bash: sudo $AGENT_HOMEDIRECTORY/scripts/select-xamarin-sdk.sh $(MONO_VERSION) - bash: sudo $(Agent.HomeDirectory)/scripts/select-xamarin-sdk.sh $(MONO_VERSION_MACOS)
displayName: Switch to the latest Xamarin SDK displayName: Switch to the latest Xamarin SDK
- bash: echo '##vso[task.setvariable variable=MD_APPLE_SDK_ROOT;]'/Applications/Xcode_$(XCODE_VERSION).app;sudo xcode-select --switch /Applications/Xcode_$(XCODE_VERSION).app/Contents/Developer - bash: echo '##vso[task.setvariable variable=MD_APPLE_SDK_ROOT;]'/Applications/Xcode_$(XCODE_VERSION).app;sudo xcode-select --switch /Applications/Xcode_$(XCODE_VERSION).app/Contents/Developer
displayName: Switch to the latest Xcode displayName: Switch to the latest Xcode
@ -78,10 +104,14 @@ jobs:
- ${{ parameters.preBuildSteps }} - ${{ parameters.preBuildSteps }}
# build # build
- ${{ if endsWith(parameters.name, '_windows') }}: - ${{ if endsWith(parameters.name, '_windows') }}:
- powershell: .\scripts\retry-command.ps1 -RetryCount ${{ parameters.retryCount }} { .\bootstrapper.ps1 -t ${{ parameters.target }} -v $env:VERBOSITY -c ${{ coalesce(variables.CONFIGURATION, 'Release') }} ${{ parameters.additionalArgs }} } - powershell: .\scripts\retry-command.ps1 -RetryCount ${{ parameters.retryCount }} { .\bootstrapper.ps1 -t ${{ parameters.target }} -v ${{ parameters.verbosity }} -c ${{ coalesce(parameters.configuration, 'Release') }} ${{ parameters.additionalArgs }} }
env:
JavaSdkDirectory: $(JAVA_HOME)
displayName: Run the bootstrapper for ${{ parameters.target }} displayName: Run the bootstrapper for ${{ parameters.target }}
- ${{ if not(endsWith(parameters.name, '_windows')) }}: - ${{ if not(endsWith(parameters.name, '_windows')) }}:
- bash: ./scripts/retry-command.sh ${{ parameters.retryCount }} ./bootstrapper.sh -t ${{ parameters.target }} -v $VERBOSITY -c ${{ coalesce(variables.CONFIGURATION, 'Release') }} ${{ parameters.additionalArgs }} - bash: ./scripts/retry-command.sh ${{ parameters.retryCount }} ./bootstrapper.sh -t ${{ parameters.target }} -v ${{ parameters.verbosity }} -c ${{ coalesce(parameters.configuration, 'Release') }} ${{ parameters.additionalArgs }}
env:
JavaSdkDirectory: $(JAVA_HOME)
displayName: Run the bootstrapper for ${{ parameters.target }} displayName: Run the bootstrapper for ${{ parameters.target }}
# post-build steps # post-build steps
- ${{ parameters.postBuildSteps }} - ${{ parameters.postBuildSteps }}

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

@ -0,0 +1,36 @@
parameters:
name: '' # in the form type_platform_host
displayName: '' # the human name
vmImage: '' # the VM image
condition: succeeded() # whether or not to run this template
buildExternals: '$(DOWNLOAD_EXTERNALS)' # the build number to download externals from
jobs:
- job: ${{ parameters.name }}
displayName: ${{ parameters.displayName }}
pool:
name: ${{ parameters.vmImage }}
condition: ${{ parameters.condition }}
steps:
- checkout: none
- task: DownloadBuildArtifacts@0
displayName: Download the pre-built ${{ parameters.name }} artifacts
inputs:
buildType: 'specific'
project: '$(System.TeamProjectId)'
pipeline: '$(System.DefinitionId)'
buildVersionToDownload: 'specific'
buildId: '${{ parameters.buildExternals }}'
downloadType: 'single'
artifactName: ${{ parameters.name }}
downloadPath: 'download-temp'
- powershell: |
New-Item '.\output\' -Type Directory -Force | Out-Null
Get-ChildItem '.\download-temp\${{ parameters.name }}\' | Copy-Item -Destination '.\output\' -Recurse -Force
Remove-Item '.\download-temp\${{ parameters.name }}\' -Recurse -Force
displayName: Move the ${{ parameters.name }} artifacts to the output directory
- task: PublishBuildArtifacts@1
displayName: Publish the ${{ parameters.name }} artifacts
inputs:
artifactName: ${{ parameters.name }}
pathToPublish: 'output'

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

@ -1,19 +1,32 @@
parameters: parameters:
name: '' # in the form type_platform_host name: '' # in the form type_platform_host
displayName: '' # the human name displayName: '' # the human name
packages: '' # any additional packages vmImage: '' # the VM image
target: '' # the bootstrapper target packages: '' # any additional packages
preBuildSteps: [] # any steps to run before the build target: '' # the bootstrapper target
postBuildSteps: [] # any additional steps to run after the build preBuildSteps: [] # any steps to run before the build
additionalArgs: '' # any additional arguments to pass to the bootstrapper postBuildSteps: [] # any additional steps to run after the build
retryCount: 1 # the number of times to retry the bootstrapper additionalArgs: '' # any additional arguments to pass to the bootstrapper
retryCount: 1 # the number of times to retry the bootstrapper
configuration: '$(CONFIGURATION)' # the build configuration
buildExternals: '$(DOWNLOAD_EXTERNALS)' # the build number to download externals from
verbosity: '$(VERBOSITY)' # the level of verbosity to use when building
jobs: jobs:
# - ${{ if and(parameters.buildExternals, startsWith(parameters.name, 'native_')) }}:
# - template: azure-templates-download.yml
# parameters:
# name: ${{ parameters.name }}
# displayName: ${{ parameters.displayName }}
# vmImage: ${{ parameters.vmImage }}
# buildExternals: ${{ parameters.buildExternals }}
# - ${{ if or(not(parameters.buildExternals), not(startsWith(parameters.name, 'native_'))) }}:
- job: ${{ parameters.name }} - job: ${{ parameters.name }}
displayName: ${{ parameters.displayName }} displayName: ${{ parameters.displayName }}
timeoutInMinutes: 120 timeoutInMinutes: 120
pool: pool:
vmImage: $(VM_IMAGE_LINUX) name: ${{ parameters.vmImage }}
container: ubuntu:14.04 container: ubuntu:14.04
steps: steps:
- checkout: self - checkout: self
@ -30,7 +43,7 @@ jobs:
# pre-build steps # pre-build steps
- ${{ parameters.preBuildSteps }} - ${{ parameters.preBuildSteps }}
# build # build
- bash: ./scripts/retry-command.sh ${{ parameters.retryCount }} ./bootstrapper.sh -t ${{ parameters.target }} -v $VERBOSITY -c ${{ coalesce(variables.CONFIGURATION, 'Release') }} ${{ parameters.additionalArgs }} - bash: ./scripts/retry-command.sh ${{ parameters.retryCount }} ./bootstrapper.sh -t ${{ parameters.target }} -v ${{ parameters.verbosity }} -c ${{ coalesce(parameters.configuration, 'Release') }} ${{ parameters.additionalArgs }}
displayName: Run the bootstrapper for ${{ parameters.target }} displayName: Run the bootstrapper for ${{ parameters.target }}
env: env:
CC: clang-3.8 CC: clang-3.8

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

@ -0,0 +1,30 @@
param (
[string] $BuildNumber,
[string] $Artifact
)
$ErrorActionPreference = 'Stop'
Add-Type -AssemblyName System.IO.Compression.FileSystem
$url = "https://dev.azure.com/xamarin/public/_apis/build/builds/$BuildNumber/artifacts?artifactName=$Artifact&api-version=5.1&%24format=zip"
$outputTemp = "./output-temp"
$dest = "$outputTemp/$Artifact.zip"
$destTemp = "$outputTemp/$Artifact"
$output = "./output"
# dowload the artifact
New-Item -Type Directory -Path $outputTemp -Force | Out-Null
Invoke-WebRequest -Uri $url -OutFile $dest
# extract it
if (Test-Path -Path $destTemp) {
Remove-Item -Path $destTemp/** -Force -Recurse
}
[System.IO.Compression.ZipFile]::ExtractToDirectory($dest, $outputTemp)
# create the output folder
New-Item -Type Directory -Path $output -Force | Out-Null
# move the items into the output folder
Get-ChildItem $destTemp | Copy-Item -Destination $output -Force -Recurse

44
scripts/download-file.ps1 Normal file
Просмотреть файл

@ -0,0 +1,44 @@
# This was modified from StackOverflow
# https://stackoverflow.com/questions/45574479/powershell-determine-new-url-of-a-permanently-moved-redirected-resource
Param (
[Parameter(Mandatory, ValueFromPipeline)] [Uri] $Uri,
[string] $OutFile,
[int] $MaxRedirections = 50 # Use same default as [System.Net.HttpWebRequest]
)
process {
$nextUri = $Uri
$ultimateFound = $false
foreach($i in 1..$($MaxRedirections+1)) {
Write-Verbose "Examining: $nextUri"
$request = [System.Net.HttpWebRequest]::Create($nextUri)
$request.AllowAutoRedirect = $False
try {
$response = $request.GetResponse()
$nextUriStr = $response.Headers['Location']
$response.Close()
if (-not $nextUriStr) {
$ultimateFound = $true
break
}
} catch [System.Net.WebException] {
$nextUriStr = try { $_.Exception.Response.Headers['Location'] } catch {}
if (-not $nextUriStr) { Throw }
}
Write-Verbose "Raw target: $nextUriStr"
if ($nextUriStr -match '^https?:') {
$nextUri = $prevUri = [Uri] $nextUriStr
} else {
$nextUri = $prevUri = [Uri] ($prevUri.Scheme + '://' + $prevUri.Authority + $nextUriStr)
}
if ($i -ge $MaxRedirections) {
break
}
}
if (-not $ultimateFound) {
Throw "Enumeration of $Uri redirections ended before reaching the ultimate target."
}
Invoke-WebRequest -Uri $nextUri -OutFile $OutFile
}

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

@ -20,7 +20,7 @@ $ndk = "$HOME/android-ndk"
if ($InstallDestination) { if ($InstallDestination) {
$ndk = $InstallDestination $ndk = $InstallDestination
} }
Write-Host "Install destination is '$ts'..." Write-Host "Install destination is '$ndk'..."
$ndkTemp = "$HOME/android-ndk-temp" $ndkTemp = "$HOME/android-ndk-temp"
$install = "$ndkTemp/android-ndk.zip" $install = "$ndkTemp/android-ndk.zip"

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

@ -16,7 +16,7 @@ $jdk = Join-Path "$HOME" "openjdk"
if ($InstallDestination) { if ($InstallDestination) {
$jdk = $InstallDestination $jdk = $InstallDestination
} }
Write-Host "Install destination is '$ts'..." Write-Host "Install destination is '$jdk'..."
$jdkTemp = Join-Path "$HOME" "openjdk-temp" $jdkTemp = Join-Path "$HOME" "openjdk-temp"
$archive = Join-Path "$jdkTemp" "openjdk.tar.gz" $archive = Join-Path "$jdkTemp" "openjdk.tar.gz"

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

@ -81,8 +81,8 @@
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Xml.Linq" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="xunit" Version="2.4.0" /> <PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
<PackageReference Include="xunit.assemblyfixture" Version="2.0.3" /> <PackageReference Include="xunit.assemblyfixture" Version="2.0.3" />
<PackageReference Include="xunit.skippablefact" Version="1.3.12" /> <PackageReference Include="xunit.skippablefact" Version="1.3.12" />
</ItemGroup> </ItemGroup>

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

@ -14,8 +14,8 @@
<SkipCopyToOutputDirectory>true</SkipCopyToOutputDirectory> <SkipCopyToOutputDirectory>true</SkipCopyToOutputDirectory>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="xunit" Version="2.4.0" /> <PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
<PackageReference Include="XunitXml.TestLogger" Version="2.1.26" /> <PackageReference Include="XunitXml.TestLogger" Version="2.1.26" />
<PackageReference Include="xunit.assemblyfixture" Version="2.0.3" /> <PackageReference Include="xunit.assemblyfixture" Version="2.0.3" />
<PackageReference Include="xunit.skippablefact" Version="1.3.12" /> <PackageReference Include="xunit.skippablefact" Version="1.3.12" />

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

@ -7,20 +7,21 @@ namespace SkiaSharp.Tests
{ {
public class GarbageCleanupFixture : IDisposable public class GarbageCleanupFixture : IDisposable
{ {
#if THROW_OBJECT_EXCEPTIONS
internal static readonly ConcurrentBag<IntPtr> ignoredExceptions = new ConcurrentBag<IntPtr>();
#endif
private static readonly string[] StaticTypes = new[] { private static readonly string[] StaticTypes = new[] {
"SkiaSharp.SKData+SKDataStatic", "SkiaSharp.SKData+SKDataStatic",
"SkiaSharp.SKFontManager+SKFontManagerStatic", "SkiaSharp.SKFontManager+SKFontManagerStatic",
"SkiaSharp.SKTypeface+SKTypefaceStatic", "SkiaSharp.SKTypeface+SKTypefaceStatic",
"SkiaSharp.SKColorSpace+SKColorSpaceStatic",
}; };
public GarbageCleanupFixture() public GarbageCleanupFixture()
{ {
Assert.Empty(SKObject.constructors); Assert.Empty(SKObject.constructors);
Assert.Empty(SKObject.instances); var aliveObjects = SKObject.instances.Values
.Select(o => o.Target)
.Where(IsExpectedToBeDead)
.ToList();
Assert.Empty(aliveObjects);
} }
public void Dispose() public void Dispose()
@ -37,21 +38,9 @@ namespace SkiaSharp.Tests
#if THROW_OBJECT_EXCEPTIONS #if THROW_OBJECT_EXCEPTIONS
// make sure all the exceptions are accounted for // make sure all the exceptions are accounted for
var ignored = ignoredExceptions
.ToList();
var exceptions = SKObject.exceptions var exceptions = SKObject.exceptions
.ToList(); .ToList();
var keep = exceptions Assert.Empty(exceptions);
.Where(ex => !ignored.Contains((IntPtr)ex.Data["Handle"]))
.ToList();
Assert.Empty(keep);
foreach (var ignore in ignored)
{
var e = exceptions
.Where(ex => ex.Data["Handle"] is IntPtr)
.ToList();
Assert.NotEmpty(e);
}
// make sure all the GCHandles are freed // make sure all the GCHandles are freed
var gcHandles = GCHandleProxy.allocatedHandles.Values var gcHandles = GCHandleProxy.allocatedHandles.Values
@ -63,6 +52,9 @@ namespace SkiaSharp.Tests
private bool IsExpectedToBeDead(object instance) private bool IsExpectedToBeDead(object instance)
{ {
if (instance == null)
return false;
var skobject = Assert.IsAssignableFrom<SKObject>(instance); var skobject = Assert.IsAssignableFrom<SKObject>(instance);
if (StaticTypes.Contains(skobject.GetType().FullName)) if (StaticTypes.Contains(skobject.GetType().FullName))

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

@ -180,7 +180,7 @@ namespace SkiaSharp.Tests
if (index > 0) { if (index > 0) {
decodeInfo = info.WithAlphaType (frameInfos [index].AlphaType); decodeInfo = info.WithAlphaType (frameInfos [index].AlphaType);
} }
bm.TryAllocPixels (decodeInfo); Assert.True (bm.TryAllocPixels (decodeInfo));
if (cachedIndex != -1) { if (cachedIndex != -1) {
Assert.True (cachedFrames [cachedIndex].CopyTo (bm)); Assert.True (cachedFrames [cachedIndex].CopyTo (bm));
} }
@ -199,7 +199,9 @@ namespace SkiaSharp.Tests
var uncachedFrame = new SKBitmap (); var uncachedFrame = new SKBitmap ();
decode (uncachedFrame, frameInfos [i].RequiredFrame, i); decode (uncachedFrame, frameInfos [i].RequiredFrame, i);
Assert.Equal (cachedFrame.Bytes, uncachedFrame.Bytes); var cachedBytes = cachedFrame.Bytes;
var uncachedBytes = uncachedFrame.Bytes;
Assert.Equal (cachedBytes, uncachedBytes);
} }
} }
} }

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

@ -16,6 +16,21 @@ namespace SkiaSharp.Tests
Assert.True(SKColorSpace.Equal(colorspace, colorspace)); Assert.True(SKColorSpace.Equal(colorspace, colorspace));
} }
[SkippableFact]
public void StaticSrgbIsReturnedAsTheStaticInstance()
{
var handle = SkiaApi.sk_colorspace_new_srgb();
try
{
var cs = SKObject.GetObject<SKColorSpace>(handle, unrefExisting: false);
Assert.Equal("SKColorSpaceStatic", cs.GetType().Name);
}
finally
{
SkiaApi.sk_refcnt_safe_unref(handle);
}
}
[SkippableFact] [SkippableFact]
public void ImageInfoHasColorSpace() public void ImageInfoHasColorSpace()
{ {
@ -226,6 +241,58 @@ namespace SkiaSharp.Tests
Assert.True(colorspace.GammaIsCloseToSrgb); Assert.True(colorspace.GammaIsCloseToSrgb);
} }
[SkippableFact]
public void ColorSpaceCorrectlyReferencesSrgbSingleton()
{
var handle1 = SkiaApi.sk_colorspace_new_srgb();
var colorspace1 = SKColorSpace.CreateSrgb();
Assert.Equal(colorspace1.Handle, handle1);
var colorspace2 = SKColorSpace.CreateSrgb();
Assert.Same(colorspace1, colorspace2);
Assert.Equal(handle1, colorspace2.Handle);
colorspace2.Dispose();
Assert.False(colorspace2.IsDisposed);
Assert.False(colorspace1.IsDisposed);
SkiaApi.sk_refcnt_safe_unref(handle1);
}
[SkippableFact]
public void SameColorSpaceCreatedDifferentWaysAreTheSameObject()
{
var colorspace1 = SKColorSpace.CreateSrgbLinear();
Assert.Equal("SkiaSharp.SKColorSpace+SKColorSpaceStatic", colorspace1.GetType().FullName);
Assert.Equal(2, colorspace1.GetReferenceCount());
var colorspace2 = SKColorSpace.CreateRgb(SKNamedGamma.Linear, SKColorSpaceGamut.Srgb);
Assert.Equal("SkiaSharp.SKColorSpace+SKColorSpaceStatic", colorspace2.GetType().FullName);
Assert.Equal(2, colorspace2.GetReferenceCount());
Assert.Same(colorspace1, colorspace2);
var colorspace3 = SKColorSpace.CreateRgb(
new SKColorSpaceTransferFn { A = 0.6f, B = 0.5f, C = 0.4f, D = 0.3f, E = 0.2f, F = 0.1f },
SKMatrix44.CreateIdentity());
Assert.NotSame(colorspace1, colorspace3);
colorspace3.Dispose();
Assert.True(colorspace3.IsDisposed);
Assert.Equal(2, colorspace1.GetReferenceCount());
colorspace2.Dispose();
Assert.False(colorspace2.IsDisposed);
Assert.Equal(2, colorspace1.GetReferenceCount());
colorspace1.Dispose();
Assert.False(colorspace1.IsDisposed);
Assert.Equal(2, colorspace1.GetReferenceCount());
}
private static void AssertMatrix(float[] expected, SKMatrix44 actual) private static void AssertMatrix(float[] expected, SKMatrix44 actual)
{ {
var actualArray = actual var actualArray = actual

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

@ -171,6 +171,8 @@ namespace SkiaSharp.Tests
[SkippableFact] [SkippableFact]
public void StreamIsNotCollectedPrematurely() public void StreamIsNotCollectedPrematurely()
{ {
VerifyImmediateFinalizers();
DoWork(out var handle); DoWork(out var handle);
CollectGarbage(); CollectGarbage();

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

@ -1,4 +1,6 @@
using System; using System;
using System.IO;
using System.Threading.Tasks;
using Xunit; using Xunit;
namespace SkiaSharp.Tests namespace SkiaSharp.Tests
@ -131,9 +133,8 @@ namespace SkiaSharp.Tests
var inst1 = new LifecycleObject(handle, true) { Value = 1 }; var inst1 = new LifecycleObject(handle, true) { Value = 1 };
#if THROW_OBJECT_EXCEPTIONS #if THROW_OBJECT_EXCEPTIONS
Assert.Throws<InvalidOperationException>(() => new LifecycleObject(handle, true) { Value = 2 }); var ex = Assert.Throws<InvalidOperationException>(() => new LifecycleObject(handle, true) { Value = 2 });
Assert.Contains("H: " + handle.ToString("x") + " ", ex.Message);
GarbageCleanupFixture.ignoredExceptions.Add(handle);
#else #else
var inst2 = new LifecycleObject(handle, true) { Value = 2 }; var inst2 = new LifecycleObject(handle, true) { Value = 2 };
Assert.True(inst1.DestroyedNative); Assert.True(inst1.DestroyedNative);
@ -230,5 +231,79 @@ namespace SkiaSharp.Tests
throw new Exception("BREAK!"); throw new Exception("BREAK!");
} }
} }
[SkippableTheory]
[InlineData(1)]
[InlineData(1000)]
public async Task EnsureMultithreadingDoesNotThrow(int iterations)
{
var imagePath = Path.Combine(PathToImages, "baboon.jpg");
var tasks = new Task[iterations];
for (var i = 0; i < iterations; i++)
{
var task = new Task(() =>
{
using (var stream = File.OpenRead(imagePath))
using (var data = SKData.Create(stream))
using (var codec = SKCodec.Create(data))
{
var info = new SKImageInfo(codec.Info.Width, codec.Info.Height);
using (var image = SKBitmap.Decode(codec, info))
{
var img = new byte[image.Height, image.Width];
}
}
});
tasks[i] = task;
task.Start();
}
await Task.WhenAll(tasks);
}
[SkippableFact]
public void EnsureConcurrencyResultsInCorrectDeregistration()
{
var handle = (IntPtr)446;
var obj = new ImmediateRecreationObject(handle, true);
Assert.Null(obj.NewInstance);
Assert.Equal(obj, SKObject.instances[handle]?.Target);
obj.Dispose();
Assert.True(SKObject.GetInstance<ImmediateRecreationObject>(handle, out _));
var newObj = obj.NewInstance;
Assert.NotEqual(obj, SKObject.instances[handle]?.Target);
Assert.Equal(newObj, SKObject.instances[handle]?.Target);
newObj.Dispose();
Assert.False(SKObject.GetInstance<ImmediateRecreationObject>(handle, out _));
}
private class ImmediateRecreationObject : SKObject
{
public ImmediateRecreationObject(IntPtr handle, bool shouldRecreate)
: base(handle, true)
{
ShouldRecreate = shouldRecreate;
}
public bool ShouldRecreate { get; }
public ImmediateRecreationObject NewInstance { get; private set; }
protected override void DisposeNative()
{
base.DisposeNative();
if (ShouldRecreate)
NewInstance = new ImmediateRecreationObject(Handle, false);
}
}
} }
} }

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

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="Cake" version="0.33.0" /> <package id="Cake" version="0.34.1" />
</packages> </packages>