private static ToolChainCandidate findVisualCpp() {
    // Search in the standard installation locations
    VisualStudioLocator vsLocator =
        new DefaultVisualStudioLocator(
            OperatingSystem.current(),
            NativeServices.getInstance().get(WindowsRegistry.class),
            NativeServices.getInstance().get(SystemInfo.class));
    VisualStudioLocator.SearchResult searchResult = vsLocator.locateVisualStudioInstalls(null);
    if (searchResult.isAvailable()) {
      VisualStudioInstall install = searchResult.getVisualStudio();
      return new InstalledVisualCpp("visual c++").withInstall(install);
    }

    return new UnavailableToolChain("visual c++");
  }
Exemple #2
0
 public void createLink(String target) {
   try {
     NativeServices.getInstance().get(FileSystem.class).createSymbolicLink(this, new File(target));
   } catch (IOException e) {
     throw new RuntimeException(e);
   }
 }
 /** This is used by consumers 1.0-milestone-3 and later */
 public DefaultConnection() {
   LOGGER.debug("Tooling API provider {} created.", GradleVersion.current().getVersion());
   LoggingServiceRegistry loggingServices = LoggingServiceRegistry.newEmbeddableLogging();
   services =
       ServiceRegistryBuilder.builder()
           .displayName("Connection services")
           .parent(loggingServices)
           .parent(NativeServices.getInstance())
           .provider(new ConnectionScopeServices(loggingServices))
           .build();
   adapter = services.get(ProtocolToModelAdapter.class);
   connection = services.get(ProviderConnection.class);
 }
  public abstract static class InstalledToolChain extends ToolChainCandidate {
    private static final ProcessEnvironment PROCESS_ENVIRONMENT =
        NativeServices.getInstance().get(ProcessEnvironment.class);
    protected final List<File> pathEntries = new ArrayList<File>();
    protected final Map<String, String> environmentVars = new HashMap<String, String>();
    private final String displayName;
    private final String pathVarName;
    private final Map<String, String> originalEnvrionmentVars = new HashMap<String, String>();
    private String originalPath;

    public InstalledToolChain(String displayName) {
      this.displayName = displayName;
      this.pathVarName = OperatingSystem.current().getPathVar();
    }

    InstalledToolChain inPath(File... pathEntries) {
      Collections.addAll(this.pathEntries, pathEntries);
      return this;
    }

    InstalledToolChain withVisualCppHidden() {
      // Change PROGRAMFILES so that Visual C++ won't be located
      environmentVars.put("PROGRAMFILES", "C:\\NOT A DIRECTORY");
      return this;
    }

    @Override
    public String getDisplayName() {
      return displayName;
    }

    @Override
    public boolean isAvailable() {
      return true;
    }

    public String getTypeDisplayName() {
      return getDisplayName().replaceAll("\\s+\\d+(\\.\\d+)*$", "");
    }

    public ExecutableFixture executable(Object path) {
      return new ExecutableFixture(
          new TestFile(OperatingSystem.current().getExecutableName(path.toString())), this);
    }

    public TestFile objectFile(Object path) {
      return new TestFile(path.toString() + ".o");
    }

    public SharedLibraryFixture sharedLibrary(Object path) {
      return new SharedLibraryFixture(
          new TestFile(OperatingSystem.current().getSharedLibraryName(path.toString())), this);
    }

    public NativeBinaryFixture staticLibrary(Object path) {
      return new NativeBinaryFixture(
          new TestFile(OperatingSystem.current().getStaticLibraryName(path.toString())), this);
    }

    public void initialiseEnvironment() {
      String compilerPath = Joiner.on(File.pathSeparator).join(pathEntries);

      if (compilerPath.length() > 0) {
        originalPath = System.getenv(pathVarName);
        String path = compilerPath + File.pathSeparator + originalPath;
        System.out.println(String.format("Using path %s", path));
        PROCESS_ENVIRONMENT.setEnvironmentVariable(pathVarName, path);
      }

      for (Map.Entry<String, String> entry : environmentVars.entrySet()) {
        String key = entry.getKey();
        String value = entry.getValue();
        originalEnvrionmentVars.put(key, System.getenv(key));
        System.out.println(String.format("Using environment key %s -> %s", key, value));
        PROCESS_ENVIRONMENT.setEnvironmentVariable(key, value);
      }
    }

    public void resetEnvironment() {
      if (originalPath != null) {
        PROCESS_ENVIRONMENT.setEnvironmentVariable(pathVarName, originalPath);
      }
      for (Map.Entry<String, String> entry : originalEnvrionmentVars.entrySet()) {
        PROCESS_ENVIRONMENT.setEnvironmentVariable(entry.getKey(), entry.getValue());
      }
    }

    public abstract String getBuildScriptConfig();

    public abstract String getImplementationClass();

    public abstract String getPluginClass();

    public boolean isVisualCpp() {
      return false;
    }

    public List<File> getPathEntries() {
      return pathEntries;
    }

    /** The environment required to execute a binary created by this toolchain. */
    // TODO:DAZ This isn't quite right (only required for MinGW and cygwin, and preferably not even
    // those)
    public List<String> getRuntimeEnv() {
      if (pathEntries.isEmpty()) {
        return Collections.emptyList();
      }

      String path =
          Joiner.on(File.pathSeparator).join(pathEntries)
              + File.pathSeparator
              + System.getenv(pathVarName);
      return Collections.singletonList(pathVarName + "=" + path);
    }

    public String getId() {
      return displayName.replaceAll("\\W", "");
    }
  }
