Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[mono] Implement System.Console for iOS #33827

Merged
merged 13 commits into from
Mar 21, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/libraries/Common/src/Interop/iOS/System.Native/Interop.Log.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Runtime.InteropServices;

internal static partial class Interop
{
internal static partial class Sys
{
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_Log")]
internal static extern unsafe bool Log(IntPtr buffer, int count);
}
}
10 changes: 10 additions & 0 deletions src/libraries/Native/Unix/System.Native/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ set(NATIVE_SOURCES
pal_sysctl.c
)

if (CLR_CMAKE_TARGET_IOS)
set(NATIVE_SOURCES ${NATIVE_SOURCES} ios/pal_sys.m)
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
else ()
set(NATIVE_SOURCES ${NATIVE_SOURCES} pal_console.c)
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
endif ()

if (CLR_CMAKE_TARGET_LINUX)
set(NATIVE_SOURCES ${NATIVE_SOURCES} pal_networkchange.c)

Expand Down Expand Up @@ -62,6 +68,10 @@ add_library(System.Native-Static
${NATIVE_SOURCES}
)

if (CLR_CMAKE_TARGET_IOS)
target_link_libraries(System.Native "-framework Foundation")
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
endif ()

set_target_properties(System.Native-Static PROPERTIES OUTPUT_NAME System.Native CLEAN_DIRECT_OUTPUT 1)

install (TARGETS System.Native-Static DESTINATION .)
11 changes: 11 additions & 0 deletions src/libraries/Native/Unix/System.Native/ios/pal_sys.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

EgorBo marked this conversation as resolved.
Show resolved Hide resolved
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#pragma once

#include "pal_compiler.h"
#include "pal_types.h"

PALEXPORT void SystemNative_Log(uint8_t* buffer, int32_t length);
57 changes: 57 additions & 0 deletions src/libraries/Native/Unix/System.Native/ios/pal_sys.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@

EgorBo marked this conversation as resolved.
Show resolved Hide resolved
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#include "pal_sys.h"
#import <Foundation/Foundation.h>

