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 12 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/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.
Copy link
Member

Choose a reason for hiding this comment

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

You put this under src/Interop/OSX. Is that correct? Or should there be an iOS-specific folder?

Copy link
Contributor

Choose a reason for hiding this comment

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

Sadly there is no general mac folder where this would be better placed (https://developer.apple.com/documentation/foundation/1395275-nslog) or is the intention to copy the interop to System.Native implementation for all OSes?

Copy link
Member

Choose a reason for hiding this comment

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

Sadly there is no general mac folder where this would be better placed

Meaning this API is available on any mac, whether OSX or iOS or watch or whatever? We can certainly create a folder for such APIs, assuming we're also making sure that the export it's targeting from System.Native is built on all such platforms. Or if we're only building in the export for iOS, then an iOS-specific folder would make more sense. Presumably in the fullness of time we'll find some APIs that are specific to iOS and so we'll want an iOS folder anyway?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'd move it to iOS honestly (just like in my previous commit) we are not going to use this API on OSX, only iOS/tvOS/watchOS which all are basically the same OS almost 🙂

Copy link
Contributor

@marek-safar marek-safar Mar 20, 2020

Choose a reason for hiding this comment

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

It's possible that we find some API which will be ios only but at the level, we operate it's most likely that PAL for ios, tvos, watchos, catalyst and some parts macOS will be identical

We could use ios name for modern apple or whatever is the better name but it might be confusing for the contributors

Copy link
Member

Choose a reason for hiding this comment

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

I'd use something generic like apple for the pieces that work across mac/ios/tvos/watchos.

Doesn't need to be done here though since there are a lot of cases where we'll need to rename the Interop/OSX folder.

Copy link
Member Author

Choose a reason for hiding this comment

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

so are we ok with OSX then?

Copy link
Member

Choose a reason for hiding this comment

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

I think so. It does not make sense to try to redo the directory structure in this PR.

// 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)
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 +67,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 .)
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];
Copy link
Member

Choose a reason for hiding this comment

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

How does error handling work in Objective C? Does this throw exceptions? What is the usual way to deal with it?

Copy link
Member Author

@EgorBo EgorBo Mar 20, 2020

Choose a reason for hiding this comment

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

initWithBytes returns nil for invalid data, then all messages ([obj message]) are no-op or return 0/nil/null. The only thing - strlen won't be ok with NULL, let me fix it.
NSLog is also fine with nil.

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 = 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
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
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