Ninja: Zeigt die Ausgabe des Unterbefehls an, wenn nur ein Befehl ausgeführt wird

Erstellt am 16. Apr. 2013  ·  16Kommentare  ·  Quelle: ninja-build/ninja

Ausgangspufferung ist großartig… …aber nur, wenn tatsächlich Parallelität vorhanden ist. Beispielsweise enthält ein cmake-Build oft einen Engpassprozess, der alle Tests ausführt (manchmal in vielen Threads, aber ctest verwaltet seine eigene Ausgabe). Wenn die Ausgabe nicht gepuffert wäre, könnten wir ein schönes Fortschrittsdiagramm sehen. So wie die Dinge stehen, bekommen wir den Graphen erst, wenn der Build fertig ist.

Hilfreichster Kommentar

Es sollte auch beachtet werden, dass einige Anwendungen (z. B. Meson) farbcodierte ANSI-Protokollmeldungen drucken, wenn sie direkt an ein Terminal schreiben, aber Klartext, wenn ihre Ausgabe an eine Pipe oder eine Datei weitergeleitet wird.

Es gibt wirklich zwei Fälle, in denen Sie die Ausgabe in Echtzeit sehen möchten: Regenerieren der Ninja-Datei und Ausführen der Testsuite. Keines davon wird gleichzeitig mit einem anderen Build-Schritt ausgeführt. Vielleicht könnte eine neue Regelvariable, sagen wir mal unbuffered_output, erstellt werden. Wenn es auf true gesetzt ist, würde Ninja die Ausgabe dieses Schritts nicht abrufen, sondern den untergeordneten Prozess direkt in stdout/stderr schreiben lassen.

Alle 16 Kommentare

Ich dachte, wir hätten hier einen Fehler, hmmm... jemand hatte in der Vergangenheit versucht, so etwas einzuhacken -- wenn wir nur einen einzigen Befehl ausführen (und wir wissen, da wir die Kontrolle über den Graphen haben, dass wir werden keinen weiteren starten, bis dieser fertig ist), sollten wir die Ausgabe dieses Befehls "live" zeigen.

Ich habe versucht, dieses Problem beim Start von Subprozessen anzugehen, indem ich keine Pipes erstellt habe, aber ich bin ein Dummkopf, wenn es um die Steuerung von Subprozessen geht, und was ich getan habe, hat nur dazu geführt, dass Ninja hängen bleibt (vermutlich darauf wartend, dass ein fd geschlossen wird). Aber ich denke, mein Ansatz war sowieso suboptimal. Ich denke, was wir wollen, ist, die Pipes immer zu erstellen, aber wenn wir einen Zustand erreichen, in dem alles darauf wartet, dass ein Befehl abgeschlossen wird, beginnen Sie mit dem Streamen dessen, was sich in seiner Pipe befindet, nach stdout. @martine , wenn du mir ein paar Hinweise gibst, wo/wie ich das angehen soll, kann ich es noch einmal versuchen.

Ich bin mir nicht sicher, aber wo ich anfangen würde, ist: Subprocess umschließt jeden Unterprozess und speichert seine Ausgabe, sobald sie in buf_ kommt. Dann gibt es einen CommandRunner , der es schafft, eine Menge davon zu betreiben. Siehe RealCommandRunner::WaitForCommand , das ein Array von Befehlen hat, auf das es wartet (so dass Sie prüfen könnten, ob es die Größe eins hat) und dann .DoWork() aufruft, um einige Ausgaben dieses Befehls zu lesen. Jedes Mal, wenn .DoWork zurückgibt, hat der Unterbefehl entweder mehr Ausgabe geschrieben oder abgeschlossen, sodass diese Schleife wahrscheinlich der Ort ist, an dem die Ausgabe gestreamt wird.

