mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Build tw: convert build output reader to kotlin
This commit is contained in:
@@ -1,235 +0,0 @@
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.build.output;
|
||||
|
||||
import com.intellij.build.BuildProgressListener;
|
||||
import com.intellij.build.events.BuildEvent;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* @author Vladislav.Soroka
|
||||
*/
|
||||
public class BuildOutputInstantReaderImpl implements BuildOutputInstantReader, Closeable, Appendable {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.build.output.BuildOutputInstantReader");
|
||||
private static final int MAX_LINES_BUFFER_SIZE = 50;
|
||||
|
||||
private final Object myBuildId;
|
||||
@Nullable
|
||||
private StringBuilder myBuffer;
|
||||
private final BlockingQueue<String> myQueue = new LinkedTransferQueue<>();
|
||||
|
||||
private int myCurrentIndex = -1;
|
||||
private final LinkedList<String> myLinesBuffer = new LinkedList<>();
|
||||
private static final String SHUTDOWN_PILL = new String("Poison Pill Shutdown");
|
||||
|
||||
private final Thread myThread;
|
||||
private final AtomicBoolean myStarted = new AtomicBoolean();
|
||||
private final AtomicBoolean myClosed = new AtomicBoolean();
|
||||
|
||||
public BuildOutputInstantReaderImpl(@NotNull Object buildId,
|
||||
@NotNull BuildProgressListener buildProgressListener,
|
||||
@NotNull List<BuildOutputParser> parsers) {
|
||||
myBuildId = buildId;
|
||||
myThread = new Thread(() -> {
|
||||
Ref<BuildEvent> lastMessageRef = Ref.create();
|
||||
Consumer<BuildEvent> messageConsumer = event -> {
|
||||
//do not add duplicates, e.g. sometimes same messages can be added both to stdout and stderr
|
||||
if (!event.equals(lastMessageRef.get())) {
|
||||
buildProgressListener.onEvent(event);
|
||||
}
|
||||
lastMessageRef.set(event);
|
||||
};
|
||||
|
||||
String line;
|
||||
while ((line = this.readLine()) != null) {
|
||||
if (line.trim().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
for (BuildOutputParser parser : parsers) {
|
||||
if (parser.parse(line, new BuildOutputInstantReaderWrapper(this), messageConsumer)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, "Build output processor");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getBuildId() {
|
||||
return myBuildId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuildOutputInstantReaderImpl append(CharSequence csq) {
|
||||
for (int i = 0; i < csq.length(); i++) {
|
||||
append(csq.charAt(i));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuildOutputInstantReaderImpl append(CharSequence csq, int start, int end) {
|
||||
append(csq.subSequence(start, end));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuildOutputInstantReaderImpl append(char c) {
|
||||
if (myBuffer == null) {
|
||||
myBuffer = new StringBuilder();
|
||||
}
|
||||
if (c == '\n') {
|
||||
doFlush();
|
||||
}
|
||||
else {
|
||||
myBuffer.append(c);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
doFlush();
|
||||
try {
|
||||
myQueue.put(SHUTDOWN_PILL);
|
||||
myThread.join(TimeUnit.MINUTES.toMillis(1));
|
||||
}
|
||||
catch (InterruptedException ignore) {
|
||||
}
|
||||
finally {
|
||||
myClosed.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void doFlush() {
|
||||
if (myBuffer == null) {
|
||||
return;
|
||||
}
|
||||
if (myClosed.get()) {
|
||||
LOG.warn("Build output reader closed");
|
||||
myBuffer.setLength(0);
|
||||
return;
|
||||
}
|
||||
|
||||
String line = myBuffer.toString();
|
||||
myBuffer.setLength(0);
|
||||
try {
|
||||
if (myStarted.compareAndSet(false, true)) {
|
||||
myThread.start();
|
||||
}
|
||||
myQueue.put(line);
|
||||
}
|
||||
catch (InterruptedException ignore) {
|
||||
myClosed.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String readLine() {
|
||||
if (myCurrentIndex < -1) {
|
||||
LOG.error("Wrong buffered output lines index");
|
||||
myCurrentIndex = -1;
|
||||
}
|
||||
if (myClosed.get()) {
|
||||
if (myCurrentIndex > 0 && myLinesBuffer.size() > myCurrentIndex) {
|
||||
return myLinesBuffer.get(myCurrentIndex++);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
myCurrentIndex++;
|
||||
if (myLinesBuffer.size() > myCurrentIndex) {
|
||||
return myLinesBuffer.get(myCurrentIndex);
|
||||
}
|
||||
try {
|
||||
String line = myQueue.take();
|
||||
if (line == SHUTDOWN_PILL) {
|
||||
myClosed.set(true);
|
||||
return null;
|
||||
}
|
||||
myLinesBuffer.addLast(line);
|
||||
if (myLinesBuffer.size() > MAX_LINES_BUFFER_SIZE) {
|
||||
myLinesBuffer.removeFirst();
|
||||
myCurrentIndex--;
|
||||
}
|
||||
return line;
|
||||
}
|
||||
catch (InterruptedException ignore) {
|
||||
myClosed.set(true);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushBack() {
|
||||
pushBack(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushBack(int numberOfLines) {
|
||||
myCurrentIndex -= numberOfLines;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCurrentLine() {
|
||||
return myCurrentIndex >= 0 && myLinesBuffer.size() > myCurrentIndex ? myLinesBuffer.get(myCurrentIndex) : null;
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
@TestOnly
|
||||
static int getMaxLinesBufferSize() {
|
||||
return MAX_LINES_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
private static class BuildOutputInstantReaderWrapper implements BuildOutputInstantReader {
|
||||
private final BuildOutputInstantReaderImpl myReader;
|
||||
private int myLinesRead = 0;
|
||||
|
||||
public BuildOutputInstantReaderWrapper(@NotNull BuildOutputInstantReaderImpl reader) {myReader = reader;}
|
||||
|
||||
@Override
|
||||
public Object getBuildId() {
|
||||
return myReader.myBuildId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String readLine() {
|
||||
String line = myReader.readLine();
|
||||
if (line != null) myLinesRead++;
|
||||
return line;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushBack() {
|
||||
pushBack(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushBack(int numberOfLines) {
|
||||
if (numberOfLines > myLinesRead) {
|
||||
numberOfLines = myLinesRead;
|
||||
}
|
||||
myReader.pushBack(numberOfLines);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCurrentLine() {
|
||||
return myReader.getCurrentLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.build.output
|
||||
|
||||
import com.intellij.build.BuildProgressListener
|
||||
import com.intellij.build.events.BuildEvent
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.util.Ref
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import java.io.Closeable
|
||||
import java.util.*
|
||||
import java.util.concurrent.LinkedTransferQueue
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
/**
|
||||
* @author Vladislav.Soroka
|
||||
*/
|
||||
class BuildOutputInstantReaderImpl(private val myBuildId: Any,
|
||||
buildProgressListener: BuildProgressListener,
|
||||
parsers: List<BuildOutputParser>) : BuildOutputInstantReader, Closeable, Appendable {
|
||||
private var myBuffer: StringBuilder? = null
|
||||
private val myQueue = LinkedTransferQueue<String>()
|
||||
|
||||
private val myLinesBuffer = LinkedList<String>()
|
||||
private var myCurrentIndex = -1
|
||||
|
||||
private val myThread: Thread
|
||||
private val myStarted = AtomicBoolean()
|
||||
private val myClosed = AtomicBoolean()
|
||||
|
||||
init {
|
||||
myThread = Thread({
|
||||
val lastMessageRef = Ref.create<BuildEvent>()
|
||||
val messageConsumer = { event: BuildEvent ->
|
||||
//do not add duplicates, e.g. sometimes same messages can be added both to stdout and stderr
|
||||
if (event != lastMessageRef.get()) {
|
||||
buildProgressListener.onEvent(event)
|
||||
}
|
||||
lastMessageRef.set(event)
|
||||
}
|
||||
|
||||
while (true) {
|
||||
val line = this.readLine() ?: break
|
||||
if (line.isBlank()) continue
|
||||
|
||||
for (parser in parsers) {
|
||||
if (parser.parse(line, BuildOutputInstantReaderWrapper(this), messageConsumer)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}, "Build output processor")
|
||||
}
|
||||
|
||||
override fun getBuildId(): Any {
|
||||
return myBuildId
|
||||
}
|
||||
|
||||
override fun append(csq: CharSequence): BuildOutputInstantReaderImpl {
|
||||
for (i in 0 until csq.length) {
|
||||
append(csq[i])
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
override fun append(csq: CharSequence, start: Int, end: Int): BuildOutputInstantReaderImpl {
|
||||
append(csq.subSequence(start, end))
|
||||
return this
|
||||
}
|
||||
|
||||
override fun append(c: Char): BuildOutputInstantReaderImpl {
|
||||
if (myBuffer == null) {
|
||||
myBuffer = StringBuilder()
|
||||
}
|
||||
if (c == '\n') {
|
||||
doFlush()
|
||||
}
|
||||
else {
|
||||
myBuffer!!.append(c)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
doFlush()
|
||||
try {
|
||||
myQueue.put(SHUTDOWN_PILL)
|
||||
myThread.join(TimeUnit.MINUTES.toMillis(1))
|
||||
}
|
||||
catch (ignore: InterruptedException) {
|
||||
}
|
||||
finally {
|
||||
myClosed.set(true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun doFlush() {
|
||||
if (myBuffer == null) {
|
||||
return
|
||||
}
|
||||
if (myClosed.get()) {
|
||||
LOG.warn("Build output reader closed")
|
||||
myBuffer!!.setLength(0)
|
||||
return
|
||||
}
|
||||
|
||||
val line = myBuffer!!.toString()
|
||||
myBuffer!!.setLength(0)
|
||||
try {
|
||||
if (myStarted.compareAndSet(false, true)) {
|
||||
myThread.start()
|
||||
}
|
||||
myQueue.put(line)
|
||||
}
|
||||
catch (ignore: InterruptedException) {
|
||||
myClosed.set(true)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun readLine(): String? {
|
||||
if (myCurrentIndex < -1) {
|
||||
LOG.error("Wrong buffered output lines index")
|
||||
myCurrentIndex = -1
|
||||
}
|
||||
if (myClosed.get()) {
|
||||
return if (myCurrentIndex > 0 && myLinesBuffer.size > myCurrentIndex) {
|
||||
myLinesBuffer[myCurrentIndex++]
|
||||
}
|
||||
else null
|
||||
}
|
||||
|
||||
myCurrentIndex++
|
||||
if (myLinesBuffer.size > myCurrentIndex) {
|
||||
return myLinesBuffer[myCurrentIndex]
|
||||
}
|
||||
try {
|
||||
val line = myQueue.take()
|
||||
if (line === SHUTDOWN_PILL) {
|
||||
myClosed.set(true)
|
||||
return null
|
||||
}
|
||||
myLinesBuffer.addLast(line)
|
||||
if (myLinesBuffer.size > getMaxLinesBufferSize()) {
|
||||
myLinesBuffer.removeFirst()
|
||||
myCurrentIndex--
|
||||
}
|
||||
return line
|
||||
}
|
||||
catch (ignore: InterruptedException) {
|
||||
myClosed.set(true)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
override fun pushBack() = pushBack(1)
|
||||
|
||||
override fun pushBack(numberOfLines: Int) {
|
||||
myCurrentIndex -= numberOfLines
|
||||
}
|
||||
|
||||
override fun getCurrentLine(): String? {
|
||||
return if (myCurrentIndex >= 0 && myLinesBuffer.size > myCurrentIndex) myLinesBuffer[myCurrentIndex] else null
|
||||
}
|
||||
|
||||
private class BuildOutputInstantReaderWrapper(private val myReader: BuildOutputInstantReaderImpl) : BuildOutputInstantReader {
|
||||
private var myLinesRead = 0
|
||||
|
||||
override fun getBuildId(): Any = myReader.myBuildId
|
||||
|
||||
override fun readLine(): String? {
|
||||
val line = myReader.readLine()
|
||||
if (line != null) myLinesRead++
|
||||
return line
|
||||
}
|
||||
|
||||
override fun pushBack() = pushBack(1)
|
||||
|
||||
override fun pushBack(numberOfLines: Int) {
|
||||
if (numberOfLines > myLinesRead) {
|
||||
myReader.pushBack(myLinesRead)
|
||||
}
|
||||
else {
|
||||
myReader.pushBack(numberOfLines)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getCurrentLine(): String? = myReader.currentLine
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG = Logger.getInstance("#com.intellij.build.output.BuildOutputInstantReader")
|
||||
@ApiStatus.Experimental
|
||||
@TestOnly
|
||||
fun getMaxLinesBufferSize() = 50
|
||||
private const val SHUTDOWN_PILL = "Poison Pill Shutdown"
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@ import com.intellij.build.BuildProgressListener
|
||||
import com.intellij.build.events.MessageEvent
|
||||
import com.intellij.build.events.MessageEvent.Kind.*
|
||||
import com.intellij.build.events.impl.MessageEventImpl
|
||||
import com.intellij.build.output.BuildOutputInstantReaderImpl.getMaxLinesBufferSize
|
||||
import com.intellij.build.output.BuildOutputInstantReaderImpl.Companion.getMaxLinesBufferSize
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
Reference in New Issue
Block a user