/* * Copyright 2013-present Facebook, Inc. * * 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. */ package com.facebook.buck.event.listener; import static com.facebook.buck.rules.BuildRuleSuccessType.BUILT_LOCALLY; import com.facebook.buck.artifact_cache.HttpArtifactCacheEvent; import com.facebook.buck.event.ConsoleEvent; import com.facebook.buck.event.InstallEvent; import com.facebook.buck.parser.ParseEvent; import com.facebook.buck.rules.BuildEvent; import com.facebook.buck.rules.BuildRuleEvent; import com.facebook.buck.rules.BuildRuleStatus; import com.facebook.buck.rules.IndividualTestEvent; import com.facebook.buck.rules.TestRunEvent; import com.facebook.buck.rules.TestStatusMessageEvent; import com.facebook.buck.test.TestResultSummaryVerbosity; import com.facebook.buck.test.TestStatusMessage; import com.facebook.buck.timing.Clock; import com.facebook.buck.util.Console; import com.facebook.buck.util.environment.ExecutionEnvironment; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.eventbus.Subscribe; import java.nio.file.Path; import java.util.Collection; import java.util.Locale; import java.util.Optional; import java.util.concurrent.atomic.AtomicLong; import java.util.logging.Level; /** * Implementation of {@code AbstractConsoleEventBusListener} for terminals that don't support ansi. */ public class SimpleConsoleEventBusListener extends AbstractConsoleEventBusListener { private final Locale locale; private final AtomicLong parseTime; private final TestResultFormatter testFormatter; private final ImmutableList.Builder<TestStatusMessage> testStatusMessageBuilder = ImmutableList.builder(); public SimpleConsoleEventBusListener( Console console, Clock clock, TestResultSummaryVerbosity summaryVerbosity, Locale locale, Path testLogPath, ExecutionEnvironment executionEnvironment) { super(console, clock, locale, executionEnvironment); this.locale = locale; this.parseTime = new AtomicLong(0); this.testFormatter = new TestResultFormatter( console.getAnsi(), console.getVerbosity(), summaryVerbosity, locale, Optional.of(testLogPath)); } @Override @Subscribe public void parseFinished(ParseEvent.Finished finished) { super.parseFinished(finished); if (console.getVerbosity().isSilent()) { return; } ImmutableList.Builder<String> lines = ImmutableList.builder(); this.parseTime.set( logEventPair( "PARSING BUCK FILES", /* suffix */ Optional.empty(), clock.currentTimeMillis(), 0L, buckFilesProcessing.values(), getEstimatedProgressOfProcessingBuckFiles(), lines)); printLines(lines); } @Override @Subscribe public void buildFinished(BuildEvent.Finished finished) { super.buildFinished(finished); if (console.getVerbosity().isSilent()) { return; } long currentMillis = clock.currentTimeMillis(); ImmutableList.Builder<String> lines = ImmutableList.builder(); long buildStartedTime = buildStarted != null ? buildStarted.getTimestamp() : Long.MAX_VALUE; long buildFinishedTime = buildFinished != null ? buildFinished.getTimestamp() : currentMillis; Collection<EventPair> processingEvents = getEventsBetween(buildStartedTime, buildFinishedTime, buckFilesProcessing.values()); long offsetMs = getTotalCompletedTimeFromEventPairs(processingEvents); logEventPair( "BUILDING", getOptionalBuildLineSuffix(), currentMillis, offsetMs, buildStarted, buildFinished, getApproximateBuildProgress(), lines); String httpStatus = renderHttpUploads(); if (httpArtifactUploadsScheduledCount.get() > 0) { lines.add("WAITING FOR HTTP CACHE UPLOADS " + httpStatus); } lines.add(getNetworkStatsLine(finished)); printLines(lines); } @Override @Subscribe public void installFinished(InstallEvent.Finished finished) { super.installFinished(finished); if (console.getVerbosity().isSilent()) { return; } ImmutableList.Builder<String> lines = ImmutableList.builder(); logEventPair( "INSTALLING", /* suffix */ Optional.empty(), clock.currentTimeMillis(), 0L, installStarted, installFinished, Optional.empty(), lines); printLines(lines); } @Subscribe public void logEvent(ConsoleEvent event) { if (console.getVerbosity().isSilent() && !event.getLevel().equals(Level.WARNING) && !event.getLevel().equals(Level.SEVERE)) { return; } ImmutableList.Builder<String> lines = ImmutableList.builder(); formatConsoleEvent(event, lines); printLines(lines); } @Override public void printSevereWarningDirectly(String line) { logEvent(ConsoleEvent.severe(line)); } @Subscribe public void testRunStarted(TestRunEvent.Started event) { if (console.getVerbosity().isSilent()) { return; } ImmutableList.Builder<String> lines = ImmutableList.builder(); testFormatter.runStarted( lines, event.isRunAllTests(), event.getTestSelectorList(), event.shouldExplainTestSelectorList(), event.getTargetNames(), TestResultFormatter.FormatMode.BEFORE_TEST_RUN); printLines(lines); } @Subscribe public void testRunCompleted(TestRunEvent.Finished event) { if (console.getVerbosity().isSilent()) { return; } ImmutableList.Builder<String> lines = ImmutableList.builder(); ImmutableList<TestStatusMessage> testStatusMessages; synchronized (testStatusMessageBuilder) { testStatusMessages = testStatusMessageBuilder.build(); } testFormatter.runComplete(lines, event.getResults(), testStatusMessages); printLines(lines); } @Subscribe public void testResultsAvailable(IndividualTestEvent.Finished event) { if (console.getVerbosity().isSilent()) { return; } ImmutableList.Builder<String> lines = ImmutableList.builder(); testFormatter.reportResult(lines, event.getResults()); printLines(lines); } @Override @Subscribe public void buildRuleFinished(BuildRuleEvent.Finished finished) { super.buildRuleFinished(finished); if (finished.getStatus() != BuildRuleStatus.SUCCESS || console.getVerbosity().isSilent()) { return; } String jobsInfo = ""; if (ruleCount.isPresent()) { jobsInfo = String.format(locale, "%d/%d JOBS", numRulesCompleted.get(), ruleCount.get()); } String line = String.format( locale, "%s %s %s %s", finished.getResultString(), jobsInfo, formatElapsedTime(finished.getDuration().getWallMillisDuration()), finished.getBuildRule().getFullyQualifiedName()); if (BUILT_LOCALLY.equals(finished.getSuccessType().orElse(null)) || console.getVerbosity().shouldPrintBinaryRunInformation()) { console.getStdErr().println(line); } } @Override @Subscribe public void onHttpArtifactCacheShutdownEvent(HttpArtifactCacheEvent.Shutdown event) { super.onHttpArtifactCacheShutdownEvent(event); if (console.getVerbosity().isSilent()) { return; } ImmutableList.Builder<String> lines = ImmutableList.builder(); logHttpCacheUploads(lines); printLines(lines); } @Subscribe public void testStatusMessageStarted(TestStatusMessageEvent.Started started) { synchronized (testStatusMessageBuilder) { if (console.getVerbosity().isSilent()) { return; } testStatusMessageBuilder.add(started.getTestStatusMessage()); } } @Subscribe public void testStatusMessageFinished(TestStatusMessageEvent.Finished finished) { synchronized (testStatusMessageBuilder) { if (console.getVerbosity().isSilent()) { return; } testStatusMessageBuilder.add(finished.getTestStatusMessage()); } } private void printLines(ImmutableList.Builder<String> lines) { // Print through the {@code DirtyPrintStreamDecorator} so printing from the simple console // is considered to dirty stderr and stdout and so it gets synchronized to avoid interlacing // output. ImmutableList<String> stringList = lines.build(); if (stringList.size() == 0) { return; } console.getStdErr().println(Joiner.on("\n").join(stringList)); } }