4

I'm doing make all -d --trace

How do I get Gnu Make to output timestamps for every line it outputs?

More generally, how do I add a timestamp to every STDOUT and STDERR statement?

There is a solution for Linux/Bash but I'm on Windows.

I created a one line batch file add_ts.bat : echo %time% %1

I tried the following but I only got one timestamp (without the lines that were output):

make all --trace -d 2>&1 | add_ts.bat

Community
  • 1
  • 1
Bob
  • 4,576
  • 7
  • 39
  • 107

2 Answers2

3

To a first approximation you need a batch file like:

add_ts.bat

@for /F "usebackq delims==" %%i in (`%1`) do @echo %time% %%i

which you would run like:

add_ts.bat "make all -d --trace" > buildlog.txt

This however isn't good enough if you want to capture and timestamp STDERR as well as STDOUT from the command passed as %1, because the backticks operator around %1 will only capture STDOUT

To fix this you'll need to capture STDERR as well as STDOUT within the backticks, by using redirection in there, which in turns means you need to run a subshell to understand the redirection, and you need to escape the redirection operators so they're not interpreted by the toplevel shell. Which all comes to:

@for /F "usebackq delims==" %%i in (`cmd /C %1 2^>^&1`) do @echo %time% %%i 

Run just the same way.

Later

what I don't get is why the | itself wasn't enough to send STDOUT and STDERR to STDIN of add_ts.bat

It is. I think you are labouring under the combination of two misconceptions.

One: You believe that a program's commandline arguments are the same as its standard input, or that it gets it commandline arguments from its standard input. It doesn't. A program's commandline arguments, if any, are passed to it as a fixed list of strings in the program-startup protocol. Its standard input is an input stream made available to it at the same time by the OS and connected by default to the console in which the program starts. This default can be overridden in the shell by redirection operators. The contents of that input stream are not fixed in advance. It will feed to the the program whatever is input to the console, or from its redirected proxy, as long as the program is running, as and when the program reads it. The program can parse or ignore its commandline arguments and, quite independently of that, it can read or ignore its standard input.

Your program add_ts.bat is a program that parses the first of its commandline arguments - it uses %1 - and ignores any more. And it ignores its standard input entirely.

Two: You believe that the effect of a pipeline, e.g.

a | b

is to start an a process and then, for each line that it writes to the standard output, start a distinct b process which will automatically receive that one line written by a as a single commandline argument (no matter who many words are in the line) and do its stuff with that single commandline argument.

That's not what happens. The OS starts one a process and one b process, and connects the standard output of the one a process to the standard input of the one b process. For the pipeline to work at all, b has got to be a program that reads its standard input. Your add_ts.bat is not such a program. It's only a program that parses its first commandline argument: the | doesn't give it any, and the commandline:

make all --trace -d 2>&1 | add_ts.bat

doesn't give it any either. The commandline:

make all --trace -d 2>&1 | add_ts.bat "Hello World"

would give it one commandline argument and:

make all --trace -d 2>&1 | add_ts.bat Hello World

would give it two, not one, commandline arguments, the second being ignored. But in any case it doesn't read its standard input so piping to it is futile.

The site ss64.com is perfectly good about CMD redirection and piping but it assumes you know what a program has to do to be a pipeline-able command: To be an upstream command, it has to write its standard output; to be a downstream command it has to read its standard input.

Mike Kinghan
  • 55,740
  • 12
  • 153
  • 182
  • Can you explain why my original bat didn't work? I imagine it has something to do with STDOUT and STDERR buffering – Bob Jun 04 '16 at 16:04
  • @Adrian When a batch file runs it replaces `%1` with the first commandline argument, and so on, then does its logic and ends. Piping its input does no magic to make it replace `%1` with the first entire *line* of `STDIN` and then re-run itself for every subsequent line. Your `add_ts.bat` was passed no commandline arguments and had no code for reading its `STDIN`, piped or otherwise, so it just printed the time once and stopped. – Mike Kinghan Jun 04 '16 at 16:27
  • what I don't get is why the `|` itself wasn't enough to send STDOUT and STDERR to STDIN of `add_ts.bat`. Is ss64.com a good place for me to understand how/why? – Bob Jun 04 '16 at 16:57
  • @Adrian See further explanation in answer. – Mike Kinghan Jun 04 '16 at 18:14
  • 1
    I just used the script to add timestamp for the stdout of one of my command line application which would normally run for tens of minutes with constant std output, but the timestamp in the redirected result are all the same, not as I expected, do I misunderstand the intended usage of the script? – Gang YIN Dec 14 '18 at 04:12
0

Using a batch file wrapper is a clever solution if you don't mind the extra overhead. Otherwise I think you'll have to modify GNU make itself to have it print out this data.

If that's not palatable for some reason, you can get that information by using ElectricMake, a GNU-make-compatible implementation of make that includes lots of enhancements, including annotated build logs that have microsecond-resolution timestamps for every job in the build. ElectricMake is part of ElectricAccelerator Huddle.

Here's a bit of the annotation for a trivial "echo Hello World!" job:

<job id="J00007fb820002000" thread="7fb82f7fe700" start="3" end="4" type="rule" name="all" file="
Makefile" line="1">
<command line="2">
<argv>echo Hello, world!</argv>
<output src="prog">Hello, world!
</output>
</command>
<commitTimes start="0.291693" wait="0.296587" commit="0.296628" write="0.296680"/>
<timing invoked="0.291403" completed="0.296544" node="ecdroid3a-59"/>
</job>

Here, the <timing> tag shows the start time (0.291403 seconds) and end time (0.296544 seconds) of the job relative to the start of the build.

These annotated build logs can be viewed and analysed graphically with ElectricInsight, a companion tool for ElectricMake.

ElectricAccelerator Huddle is the freemium version of ElectricAccelerator -- usage is entirely free up to a point, with modest pay-as-you-go fees beyond that

Disclaimer: I'm the architect of ElectricAccelerator.

Eric Melski
  • 16,432
  • 3
  • 38
  • 52
  • `emake` works BUT my company doesn't want to be tied to it because ECloud might decide to make it non-free at will or even discontinue it. I talked to your sales rep and they said the ***free*** version must have its license renewed every 3 months. And what if it's discontinued because it's cannibalizing your Enterprise sales? Can you speak to these concerns? – Bob Jun 30 '16 at 15:46
  • @Adrian ElectricAccelerator Huddle is a _freemium_ product, which means it has both a free component and a for-pay component which grants additional functionality. – Eric Melski Jul 01 '16 at 21:24
  • I know but you still haven't addressed why the "free" version is kept on a short leash i.e. requiring the license to be renewed every 3 months. – Bob Jul 01 '16 at 21:25
  • @Adrian you've misunderstood. The free functionality in Huddle is always free. That is, for free, forever, you can use Huddle to run reliable parallel builds using up-to-four local agents. You also get the annotated build logs. The license governs the paid-for functionality, which allows the use of more-than-four local agents as well as unlimited agents from other hosts. Technically, while Huddle is in beta, that extended functionality is also free, governed by a 3-month license. You can still use the always-free part after that expires. – Eric Melski Jul 01 '16 at 22:03
  • I'm still not clear on what the 3 month term applies to. It's only the "more than 4 core" functionality? – Bob Jul 01 '16 at 22:05
  • @Adrian The license only governs the non-free functionality: more than four local cores and distributed cores. If you are still not clear, I suggest that we take this conversation offline -- I don't think StackOverflow is an appropriate forum for this level of detail. – Eric Melski Jul 01 '16 at 22:14
  • @Adrian I've asked our people to reach out to you via email. – Eric Melski Jul 01 '16 at 22:59