void SystemNative_Log (uint8_t* buffer, int32_t length)
{
// COOP: no managed memory access: any mode.
NSString *msg = [[NSString alloc] initWithBytes: buffer length: length encoding: NSUTF16LittleEndianStringEncoding];

// TODO: TARGET_OS_WATCH is not supported yet
#if TARGET_OS_WATCH && defined (__arm__)
const char* utf8 = [msg UTF8String];
size_t len = strlen (utf8);
fwrite (utf8, 1, len, stdout);
if (len == 0 || utf8 [len - 1] != '\n')
{
fwrite ("\n", 1, 1, stdout);
}
fflush (stdout);
#else
if (length > 4096)
{
// Write in chunks of max 4096 characters; older versions of iOS seems to have a bug where NSLog may hang with long strings (!).
// https://github.com/xamarin/maccore/issues/1014
const char* utf8 = [msg UTF8String];
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the issue it reproduces even on iOS 11 which is higher than the minimal version we support as far as I understand.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is Objective C? Do we have any other use of Objective C in System.Native? Is it required to build this for iOS?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably possible to avoid it but does it matter? It's iOS only and we most likely will need to bring more iOS specific pieces here for other APIs, most likely for globalization stuff

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's iOS only

How should I interpret that in conjunction with Marek's comments about the interop .cs file being moved to a general mac folder?

does it matter?

I don't know. I don't have a lot of experience with Objective C and whatever toolset requirements it might or might not place on the repo.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How should I interpret that in conjunction with Marek's comments about the interop .cs file being moved to a general mac folder?

Replied on the other thread, it should be some generic apple folder, not mac.

Objective C and whatever toolset requirements it might or might not place on the repo.

No dependencies, it's part of the default XCode toolchain you need on Mac for everything.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I guess next will be Environment.GetFolderPath https://github.com/xamarin/xamarin-macios/blob/3f0985ecac82b00aada1a41a464b15d1f58b6744/runtime/xamarin-support.m#L173-L182 so objc will be unavoidable (to call the native API).

size_t len = strlen (utf8);
const size_t max_size = 4096;
while (len > 0)
{
size_t chunk_size = len > max_size ? max_size : len;

// Try to not break in the middle of a line, by looking backwards for a newline
while (chunk_size > 0 && utf8 [chunk_size] != 0 && utf8 [chunk_size] != '\n')
{
chunk_size--;
}
if (chunk_size == 0)
{
// No newline found, break in the middle.
chunk_size = len > max_size ? max_size : len;
}
NSLog (@"%.*s", (int) chunk_size, utf8);
len -= chunk_size;
utf8 += chunk_size;
}
}
else
{
NSLog (@"%@", msg);
}
#endif
[msg release];
}
14 changes: 12 additions & 2 deletions src/libraries/System.Console/src/System.Console.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<RootNamespace>System.Console</RootNamespace>
<AssemblyName>System.Console</AssemblyName>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<TargetFrameworks>$(NetCoreAppCurrent)-Windows_NT;$(NetCoreAppCurrent)-Unix</TargetFrameworks>
<TargetFrameworks>$(NetCoreAppCurrent)-Windows_NT;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-iOS</TargetFrameworks>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
Expand All @@ -21,6 +21,16 @@
<Link>Common\System\Text\ConsoleEncoding.cs</Link>
</Compile>
</ItemGroup>
<!-- iOS -->
<ItemGroup Condition="'$(TargetsiOS)' == 'true'">
<Compile Include="System\ConsolePal.iOS.cs" />
<Compile Include="$(CommonPath)Interop\iOS\System.Native\Interop.Log.cs">
<Link>Common\Interop\iOS\Interop.iOS.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Unix\Interop.Libraries.cs">
<Link>Common\Interop\Unix\Interop.Libraries.cs</Link>
</Compile>
</ItemGroup>
<!-- Windows -->
<ItemGroup Condition="'$(TargetsWindows)' == 'true'">
<Compile Include="System\ConsolePal.Windows.cs" />
Expand Down Expand Up @@ -164,7 +174,7 @@
</Compile>
</ItemGroup>
<!-- Unix -->
<ItemGroup Condition=" '$(TargetsUnix)' == 'true'">
<ItemGroup Condition=" '$(TargetsUnix)' == 'true' AND '$(TargetsiOS)' != 'true'">
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
<Compile Include="System\ConsolePal.Unix.cs" />
<Compile Include="System\TermInfo.cs" />
<Compile Include="System\IO\StdInReader.cs" />
Expand Down
188 changes: 188 additions & 0 deletions src/libraries/System.Console/src/System/ConsolePal.iOS.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.IO;
using System.Text;

