diff --git a/compile.sh b/compile.sh index 047d0e2b67e615..cf123779beb31b 100755 --- a/compile.sh +++ b/compile.sh @@ -121,6 +121,8 @@ if [ $DO_TOOLS_COMPILATION ]; then tools/objc/precomp_plmerge_deploy.jar bazel_bootstrap //src/objc_tools/xcodegen:xcodegen_deploy.jar \ tools/objc/precomp_xcodegen_deploy.jar + bazel_bootstrap //src/tools/xcode/stdredirect:StdRedirect.dylib \ + tools/objc/StdRedirect.dylib fi fi diff --git a/src/tools/xcode/stdredirect/BUILD b/src/tools/xcode/stdredirect/BUILD new file mode 100644 index 00000000000000..77b03249fa08a7 --- /dev/null +++ b/src/tools/xcode/stdredirect/BUILD @@ -0,0 +1,13 @@ +package(default_visibility = ["//src:__subpackages__"]) + +# This target will only build on a Mac. +genrule( + name = "stdredirect_genrule", + srcs = ["StdRedirect.c"], + outs = ["StdRedirect.dylib"], + cmd = "/usr/bin/xcrun clang -arch i386 -arch x86_64 -mios-simulator-version-min=7.0 -dynamiclib " + + " -isysroot \"$$(/usr/bin/xcrun --sdk iphonesimulator --show-sdk-path)\" " + + " -o $@ $<", + output_to_bindir = 1, + visibility = ["//visibility:public"], +) diff --git a/src/tools/xcode/stdredirect/README b/src/tools/xcode/stdredirect/README new file mode 100644 index 00000000000000..684e64f6b1760d --- /dev/null +++ b/src/tools/xcode/stdredirect/README @@ -0,0 +1,3 @@ +StdRedirect is a library that we DYLD_INSERT when running applications on the simulator so that we can redirect stdout and stderr. + +StdRedirect only builds/runs on Darwin. diff --git a/src/tools/xcode/stdredirect/StdRedirect.c b/src/tools/xcode/stdredirect/StdRedirect.c new file mode 100644 index 00000000000000..d9d44b011f1012 --- /dev/null +++ b/src/tools/xcode/stdredirect/StdRedirect.c @@ -0,0 +1,69 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +// StdRedirect.c +// +// Used for controlling stdin, stdout, stderr of iOS Applications launched in the simulator +// using simctl. stdin, stdout, and stderr are set using environment variables. +// +// To use: +// +// export GSTDIN="PATH_TO_STD_IN" +// export GSTDOUT="PATH_TO_STD_OUT" +// export GSTDERR="PATH_TO_STD_ERR" +// PLATFORM_PATH="$($(xcrun --sdk iphonesimulator --show-sdk-platform-path)" +// export SIMCTL_CHILD_DYLD_FALLBACK_FRAMEWORK_PATH="$PLATFORM_PATH/Developer/Library/Frameworks" +// export SIMCTL_CHILD_DYLD_INSERT_LIBRARIES="$PLATFORM_PATH/Developer/Library/PrivateFrameworks" \ +// "/IDEBundleInjection.framework/IDEBundleInjection:" +// export SIMCTL_CHILD_XCInjectBundle="Full path to your *.xctest Bundle" +// export SIMCTL_CHILD_XCInjectBundleInto="Full path to your app binary inside of " \ +// "~/Library/Developer/CoreSimulator/Devices" +// +// xcrun simctl launch booted -XCTest All + +// Note that all of GSTDIN/GSTDOUT/GSTDERR are optional. Xcode dumps test results to GSTDERR. + +// For a practical example of using it see run_tests.sh + +#include +#include +#include +#include +#include +#include +#include + +static void SetUpStdFileDescriptor(const char *env_name, int fileNo) { + const char *path = getenv(env_name); + if (path) { + int fd = open(path, O_RDWR | O_CREAT | O_APPEND); + if (fd == -1) { + syslog(LOG_ERR, "Could not open %s for %s - %s", env_name, path, strerror(errno)); + } else { + if (fchmod(fd, 0666) == -1) { + syslog(LOG_ERR, "Could not chmod %s for %s - %s", env_name, path, strerror(errno)); + } + if (dup2(fd, fileNo) == -1) { + syslog(LOG_ERR, "Could not dup %s for %s - %s", env_name, path, strerror(errno)); + } + } + } +} + +__attribute__((constructor)) static void SetUpStdFileDescriptors() { + SetUpStdFileDescriptor("GSTDIN", STDIN_FILENO); + SetUpStdFileDescriptor("GSTDOUT", STDOUT_FILENO); + SetUpStdFileDescriptor("GSTDERR", STDERR_FILENO); +} diff --git a/src/tools/xcode/stdredirect/run_tests.sh b/src/tools/xcode/stdredirect/run_tests.sh new file mode 100755 index 00000000000000..361cc4ae1d4244 --- /dev/null +++ b/src/tools/xcode/stdredirect/run_tests.sh @@ -0,0 +1,74 @@ +#!/bin/sh + +# Copyright 2015 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Runs a series of unit tests for an app bundle. +# This is an example script to show how to use the StdRedirect library. + +# $1 path to app bundle +# $2 path to xctest bundle +# $3 path to stderr +# $4 path to StdRedirect.dylib + +set -e + +if [ $# -ne 4 ]; then + echo "Usage RunTests " + exit 1 +fi + +SIMULATOR_RUNNING=$(osascript -e "tell application \"System Events\" to (name of processes) contains \"iOS Simulator\"") + +TEST_DEVICE_ID=$(xcrun simctl create TestDevice com.apple.CoreSimulator.SimDeviceType.iPhone-6 com.apple.CoreSimulator.SimRuntime.iOS-8-3) + +# Instruments will return an error because we are calling it without a template arg. +# It's the only way I know of to launch the simulator safely using xcrun. +# This will launch the simulator with a given device. If the simulator is already running +# it will switch to the given device. +# Radar 21392428 xcrun should allow me to specifiy "iOS Simulator" in some manner +xcrun instruments -w $TEST_DEVICE_ID &>/dev/null || true + +xcrun simctl install $TEST_DEVICE_ID $1 + +PLATFORM_PATH="$(xcrun --sdk iphonesimulator --show-sdk-platform-path)" +export SIMCTL_CHILD_DYLD_INSERT_LIBRARIES="$PLATFORM_PATH/Developer/Library/PrivateFrameworks/IDEBundleInjection.framework/IDEBundleInjection:$4" +export SIMCTL_CHILD_GSTDERR="$3" +export SIMCTL_CHILD_XCInjectBundle="$2" +BUNDLE_BASE=$(basename $1) +BUNDLE_INFO_PLIST="$1/Info.plist" +EXECUTABLE_NAME=$(xcrun PlistBuddy -c "Print :CFBundleExecutable" "$BUNDLE_INFO_PLIST") +BUNDLE_ID=$(xcrun PlistBuddy -c "Print :CFBundleIdentifier" "$BUNDLE_INFO_PLIST") + +# The "*" is unfortunate, but there is no way to get back the UUID of the installed application. +# Since we created the simulator from scratch, there should only be one app installed on it. +# Radar 21392479 simctl install should return the UUID of the installed app. +# Radar 21392325 simctl getenv never appears to function +export SIMCTL_CHILD_XCInjectBundleInto="$HOME/Library/Developer/CoreSimulator/Devices/$TEST_DEVICE_ID/data/Containers/Bundle/Application/*/$BUNDLE_BASE/$EXECUTABLE_NAME" +export SIMCTL_CHILD_DYLD_FALLBACK_FRAMEWORK_PATH="$PLATFORM_PATH/Developer/Library/Frameworks" +IOS_PID=$(xcrun simctl launch $TEST_DEVICE_ID "$BUNDLE_ID" -XCTest All) +IOS_PID=$(echo $IOS_PID | awk '{ print $2 }') + +# The simulator is not a subprocess of the script, so we cannot wait on it and must poll instead. +while kill -0 "$IOS_PID" &>/dev/null; do + sleep 0.5 +done + +# If the simulator wasn't running when we started, then we should clean it up. +if [ SIMULATOR_RUNNING == "false" ]; then + osascript -e "tell application \"iOS Simulator\" to quit" +fi + +# Radar 21392585 simctl delete should allows me to delete multiple devices in one call +xcrun simctl delete $TEST_DEVICE_ID