@RunWith(JMock.class)
public class WorkerProcessIntegrationTest {
  private final JUnit4Mockery context = new JUnit4Mockery();
  private final TestListenerInterface listenerMock = context.mock(TestListenerInterface.class);
  private final MessagingServices messagingServices =
      new MessagingServices(getClass().getClassLoader());
  private final MessagingServer server = messagingServices.get(MessagingServer.class);
  @Rule public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
  private final ProcessMetaDataProvider metaDataProvider =
      new DefaultProcessMetaDataProvider(
          NativeServices.getInstance().get(ProcessEnvironment.class));
  private final CacheFactory factory =
      new DefaultCacheFactory(
              new DefaultFileLockManager(metaDataProvider, new NoOpFileLockListener()))
          .create();
  private final CacheRepository cacheRepository =
      new DefaultCacheRepository(tmpDir.getTestDirectory(), null, CacheUsage.ON, factory);
  private final ModuleRegistry moduleRegistry = new DefaultModuleRegistry();
  private final ClassPathRegistry classPathRegistry =
      new DefaultClassPathRegistry(
          new DefaultClassPathProvider(moduleRegistry),
          new WorkerProcessClassPathProvider(cacheRepository, moduleRegistry));
  private final DefaultWorkerProcessFactory workerFactory =
      new DefaultWorkerProcessFactory(
          LogLevel.INFO,
          server,
          classPathRegistry,
          TestFiles.resolver(tmpDir.getTestDirectory()),
          new LongIdGenerator());
  private final ListenerBroadcast<TestListenerInterface> broadcast =
      new ListenerBroadcast<TestListenerInterface>(TestListenerInterface.class);
  private final RemoteExceptionListener exceptionListener = new RemoteExceptionListener(broadcast);

  @Before
  public void setUp() {
    broadcast.add(listenerMock);
  }

  @After
  public void tearDown() {
    messagingServices.stop();
  }

  @Test
  public void workerProcessCanSendMessagesToThisProcess() throws Throwable {
    context.checking(
        new Expectations() {
          {
            Sequence sequence = context.sequence("sequence");
            one(listenerMock).send("message 1", 1);
            inSequence(sequence);
            one(listenerMock).send("message 2", 2);
            inSequence(sequence);
          }
        });

    execute(worker(new RemoteProcess()));
  }

  @Test
  public void thisProcessCanSendEventsToWorkerProcess() throws Throwable {
    execute(
        worker(new PingRemoteProcess())
            .onServer(
                new Action<ObjectConnection>() {
                  public void execute(ObjectConnection objectConnection) {
                    TestListenerInterface listener =
                        objectConnection.addOutgoing(TestListenerInterface.class);
                    listener.send("1", 0);
                    listener.send("1", 1);
                    listener.send("1", 2);
                    listener.send("stop", 3);
                  }
                }));
  }

  @Test
  public void multipleWorkerProcessesCanSendMessagesToThisProcess() throws Throwable {
    context.checking(
        new Expectations() {
          {
            Sequence process1 = context.sequence("sequence1");
            one(listenerMock).send("message 1", 1);
            inSequence(process1);
            one(listenerMock).send("message 2", 2);
            inSequence(process1);
            Sequence process2 = context.sequence("sequence2");
            one(listenerMock).send("other 1", 1);
            inSequence(process2);
            one(listenerMock).send("other 2", 2);
            inSequence(process2);
          }
        });

    execute(worker(new RemoteProcess()), worker(new OtherRemoteProcess()));
  }

  @Test
  public void handlesWorkerProcessWhichCrashes() throws Throwable {
    context.checking(
        new Expectations() {
          {
            atMost(1).of(listenerMock).send("message 1", 1);
            atMost(1).of(listenerMock).send("message 2", 2);
          }
        });

    execute(worker(new CrashingRemoteProcess()).expectStopFailure());
  }

  @Test
  public void handlesWorkerActionWhichThrowsException() throws Throwable {
    execute(worker(new BrokenRemoteProcess()).expectStopFailure());
  }

  @Test
  public void handlesWorkerActionThatLeavesThreadsRunning() throws Throwable {
    context.checking(
        new Expectations() {
          {
            one(listenerMock).send("message 1", 1);
            one(listenerMock).send("message 2", 2);
          }
        });

    execute(worker(new NoCleanUpRemoteProcess()));
  }

  @Test
  public void handlesWorkerProcessWhichNeverConnects() throws Throwable {
    execute(worker(new NoConnectRemoteProcess()).expectStartFailure());
  }

  @Test
  public void handlesWorkerProcessWhenJvmFailsToStart() throws Throwable {
    execute(worker(Actions.doNothing()).jvmArgs("--broken").expectStartFailure());
  }

  private ChildProcess worker(Action<? super WorkerProcessContext> action) {
    return new ChildProcess(action);
  }

  void execute(ChildProcess... processes) throws Throwable {
    for (ChildProcess process : processes) {
      process.start();
    }
    for (ChildProcess process : processes) {
      process.waitForStop();
    }
    messagingServices.stop();
    exceptionListener.rethrow();
  }

  private class ChildProcess {
    private boolean stopFails;
    private boolean startFails;
    private WorkerProcess proc;
    private Action<? super WorkerProcessContext> action;
    private List<String> jvmArgs = Collections.emptyList();
    private Action<ObjectConnection> serverAction;

    public ChildProcess(Action<? super WorkerProcessContext> action) {
      this.action = action;
    }

    ChildProcess expectStopFailure() {
      stopFails = true;
      return this;
    }

    ChildProcess expectStartFailure() {
      startFails = true;
      return this;
    }

    public void start() {
      WorkerProcessBuilder builder = workerFactory.create();
      builder.applicationClasspath(classPathRegistry.getClassPath("ANT").getAsFiles());
      builder.sharedPackages("org.apache.tools.ant");
      builder.getJavaCommand().systemProperty("test.system.property", "value");
      builder.getJavaCommand().environment("TEST_ENV_VAR", "value");
      builder.worker(action);

      builder.getJavaCommand().jvmArgs(jvmArgs);

      proc = builder.build();
      try {
        proc.start();
        assertFalse(startFails);
      } catch (ExecException e) {
        assertTrue(startFails);
        return;
      }
      proc.getConnection().addIncoming(TestListenerInterface.class, exceptionListener);
      if (serverAction != null) {
        serverAction.execute(proc.getConnection());
      }
    }

    public void waitForStop() {
      if (startFails) {
        return;
      }
      try {
        proc.waitForStop();
        assertFalse("Expected process to fail", stopFails);
      } catch (ExecException e) {
        assertTrue("Unexpected failure in worker process", stopFails);
      }
    }

    public ChildProcess onServer(Action<ObjectConnection> action) {
      this.serverAction = action;
      return this;
    }

    public ChildProcess jvmArgs(String... jvmArgs) {
      this.jvmArgs = Arrays.asList(jvmArgs);
      return this;
    }
  }

  public static class RemoteExceptionListener implements Dispatch<MethodInvocation> {
    Throwable ex;
    final Dispatch<MethodInvocation> dispatch;

    public RemoteExceptionListener(Dispatch<MethodInvocation> dispatch) {
      this.dispatch = dispatch;
    }

    public void dispatch(MethodInvocation message) {
      try {
        dispatch.dispatch(message);
      } catch (Throwable e) {
        ex = e;
      }
    }

    public void rethrow() throws Throwable {
      if (ex != null) {
        throw ex;
      }
    }
  }

  public static class RemoteProcess implements Action<WorkerProcessContext>, Serializable {
    public void execute(WorkerProcessContext workerProcessContext) {
      // Check environment
      assertThat(System.getProperty("test.system.property"), equalTo("value"));
      assertThat(System.getenv().get("TEST_ENV_VAR"), equalTo("value"));

      // Check ClassLoaders
      ClassLoader antClassLoader = Project.class.getClassLoader();
      ClassLoader thisClassLoader = getClass().getClassLoader();
      ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();

      assertThat(antClassLoader, not(sameInstance(systemClassLoader)));
      assertThat(thisClassLoader, not(sameInstance(systemClassLoader)));
      assertThat(antClassLoader.getParent(), equalTo(systemClassLoader.getParent()));
      try {
        assertThat(
            thisClassLoader.loadClass(Project.class.getName()),
            sameInstance((Object) Project.class));
      } catch (ClassNotFoundException e) {
        throw new RuntimeException(e);
      }

      // Send some messages
      TestListenerInterface sender =
          workerProcessContext.getServerConnection().addOutgoing(TestListenerInterface.class);
      sender.send("message 1", 1);
      sender.send("message 2", 2);
    }
  }

  public static class OtherRemoteProcess implements Action<WorkerProcessContext>, Serializable {
    public void execute(WorkerProcessContext workerProcessContext) {
      TestListenerInterface sender =
          workerProcessContext.getServerConnection().addOutgoing(TestListenerInterface.class);
      sender.send("other 1", 1);
      sender.send("other 2", 2);
    }
  }

  public static class NoCleanUpRemoteProcess implements Action<WorkerProcessContext>, Serializable {
    public void execute(WorkerProcessContext workerProcessContext) {
      final Lock lock = new ReentrantLock();
      lock.lock();
      new Thread(
              new Runnable() {
                public void run() {
                  lock.lock();
                }
              })
          .start();

      TestListenerInterface sender =
          workerProcessContext.getServerConnection().addOutgoing(TestListenerInterface.class);
      sender.send("message 1", 1);
      sender.send("message 2", 2);
    }
  }

  public static class PingRemoteProcess
      implements Action<WorkerProcessContext>, Serializable, TestListenerInterface {
    CountDownLatch stopReceived;
    int count;

    public void send(String message, int count) {
      assertEquals(this.count, count);
      this.count++;
      if (message.equals("stop")) {
        assertEquals(4, this.count);
        stopReceived.countDown();
      }
    }

    public void execute(WorkerProcessContext workerProcessContext) {
      stopReceived = new CountDownLatch(1);
      workerProcessContext.getServerConnection().addIncoming(TestListenerInterface.class, this);
      try {
        stopReceived.await();
      } catch (InterruptedException e) {
        throw new RuntimeException(e);
      }
    }
  }

  public static class CrashingRemoteProcess implements Action<WorkerProcessContext>, Serializable {
    public void execute(WorkerProcessContext workerProcessContext) {
      TestListenerInterface sender =
          workerProcessContext.getServerConnection().addOutgoing(TestListenerInterface.class);
      sender.send("message 1", 1);
      sender.send("message 2", 2);
      // crash
      Runtime.getRuntime().halt(1);
    }
  }

  public static class BrokenRemoteProcess implements Action<WorkerProcessContext>, Serializable {
    public void execute(WorkerProcessContext workerProcessContext) {
      throw new RuntimeException("broken");
    }
  }

  public static class NoConnectRemoteProcess implements Action<WorkerProcessContext>, Serializable {
    private void readObject(ObjectInputStream instr) {
      System.exit(0);
    }

    public void execute(WorkerProcessContext workerProcessContext) {
      throw new UnsupportedOperationException();
    }
  }

  public interface TestListenerInterface {
    public void send(String message, int count);
  }
}
class InProcessGradleExecuter extends AbstractGradleExecuter {

  private final ProcessEnvironment processEnvironment =
      NativeServices.getInstance().get(ProcessEnvironment.class);

  InProcessGradleExecuter(
      GradleDistribution distribution, TestDirectoryProvider testDirectoryProvider) {
    super(distribution, testDirectoryProvider);
  }

  @Override
  public GradleExecuter reset() {
    DeprecationLogger.reset();
    return super.reset();
  }

  @Override
  protected ExecutionResult doRun() {
    OutputListenerImpl outputListener = new OutputListenerImpl();
    OutputListenerImpl errorListener = new OutputListenerImpl();
    BuildListenerImpl buildListener = new BuildListenerImpl();
    BuildResult result = doRun(outputListener, errorListener, buildListener);
    try {
      result.rethrowFailure();
    } catch (Exception e) {
      throw new UnexpectedBuildFailure(e);
    }
    return assertResult(
        new InProcessExecutionResult(
            buildListener.executedTasks,
            buildListener.skippedTasks,
            new OutputScrapingExecutionResult(
                outputListener.toString(), errorListener.toString())));
  }

  @Override
  protected ExecutionFailure doRunWithFailure() {
    OutputListenerImpl outputListener = new OutputListenerImpl();
    OutputListenerImpl errorListener = new OutputListenerImpl();
    BuildListenerImpl buildListener = new BuildListenerImpl();
    try {
      doRun(outputListener, errorListener, buildListener).rethrowFailure();
      throw new AssertionError("expected build to fail but it did not.");
    } catch (GradleException e) {
      return assertResult(
          new InProcessExecutionFailure(
              buildListener.executedTasks,
              buildListener.skippedTasks,
              new OutputScrapingExecutionFailure(
                  outputListener.writer.toString(), errorListener.writer.toString()),
              e));
    }
  }

  private <T extends ExecutionResult> T assertResult(T result) {
    getResultAssertion().execute(result);
    return result;
  }

  @Override
  protected GradleHandle doStart() {
    return new ForkingGradleHandle(
            getResultAssertion(),
            getDefaultCharacterEncoding(),
            new Factory<JavaExecHandleBuilder>() {
              public JavaExecHandleBuilder create() {
                JavaExecHandleBuilder builder =
                    new JavaExecHandleBuilder(new IdentityFileResolver());
                builder.workingDir(getWorkingDir());
                Set<File> classpath = new DefaultModuleRegistry().getFullClasspath();
                builder.classpath(classpath);
                builder.setMain(Main.class.getName());
                builder.args(getAllArgs());
                return builder;
              }
            })
        .start();
  }

  private BuildResult doRun(
      final OutputListenerImpl outputListener,
      OutputListenerImpl errorListener,
      BuildListenerImpl listener) {
    // Capture the current state of things that we will change during execution
    InputStream originalStdIn = System.in;
    Properties originalSysProperties = new Properties();
    originalSysProperties.putAll(System.getProperties());
    File originalUserDir = new File(originalSysProperties.getProperty("user.dir"));
    Map<String, String> originalEnv = new HashMap<String, String>(System.getenv());

    // Augment the environment for the execution
    System.setIn(getStdin());
    processEnvironment.maybeSetProcessDir(getWorkingDir());
    for (Map.Entry<String, String> entry : getEnvironmentVars().entrySet()) {
      processEnvironment.maybeSetEnvironmentVariable(entry.getKey(), entry.getValue());
    }
    Map<String, String> implicitJvmSystemProperties = getImplicitJvmSystemProperties();
    System.getProperties().putAll(implicitJvmSystemProperties);

    DefaultStartParameter parameter = new DefaultStartParameter();
    parameter.setCurrentDir(getWorkingDir());
    parameter.setShowStacktrace(ShowStacktrace.ALWAYS);

    CommandLineParser parser = new CommandLineParser();
    DefaultCommandLineConverter converter = new DefaultCommandLineConverter();
    converter.configure(parser);
    ParsedCommandLine parsedCommandLine = parser.parse(getAllArgs());

    BuildLayoutParameters layout = converter.getLayoutConverter().convert(parsedCommandLine);

    Map<String, String> properties = new HashMap<String, String>();
    new LayoutToPropertiesConverter().convert(layout, properties);
    converter.getSystemPropertiesConverter().convert(parsedCommandLine, properties);

    new PropertiesToStartParameterConverter().convert(properties, parameter);
    converter.convert(parsedCommandLine, parameter);

    DefaultGradleLauncherFactory factory =
        DeprecationLogger.whileDisabled(
            new Factory<DefaultGradleLauncherFactory>() {
              public DefaultGradleLauncherFactory create() {
                return (DefaultGradleLauncherFactory) GradleLauncher.getFactory();
              }
            });
    factory.addListener(listener);
    GradleLauncher gradleLauncher = factory.newInstance(parameter);
    gradleLauncher.addStandardOutputListener(outputListener);
    gradleLauncher.addStandardErrorListener(errorListener);
    try {
      return gradleLauncher.run();
    } finally {
      // Restore the environment
      System.setProperties(originalSysProperties);
      processEnvironment.maybeSetProcessDir(originalUserDir);
      for (String envVar : getEnvironmentVars().keySet()) {
        String oldValue = originalEnv.get(envVar);
        if (oldValue != null) {
          processEnvironment.maybeSetEnvironmentVariable(envVar, oldValue);
        } else {
          processEnvironment.maybeRemoveEnvironmentVariable(envVar);
        }
      }
      factory.removeListener(listener);
      System.setIn(originalStdIn);
    }
  }

  public DaemonRegistry getDaemonRegistry() {
    throw new UnsupportedOperationException();
  }

  public void assertCanExecute() {
    assertNull(getExecutable());
    assertEquals(getJavaHome(), Jvm.current().getJavaHome());
    String defaultEncoding = getImplicitJvmSystemProperties().get("file.encoding");
    if (defaultEncoding != null) {
      assertEquals(Charset.forName(defaultEncoding), Charset.defaultCharset());
    }
    assertFalse(isRequireGradleHome());
  }

  private static class BuildListenerImpl implements TaskExecutionGraphListener {
    private final List<String> executedTasks = new CopyOnWriteArrayList<String>();
    private final Set<String> skippedTasks = new CopyOnWriteArraySet<String>();

    public void graphPopulated(TaskExecutionGraph graph) {
      List<Task> planned = new ArrayList<Task>(graph.getAllTasks());
      graph.addTaskExecutionListener(new TaskListenerImpl(planned, executedTasks, skippedTasks));
    }
  }

  private static class OutputListenerImpl implements StandardOutputListener {
    private StringWriter writer = new StringWriter();

    @Override
    public String toString() {
      return writer.toString();
    }

    public void onOutput(CharSequence output) {
      writer.append(output);
    }
  }

  private static class TaskListenerImpl implements TaskExecutionListener {
    private final List<Task> planned;
    private final List<String> executedTasks;
    private final Set<String> skippedTasks;

    public TaskListenerImpl(
        List<Task> planned, List<String> executedTasks, Set<String> skippedTasks) {
      this.planned = planned;
      this.executedTasks = executedTasks;
      this.skippedTasks = skippedTasks;
    }

    public void beforeExecute(Task task) {
      assertTrue(planned.contains(task));

      String taskPath = path(task);
      if (taskPath.startsWith(":buildSrc:")) {
        return;
      }

      executedTasks.add(taskPath);
    }

    public void afterExecute(Task task, TaskState state) {
      String taskPath = path(task);
      if (taskPath.startsWith(":buildSrc:")) {
        return;
      }

      if (state.getSkipped()) {
        skippedTasks.add(taskPath);
      }
    }

    private String path(Task task) {
      return task.getProject().getGradle().getParent() == null
          ? task.getPath()
          : ":" + task.getProject().getRootProject().getName() + task.getPath();
    }
  }

  public static class InProcessExecutionResult implements ExecutionResult {
    private final List<String> plannedTasks;
    private final Set<String> skippedTasks;
    private final OutputScrapingExecutionResult outputResult;

    public InProcessExecutionResult(
        List<String> plannedTasks,
        Set<String> skippedTasks,
        OutputScrapingExecutionResult outputResult) {
      this.plannedTasks = plannedTasks;
      this.skippedTasks = skippedTasks;
      this.outputResult = outputResult;
    }

    public String getOutput() {
      return outputResult.getOutput();
    }

    public ExecutionResult assertOutputEquals(
        String expectedOutput, boolean ignoreExtraLines, boolean ignoreLineOrder) {
      outputResult.assertOutputEquals(expectedOutput, ignoreExtraLines, ignoreLineOrder);
      return this;
    }

    public String getError() {
      return outputResult.getError();
    }

    public List<String> getExecutedTasks() {
      return new ArrayList<String>(plannedTasks);
    }

    public ExecutionResult assertTasksExecuted(String... taskPaths) {
      List<String> expected = Arrays.asList(taskPaths);
      assertThat(plannedTasks, equalTo(expected));
      outputResult.assertTasksExecuted(taskPaths);
      return this;
    }

    public Set<String> getSkippedTasks() {
      return new HashSet<String>(skippedTasks);
    }

    public ExecutionResult assertTasksSkipped(String... taskPaths) {
      Set<String> expected = new HashSet<String>(Arrays.asList(taskPaths));
      assertThat(skippedTasks, equalTo(expected));
      outputResult.assertTasksSkipped(taskPaths);
      return this;
    }

    public ExecutionResult assertTaskSkipped(String taskPath) {
      assertThat(skippedTasks, hasItem(taskPath));
      outputResult.assertTaskSkipped(taskPath);
      return this;
    }

    public ExecutionResult assertTasksNotSkipped(String... taskPaths) {
      Set<String> expected = new HashSet<String>(Arrays.asList(taskPaths));
      Set<String> notSkipped = getNotSkippedTasks();
      assertThat(notSkipped, equalTo(expected));
      outputResult.assertTasksNotSkipped(taskPaths);
      return this;
    }

    public ExecutionResult assertTaskNotSkipped(String taskPath) {
      assertThat(getNotSkippedTasks(), hasItem(taskPath));
      outputResult.assertTaskNotSkipped(taskPath);
      return this;
    }

    private Set<String> getNotSkippedTasks() {
      Set<String> notSkipped = new HashSet<String>(plannedTasks);
      notSkipped.removeAll(skippedTasks);
      return notSkipped;
    }
  }

