Skip to content

Commit

Permalink
[mono] Implement System.Console for iOS (#33827)
Browse files Browse the repository at this point in the history
* Add iOS version of System.Console
  • Loading branch information
EgorBo committed Mar 21, 2020
1 parent 6f0c10a commit 9c23058
Show file tree
Hide file tree
Showing 9 changed files with 284 additions and 27 deletions.
15 changes: 15 additions & 0 deletions src/libraries/Common/src/Interop/OSX/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(byte* buffer, int count);
}
}
11 changes: 10 additions & 1 deletion src/libraries/Native/Unix/System.Native/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
project(System.Native C)

set(NATIVE_SOURCES
pal_console.c
pal_errno.c
pal_interfaceaddresses.c
pal_io.c
Expand All @@ -23,6 +22,12 @@ set(NATIVE_SOURCES
pal_sysctl.c
)

if (CLR_CMAKE_TARGET_IOS)
set(NATIVE_SOURCES ${NATIVE_SOURCES} pal_log.m)
else ()
set(NATIVE_SOURCES ${NATIVE_SOURCES} pal_console.c)
endif ()

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

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

if (CLR_CMAKE_TARGET_IOS)
target_link_libraries(System.Native "-framework Foundation")
endif ()

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

install (TARGETS System.Native-Static DESTINATION .)
14 changes: 14 additions & 0 deletions src/libraries/Native/Unix/System.Native/pal_log.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// 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);

// Called by pal_signal.cpp to reinitialize the console on SIGCONT/SIGCHLD.
void ReinitializeTerminal(void) {}
void UninitializeTerminal(void) {}
42 changes: 42 additions & 0 deletions src/libraries/Native/Unix/System.Native/pal_log.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// 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_log.h"
#import <Foundation/Foundation.h>

void SystemNative_Log (uint8_t* buffer, int32_t length)
{
NSString *msg = [[NSString alloc] initWithBytes: buffer length: length encoding: NSUTF16LittleEndianStringEncoding];
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];
size_t len = utf8 == NULL ? 0 : 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);
}
[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\OSX\System.Native\Interop.Log.cs">
<Link>Common\Interop\OSX\Interop.Log.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'">
<Compile Include="System\ConsolePal.Unix.cs" />
<Compile Include="System\TermInfo.cs" />
<Compile Include="System\IO\StdInReader.cs" />
Expand Down
170 changes: 170 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,170 @@
// 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 sealed class NSLogStream : ConsoleStream
{
public NSLogStream() : base(FileAccess.Write) {}

public override int Read(byte[] buffer, int offset, int count) => throw Error.GetReadNotSupported();

public override unsafe void Write(byte[] buffer, int offset, int count)
{
ValidateWrite(buffer, offset, count);

fixed (byte* ptr = buffer)
{
Interop.Sys.Log(ptr + offset, count);
}
}
}

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;

// underlying API expects only utf-16
public static void SetConsoleOutputEncoding(Encoding enc) => throw new PlatformNotSupportedException();

public static bool IsInputRedirectedCore() => false;

public static bool IsOutputRedirectedCore() => false;

public static bool IsErrorRedirectedCore() => false;

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

public static bool NumberLock => false;

public static bool CapsLock => false;

public static bool KeyAvailable => false;

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
3 changes: 1 addition & 2 deletions src/mono/netcore/sample/iOS/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public static class Program

// Called by native code, see main.m
[MonoPInvokeCallback(typeof(Action))]
private static async void OnButtonClick()
private static void OnButtonClick()
{
ios_set_text("OnButtonClick! #" + counter++);
}
Expand All @@ -44,7 +44,6 @@ public static async Task Main(string[] args)
await Task.Delay(100);
}

// TODO: https://github.com/dotnet/runtime/issues/33667
Console.WriteLine("Done!");
}
}
Loading

0 comments on commit 9c23058

Please sign in to comment.