Skip to content

Commit

Permalink
Cargo logging listener (#23)
Browse files Browse the repository at this point in the history
* Add cargo logging listener

Issue: #6

* Enclose compilation messages

Issue: #6

* Fix filename case
  • Loading branch information
dtretyakov authored Sep 27, 2016
1 parent d234e20 commit a8f902f
Show file tree
Hide file tree
Showing 16 changed files with 609 additions and 1 deletion.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ project(':plugin-rust-agent') {
compile project(':plugin-rust-common')
provided "org.jetbrains.teamcity.internal:agent:${teamcityVersion}"
testCompile 'org.testng:testng:6.8'
testCompile 'org.jmock:jmock:2.5.1'
}
agentPlugin.version = null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@
import jetbrains.buildServer.RunBuildException;
import jetbrains.buildServer.agent.ToolCannotBeFoundException;
import jetbrains.buildServer.agent.runner.BuildServiceAdapter;
import jetbrains.buildServer.agent.runner.ProcessListener;
import jetbrains.buildServer.agent.runner.ProgramCommandLine;
import jetbrains.buildServer.rust.cargo.*;
import jetbrains.buildServer.rust.logging.CargoLoggerFactory;
import jetbrains.buildServer.rust.logging.CargoLoggingListener;
import jetbrains.buildServer.util.StringUtil;
import org.jetbrains.annotations.NotNull;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -23,7 +27,6 @@
* Cargo runner service.
*/
public class CargoRunnerBuildService extends BuildServiceAdapter {

private final Map<String, ArgumentsProvider> myArgumentsProviders;

public CargoRunnerBuildService() {
Expand Down Expand Up @@ -74,4 +77,11 @@ public ProgramCommandLine makeProgramCommandLine() throws RunBuildException {

return createProgramCommandline(toolPath, arguments);
}

@NotNull
@Override
public List<ProcessListener> getListeners() {
final CargoLoggerFactory loggerFactory = new CargoLoggerFactory(getLogger());
return Collections.<ProcessListener>singletonList(new CargoLoggingListener(loggerFactory));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* See LICENSE in the project root for license information.
*/

package jetbrains.buildServer.rust.logging;

import jetbrains.buildServer.agent.BuildProgressLogger;
import org.jetbrains.annotations.NotNull;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* Compiling logger.
*/
public class CargoCompileLogger extends CargoDefaultLogger {
private static Pattern PROJECT_NAME_PATTERN = Pattern.compile("([^\\s]+)");
private static final String COMPILATION_STARTED_FORMAT = "##teamcity[compilationStarted compiler='rustc%s']";
private static final String COMPILATION_FINISHED_FORMAT = "##teamcity[compilationFinished compiler='rustc%s']";
private static final String COMPILATION_MESSAGE_FORMAT = "##teamcity[message text='%s']";
private final BuildProgressLogger myLogger;
private String myProjectName;

public CargoCompileLogger(@NotNull final BuildProgressLogger logger) {
super(logger);
myLogger = logger;
}

@Override
public void onEnter(@NotNull final String text) {
final Matcher matcher = PROJECT_NAME_PATTERN.matcher(text);
myProjectName = matcher.find() ? ":" + matcher.group(1) : "";
myLogger.message(String.format(COMPILATION_STARTED_FORMAT, myProjectName));

final String message = String.format("%s %s", CargoState.Compiling, text);
myLogger.message(String.format(COMPILATION_MESSAGE_FORMAT, message));
}

@Override
public void processLine(@NotNull final String text) {
myLogger.message(String.format(COMPILATION_MESSAGE_FORMAT, text));
}

@Override
public boolean canChangeState(CargoState state, String text) {
return !(state == CargoState.Running && text.startsWith("`rustc"));
}

@Override
public void onLeave() {
myLogger.message(String.format(COMPILATION_FINISHED_FORMAT, myProjectName));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* See LICENSE in the project root for license information.
*/

package jetbrains.buildServer.rust.logging;

import jetbrains.buildServer.agent.BuildProgressLogger;
import org.jetbrains.annotations.NotNull;

/**
* Default logger implementation.
*/
public class CargoDefaultLogger implements CargoLogger {

private final BuildProgressLogger myLogger;

public CargoDefaultLogger(@NotNull final BuildProgressLogger logger){
myLogger = logger;
}

@Override
public void onEnter(@NotNull final String text) {
myLogger.message(text);
}

@Override
public void processLine(@NotNull final String text) {
myLogger.message(text);
}

@Override
public void processError(@NotNull final String text) {
myLogger.error(text);
}

@Override
public void onLeave() {
}

@Override
public boolean canChangeState(CargoState state, String text) {
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* See LICENSE in the project root for license information.
*/

package jetbrains.buildServer.rust.logging;

import org.jetbrains.annotations.NotNull;

/**
* Cargo logger.
*/
public interface CargoLogger {
void onEnter(@NotNull final String text);
void processLine(@NotNull final String text);
void processError(@NotNull final String text);
void onLeave();
boolean canChangeState(CargoState state, String text);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* See LICENSE in the project root for license information.
*/

package jetbrains.buildServer.rust.logging;

import jetbrains.buildServer.agent.BuildProgressLogger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.HashMap;
import java.util.Map;

/**
* @author Dmitry.Tretyakov
* Date: 25.09.2016
* Time: 21:49
*/
public class CargoLoggerFactory {
private final Map<CargoState, CargoLogger> myLoggers;

public CargoLoggerFactory(@NotNull final BuildProgressLogger logger){
myLoggers = new HashMap<CargoState, CargoLogger>();
myLoggers.put(CargoState.Running, new CargoStateLogger(logger, CargoState.Running));
myLoggers.put(CargoState.Compiling, new CargoCompileLogger(logger));
myLoggers.put(CargoState.Error, new CargoStateLogger(logger, CargoState.Error));
myLoggers.put(CargoState.Warning, new CargoStateLogger(logger, CargoState.Warning));
myLoggers.put(CargoState.Documenting, new CargoStateLogger(logger, CargoState.Documenting));
myLoggers.put(CargoState.Fresh, new CargoStateLogger(logger, CargoState.Fresh));
myLoggers.put(CargoState.Updating, new CargoStateLogger(logger, CargoState.Updating));
myLoggers.put(CargoState.Adding, new CargoStateLogger(logger, CargoState.Adding));
myLoggers.put(CargoState.Removing, new CargoStateLogger(logger, CargoState.Removing));
myLoggers.put(CargoState.DocTests, new CargoStateLogger(logger, CargoState.DocTests));
myLoggers.put(CargoState.Packaging, new CargoStateLogger(logger, CargoState.Packaging));
myLoggers.put(CargoState.Downloading, new CargoStateLogger(logger, CargoState.Downloading));
myLoggers.put(CargoState.Uploading, new CargoStateLogger(logger, CargoState.Uploading));
myLoggers.put(CargoState.Verifying, new CargoStateLogger(logger, CargoState.Verifying));
myLoggers.put(CargoState.Archiving, new CargoStateLogger(logger, CargoState.Archiving));
myLoggers.put(CargoState.Installing, new CargoStateLogger(logger, CargoState.Installing));
myLoggers.put(CargoState.Replacing, new CargoStateLogger(logger, CargoState.Replacing));
myLoggers.put(CargoState.Default, new CargoDefaultLogger(logger));
myLoggers.put(CargoState.Testing, new CargoTestingLogger(logger));
}

@NotNull
public CargoLogger getLogger(final CargoState state) {
return myLoggers.get(state);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* See LICENSE in the project root for license information.
*/

package jetbrains.buildServer.rust.logging;

import jetbrains.buildServer.agent.runner.ProcessListenerAdapter;
import jetbrains.buildServer.util.StringUtil;
import org.jetbrains.annotations.NotNull;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* Handles build messages from cargo tool.
*/
public class CargoLoggingListener extends ProcessListenerAdapter {
private static Pattern myStatement = Pattern.compile("^([\\w][\\w-]+\\:?)\\s+(.*)?$");
private static Pattern myTestsStart = Pattern.compile("^running \\d+ tests?$");
private final CargoLoggerFactory myLoggerFactory;
private CargoLogger myLogger;
private String myLastLine;

public CargoLoggingListener(@NotNull final CargoLoggerFactory loggerFactory) {
myLoggerFactory = loggerFactory;
myLogger = loggerFactory.getLogger(CargoState.Default);
}

@Override
public void onStandardOutput(@NotNull String text) {
text = text.trim();

if (StringUtil.isEmptyOrSpaces(text)) return;

final String lastLine = myLastLine;
myLastLine = text.trim();

final Matcher testsMatcher = myTestsStart.matcher(text);
if (testsMatcher.find()) {
final String testSuiteName = getTestSuiteName(lastLine);
changeState(CargoState.Testing, testSuiteName);
return;
}

final Matcher stateMatcher = myStatement.matcher(text);
if (stateMatcher.find()) {
final String stateKey = stateMatcher.group(1);
final String stateText = stateMatcher.group(2);

final CargoState state = CargoState.get(stateKey);
if (state != null && myLogger.canChangeState(state, stateText)) {
changeState(state, stateText);
return;
}
}

myLogger.processLine(text);
}

private void changeState(@NotNull final CargoState state, @NotNull final String text) {
final CargoLogger logger = myLoggerFactory.getLogger(state);

myLogger.onLeave();
logger.onEnter(text);
myLogger = logger;
}

@Override
public void onErrorOutput(@NotNull final String text) {
myLogger.processError(text);
}

@Override
public void processFinished(int exitCode) {
myLogger.onLeave();
}

@NotNull
private static String getTestSuiteName(@NotNull final String text) {
if (text.startsWith(CargoState.Running.toString())) {
int slashIndex = text.lastIndexOf("/");
if (slashIndex < 0) {
slashIndex = text.lastIndexOf("\\");
}

if (slashIndex > 0) {
final int dashIndex = text.lastIndexOf("-");
if (dashIndex > slashIndex) {
return text.substring(slashIndex + 1, dashIndex);
}
}
} else if (text.startsWith(CargoState.DocTests.toString())) {
return text;
}

return "";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* See LICENSE in the project root for license information.
*/

package jetbrains.buildServer.rust.logging;

import org.jetbrains.annotations.NotNull;

import java.util.HashMap;
import java.util.Map;

/**
* Cargo states.
* Reference: https://github.com/rust-lang/cargo/blob/master/tests/cargotest/support/mod.rs
*/
public enum CargoState {
Running("Running"),
Compiling("Compiling"),
Error("error:"),
Warning("warning:"),
Documenting("Documenting"),
Fresh("Fresh"),
Updating("Updating"),
Adding("Adding"),
Removing("Removing"),
DocTests("Doc-tests"),
Packaging("Packaging"),
Downloading("Downloading"),
Uploading("Uploading"),
Verifying("Verifying"),
Archiving("Archiving"),
Installing("Installing"),
Replacing("Replacing"),

Default("Default"),
Testing("Testing");

private String myName;

CargoState(@NotNull final String name) {
myName = name;
}

@Override
public String toString() {
return myName;
}

private static final Map<String, CargoState> STATES = new HashMap<String, CargoState>();

static {
for (CargoState state : values()) {
STATES.put(state.myName, state);
}
}

public static CargoState get(String value) {
return STATES.get(value);
}
}
Loading

0 comments on commit a8f902f

Please sign in to comment.