namespace System
{
internal class NSLogStream : Stream
{
public override void Flush() { }

public override int Read(byte[] buffer, int offset, int count) => throw new NotSupportedException();

public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();

public override void SetLength(long value) => throw new NotSupportedException();

public override unsafe void Write(byte[] buffer, int offset, int count)
{
if (count > 0 && buffer.Length >= offset + count)
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
{
fixed (byte* ptr = buffer)
{
Interop.Sys.Log((IntPtr)(ptr + offset), count);
}
}
}

public override bool CanRead => false;

public override bool CanSeek => false;

public override bool CanWrite => true;

public override long Length => throw new NotSupportedException();

public override long Position
{
get => throw new NotSupportedException();
set => throw new NotSupportedException();
}
}

internal static class ConsolePal
{
public static Stream OpenStandardInput() => throw new PlatformNotSupportedException();

public static Stream OpenStandardOutput() => new NSLogStream();

public static Stream OpenStandardError() => new NSLogStream();

public static Encoding InputEncoding => throw new PlatformNotSupportedException();

public static void SetConsoleInputEncoding(Encoding enc) => throw new PlatformNotSupportedException();

public static Encoding OutputEncoding => Encoding.Unicode;

public static void SetConsoleOutputEncoding(Encoding enc) => throw new PlatformNotSupportedException();

public static bool IsInputRedirectedCore() => throw new PlatformNotSupportedException();

public static bool IsOutputRedirectedCore() => throw new PlatformNotSupportedException();

public static bool IsErrorRedirectedCore() => throw new PlatformNotSupportedException();
EgorBo marked this conversation as resolved.
Show resolved Hide resolved

internal static TextReader GetOrCreateReader() => throw new PlatformNotSupportedException();

public static bool NumberLock => throw new PlatformNotSupportedException();

public static bool CapsLock => throw new PlatformNotSupportedException();

public static bool KeyAvailable => throw new PlatformNotSupportedException();

public static ConsoleKeyInfo ReadKey(bool intercept) => throw new PlatformNotSupportedException();

public static bool TreatControlCAsInput
{
get => throw new PlatformNotSupportedException();
set => throw new PlatformNotSupportedException();
}

public static ConsoleColor BackgroundColor
{
get => throw new PlatformNotSupportedException();
set => throw new PlatformNotSupportedException();
}

public static ConsoleColor ForegroundColor
{
get => throw new PlatformNotSupportedException();
set => throw new PlatformNotSupportedException();
}

public static void ResetColor() => throw new PlatformNotSupportedException();

public static int CursorSize
{
get => throw new PlatformNotSupportedException();
set => throw new PlatformNotSupportedException();
}

public static bool CursorVisible
{
get => throw new PlatformNotSupportedException();
set => throw new PlatformNotSupportedException();
}

public static int CursorLeft => throw new PlatformNotSupportedException();

public static int CursorTop => throw new PlatformNotSupportedException();

public static string Title
{
get => throw new PlatformNotSupportedException();
set => throw new PlatformNotSupportedException();
}

public static void Beep() => throw new PlatformNotSupportedException();

public static void Beep(int frequency, int duration) => throw new PlatformNotSupportedException();

public static void MoveBufferArea(int sourceLeft, int sourceTop,
int sourceWidth, int sourceHeight, int targetLeft, int targetTop,
char sourceChar, ConsoleColor sourceForeColor,
ConsoleColor sourceBackColor) => throw new PlatformNotSupportedException();

public static void Clear() => throw new PlatformNotSupportedException();

public static void SetCursorPosition(int left, int top) => throw new PlatformNotSupportedException();

public static int BufferWidth
{
get => throw new PlatformNotSupportedException();
set => throw new PlatformNotSupportedException();
}

public static int BufferHeight
{
get => throw new PlatformNotSupportedException();
set => throw new PlatformNotSupportedException();
}

public static void SetBufferSize(int width, int height) => throw new PlatformNotSupportedException();

public static int LargestWindowWidth => throw new PlatformNotSupportedException();

public static int LargestWindowHeight => throw new PlatformNotSupportedException();

public static int WindowLeft
{
get => throw new PlatformNotSupportedException();
set => throw new PlatformNotSupportedException();
}

public static int WindowTop
{
get => throw new PlatformNotSupportedException();
set => throw new PlatformNotSupportedException();
}

public static int WindowWidth
{
get => throw new PlatformNotSupportedException();
set => throw new PlatformNotSupportedException();
}

public static int WindowHeight
{
get => throw new PlatformNotSupportedException();
set => throw new PlatformNotSupportedException();
}

public static void SetWindowPosition(int left, int top) => throw new PlatformNotSupportedException();

public static void SetWindowSize(int width, int height) => throw new PlatformNotSupportedException();

internal sealed class ControlCHandlerRegistrar
{
internal ControlCHandlerRegistrar() => throw new PlatformNotSupportedException();

internal void Register() => throw new PlatformNotSupportedException();

internal void Unregister() => throw new PlatformNotSupportedException();
}
}
}
37 changes: 16 additions & 21 deletions src/mono/netcore/sample/iOS/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,25 @@ ARTIFACTS_MONO=$(ARTIFACTS_BIN)/mono/iOS.$(MONO_ARCH).$(MONO_CONFIG)
DOTNET := $(shell cd ../../ && bash init-tools.sh | tail -1)
SYSROOT := $(shell xcrun --sdk iphoneos --show-sdk-path)