  private static class InProcessExecutionFailure extends InProcessExecutionResult
      implements ExecutionFailure {
    private static final Pattern LOCATION_PATTERN =
        Pattern.compile("(?m)^((\\w+ )+'.+') line: (\\d+)$");
    private final OutputScrapingExecutionFailure outputFailure;
    private final GradleException failure;
    private final String fileName;
    private final String lineNumber;
    private final String description;

    public InProcessExecutionFailure(
        List<String> tasks,
        Set<String> skippedTasks,
        OutputScrapingExecutionFailure outputFailure,
        GradleException failure) {
      super(tasks, skippedTasks, outputFailure);
      this.outputFailure = outputFailure;
      this.failure = failure;

      // Chop up the exception message into its expected parts
      java.util.regex.Matcher matcher = LOCATION_PATTERN.matcher(failure.getMessage());
      if (matcher.find()) {
        fileName = matcher.group(1);
        lineNumber = matcher.group(3);
        description = failure.getMessage().substring(matcher.end()).trim();
      } else {
        fileName = "";
        lineNumber = "";
        description = failure.getMessage().trim();
      }
    }

    public ExecutionFailure assertHasLineNumber(int lineNumber) {
      assertThat(this.lineNumber, equalTo(String.valueOf(lineNumber)));
      outputFailure.assertHasLineNumber(lineNumber);
      return this;
    }

    public ExecutionFailure assertHasFileName(String filename) {
      assertThat(this.fileName, equalTo(filename));
      outputFailure.assertHasFileName(filename);
      return this;
    }

    public ExecutionFailure assertHasCause(String description) {
      assertThatCause(startsWith(description));
      return this;
    }

    public ExecutionFailure assertThatCause(final Matcher<String> matcher) {
      List<Throwable> causes = new ArrayList<Throwable>();
      extractCauses(failure, causes);
      assertThat(causes, Matchers.<Throwable>hasItem(hasMessage(matcher)));
      outputFailure.assertThatCause(matcher);
      return this;
    }

    private void extractCauses(Throwable failure, List<Throwable> causes) {
      if (failure instanceof MultipleBuildFailures) {
        MultipleBuildFailures exception = (MultipleBuildFailures) failure;
        for (Throwable componentFailure : exception.getCauses()) {
          extractCauses(componentFailure, causes);
        }
      } else if (failure instanceof LocationAwareException) {
        causes.addAll(((LocationAwareException) failure).getReportableCauses());
      } else {
        causes.add(failure.getCause());
      }
    }

    public ExecutionFailure assertHasNoCause() {
      if (failure instanceof LocationAwareException) {
        LocationAwareException exception = (LocationAwareException) failure;
        assertThat(exception.getReportableCauses(), isEmpty());
      } else {
        assertThat(failure.getCause(), nullValue());
      }
      outputFailure.assertHasNoCause();
      return this;
    }

    public ExecutionFailure assertHasDescription(String context) {
      assertThatDescription(equalTo(context));
      return this;
    }

    public ExecutionFailure assertThatDescription(Matcher<String> matcher) {
      assertThat(description, matcher);
      outputFailure.assertThatDescription(matcher);
      return this;
    }

    public ExecutionFailure assertTestsFailed() {
      new DetailedExecutionFailure(this).assertTestsFailed();
      return this;
    }

    public DependencyResolutionFailure assertResolutionFailure(String configuration) {
      return new DependencyResolutionFailure(this, configuration);
    }
  }
}
 public DaemonClientServicesSupport(
     ServiceRegistry loggingServices, InputStream buildStandardInput) {
   super(NativeServices.getInstance(), loggingServices);
   this.buildStandardInput = buildStandardInput;
 }
  public abstract static class InstalledToolChain extends ToolChainCandidate {
    private static final ProcessEnvironment PROCESS_ENVIRONMENT =
        NativeServices.getInstance().get(ProcessEnvironment.class);
    protected final List<File> pathEntries = new ArrayList<File>();
    private final String displayName;
    protected final String pathVarName;
    private String originalPath;

    public InstalledToolChain(String displayName) {
      this.displayName = displayName;
      this.pathVarName = OperatingSystem.current().getPathVar();
    }

    InstalledToolChain inPath(File... pathEntries) {
      Collections.addAll(this.pathEntries, pathEntries);
      return this;
    }

    @Override
    public String getDisplayName() {
      return displayName;
    }

    @Override
    public boolean isAvailable() {
      return true;
    }

    public String getTypeDisplayName() {
      return getDisplayName().replaceAll("\\s+\\d+(\\.\\d+)*$", "");
    }

    public abstract String getInstanceDisplayName();

    public ExecutableFixture executable(Object path) {
      return new ExecutableFixture(
          new TestFile(OperatingSystem.current().getExecutableName(path.toString())), this);
    }

    public TestFile objectFile(Object path) {
      return new TestFile(path.toString() + ".o");
    }

    public SharedLibraryFixture sharedLibrary(Object path) {
      return new SharedLibraryFixture(
          new TestFile(OperatingSystem.current().getSharedLibraryName(path.toString())), this);
    }

    public StaticLibraryFixture staticLibrary(Object path) {
      return new StaticLibraryFixture(
          new TestFile(OperatingSystem.current().getStaticLibraryName(path.toString())), this);
    }

    public NativeBinaryFixture resourceOnlyLibrary(Object path) {
      return new NativeBinaryFixture(
          new TestFile(OperatingSystem.current().getSharedLibraryName(path.toString())), this);
    }

    /**
     * Initialise the process environment so that this tool chain is visible to the default
     * discovery mechanism that the plugin uses (eg add the compiler to the PATH).
     */
    public void initialiseEnvironment() {
      String compilerPath = Joiner.on(File.pathSeparator).join(pathEntries);

      if (compilerPath.length() > 0) {
        originalPath = System.getenv(pathVarName);
        String path = compilerPath + File.pathSeparator + originalPath;
        System.out.println(String.format("Using path %s", path));
        PROCESS_ENVIRONMENT.setEnvironmentVariable(pathVarName, path);
      }
    }

    public void resetEnvironment() {
      if (originalPath != null) {
        PROCESS_ENVIRONMENT.setEnvironmentVariable(pathVarName, originalPath);
      }
    }

    public abstract String getBuildScriptConfig();

    public abstract String getImplementationClass();

    public abstract String getPluginClass();

    public boolean isVisualCpp() {
      return false;
    }

    public List<File> getPathEntries() {
      return pathEntries;
    }

    /** The environment required to execute a binary created by this toolchain. */
    public List<String> getRuntimeEnv() {
      // Toolchains should be linking against stuff in the standard locations
      return Collections.emptyList();
    }

    public String getId() {
      return displayName.replaceAll("\\W", "");
    }
  }