private static string GetRuntimeConfigPath(RemoteInvokeOptions options, out IEnumerable <IDisposable> toDispose) { if (options.RuntimeConfigurationOptions?.Any() != true) { toDispose = null; return(RuntimeConfigPath); } // to support RuntimeConfigurationOptions, copy the runtimeconfig.json file to // a temp file and add the options to the runtimeconfig.dev.json file. // NOTE: using the dev.json file so we don't need to parse and edit the runtimeconfig.json // which would require a reference to System.Text.Json. string tempFile = System.IO.Path.GetTempFileName(); string configFile = tempFile + ".runtimeconfig.json"; string devConfigFile = System.IO.Path.ChangeExtension(configFile, "dev.json"); File.Copy(RuntimeConfigPath, configFile); string configProperties = string.Join( "," + Environment.NewLine, options.RuntimeConfigurationOptions.Select(kvp => $"\"{kvp.Key}\": {ToJsonString(kvp.Value)}")); string devConfigFileContents = @" { ""runtimeOptions"": { ""configProperties"": { " + configProperties + @" } } }"; File.WriteAllText(devConfigFile, devConfigFileContents); toDispose = new IDisposable[] { new FileDeleter(tempFile, configFile, devConfigFile) }; return(configFile); }
private static string GetConsoleAppArgs(RemoteInvokeOptions options, out IEnumerable <IDisposable> toDispose) { bool isNetCore = IsNetCore(); if (options.RuntimeConfigurationOptions?.Any() == true && !isNetCore) { throw new InvalidOperationException("RuntimeConfigurationOptions are only supported on .NET Core"); } if (!isNetCore) { toDispose = null; return(string.Empty); } string args = "exec"; string runtimeConfigPath = GetRuntimeConfigPath(options, out toDispose); if (runtimeConfigPath != null) { args += $" --runtimeconfig \"{runtimeConfigPath}\""; } if (DepsJsonPath != null) { args += $" --depsfile \"{DepsJsonPath}\""; } if (!string.IsNullOrEmpty(options.RollForward)) { args += $" --roll-forward {options.RollForward}"; } args += $" \"{Path}\""; return(args); }
/// <summary>Invokes the method from this assembly in another process using the specified arguments without performing any modifications to the arguments.</summary> /// <param name="method">The method to invoke.</param> /// <param name="unparsedArg">The arguments to pass to the method.</param> /// <param name="options">Options to use for the invocation.</param> public static RemoteInvokeHandle InvokeRaw(Delegate method, string unparsedArg, RemoteInvokeOptions options = null) { return(Invoke(GetMethodInfo(method), new[] { unparsedArg }, options, pasteArguments: false)); }
/// <summary>Invokes the method from this assembly in another process using the specified arguments.</summary> /// <param name="method">The method to invoke.</param> /// <param name="arg1">The first argument to pass to the method.</param> /// <param name="arg2">The second argument to pass to the method.</param> /// <param name="arg3">The third argument to pass to the method.</param> /// <param name="arg4">The fourth argument to pass to the method.</param> /// <param name="arg5">The fifth argument to pass to the method.</param> /// <param name="options">Options to use for the invocation.</param> public static RemoteInvokeHandle Invoke(Func <string, string, string, string, string, int> method, string arg1, string arg2, string arg3, string arg4, string arg5, RemoteInvokeOptions options = null) { return(Invoke(GetMethodInfo(method), new[] { arg1, arg2, arg3, arg4, arg5 }, options)); }
/// <summary>Invokes the method from this assembly in another process using the specified arguments.</summary> /// <param name="method">The method to invoke.</param> /// <param name="arg1">The first argument to pass to the method.</param> /// <param name="arg2">The second argument to pass to the method.</param> /// <param name="options">Options to use for the invocation.</param> public static RemoteInvokeHandle Invoke(Func <string, string, Task <int> > method, string arg1, string arg2, RemoteInvokeOptions options = null) { return(Invoke(GetMethodInfo(method), new[] { arg1, arg2 }, options)); }
/// <summary>Invokes the method from this assembly in another process using the specified arguments.</summary> /// <param name="method">The method to invoke.</param> /// <param name="options">Options to use for the invocation.</param> public static RemoteInvokeHandle Invoke(Func <Task <int> > method, RemoteInvokeOptions options = null) { return(Invoke(GetMethodInfo(method), Array.Empty <string>(), options)); }
/// <summary>Invokes the method from this assembly in another process using the specified arguments.</summary> /// <param name="method">The method to invoke.</param> /// <param name="options">Options to use for the invocation.</param> public static RemoteInvokeHandle Invoke(Action <string, string, string, string> method, string arg1, string arg2, string arg3, string arg4, RemoteInvokeOptions options = null) { // There's no exit code to check options = options ?? new RemoteInvokeOptions(); options.CheckExitCode = false; return(Invoke(GetMethodInfo(method), new[] { arg1, arg2, arg3, arg4 }, options)); }
/// <summary>Invokes the method from this assembly in another process using the specified arguments.</summary> /// <param name="method">The method to invoke.</param> /// <param name="args">The arguments to pass to the method.</param> /// <param name="options">Options to use for the invocation.</param> /// <param name="pasteArguments">true if this function should paste the arguments (e.g. surrounding with quotes); false if that responsibility is left up to the caller.</param> private static RemoteInvokeHandle Invoke(MethodInfo method, string[] args, RemoteInvokeOptions options, bool pasteArguments = true) { options = options ?? new RemoteInvokeOptions(); // For platforms that do not support RemoteExecutor if (!IsSupported) { throw new PlatformNotSupportedException("RemoteExecutor is not supported on this platform."); } // Verify the specified method returns an int (the exit code) or nothing, // and that if it accepts any arguments, they're all strings. Assert.True(method.ReturnType == typeof(void) || method.ReturnType == typeof(int) || method.ReturnType == typeof(Task) || method.ReturnType == typeof(Task <int>)); Assert.All(method.GetParameters(), pi => Assert.Equal(typeof(string), pi.ParameterType)); // And make sure it's in this assembly. This isn't critical, but it helps with deployment to know // that the method to invoke is available because we're already running in this assembly. Type t = method.DeclaringType; Assembly a = t.GetTypeInfo().Assembly; // Start the other process and return a wrapper for it to handle its lifetime and exit checking. ProcessStartInfo psi = options.StartInfo; psi.UseShellExecute = false; if (!options.EnableProfiling) { // Profilers / code coverage tools doing coverage of the test process set environment // variables to tell the targeted process what profiler to load. We don't want the child process // to be profiled / have code coverage, so we remove these environment variables for that process // before it's started. psi.Environment.Remove("Cor_Profiler"); psi.Environment.Remove("Cor_Enable_Profiling"); psi.Environment.Remove("CoreClr_Profiler"); psi.Environment.Remove("CoreClr_Enable_Profiling"); } // If we need the host (if it exists), use it, otherwise target the console app directly. string metadataArgs = PasteArguments.Paste(new string[] { a.FullName, t.FullName, method.Name, options.ExceptionFile }, pasteFirstArgumentUsingArgV0Rules: false); string passedArgs = pasteArguments ? PasteArguments.Paste(args, pasteFirstArgumentUsingArgV0Rules: false) : string.Join(" ", args); string testConsoleAppArgs = s_extraParameter.Value + " " + metadataArgs + " " + passedArgs; if (options.RunAsSudo) { psi.FileName = "sudo"; psi.Arguments = HostRunner + " " + testConsoleAppArgs; // Create exception file up front so there are no permission issue when RemoteInvokeHandle tries to delete it. File.WriteAllText(options.ExceptionFile, ""); } else { psi.FileName = HostRunner; psi.Arguments = testConsoleAppArgs; } // Return the handle to the process, which may or not be started return(new RemoteInvokeHandle(options.Start ? Process.Start(psi) : new Process() { StartInfo = psi }, options, a.FullName, t.FullName, method.Name)); }