BCL_LIBS = \
System.Runtime.dll \
System.Runtime.Extensions.dll \
System.Collections.dll \
System.Core.dll \
System.Threading.dll \
System.Threading.Tasks.dll \
System.Linq.dll \
System.Memory.dll \
System.Runtime.InteropServices.dll \
System.Text.Encoding.Extensions.dll \
Microsoft.Win32.Primitives.dll \
System.Console.dll

# once a new library is added here it should also be
# added in mono_ios_register_modules() (runtime.m)
all: prepare
make aot-lib-${MONO_ARCH} LIB=$(ARTIFACTS_MONO)/System.Private.CoreLib.dll
make aot-lib-${MONO_ARCH} LIB=$(ARTIFACTS_BCL)/System.Runtime.dll
make aot-lib-${MONO_ARCH} LIB=$(ARTIFACTS_BCL)/System.Runtime.Extensions.dll
make aot-lib-${MONO_ARCH} LIB=$(ARTIFACTS_BCL)/System.Collections.dll
make aot-lib-${MONO_ARCH} LIB=$(ARTIFACTS_BCL)/System.Core.dll
make aot-lib-${MONO_ARCH} LIB=$(ARTIFACTS_BCL)/System.Threading.dll
make aot-lib-${MONO_ARCH} LIB=$(ARTIFACTS_BCL)/System.Threading.Tasks.dll
make aot-lib-${MONO_ARCH} LIB=$(ARTIFACTS_BCL)/System.Linq.dll
make aot-lib-${MONO_ARCH} LIB=$(ARTIFACTS_BCL)/System.Memory.dll
make aot-lib-${MONO_ARCH} LIB=$(ARTIFACTS_BCL)/System.Runtime.InteropServices.dll
make aot-lib-${MONO_ARCH} LIB=$(ARTIFACTS_BCL)/System.Text.Encoding.Extensions.dll
make aot-lib-${MONO_ARCH} LIB=$(ARTIFACTS_BCL)/Microsoft.Win32.Primitives.dll
make aot-lib-${MONO_ARCH} LIB=$(ARTIFACTS_BCL)/System.Console.dll
for lib in $(BCL_LIBS); do make aot-lib-${MONO_ARCH} LIB=$(ARTIFACTS_BCL)/$$lib; done
make Program.dll.o

# recompile Program.cs AOT
Expand All @@ -34,15 +37,7 @@ Program.dll.o: bin/Program.dll Makefile
# we need to copy some BCL libs to ARTIFACTS_MONO
# to be able to aot other bcl libs
prepare:
cp $(ARTIFACTS_BCL)/System.Memory.dll $(ARTIFACTS_MONO)
cp $(ARTIFACTS_BCL)/System.Collections.dll $(ARTIFACTS_MONO)
cp $(ARTIFACTS_BCL)/System.Threading.dll $(ARTIFACTS_MONO)
cp $(ARTIFACTS_BCL)/System.Threading.Thread.dll $(ARTIFACTS_MONO)
cp $(ARTIFACTS_BCL)/System.Runtime.dll $(ARTIFACTS_MONO)
cp $(ARTIFACTS_BCL)/System.Runtime.InteropServices.dll $(ARTIFACTS_MONO)
cp $(ARTIFACTS_BCL)/System.Text.Encoding.Extensions.dll $(ARTIFACTS_MONO)
cp $(ARTIFACTS_BCL)/Microsoft.Win32.Primitives.dll $(ARTIFACTS_MONO)
cp $(ARTIFACTS_BCL)/System.Console.dll $(ARTIFACTS_MONO)
for lib in $(BCL_LIBS); do cp $(ARTIFACTS_BCL)/$$lib $(ARTIFACTS_MONO); done

bin/Program.dll: Program.cs
$(DOTNET) build -c Debug Program.csproj
Expand Down
Loading