Hier ist eine Einfügung des vorhandenen Codes mit einer neuen Ergänzung:

  while ((subproc = subprocs_.NextFinished()) == NULL) {
    if (subprocs_.size() == 1) {
      // new code, we know we're waiting for only one process
      // perhaps figure out the one subproc here, then loop calling dowork and tracking what part of the buf we've printed
    }
    bool interrupted = subprocs_.DoWork();
    if (interrupted)
      return false;
  }

Es ist ziemlich hässlich, den einzelnen Prozess dort in einen Sonderfall zu versetzen, aber vielleicht gibt es eine Möglichkeit, diesen Code so umzugestalten, dass er nur einen Fluss hat. Außerdem sollte diese Funktion nicht direkt drucken, sondern einen Zeiger auf das BuildStatus -Objekt des Aufrufers nehmen und seine inkrementellen Aktualisierungen darüber senden. (Dadurch funktionieren Dinge wie ninja -v immer noch richtig.)

Dies bricht auch eine Annahme, die unter Windows gemacht wurde, wo wir die Ausgabe eines Befehls parsen, bevor wir sie anzeigen. Aber ich denke, wir können uns später darum kümmern und diese Funktion vielleicht einfach deaktivieren, wenn wir in diesem Zustand sind.

Ich habe auch darüber nachgedacht, dies zu beheben, habe keine Zeit, es zu geben
Priorität. Weitere Gedanken (Linux):

  • Wenn wir einfach weiterhin 'pipe' verwenden, wird die Ausgabe der Befehle sein
    von libc/OS gepuffert, bevor es zu Ninja gelangt (siehe z. B.
    http://www.pixelbeat.org/programming/stdio_buffering/), was evtl
    enttäuschend.
  • Wenn wir trotzdem puffern, könnten wir WaitForComand erweitern, um dies zuzulassen
    Rückgabe bei 'timeout' (Übergabe einer Zeitüberschreitung an pselect(), Hinzufügen einer zusätzlichen
    Wert auf ExitStatus.
    Dann dieser Code:
  • if (!command_runner_->WaitForCommand(&result) ||
    result.status == ExitInterrupted) {
    status_->BuildFinished();
    *err = "vom Benutzer unterbrochen";
    *
    könnte erweitert werden, um entweder Teilausgaben zu drucken, wenn ein Prozess ausgeführt wird
    läuft derzeit ODER aktualisiere den Status, um „[XXX] YYY läuft noch /“ zu drucken,
    was für lange laufende Silent-Befehle, wie zB linker, nett sein könnte.

Am Freitag, den 14. Juni 2013 um 20:17 Uhr schrieb Evan Martin [email protected] :

Ich bin mir nicht sicher, aber wo ich anfangen würde, ist: Subprocess umschließt jeden Subprozess
und speichert seine Ausgabe, sobald sie in buf_ eingeht. Dann gibt es noch einen CommandRunner, der eine Reihe davon verwaltet. Sehen
RealCommandRunner::WaitForCommand, das eine Reihe von Befehlen enthält
warten (damit man prüfen könnte, ob das von Größe eins ist) und dann anruft
.DoWork(), um einige Ausgaben dieses Befehls zu lesen. Jedes Mal, wenn .DoWork zurückkehrt, hat der Unterbefehl entweder mehr Ausgabe geschrieben oder abgeschlossen.
Diese Schleife ist also wahrscheinlich der Ort, an dem die Ausgabe gestreamt werden kann.

Etwas wie:

while ((subproc = subprocs_.NextFinished()) == NULL) {
if (subprocs_.size() == 1) {
// neuer Code, wir wissen, dass wir nur auf einen Prozess warten
// Vielleicht den einen Subproc hier herausfinden, dann dowork aufrufen und nachverfolgen, welchen Teil des Buf wir ausgegeben haben
}
bool unterbrochen = subprocs_.DoWork();
wenn (unterbrochen)
falsch zurückgeben;
}

Es ist jedoch vielleicht etwas hässlich, den einzelnen Prozess dort in einen Sonderfall zu versetzen
Es gibt eine Möglichkeit, diesen Code so umzugestalten, dass er nur einen Fluss hat. Und auch dies
Funktion sollte nicht direkt drucken, es sollte einen Zeiger auf die nehmen
das BuildStatus-Objekt des Aufrufers und sendet seine inkrementellen Aktualisierungen durch
das. (Dadurch funktionieren Dinge wie ninja -v immer noch richtig.)

Dies bricht auch eine Annahme, die unter Windows gemacht wurde, wo wir die eines Befehls parsen
Ausgabe vor der Anzeige. Aber ich denke, darüber können wir uns später Gedanken machen
Vielleicht deaktivieren Sie diese Funktion einfach, wenn wir uns in diesem Zustand befinden.


Antworten Sie direkt auf diese E-Mail oder zeigen Sie sie auf GitHubhttps://github.com/martine/ninja/issues/545#issuecomment -19469960 an
.

Maxime

Hmm, ich glaube, ich bin hier etwas überfordert, also denke ich, ich verliere besser keine Zeit mehr in dieses hier. Leider verwende ich make anstelle von Ninja, nur weil ich den Fortschritt langlaufender Unterjobs beobachten kann.

Es sollte auch beachtet werden, dass einige Anwendungen (z. B. Meson) farbcodierte ANSI-Protokollmeldungen drucken, wenn sie direkt an ein Terminal schreiben, aber Klartext, wenn ihre Ausgabe an eine Pipe oder eine Datei weitergeleitet wird.

Es gibt wirklich zwei Fälle, in denen Sie die Ausgabe in Echtzeit sehen möchten: Regenerieren der Ninja-Datei und Ausführen der Testsuite. Keines davon wird gleichzeitig mit einem anderen Build-Schritt ausgeführt. Vielleicht könnte eine neue Regelvariable, sagen wir mal unbuffered_output, erstellt werden. Wenn es auf true gesetzt ist, würde Ninja die Ausgabe dieses Schritts nicht abrufen, sondern den untergeordneten Prozess direkt in stdout/stderr schreiben lassen.

Ich erinnere mich, dass es einmal einen Pull-Request gab, der genau das tat, was Sie oben vorschlagen, den ich jetzt nicht finden konnte. Außerdem gibt es einen Pull-Request, den ich kürzlich veröffentlicht habe, https://github.com/martine/ninja/pull/629, wo ich versucht habe, dies so zu implementieren, wie ich es für richtig halte. Vielleicht können Sie beide ausprobieren und ein Feedback hinterlassen. ;)

Zusätzlich zu den von Ihnen angegebenen Anwendungsfällen gibt es einen weiteren (schwachen) Anwendungsfall. Das Drucken langer Befehlsausgaben in Echtzeit (insbesondere wenn dieses Tool ein "fremdes" Build-System ist, das eine Bibliothek eines Drittanbieters erstellt) gibt Ihnen die Möglichkeit, seine Ausgabe zu sehen und das Abfangen einer unerwarteten Warnung, die vom "Tool" ausgegeben wird.

Ein weiterer Anwendungsfall dafür: Ich habe «exe»/debug -Ziele, die «exe» mit einem Debugger (wie gdb) erstellen und starten. Derzeit ist es nicht möglich, einen interaktiven Befehl von ninja aus zu starten, daher verwende ich Anwendungsskripting, um gdb in einem neuen Terminalfenster zu starten.

Für diesen Anwendungsfall wäre es ideal, wenn Ninja der Aufgabe erlauben würde, Ninjas stdin/out/err-Dateideskriptoren zu erben, aber eine solche Lösung kollidiert mit #629 und erlaubt Ninja nicht wirklich zu erkennen, dass die Aufgabe ist Ausgabe erzeugen (dafür müssten wir es stattdessen in eine Pipe schreiben lassen, aber dann verlieren wir das tty).

@sorbits (zu https://github.com/martine/ninja/issues/646#issuecomment-23972234): Der Vorbehalt besteht darin, dass die aktuelle Implementierung keinen Ninja-Status druckt, sobald der Befehl, der solo ausgeführt wird, beginnt, seine Ausgabe zu spucken, bevor er beendet ist.
Stellen Sie sich vor, Sie führen ein „configure“-Skript einer Komponente eines Drittanbieters unter Ninja aus. Es gibt eine Menge Output aus und braucht einige Zeit, um fertig zu werden. Während es läuft, können Sie nur seinen eigenen Fortschritt beurteilen, aber Sie sehen nicht den Build-Status von Ninja für den gesamten Build-Fortschritt.

Aber da der configure-Task solo läuft, kommt ninja nicht weiter
Fortschritt (während darauf gewartet wird, dass die Konfiguration abgeschlossen wird), also welche Ausgabe von
Ninja vermisst du?

Am 7. September 2013 um 10:19 Uhr schrieb Maxim Kalaev:

@sorbits (zu
https://github.com/martine/ninja/issues/646#issuecomment-23972234):
Der Vorbehalt ist, dass die aktuelle Implementierung Ninja nicht druckt
Status, sobald der Befehl, der solo ausgeführt wird, beginnt, seine Ausgabe vor ihm auszuspucken
endet.
Stellen Sie sich vor, Sie führen ein „configure“-Skript einer Komponente eines Drittanbieters aus
Ninja. Es gibt eine Menge Output aus und braucht einige Zeit, um fertig zu werden. Während
es läuft, können Sie nur über seinen eigenen Fortschritt urteilen, aber Sie tun es nicht
siehe ninjas Build-Status für den gesamten Build-Fortschritt.


Antworten Sie direkt auf diese E-Mail oder sehen Sie sie auf GitHub an:
https://github.com/martine/ninja/issues/545#issuecomment -23985051

Nun, sagen wir es mal so: Als Autor des Patches freue ich mich, dass es bei dir funktioniert.
Was das Aussehen betrifft - das ist subjektiv. :)

Gibt es Fortschritte bei diesem Thema? Das ärgert mich genauso wie Dave. Die Ausgabe der Komponententests nicht zu sehen, während sie ausgeführt werden, ist wirklich scheiße. Vor allem, wenn einer der Unit-Tests hängt. Ich umgehe dieses Problem, indem ich in das Build-Verzeichnis gehe und ctest manuell ausführe ...

Ninja unterstützt dies seit https://github.com/martine/ninja/commit/2832613dc7c1a4a8ff3b9df729954715762a8381
aber cmake unterstützt es noch nicht. Sie sollten auf der Mailingliste von cmake nachfragen oder einen Patch senden.

@nico dieses Thema sollte geschlossen werden.

Äh, gut zu wissen. Sorry für den Lärm dann. :)

Ich habe zwei weitere Anwendungsfälle für das erneute Ausführen von cmake:

1) Visual Studio über Ninja: Ich erstelle zwei cmake-generierte Ausgaben, eine ist die Ninja-Ausgabe, die den Build durchführt, die andere ist eine von Visual Studio generierte Ausgabe, wobei alle Ziele von der Lösung ausgeschlossen sind. Dann füge ich der Visual Studio-Ausgabe ein benutzerdefiniertes Ziel hinzu, das cmake mit Ninja erneut ausführt. Viola! Visual Studio IDE mit Ninja-Geschwindigkeit!

2) Generierung von IDL-Dateiregeln: In Phase 1 kompiliere ich IDL-Dateien, um zu bestimmen, welche Ausgaben sie erzeugen, und GLOB, die cmake-Regeldateien ausgeben und schreiben. In Phase 2 führe ich den eigentlichen Build durch, einschließlich der erstellten Regeldatei. ALL hängt vom benutzerdefinierten Ziel „go“ ab, das cmake --target PHASE1 und dann cmake --target PHASE2 erneut ausführt.

Diese beiden Anwendungsfälle sind nicht parallel, und das Puffern der Ausgabe ist nicht hilfreich.

Wie oben, wenn Sie keine Pufferung wünschen, sollten Sie dafür sorgen, dass diese Aufgaben den Pool console verwenden.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen