diff options
author | Zoltan Varga <vargaz@gmail.com> | 2017-12-19 17:04:44 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-12-19 17:04:44 +0300 |
commit | a2407ac76a50cc1b5fdfbea83adb135d9be40356 (patch) | |
tree | dfa460cbae693e9f9e286d6eefc0b0a159995edd /sdks/ios | |
parent | e9e17e069e6949600a382341e8888d7486553f21 (diff) |
IOS SDKs (#6257)
* [ios-sdk] Rename test-runner directory to 'runtime' to be consistent with xamarin.ios.
* [ios-sdk] Add missing xamarin_log () icall used by Console.Write ().
* [ios-sdk] Change the run-ios-sim target so it works on ios11 as well, the --console option to simctl appears to be broken.
* [ios-sdk] Add a test harness.
The test harness is resposible for starting the emulator, installing the app on the emulator, running it, and
collecting application output using the osx logging facilities.
* [ios-sdk] Fix detection of the app exiting with an unhandled exception.
* [ios-sdk] Add CI support for running some test suites on the simulator.
* [ios-sdk] Kill the log reader process on shutdown.
* [ios-sdk] Disable android on CI because the make targets error out if the android sdk is not installed.
* [ios-sdk] Fix error checking in make targets.
* [ios-sdk] Remove accidently commited binary.
* [ios-sdk] Switch to using os_log instead of NSLog (), the latter doesn't seem to show up in the logs anymore. Switch to using the default ios sdk instead of hardcoding 11.1.
* [ios-sdk] Don't hardcode the ios version when creating a simulator.
* [ios-sdk] Parse the output of simctl list runtimes as json.
* [ios-sdk] Use 'syslog' style logging, its supported by older osx versions.
* [ios-sdk] Fix logfile parsing.
* [ios-sdk] Fix log filtering.
* [ios-sdk] Add test exclusions file for System.
* [ios-sdk] Redirect runtime logging to os_log ().
* [ios-sdk] Link libMonoPosixHelper and zlib into the test runner.
* [ios-sdk] Fix the target64 and cross64 builds.
* [ios-sdk] Build target64/cross64 on CI.
* [ios-sdk] Add device support to the runtime lib. Mark strings in os_log () calls as public.
* [ios-sdk] Log stdout messages using a separate subsystem for easier filtering.
* [ios-sdk] Add an 'appbuilder' tool which can generate simulator and device apps.
* [ios-sdk] Build device tests on CI.
* [ios-sdk] Add a test-runner instead of using nunit-lite-console.exe. The two are identical right now, but they might diverge in the future.
* [ios-sdk] Add a script to download prebuild llvm binaries.
* [ios-sdk] Run the runtime initialization on a separate thread, not the UI thread.
* [ios-sdks] Fix the build.
* [ios-sdk] Build the cross compiler against llvm.
* [ios-sdk] Add missing file.
* [ios-sdk] Add llvm support, enabled by passing LLVM=1 to make build-ios-dev-<app>.
* [bcl] Enable System.Security tests on the MOBILE profile.
* [ios-sdk] Enable more test suites.
* [ios-sdk] Fix device builds.
* [ios-sdk] Make some test steps non-fatal.
* [ios-dev] Use ad-hoc signing to sign device apps.
* [ios-sdk] Decrease the timeout on run-sim to 20m.
* [ios-sdk] Send back the test results using a tcp connection, parsing the ios logs is too fragile.
* [ios-sdk] Mark System.Net.Http tests as notworking, they seem to fail on CI.
* [ios-sdk] Compile the 32 bit cross compiler against llvm as well.
* [ios-sdk] Package up the binaries which can be uploaded to storage.
* [ios-sdk] Fix the download-llvm.sh script.
* [ios-sdk] Compile cross32 on CI.
* [ios-sdk] Use Options.cs from the mcs sources instead of making a copy.
* [ios-sdk] Avoid hardcoding the xcode sysroot, obtain it using 'xcode-select -p'.
* [ios-sdk] Fix the previous change.
* [ios-sdk] Add an option to appbuilder to cache aot compilation results between building different apps.
* [ios-sdk] Build device apps with llvm as well on CI.
* [ios-skd] Use XCODE_ROOT in a few more places, update README.md.
Diffstat (limited to 'sdks/ios')
-rw-r--r-- | sdks/ios/Makefile | 202 | ||||
-rw-r--r-- | sdks/ios/README.md | 36 | ||||
-rw-r--r-- | sdks/ios/appbuilder/appbuilder.cs | 247 | ||||
-rw-r--r-- | sdks/ios/harness/harness.cs | 196 | ||||
-rw-r--r-- | sdks/ios/runtime/AppDelegate.h (renamed from sdks/ios/test-runner/AppDelegate.h) | 0 | ||||
-rw-r--r-- | sdks/ios/runtime/AppDelegate.m (renamed from sdks/ios/test-runner/AppDelegate.m) | 0 | ||||
-rw-r--r-- | sdks/ios/runtime/Base.lproj/LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib (renamed from sdks/ios/test-runner/Base.lproj/LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib) | bin | 1136 -> 1136 bytes | |||
-rw-r--r-- | sdks/ios/runtime/Base.lproj/LaunchScreen.storyboardc/Info.plist (renamed from sdks/ios/test-runner/Base.lproj/LaunchScreen.storyboardc/Info.plist) | bin | 258 -> 258 bytes | |||
-rw-r--r-- | sdks/ios/runtime/Base.lproj/LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib (renamed from sdks/ios/test-runner/Base.lproj/LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib) | bin | 832 -> 832 bytes | |||
-rw-r--r-- | sdks/ios/runtime/Base.lproj/Main.storyboardc/BYZ-38-t0r-view-8bC-Xf-vdC.nib (renamed from sdks/ios/test-runner/Base.lproj/Main.storyboardc/BYZ-38-t0r-view-8bC-Xf-vdC.nib) | bin | 1136 -> 1136 bytes | |||
-rw-r--r-- | sdks/ios/runtime/Base.lproj/Main.storyboardc/Info.plist (renamed from sdks/ios/test-runner/Base.lproj/Main.storyboardc/Info.plist) | bin | 258 -> 258 bytes | |||
-rw-r--r-- | sdks/ios/runtime/Base.lproj/Main.storyboardc/UIViewController-BYZ-38-t0r.nib (renamed from sdks/ios/test-runner/Base.lproj/Main.storyboardc/UIViewController-BYZ-38-t0r.nib) | bin | 916 -> 916 bytes | |||
-rw-r--r-- | sdks/ios/runtime/Entitlements.xcent | 12 | ||||
-rw-r--r-- | sdks/ios/runtime/Info.plist.in (renamed from sdks/ios/test-runner/Info.plist.in) | 0 | ||||
-rw-r--r-- | sdks/ios/runtime/Makefile | 74 | ||||
-rw-r--r-- | sdks/ios/runtime/ViewController.h (renamed from sdks/ios/test-runner/ViewController.h) | 0 | ||||
-rw-r--r-- | sdks/ios/runtime/ViewController.m (renamed from sdks/ios/test-runner/ViewController.m) | 8 | ||||
-rw-r--r-- | sdks/ios/runtime/main.m (renamed from sdks/ios/test-runner/main.m) | 0 | ||||
-rw-r--r-- | sdks/ios/runtime/runtime.h (renamed from sdks/ios/test-runner/runtime.h) | 0 | ||||
-rw-r--r-- | sdks/ios/runtime/runtime.m (renamed from sdks/ios/test-runner/runtime.m) | 171 | ||||
-rw-r--r-- | sdks/ios/test-runner/Makefile | 29 | ||||
-rw-r--r-- | sdks/ios/test-runner/runner.cs | 72 |
22 files changed, 970 insertions, 77 deletions
diff --git a/sdks/ios/Makefile b/sdks/ios/Makefile index fe168f9ade8..f6cc556691f 100644 --- a/sdks/ios/Makefile +++ b/sdks/ios/Makefile @@ -1,53 +1,153 @@ -all: +all: harness.exe appbuilder.exe test-runner.exe BCL_DIR = ../../mcs/class/lib/monotouch +XCODE_ROOT=$(shell xcode-select -p) +SYSROOT=$(XCODE_ROOT)/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk + +TEST_SUITES = \ + Mono.Runtime.Tests \ + corlib \ + System.Core \ + System.Data \ + System.Numerics \ + System.Runtime.Serialization \ + System.Transactions \ + System.IO.Compression \ + System.IO.Compression.FileSystem \ + System.Json \ + System.ComponentModel.DataAnnotations \ + System.Security \ + System.Xml.Linq \ + System.ServiceModel.Web \ + Mono.Data.Tds \ + Mono.Security + +TEST_SUITES_NOTWORKING = \ + System \ + System.Net.Http \ + System.Web.Services \ + System.Xml \ + Mono.Data.Sqlite \ + Mono.CSharp \ + +SIM_NAME = xamarin.ios-sdk.sim + +OPTIONS_CS = ../../mcs/class/Mono.Options//Mono.Options/Options.cs + +harness.exe: harness/harness.cs $(OPTIONS_CS) + csc /out:$@ -r:System.Json.dll $^ + +appbuilder.exe: appbuilder/appbuilder.cs $(OPTIONS_CS) + csc /out:$@ $^ + +test-runner.exe: test-runner/runner.cs + csc /out:$@ -r:$(BCL_DIR)/nunitlite.dll test-runner/runner.cs + +runtime/runtime: + make -C runtime + +runtime/libmonoios.a: + make -C runtime # Build % from assemblies %_ASSEMBLIES # The end result is in bin/ios-sim/test-%.app -build-ios-sim-%: - make -C test-runner/ - rm -rf tmp.app - mkdir -p tmp.app - cp test-runner/test-runner tmp.app/test-$* - cp test-runner/Info.plist.in tmp.app/1 - sed -e "s/BUNDLE_IDENTIFIER/com.xamarin.mono.ios.test-$*/g" < tmp.app/1 > tmp.app/2 - sed -e "s/BUNDLE_EXECUTABLE/test-$*/g" < tmp.app/2 > tmp.app/3 - sed -e "s/BUNDLE_NAME/test-$*/g" < tmp.app/3 > tmp.app/Info.plist - rm -f tmp.app/{1,2,3} - plutil -convert binary1 tmp.app/Info.plist - cp -r test-runner/Base.lproj tmp.app - cp -r $(BCL_DIR)/{mscorlib.dll,System.dll,System.Xml.dll,System.Core.dll,I18N.dll,I18N.West.dll,Mono.Simd.dll,System.Numerics.dll,System.Numerics.Vectors.dll,nunitlite.dll,nunit-lite-console.exe} $($*_ASSEMBLIES) $($*_EXTRA_FILES) tmp.app - /usr/bin/codesign --force --sign - --timestamp=none tmp.app - mkdir -p bin/ios-sim - rm -rf bin/ios-sim/test-$*.app - mv tmp.app bin/ios-sim/test-$*.app -# Install % on the simulator -install-ios-sim-%: - xcrun simctl install booted bin/ios-sim/test-$*.app +TEST_ASSEMBLIES = $(BCL_DIR)/mscorlib.dll \ + $(BCL_DIR)/System.dll \ + $(BCL_DIR)/System.Xml.dll \ + $(BCL_DIR)/System.Core.dll \ + $(BCL_DIR)/I18N.dll \ + $(BCL_DIR)/I18N.West.dll \ + $(BCL_DIR)/Mono.Simd.dll \ + $(BCL_DIR)/Mono.Security.dll \ + $(BCL_DIR)/System.Numerics.dll \ + $(BCL_DIR)/System.Numerics.Vectors.dll \ + $(BCL_DIR)/nunitlite.dll \ + test-runner.exe + +build-ios-sim-%: appbuilder.exe test-runner.exe runtime/runtime $($*_ASSEMBLIES) + mono appbuilder.exe --target ios-sim64 --mono-sdkdir $(PWD)/../out --appdir $(PWD)/bin/ios-sim/test-$*.app --runtimedir $(PWD)/runtime --builddir obj/ios-sim/test-$*.app --sysroot $(SYSROOT) --bundle-executable test-$* --bundle-identifier com.xamarin.mono.ios.test-$* --bundle-name test-$* $(patsubst %,-r %,$(TEST_ASSEMBLIES) $($*_ASSEMBLIES)) + mkdir -p $(PWD)/bin/ios-sim/test-$*.app + if test "x$($*_EXTRA_FILES)" != "x"; then cp -r $($*_EXTRA_FILES) $(PWD)/bin/ios-sim/test-$*.app/; fi + ninja -C obj/ios-sim/test-$*.app -v + +ifdef LLVM +APPBUILDER_ARGS += --llvm +endif + +# +# This enables caching of aot outputs between different apps. +# Changes to the assemblies/runtimes etc. are not detected, so this should only +# be used when compiling multiple apps with the same assemblies, i.e. the -all +# targets. +# +ifdef ENABLE_AOT_CACHE +APPBUILDER_ARGS += --aot-cachedir $(PWD)/aot-cache +endif + +build-ios-dev-%: appbuilder.exe test-runner.exe runtime/libmonoios.a $($*_ASSEMBLIES) + mkdir -p $(PWD)/aot-cache + mono appbuilder.exe $(APPBUILDER_ARGS) --target ios-dev64 --mono-sdkdir $(PWD)/../out --appdir $(PWD)/bin/ios-dev/test-$*.app --runtimedir $(PWD)/runtime --builddir obj/ios-dev/test-$*.app --sysroot $(SYSROOT) --bundle-executable test-$* --bundle-identifier com.xamarin.mono.ios.test-$* --bundle-name test-$* --exe test-runner.exe $(patsubst %,-r %,$(TEST_ASSEMBLIES) $($*_ASSEMBLIES)) + mkdir -p $(PWD)/bin/ios-dev/test-$*.app + if test "x$($_EXTRA_FILES)" != "x"; then cp -r $($*_EXTRA_FILES) $(PWD)/bin/ios-dev/test-$*.app/; fi + ninja -C obj/ios-dev/test-$*.app -v # Clean % clean-ios-sim-%: - $(RM) -rf bin/ios-sim/test-$*.app + $(RM) -rf obj/ios-sim/test-$*.app bin/ios-sim/test-$*.app -# Run % on the simulator with args $(ARGS) %_ARGS -# 'launch' doesn't propagate the error code -run-ios-sim-%: - xcrun simctl terminate booted com.xamarin.mono.ios.test-$* - xcrun simctl launch --console booted com.xamarin.mono.ios.test-$* $(ARGS) $($*_ARGS) +clean-ios-dev-%: + $(RM) -rf obj/ios-dev/test-$*.app bin/ios-dev/test-$*.app + +# Install and run % on the simulator with args $(ARGS) %_ARGS +run-ios-sim-%: harness.exe + mono harness.exe --start-sim + xcrun simctl install $(SIM_NAME) bin/ios-sim/test-$*.app + mono harness.exe --run-sim --logfile ios-sim-$*.log --bundle-id com.xamarin.mono.ios.test-$* --bundle-dir bin/ios-sim/test-$*.app $(ARGS) $($*_ARGS) clean: - $(MAKE) -C test-runner clean - $(RM) -rf bin + $(MAKE) -C runtime clean + $(RM) -rf bin obj *.exe *.log aot-cache + +# Compile all the test assemblies +compile-tests: + for suite in $(TEST_SUITES); do make -C ../../mcs/class/$$suite PROFILE=monotouch test || exit 1; done + +run-ios-sim-all: + for suite in $(TEST_SUITES); do make build-ios-sim-$$suite || exit 1; make run-ios-sim-$$suite || exit 1; done + +build-ios-dev-all: + rm -rf aot-cache + for suite in $(TEST_SUITES); do make clean-ios-dev-$$suite; make build-ios-dev-$$suite ENABLE_AOT_CACHE=1 || exit 1; done + +build-ios-dev-llvm-all: + rm -rf aot-cache + for suite in $(TEST_SUITES); do echo "*** $$suite ***"; make clean-ios-dev-$$suite; make build-ios-dev-$$suite LLVM=1 ENABLE_AOT_CACHE=1 || exit 1; done + +# Developer targets, ignore +# 'launch' doesn't propagate the error code +# With ios11, --console doesn't work any more, it makes the app deadlock +# Install % on the simulator +install-ios-sim-%: + xcrun simctl install $(SIM_NAME) bin/ios-sim/test-$*.app + +run-ios-sim-direct-%: + xcrun simctl terminate $(SIM_NAME) com.xamarin.mono.ios.test-$* + xcrun simctl launch $(SIM_NAME) com.xamarin.mono.ios.test-$* $(ARGS) $($*_ARGS) + log stream --level debug --predicate 'senderImagePath contains "$*"' --style compact + +create-sim: + xcrun simctl create $(SIM_NAME) 'iPhone 7' com.apple.CoreSimulator.SimRuntime.iOS-11-1 start-sim: - xcrun simctl boot 94619BF8-ED9C-4A14-BA8C-6A22140ABA2C + xcrun simctl boot $(SIM_NAME) stop-sim: - xcrun simctl shutdown booted + xcrun simctl shutdown $(SIM_NAME) -NUNIT = nunit-lite-console.exe -exclude:MobileNotWorking,NotOnMac,NotWorking,ValueAdd,CAS,InetAccess,NotWorkingLinqInterpreter -labels +# CONNSTR will be replace by the harness with the real connection string +NUNIT = test-runner.exe CONNSTR -exclude:MobileNotWorking,NotOnMac,NotWorking,ValueAdd,CAS,InetAccess,NotWorkingLinqInterpreter -labels TESTDIR = $(BCL_DIR)/tests # Options for each test @@ -58,8 +158,44 @@ corlib_EXTRA_FILES = ../../mcs/class/corlib/{es-ES,nn-NO} corlib_ARGS = $(NUNIT) monotouch_corlib_test.dll System.Core_ASSEMBLIES = $(TESTDIR)/monotouch_System.Core_test.dll System.Core_ARGS = $(NUNIT) monotouch_System.Core_test.dll -System_ASSEMBLIES = $(TESTDIR)/monotouch_System_test.dll +System_ASSEMBLIES = $(TESTDIR)/monotouch_System_test.dll $(BCL_DIR)/Mono.Security.dll System_ARGS = $(NUNIT) monotouch_System_test.dll System.Data_ASSEMBLIES = $(BCL_DIR)/System.Data.dll $(BCL_DIR)/System.Transactions.dll $(TESTDIR)/monotouch_System.Data_test.dll System.Data_EXTRA_FILES = ../../mcs/class/System.Data/Test System.Data_ARGS = $(NUNIT) monotouch_System.Data_test.dll +System.Net.Http_ASSEMBLIES = $(BCL_DIR)/System.Net.Http.dll $(TESTDIR)/monotouch_System.Net.Http_test.dll +System.Net.Http_ARGS = $(NUNIT) monotouch_System.Net.Http_test.dll +System.Numerics_ASSEMBLIES = $(TESTDIR)/monotouch_System.Numerics_test.dll +System.Numerics_ARGS = $(NUNIT) monotouch_System.Numerics_test.dll +System.Runtime.Serialization_ASSEMBLIES = $(BCL_DIR)/System.Runtime.Serialization.dll $(BCL_DIR)/System.ServiceModel.dll $(BCL_DIR)/System.ServiceModel.Internals.dll $(TESTDIR)/monotouch_System.Runtime.Serialization_test.dll +System.Runtime.Serialization_ARGS = $(NUNIT) monotouch_System.Runtime.Serialization_test.dll +System.Transactions_ASSEMBLIES = $(BCL_DIR)/System.Transactions.dll $(TESTDIR)/monotouch_System.Transactions_test.dll +System.Transactions_ARGS = $(NUNIT) monotouch_System.Transactions_test.dll +System.IO.Compression_ASSEMBLIES = $(BCL_DIR)/System.IO.Compression.dll $(TESTDIR)/monotouch_System.IO.Compression_test.dll +System.IO.Compression_ARGS = $(NUNIT) monotouch_System.IO.Compression_test.dll +System.IO.Compression_EXTRA_FILES = ../../mcs/class/System.IO.Compression/{archive.zip,test.nupkg} +System.IO.Compression.FileSystem_ASSEMBLIES = $(BCL_DIR)/System.IO.Compression.FileSystem.dll $(BCL_DIR)/System.IO.Compression.dll $(TESTDIR)/monotouch_System.IO.Compression.FileSystem_test.dll +System.IO.Compression.FileSystem_ARGS = $(NUNIT) monotouch_System.IO.Compression.FileSystem_test.dll +System.IO.Compression.FileSystem_EXTRA_FILES = ../../mcs/class/System.IO.Compression.FileSystem/foo +Mono.CSharp_ASSEMBLIES = $(BCL_DIR)/Mono.CSharp.dll $(TESTDIR)/monotouch_Mono.CSharp_test.dll +Mono.CSharp_ARGS = $(NUNIT) monotouch_Mono.CSharp_test.dll +System.Json_ASSEMBLIES = $(BCL_DIR)/System.Json.dll $(TESTDIR)/monotouch_System.Json_test.dll +System.Json_ARGS = $(NUNIT) monotouch_System.Json_test.dll +System.ComponentModel.DataAnnotations_ASSEMBLIES = $(BCL_DIR)/System.ComponentModel.DataAnnotations.dll $(TESTDIR)/monotouch_System.ComponentModel.DataAnnotations_test.dll +System.ComponentModel.DataAnnotations_ARGS = $(NUNIT) monotouch_System.ComponentModel.DataAnnotations_test.dll +Mono.Data.Sqlite_ASSEMBLIES = $(BCL_DIR)/Mono.Data.Sqlite.dll $(TESTDIR)/monotouch_Mono.Data.Sqlite_test.dll +Mono.Data.Sqlite_ARGS = $(NUNIT) monotouch_Mono.Data.Sqlite_test.dll +Mono.Data.Tds_ASSEMBLIES = $(BCL_DIR)/Mono.Data.Tds.dll $(TESTDIR)/monotouch_Mono.Data.Tds_test.dll +Mono.Data.Tds_ARGS = $(NUNIT) monotouch_Mono.Data.Tds_test.dll +System.Security_ASSEMBLIES = $(BCL_DIR)/System.Security.dll $(TESTDIR)/monotouch_System.Security_test.dll +System.Security_ARGS = $(NUNIT) monotouch_System.Security_test.dll +System.Xml_ASSEMBLIES = $(BCL_DIR)/System.Xml.dll $(TESTDIR)/monotouch_System.Xml_test.dll +System.Xml_ARGS = $(NUNIT) monotouch_System.Xml_test.dll +System.Xml.Linq_ASSEMBLIES = $(BCL_DIR)/System.Xml.Linq.dll $(TESTDIR)/monotouch_System.Xml.Linq_test.dll +System.Xml.Linq_ARGS = $(NUNIT) monotouch_System.Xml.Linq_test.dll +Mono.Security_ASSEMBLIES = $(TESTDIR)/monotouch_Mono.Security_test.dll +Mono.Security_ARGS = $(NUNIT) monotouch_Mono.Security_test.dll +System.ServiceModel.Web_ASSEMBLIES = $(BCL_DIR)/System.ServiceModel.Web.dll $(BCL_DIR)/System.ServiceModel.dll $(BCL_DIR)/System.ServiceModel.Internals.dll $(BCL_DIR)/System.IdentityModel.dll $(BCL_DIR)/System.Runtime.Serialization.dll $(TESTDIR)/monotouch_System.ServiceModel.Web_test.dll +System.ServiceModel.Web_ARGS = $(NUNIT) monotouch_System.ServiceModel.Web_test.dll +System.Web.Services_ASSEMBLIES = $(BCL_DIR)/System.Web.Services.dll $(TESTDIR)/monotouch_System.Web.Services_test.dll +System.Web.Services_ARGS = $(NUNIT) monotouch_System.Web.Services_test.dll diff --git a/sdks/ios/README.md b/sdks/ios/README.md index 2490d0dc1b2..bcd89f2a8e4 100644 --- a/sdks/ios/README.md +++ b/sdks/ios/README.md @@ -3,6 +3,16 @@ The test runner is an objective-c app which embeds the runtime. It has a command line interface similar to the mono runtime, i.e. <exe> <arguments>. +# The test harness + +The test harness is a C# app which automates running a test suite. It +install the app on the simulator, runs it, and collects output into +a log file. + +# The app builder + +This is a C# app which is used to create an ios .app bundle. + # Make targets Similar to the ones in xamarin-macios/tests @@ -22,7 +32,33 @@ Similar to the ones in xamarin-macios/tests * Where * -sim- + * -dev- * Project * corlib etc. + +The test apps require the corresponding test assembly to be already +built, i.e. by running make PROFILE=monotouch test in mcs/class/corlib +etc. + +# Running tests on device + +This is currently not implemented. + +The app can be started on the device by using ios-deploy (https://github.com/phonegap/ios-deploy): +`ios-deploy -d -b bin/ios-dev/test-Mono.Runtime.Tests.app/ -d -a 'nunit-lite-console.exe bin/ios-dev/test-Mono.Runtime.Tests.app/monotouch_Mono.Runtime.Tests_test.dll -labels'` + +Getting test results from the app is more complicated. Some possible approaches: +* Using the ios os_log facility. Unfortunately, the command line 'log' tool cannot +seem to read the device logs, only the graphical Console app can. +* Using `idevicesyslog` from `libimobiledevice`. +* Have the app send back results using a tcp connection. This requires starting a +server from the test harness, and passing the address to the app using a command line +option. It also requires the device and the host to be on the same network. +* Have the app listen on a port, and have the test harness connect to it using +`libimobiledevice`, i.e. https://github.com/rsms/peertalk. +* Use a publish-subscribe pattern by uploading test results to some cloud service like +Azure EventHub. This only requires client side internet access on the device and +the test harness. + diff --git a/sdks/ios/appbuilder/appbuilder.cs b/sdks/ios/appbuilder/appbuilder.cs new file mode 100644 index 00000000000..bd8f6bf5247 --- /dev/null +++ b/sdks/ios/appbuilder/appbuilder.cs @@ -0,0 +1,247 @@ +using System; +using System.IO; +using System.Reflection; +using System.Collections.Generic; +using Mono.Options; + +public class AppBuilder +{ + public static void Main (String[] args) { + new AppBuilder ().Run (args); + } + + void GenMain (string builddir, List<string> assembly_names) { + var symbols = new List<string> (); + foreach (var img in assembly_names) { + symbols.Add (String.Format ("mono_aot_module_{0}_info", img.Replace ('.', '_').Replace ('-', '_'))); + } + + var w = File.CreateText (Path.Combine (builddir, "main.m")); + + w.WriteLine ($"extern void mono_aot_register_module (char *name);"); + + foreach (var symbol in symbols) { + w.WriteLine ($"extern void *{symbol};"); + } + + w.WriteLine (); + w.WriteLine ("void mono_ios_register_modules (void)"); + w.WriteLine ("{"); + foreach (var symbol in symbols) { + w.WriteLine ($"\tmono_aot_register_module ({symbol});"); + } + w.WriteLine ("}"); + w.Close (); + } + + void check_mandatory (string val, string name) { + if (val == null) { + Console.Error.WriteLine ($"The {name} argument is mandatory."); + Environment.Exit (1); + } + } + + void Run (String[] args) { + string target = null; + string appdir = null; + string builddir = null; + string runtimedir = null; + string mono_sdkdir = null; + string bundle_identifier = null; + string bundle_name = null; + string bundle_executable = null; + string sysroot = null; + string aotdir = null; + string exe = null; + bool isdev = false; + bool isrelease = false; + bool isllvm = false; + var assemblies = new List<string> (); + var p = new OptionSet () { + { "target=", s => target = s }, + { "appdir=", s => appdir = s }, + { "builddir=", s => builddir = s }, + { "runtimedir=", s => runtimedir = s }, + { "mono-sdkdir=", s => mono_sdkdir = s }, + { "sysroot=", s => sysroot = s }, + { "aot-cachedir=", s => aotdir = s }, + { "bundle-identifier=", s => bundle_identifier = s }, + { "bundle-name=", s => bundle_name = s }, + { "bundle-executable=", s => bundle_executable = s }, + { "llvm", s => isllvm = true }, + { "exe=", s => exe = s }, + { "r=", s => assemblies.Add (s) }, + }; + + var new_args = p.Parse (args).ToArray (); + + check_mandatory (target, "--target"); + check_mandatory (runtimedir, "--runtimedir"); + check_mandatory (appdir, "--appdir"); + check_mandatory (mono_sdkdir, "--mono-sdkdir"); + check_mandatory (sysroot, "--sysroot"); + + switch (target) { + case "ios-dev64": + isdev = true; + break; + case "ios-sim64": + break; + default: + Console.WriteLine ($"Possible values for the '--target=' argument are 'ios-dev64', 'ios-sim64', got {target}."); + Environment.Exit (1); + break; + } + + if (isllvm) + isrelease = true; + + string aot_args = ""; + string cross_runtime_args = ""; + if (!isrelease) + aot_args = "soft-debug"; + if (isllvm) { + cross_runtime_args = "--llvm"; + aot_args = ",llvm-path=$mono_sdkdir/llvm64/bin,llvm-outfile=$llvm_outfile"; + } + + Directory.CreateDirectory (builddir); + + // Create Info.plist file + var lines = File.ReadAllLines (Path.Combine (runtimedir, "Info.plist.in")); + for (int i = 0; i < lines.Length; ++i) { + string line = lines [i]; + line = line.Replace ("BUNDLE_IDENTIFIER", bundle_identifier); + line = line.Replace ("BUNDLE_EXECUTABLE", bundle_executable); + line = line.Replace ("BUNDLE_NAME", bundle_name); + line = line.Replace ("PLATFORM", isdev ? "iPhoneOS" : "iPhoneSimulator"); + lines [i] = line; + } + File.WriteAllLines (Path.Combine (builddir, "Info.plist"), lines); + + // Create config.json file + string config = "{ \"exe\" : \"" + exe + "\" }"; + File.WriteAllLines (Path.Combine (builddir, "config.json"), new string [] { config }); + + var ninja = File.CreateText (Path.Combine (builddir, "build.ninja")); + + // Defines + ninja.WriteLine ($"mono_sdkdir = {mono_sdkdir}"); + ninja.WriteLine ($"monoios_dir = {runtimedir}"); + ninja.WriteLine ($"appdir = {appdir}"); + ninja.WriteLine ($"sysroot = {sysroot}"); + ninja.WriteLine ("cross = $mono_sdkdir/ios-cross64/bin/aarch64-darwin-mono-sgen"); + ninja.WriteLine ($"builddir = ."); + if (aotdir != null) + ninja.WriteLine ($"aotdir = {aotdir}"); + // Rules + ninja.WriteLine ("rule aot"); + ninja.WriteLine ($" command = MONO_PATH=$mono_path $cross -O=gsharedvt,float32 --debug {cross_runtime_args} --aot=mtriple=arm64-ios,full,static,asmonly,direct-icalls,no-direct-calls,dwarfdebug,{aot_args},outfile=$outfile,data-outfile=$data_outfile $src_file"); + ninja.WriteLine (" description = [AOT] $src_file -> $outfile"); + // ninja remakes files it hadn't seen before even if the timestamp is newer, so have to add a test ourselves + ninja.WriteLine ("rule aot-cached"); + ninja.WriteLine ($" command = if ! test -f $outfile; then MONO_PATH=$mono_path $cross -O=gsharedvt,float32 --debug {cross_runtime_args} --aot=mtriple=arm64-ios,full,static,asmonly,direct-icalls,no-direct-calls,dwarfdebug,{aot_args},outfile=$outfile,data-outfile=$data_outfile $src_file; fi"); + ninja.WriteLine (" description = [AOT] $src_file -> $outfile"); + ninja.WriteLine ("rule assemble"); + ninja.WriteLine (" command = clang -isysroot $sysroot -miphoneos-version-min=10.1 -arch arm64 -c -o $out $in"); + ninja.WriteLine (" description = [ASM] $in -> $out"); + ninja.WriteLine ("rule assemble-cached"); + ninja.WriteLine (" command = if ! test -f $out; then clang -isysroot $sysroot -miphoneos-version-min=10.1 -arch arm64 -c -o $out $in; fi"); + ninja.WriteLine (" description = [ASM] $in -> $out"); + ninja.WriteLine ("rule cp"); + ninja.WriteLine (" command = cp $in $out"); + ninja.WriteLine (" description = [CP] $in -> $out"); + ninja.WriteLine ("rule cp-recursive"); + ninja.WriteLine (" command = cp -r $in $out"); + ninja.WriteLine ("rule cpifdiff"); + ninja.WriteLine (" command = if cmp -s $in $out ; then : ; else cp $in $out ; fi"); + ninja.WriteLine (" restat = true"); + ninja.WriteLine ("rule plutil"); + ninja.WriteLine (" command = cp $in $out; plutil -convert binary1 $out"); + ninja.WriteLine ("rule codesign"); + ninja.WriteLine (" command = codesign -v --force --sign - --entitlements $entitlements --timestamp=none $in"); + ninja.WriteLine ("rule codesign-sim"); + ninja.WriteLine (" command = codesign --force --sign - --timestamp=none $in"); + ninja.WriteLine ("rule mkdir"); + ninja.WriteLine (" command = mkdir -p $out"); + ninja.WriteLine ("rule compile-objc"); + ninja.WriteLine (" command = clang -isysroot $sysroot -miphoneos-version-min=10.1 -arch arm64 -c -o $out $in"); + ninja.WriteLine ("rule gen-exe"); + ninja.WriteLine (" command = mkdir $appdir"); + ninja.WriteLine ($" command = clang -ObjC -isysroot $sysroot -miphoneos-version-min=10.1 -arch arm64 -framework Foundation -framework UIKit -o $appdir/{bundle_executable} $in -liconv"); + + var ofiles = ""; + var assembly_names = new List<string> (); + foreach (var assembly in assemblies) { + string filename = Path.GetFileName (assembly); + var filename_noext = Path.GetFileNameWithoutExtension (filename); + + File.Copy (assembly, Path.Combine (builddir, filename), true); + if (aotdir != null && !File.Exists (Path.Combine (aotdir, filename))) + /* Don't overwrite to avoid changing the timestamp */ + File.Copy (assembly, Path.Combine (aotdir, filename), false); + + ninja.WriteLine ($"build $appdir/{filename}: cpifdiff $builddir/{filename}"); + if (isdev) { + string destdir = null; + string srcfile = null; + string assemble_rule = null; + if (aotdir != null) { + destdir = "$aotdir"; + srcfile = Path.Combine (aotdir, filename); + assemble_rule = "assemble-cached"; + } else { + destdir = "$builddir"; + srcfile = $"{filename}"; + assemble_rule = "assemble"; + } + string outputs = $"{destdir}/{filename}.s {destdir}/{filename_noext}.aotdata"; + if (isllvm) + outputs += $" {destdir}/{filename}.llvm.s"; + if (aotdir != null) + ninja.WriteLine ($"build {outputs}: aot-cached {srcfile}"); + else + ninja.WriteLine ($"build {outputs}: aot {srcfile}"); + ninja.WriteLine ($" src_file={srcfile}"); + ninja.WriteLine ($" outfile={destdir}/{filename}.s"); + ninja.WriteLine ($" data_outfile={destdir}/{filename_noext}.aotdata"); + ninja.WriteLine ($" mono_path={destdir}"); + if (isllvm) + ninja.WriteLine ($" llvm_outfile={destdir}/{filename}.llvm.s"); + ninja.WriteLine ($"build {destdir}/{filename}.o: {assemble_rule} {destdir}/{filename}.s"); + if (isllvm) + ninja.WriteLine ($"build {destdir}/{filename}.llvm.o: {assemble_rule} {destdir}/{filename}.llvm.s"); + ninja.WriteLine ($"build $appdir/{filename_noext}.aotdata: cp {destdir}/{filename_noext}.aotdata"); + + ofiles += " " + ($"{destdir}/{filename}.o"); + if (isllvm) + ofiles += " " + ($"{destdir}/{filename}.llvm.o"); + } + var aname = AssemblyName.GetAssemblyName (assembly); + assembly_names.Add (aname.Name); + } + + ninja.WriteLine ("build $appdir: mkdir"); + + if (isdev) { + ninja.WriteLine ($"build $appdir/{bundle_executable}: gen-exe {ofiles} $builddir/main.o $mono_sdkdir/ios-target64/lib/libmonosgen-2.0.a $monoios_dir/libmonoios.a"); + ninja.WriteLine ("build $builddir/main.o: compile-objc $builddir/main.m"); + } else { + ninja.WriteLine ($"build $appdir/{bundle_executable}: cp $monoios_dir/runtime"); + } + ninja.WriteLine ("build $builddir/Info.plist.binary: plutil $builddir/Info.plist"); + ninja.WriteLine ("build $appdir/Info.plist: cpifdiff $builddir/Info.plist.binary"); + ninja.WriteLine ("build $appdir/config.json: cpifdiff $builddir/config.json"); + ninja.WriteLine ("build $builddir/Entitlements.xcent: cpifdiff $monoios_dir/Entitlements.xcent"); + if (isdev) + ninja.WriteLine ($"build $appdir/_CodeSignature: codesign $appdir/{bundle_executable} | $builddir/Entitlements.xcent"); + else + ninja.WriteLine ($"build $appdir/_CodeSignature: codesign-sim $appdir/{bundle_executable} | $builddir/Entitlements.xcent"); + ninja.WriteLine (" entitlements=$builddir/Entitlements.xcent"); + ninja.WriteLine ("build $appdir/Base.lproj: cp-recursive $monoios_dir/Base.lproj"); + + ninja.Close (); + + GenMain (builddir, assembly_names); + } +} diff --git a/sdks/ios/harness/harness.cs b/sdks/ios/harness/harness.cs new file mode 100644 index 00000000000..cd838be4ed2 --- /dev/null +++ b/sdks/ios/harness/harness.cs @@ -0,0 +1,196 @@ +using System; +using System.IO; +using System.Json; +using System.Threading; +using System.Diagnostics; +using System.Net; +using System.Net.Sockets; +using Mono.Options; + +public class Harness +{ + public const string SIM_NAME = "xamarin.ios-sdk.sim"; + + static void Usage () { + Console.WriteLine ("Usage: mono harness.exe <options> <app dir>"); + Console.WriteLine ("Where options are:"); + Console.WriteLine ("\t--run-sim"); + Console.WriteLine ("\t--app=<app bundle id>"); + Console.WriteLine ("\t--logfile=<log file name>"); + } + + public static int Main (string[] args) { + new Harness ().Run (args); + return 0; + } + + string bundle_id; + string bundle_dir; + string logfile_name; + string[] new_args; + + public void Run (string[] args) { + string action = ""; + bundle_id = ""; + bundle_dir = ""; + logfile_name = ""; + + var p = new OptionSet () { + { "start-sim", s => action = "start-sim" }, + { "run-sim", s => action = "run-sim" }, + { "bundle-id=", s => bundle_id = s }, + { "bundle-dir=", s => bundle_dir = s }, + { "logfile=", s => logfile_name = s }, + }; + new_args = p.Parse (args).ToArray (); + + if (action == "start-sim") { + StartSim (); + } else if (action == "run-sim") { + if (bundle_id == "" || bundle_dir == "") { + Console.WriteLine ("The --bundle-id and --bundle-dir arguments are mandatory."); + Environment.Exit (1); + } + RunSim (); + } else { + Usage (); + Environment.Exit (1); + } + } + + void StartSim () { + // Check whenever our simulator instance exists + string state_line = ""; + { + var args = "simctl list devices"; + Console.WriteLine ("Running: " + "xcrun " + args); + var start_info = new ProcessStartInfo ("xcrun", args); + start_info.RedirectStandardOutput = true; + start_info.UseShellExecute = false; + var process = Process.Start (start_info); + var stream = process.StandardOutput; + string line = ""; + while (true) { + line = stream.ReadLine (); + if (line == null) + break; + if (line.Contains (SIM_NAME)) { + state_line = line; + break; + } + } + process.WaitForExit (); + if (process.ExitCode != 0) + Environment.Exit (1); + } + + bool need_start = false; + if (state_line == "") { + // Get the runtime type + var args = "simctl list -j runtimes"; + Console.WriteLine ("Running: " + "xcrun " + args); + var start_info = new ProcessStartInfo ("xcrun", args); + start_info.RedirectStandardOutput = true; + start_info.UseShellExecute = false; + var process = Process.Start (start_info); + var stream = process.StandardOutput; + JsonObject value = JsonValue.Parse (stream.ReadToEnd ()) as JsonObject; + string runtime = value ["runtimes"][0]["identifier"]; + + // Create the simulator + args = "simctl create " + SIM_NAME + " 'iPhone 7' " + runtime; + Console.WriteLine ("Running: " + "xcrun " + args); + process = Process.Start ("xcrun", args); + process.WaitForExit (); + if (process.ExitCode != 0) + Environment.Exit (1); + need_start = true; + } else if (state_line.Contains ("(Shutdown)")) { + need_start = true; + } + + if (need_start) { + var args = "simctl boot " + SIM_NAME; + Console.WriteLine ("Running: " + "xcrun " + args); + var process = Process.Start ("xcrun", args); + process.WaitForExit (); + if (process.ExitCode != 0) + Environment.Exit (1); + } + } + + void RunSim () { + Console.WriteLine ("App: " + bundle_id); + + StartSim (); + + // Install the app + // We do this all the time since its cheap + string exe = "xcrun"; + string args = "simctl install " + SIM_NAME + " " + bundle_dir; + Console.WriteLine ("Running: " + exe + " " + args); + var process = Process.Start (exe, args); + process.WaitForExit (); + if (process.ExitCode != 0) + Environment.Exit (1); + + // + // Test results are returned using an socket connection. + // + var host = Dns.GetHostEntry (Dns.GetHostName ()); + var server = new TcpListener (System.Net.IPAddress.Loopback, 0); + server.Start (); + int port = ((IPEndPoint)server.LocalEndpoint).Port; + + string app_args = ""; + foreach (var a in new_args) + app_args += a + " "; + if (!app_args.Contains ("CONNSTR")) + throw new Exception (); + app_args = app_args.Replace ("CONNSTR", $"tcp:localhost:{port}"); + + // Terminate previous app + exe = "xcrun"; + args = "simctl terminate " + SIM_NAME + " " + bundle_id; + Console.WriteLine ("Running: " + exe + " " + args); + process = Process.Start (exe, args); + process.WaitForExit (); + if (process.ExitCode != 0) + Environment.Exit (1); + + // Launch new app + exe = "xcrun"; + args = "simctl launch " + SIM_NAME + " " + bundle_id + " " + app_args; + Console.WriteLine ("Running: " + exe + " " + args); + process = Process.Start (exe, args); + process.WaitForExit (); + if (process.ExitCode != 0) + Environment.Exit (1); + + // + // Read test results from the tcp connection + // + TextWriter w = new StreamWriter (logfile_name); + string result_line = null; + var client = server.AcceptTcpClient (); + var stream = client.GetStream (); + var reader = new StreamReader (stream); + while (true) { + var line = reader.ReadLine (); + if (line == null) + break; + Console.WriteLine (line); + w.WriteLine (line); + if (line.Contains ("Tests run:")) + result_line = line; + // Printed by the runtime + if (line.Contains ("Exit code:")) + break; + } + + if (result_line != null && result_line.Contains ("Errors: 0")) + Environment.Exit (0); + else + Environment.Exit (1); + } +} diff --git a/sdks/ios/test-runner/AppDelegate.h b/sdks/ios/runtime/AppDelegate.h index fd49c47b337..fd49c47b337 100644 --- a/sdks/ios/test-runner/AppDelegate.h +++ b/sdks/ios/runtime/AppDelegate.h diff --git a/sdks/ios/test-runner/AppDelegate.m b/sdks/ios/runtime/AppDelegate.m index 1578910d7b2..1578910d7b2 100644 --- a/sdks/ios/test-runner/AppDelegate.m +++ b/sdks/ios/runtime/AppDelegate.m diff --git a/sdks/ios/test-runner/Base.lproj/LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib b/sdks/ios/runtime/Base.lproj/LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib Binary files differindex 9aa00f51419..9aa00f51419 100644 --- a/sdks/ios/test-runner/Base.lproj/LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib +++ b/sdks/ios/runtime/Base.lproj/LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib diff --git a/sdks/ios/test-runner/Base.lproj/LaunchScreen.storyboardc/Info.plist b/sdks/ios/runtime/Base.lproj/LaunchScreen.storyboardc/Info.plist Binary files differindex 32288e88f6e..32288e88f6e 100644 --- a/sdks/ios/test-runner/Base.lproj/LaunchScreen.storyboardc/Info.plist +++ b/sdks/ios/runtime/Base.lproj/LaunchScreen.storyboardc/Info.plist diff --git a/sdks/ios/test-runner/Base.lproj/LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib b/sdks/ios/runtime/Base.lproj/LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib Binary files differindex 6b39fef6975..6b39fef6975 100644 --- a/sdks/ios/test-runner/Base.lproj/LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib +++ b/sdks/ios/runtime/Base.lproj/LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib diff --git a/sdks/ios/test-runner/Base.lproj/Main.storyboardc/BYZ-38-t0r-view-8bC-Xf-vdC.nib b/sdks/ios/runtime/Base.lproj/Main.storyboardc/BYZ-38-t0r-view-8bC-Xf-vdC.nib Binary files differindex de467bf1fc8..de467bf1fc8 100644 --- a/sdks/ios/test-runner/Base.lproj/Main.storyboardc/BYZ-38-t0r-view-8bC-Xf-vdC.nib +++ b/sdks/ios/runtime/Base.lproj/Main.storyboardc/BYZ-38-t0r-view-8bC-Xf-vdC.nib diff --git a/sdks/ios/test-runner/Base.lproj/Main.storyboardc/Info.plist b/sdks/ios/runtime/Base.lproj/Main.storyboardc/Info.plist Binary files differindex 9a41f2cb91b..9a41f2cb91b 100644 --- a/sdks/ios/test-runner/Base.lproj/Main.storyboardc/Info.plist +++ b/sdks/ios/runtime/Base.lproj/Main.storyboardc/Info.plist diff --git a/sdks/ios/test-runner/Base.lproj/Main.storyboardc/UIViewController-BYZ-38-t0r.nib b/sdks/ios/runtime/Base.lproj/Main.storyboardc/UIViewController-BYZ-38-t0r.nib Binary files differindex e22d2126647..e22d2126647 100644 --- a/sdks/ios/test-runner/Base.lproj/Main.storyboardc/UIViewController-BYZ-38-t0r.nib +++ b/sdks/ios/runtime/Base.lproj/Main.storyboardc/UIViewController-BYZ-38-t0r.nib diff --git a/sdks/ios/runtime/Entitlements.xcent b/sdks/ios/runtime/Entitlements.xcent new file mode 100644 index 00000000000..ff24d120cea --- /dev/null +++ b/sdks/ios/runtime/Entitlements.xcent @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>get-task-allow</key> + <true/> + <key>application-identifier</key> + <string>C9D4426WFH.com.xamarin.test2</string> + <key>com.apple.developer.team-identifier</key> + <string>C9D4426WFH</string> +</dict> +</plist> diff --git a/sdks/ios/test-runner/Info.plist.in b/sdks/ios/runtime/Info.plist.in index 9c4c239cb34..9c4c239cb34 100644 --- a/sdks/ios/test-runner/Info.plist.in +++ b/sdks/ios/runtime/Info.plist.in diff --git a/sdks/ios/runtime/Makefile b/sdks/ios/runtime/Makefile new file mode 100644 index 00000000000..6fcfe7b36c9 --- /dev/null +++ b/sdks/ios/runtime/Makefile @@ -0,0 +1,74 @@ +all: runtime libmonoios.a + +XCODE_ROOT=$(shell xcode-select -p) + +UNREFERENCED_SYMBOLS = \ + _xamarin_log \ + _xamarin_timezone_get_data \ + _xamarin_timezone_get_names \ + _CloseZStream \ + _CreateZStream \ + _Flush \ + _ReadZStream \ + _WriteZStream + +CC = $(XCODE_ROOT)/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang +SIM_SYSROOT = $(XCODE_ROOT)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk +SIM_ARCH = x86_64 +SIM_SDK_DIR = ../../out/ios-sim64 +SIM_CFLAGS = \ + -std=gnu11 \ + -fobjc-arc \ + -isysroot $(SIM_SYSROOT) \ + -mios-simulator-version-min=10.1 \ + -g \ + -I$(SIM_SDK_DIR)/include/mono-2.0 + +SIM_LDFLAGS = \ + -isysroot $(SIM_SYSROOT) \ + -mios-simulator-version-min=10.1 \ + -framework Foundation \ + -framework UIKit \ + $(foreach u,$(UNREFERENCED_SYMBOLS),-u $u) \ + $(SIM_SDK_DIR)/lib/libmonosgen-2.0.a \ + $(SIM_SDK_DIR)/lib/libMonoPosixHelper.a \ + -liconv -lz + +sim-%.o: %.m + $(ENV) $(CC) -arch $(SIM_ARCH) $(SIM_CFLAGS) -c -o $@ $^ + +runtime: sim-main.o sim-runtime.o sim-AppDelegate.o sim-ViewController.o + $(ENV) $(CC) -arch $(SIM_ARCH) $(SIM_LDFLAGS) -o $@ $^ + +DEV_ARCH = arm64 +DEV_SDK_DIR = ../../out/ios-target64 +DEV_SYSROOT = $(XCODE_ROOT)/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/ +DEV_CFLAGS = \ + -isysroot $(DEV_SYSROOT) \ + -std=gnu11 \ + -fobjc-arc \ + -mios-simulator-version-min=10.1 \ + -g \ + -fPIC \ + -DDEVICE=1 \ + -I$(DEV_SDK_DIR)/include/mono-2.0 + +DEV_LDFLAGS = \ + -isysroot $(DEV_SYSROOT) \ + -mios-simulator-version-min=10.1 \ + -framework Foundation \ + -framework UIKit \ + -Wl,-pie \ + $(foreach u,$(UNREFERENCED_SYMBOLS),-u $u) \ + $(DEV_SDK_DIR)/lib/libmonosgen-2.0.a \ + $(DEV_SDK_DIR)/lib/libMonoPosixHelper.a \ + -liconv -lz + +dev-%.o: %.m + $(ENV) $(CC) -arch $(DEV_ARCH) $(DEV_CFLAGS) -c -o $@ $^ + +libmonoios.a: dev-main.o dev-runtime.o dev-AppDelegate.o dev-ViewController.o + libtool -static -o $@ $^ + +clean: + $(RM) -rf runtime *.o *.a diff --git a/sdks/ios/test-runner/ViewController.h b/sdks/ios/runtime/ViewController.h index 2904d029aad..2904d029aad 100644 --- a/sdks/ios/test-runner/ViewController.h +++ b/sdks/ios/runtime/ViewController.h diff --git a/sdks/ios/test-runner/ViewController.m b/sdks/ios/runtime/ViewController.m index a7e9b4c16a4..d7d0b8b49bc 100644 --- a/sdks/ios/test-runner/ViewController.m +++ b/sdks/ios/runtime/ViewController.m @@ -18,11 +18,15 @@ - (void)viewDidLoad { [super viewDidLoad]; - NSLog (@"HELLO!\n"); - mono_ios_runtime_init (); + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [self startRuntime]; + }); // Do any additional setup after loading the view, typically from a nib. } +- (void)startRuntime { + mono_ios_runtime_init (); +} - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; diff --git a/sdks/ios/test-runner/main.m b/sdks/ios/runtime/main.m index ff82643884a..ff82643884a 100644 --- a/sdks/ios/test-runner/main.m +++ b/sdks/ios/runtime/main.m diff --git a/sdks/ios/test-runner/runtime.h b/sdks/ios/runtime/runtime.h index 7f6450f8204..7f6450f8204 100644 --- a/sdks/ios/test-runner/runtime.h +++ b/sdks/ios/runtime/runtime.h diff --git a/sdks/ios/test-runner/runtime.m b/sdks/ios/runtime/runtime.m index d88b66891ef..39a2a2867d7 100644 --- a/sdks/ios/test-runner/runtime.m +++ b/sdks/ios/runtime/runtime.m @@ -4,13 +4,17 @@ // #import <Foundation/Foundation.h> +#import <os/log.h> #include <mono/utils/mono-publib.h> +#include <mono/utils/mono-logger.h> #include <mono/metadata/assembly.h> #include <mono/metadata/mono-debug.h> #include <mono/metadata/exception.h> #include <mono/jit/jit.h> +#include <mono/jit/jit.h> #include <sys/stat.h> +#include <sys/mman.h> // // Based on runtime/ in xamarin-macios @@ -18,6 +22,13 @@ #define PRINT(...) do { printf (__VA_ARGS__); } while (0); +static os_log_t stdout_log; + +/* These are not in public headers */ +typedef unsigned char* (*MonoLoadAotDataFunc) (MonoAssembly *assembly, int size, void *user_data, void **out_handle); +typedef void (*MonoFreeAotDataFunc) (MonoAssembly *assembly, int size, void *user_data, void *handle); +void mono_install_load_aot_data_hook (MonoLoadAotDataFunc load_func, MonoFreeAotDataFunc free_func, void *user_data); + bool file_exists (const char *path) { @@ -43,6 +54,50 @@ get_bundle_path (void) return bundle_path; } +static unsigned char * +load_aot_data (MonoAssembly *assembly, int size, void *user_data, void **out_handle) +{ + *out_handle = NULL; + + char path [1024]; + int res; + + MonoAssemblyName *assembly_name = mono_assembly_get_name (assembly); + const char *aname = mono_assembly_name_get_name (assembly_name); + const char *bundle = get_bundle_path (); + + // LOG (PRODUCT ": Looking for aot data for assembly '%s'.", name); + res = snprintf (path, sizeof (path) - 1, "%s/%s.aotdata", bundle, aname); + assert (res > 0); + + int fd = open (path, O_RDONLY); + if (fd < 0) { + //LOG (PRODUCT ": Could not load the aot data for %s from %s: %s\n", aname, path, strerror (errno)); + return NULL; + } + + void *ptr = mmap (NULL, size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if (ptr == MAP_FAILED) { + //LOG (PRODUCT ": Could not map the aot file for %s: %s\n", aname, strerror (errno)); + close (fd); + return NULL; + } + + close (fd); + + //LOG (PRODUCT ": Loaded aot data for %s.\n", name); + + *out_handle = ptr; + + return (unsigned char *) ptr; +} + +static void +free_aot_data (MonoAssembly *assembly, int size, void *user_data, void *handle) +{ + munmap (handle, size); +} + static MonoAssembly* load_assembly (const char *name, const char *culture) { @@ -50,7 +105,7 @@ load_assembly (const char *name, const char *culture) char path [1024]; int res; - NSLog (@"assembly_preload_hook: %s %s %s\n", name, culture, bundle); + os_log_info (OS_LOG_DEFAULT, "assembly_preload_hook: %{public}s %{public}s %{public}s\n", name, culture, bundle); if (culture && strcmp (culture, "")) res = snprintf (path, sizeof (path) - 1, "%s/%s/%s", bundle, culture, name); else @@ -134,22 +189,73 @@ unhandled_exception_handler (MonoObject *exc, void *user_data) free (message); free (type_name); - NSLog (@"%@", msg); + os_log_info (OS_LOG_DEFAULT, "%@", msg); + os_log_info (OS_LOG_DEFAULT, "Exit code: %d.", 1); exit (1); } void +log_callback (const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data) +{ + os_log_info (OS_LOG_DEFAULT, "(%s %s) %s", log_domain, log_level, message); + NSLog (@"(%s %s) %s", log_domain, log_level, message); + if (fatal) { + os_log_info (OS_LOG_DEFAULT, "Exit code: %d.", 1); + exit (1); + } +} + +/* Implemented by generated code */ +void mono_ios_register_modules (void); + +void mono_ios_runtime_init (void) { - int res; + NSBundle *main_bundle = [NSBundle mainBundle]; + NSString *path; + char *result; + int res, nargs, config_nargs; + char *executable; + char **args, **config_args = NULL; + + stdout_log = os_log_create ("com.xamarin", "stdout"); id args_array = [[NSProcessInfo processInfo] arguments]; - int nargs = [args_array count]; - char **args; + nargs = [args_array count]; + + // + // Read executable name etc. from an embedded config file if its exists + // + path = [[NSString alloc] initWithFormat: @"%@/config.json", [main_bundle bundlePath]]; + NSData *data = [NSData dataWithContentsOfFile: path]; + if (data) { + NSError *error = nil; + id json = [NSJSONSerialization + JSONObjectWithData:data + options:0 + error:&error]; + assert (!error); + assert ([json isKindOfClass:[NSDictionary class]]); + NSDictionary *dict = (NSDictionary*)json; + id val = dict [@"exe"]; + assert (val); + executable = strdup ([((NSString*)val) UTF8String]); + config_nargs = 2; + config_args = malloc (nargs * sizeof (char*)); + config_args [0] = strdup ([((NSString*)[args_array objectAtIndex: 0]) UTF8String]); + config_args [1] = executable; + } - args = malloc (nargs * sizeof (char*)); - for (int i = 0; i < nargs; ++i) - args [i] = strdup ([((NSString*)[args_array objectAtIndex: i]) UTF8String]); + if (nargs == 1) { + /* Use the args from the config file */ + nargs = config_nargs; + args = config_args; + } else { + /* Use the real command line args */ + args = malloc (nargs * sizeof (char*)); + for (int i = 0; i < nargs; ++i) + args [i] = strdup ([((NSString*)[args_array objectAtIndex: i]) UTF8String]); + } int aindex = 1; while (aindex < nargs) { @@ -163,30 +269,42 @@ mono_ios_runtime_init (void) *eq = '\0'; char *name = strdup (p); char *val = strdup (eq + 1); - NSLog (@"%s=%s.", name, val); + os_log_info (OS_LOG_DEFAULT, "%s=%s.", name, val); setenv (name, val, TRUE); } aindex ++; } - assert (aindex < nargs); - char *executable = args [aindex]; + if (aindex == nargs) { + os_log_info (OS_LOG_DEFAULT, "Executable argument missing."); + exit (1); + } + executable = args [aindex]; aindex ++; const char *bundle = get_bundle_path (); chdir (bundle); +#ifdef DEVICE + mono_ios_register_modules (); + mono_jit_set_aot_mode (MONO_AOT_MODE_FULL); +#endif + mono_debug_init (MONO_DEBUG_FORMAT_MONO); mono_install_assembly_preload_hook (assembly_preload_hook, NULL); + mono_install_load_aot_data_hook (load_aot_data, free_aot_data, NULL); mono_install_unhandled_exception_hook (unhandled_exception_handler, NULL); + mono_trace_set_log_handler (log_callback, NULL); mono_set_signal_chaining (TRUE); mono_set_crash_chaining (TRUE); + //setenv ("MONO_LOG_LEVEL", "debug", TRUE); + mono_jit_init_version ("Mono.ios", "mobile"); MonoAssembly *assembly = load_assembly (executable, NULL); assert (assembly); - NSLog (@"Executable: %s", executable); + os_log_info (OS_LOG_DEFAULT, "Executable: %{public}s", executable); int managed_argc = nargs - aindex; char *managed_argv [128]; assert (managed_argc < 128 - 2); @@ -194,7 +312,7 @@ mono_ios_runtime_init (void) managed_argv [managed_aindex ++] = "test-runner"; for (int i = 0; i < managed_argc; ++i) { managed_argv [managed_aindex] = args [aindex]; - NSLog (@"Arg: %s", managed_argv [managed_aindex]); + os_log_info (OS_LOG_DEFAULT, "Arg: %s", managed_argv [managed_aindex]); managed_aindex ++; aindex ++; } @@ -202,6 +320,8 @@ mono_ios_runtime_init (void) managed_argc = managed_aindex; res = mono_jit_exec (mono_domain_get (), assembly, managed_argc, managed_argv); + // Print this so apps parsing logs can detect when we exited + os_log_info (OS_LOG_DEFAULT, "Exit code: %d.", res); exit (res); } @@ -251,3 +371,28 @@ xamarin_GetFolderPath (int folder) NSString *path = [url path]; return strdup ([path UTF8String]); } + + +// mcs/class/corlib/System/Console.iOS.cs +void +xamarin_log (const unsigned short *unicodeMessage) +{ + // COOP: no managed memory access: any mode. + int length = 0; + const unsigned short *ptr = unicodeMessage; + while (*ptr++) + length += sizeof (unsigned short); + NSString *msg = [[NSString alloc] initWithBytes: unicodeMessage length: length encoding: NSUTF16LittleEndianStringEncoding]; + +#if TARGET_OS_WATCH && defined (__arm__) // maybe make this configurable somehow? + const char *utf8 = [msg UTF8String]; + int len = strlen (utf8); + fwrite (utf8, 1, len, stdout); + if (len == 0 || utf8 [len - 1] != '\n') + fwrite ("\n", 1, 1, stdout); + fflush (stdout); +#else + os_log (stdout_log, "%{public}@", msg); + //NSLog (@"%@", msg); +#endif +} diff --git a/sdks/ios/test-runner/Makefile b/sdks/ios/test-runner/Makefile deleted file mode 100644 index 404334f81dd..00000000000 --- a/sdks/ios/test-runner/Makefile +++ /dev/null @@ -1,29 +0,0 @@ -all: test-runner - -CC = /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -ARCH = x86_64 -SDK_DIR = ../../out/ios-sim64 -CFLAGS = \ - -std=gnu11 \ - -fobjc-arc \ - -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator11.1.sdk \ - -mios-simulator-version-min=11.1 \ - -g \ - -I$(SDK_DIR)/include/mono-2.0 - -LDFLAGS = \ - -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator11.1.sdk \ - -mios-simulator-version-min=11.1 \ - -framework Foundation \ - -framework UIKit \ - $(SDK_DIR)/lib/libmonosgen-2.0.a \ - -liconv - -%.o: %.m - $(ENV) $(CC) -arch $(ARCH) $(CFLAGS) -c -o $@ $^ - -test-runner: main.o runtime.o AppDelegate.o ViewController.o - $(ENV) $(CC) -arch $(ARCH) $(LDFLAGS) -o $@ $^ - -clean: - $(RM) -rf test-runner *.o diff --git a/sdks/ios/test-runner/runner.cs b/sdks/ios/test-runner/runner.cs new file mode 100644 index 00000000000..dc8d87cb147 --- /dev/null +++ b/sdks/ios/test-runner/runner.cs @@ -0,0 +1,72 @@ +using System; +using System.IO; +using System.Linq; +using System.Text; +using System.Net.Sockets; +using NUnitLite.Runner; +using NUnit.Framework.Internal; + + class TcpWriter : TextWriter + { + private string hostName; + private int port; + + private TcpClient client; + private NetworkStream stream; + private StreamWriter writer; + + public TcpWriter(string hostName, int port) + { + this.hostName = hostName; + this.port = port; + this.client = new TcpClient(hostName, port); + this.stream = client.GetStream(); + this.writer = new StreamWriter(stream); + } + + public override void Write(char value) + { + writer.Write(value); + } + + public override void Write(string value) + { + writer.Write(value); + } + + public override void WriteLine(string value) + { + writer.WriteLine(value); + writer.Flush(); + } + + public override System.Text.Encoding Encoding + { + get { return System.Text.Encoding.Default; } + } + } + +public class TestRunner +{ + public static int Main(string[] args) { + TextUI runner; + + // First argument is the connection string + if (args [0].StartsWith ("tcp:")) { + var parts = args [0].Split (':'); + if (parts.Length != 3) + throw new Exception (); + string host = parts [1]; + string port = parts [2]; + args = args.Skip (1).ToArray (); + + Console.WriteLine ($"Connecting to harness at {host}:{port}."); + runner = new TextUI (new TcpWriter (host, Int32.Parse (port))); + } else { + runner = new TextUI (); + } + runner.Execute (args); + + return (runner.Failure ? 1 : 0); + } +} |