how to read special character from a file in verilog? - file-io

I wrote a test Verilog file reading some data from a file. The file has some header lines starting with ### and repeated lines containing 8 float numbers all in text format, like below...
### kr = 0 ##
279.430 243.666 246.522 234.073 218.708 211.012 212.915 222.156
227.269 225.331 212.973 203.453 203.286 219.196 236.336 236.591
236.760 240.655 234.219 226.415 222.691 225.924 229.316 223.291
223.373 229.256 229.021 221.693 208.506 208.094 217.843 232.213
...
-0.000 -0.000 -0.000 -0.000 -0.000 -0.000 -0.000 -0.000
-0.000 -0.000 -0.000 -0.000 -0.000 -0.000 -0.000 2.624
### kr = 1 ##
192.269 28.298 0.356 -0.000 -0.000 -0.000 -0.000 -0.000
-0.000 -0.000 -0.000 -0.000 -0.000 -0.000 9.506 -0.000
-0.000 -0.000 -0.000 -0.000 -0.000 -0.000 -0.000 -0.000
-0.000 2.147 2.839 1.402 0.004 16.810 37.424 47.157
...
The code reading the file is like below. (Actually, I have to read many files, but below is a simple code to test some aspects.). I'm using system verilog.
integer file;
integer c;
initial begin
fname = $sformatf("ext1/L02_Convolution_B0_FN%03d.txt",3);
$dislay("fname = %s", fname);
file = $fopen(fname, "r");
c = $fgetc(file);
while (c != `EOF)
begin
if (c == '#')
r = $fgets(line, `MAX_LINE_LENGTH, file);
else
begin
r = $ungetc(c, file);
r = $fscanf(file, "%f %f %f %f %f %f %f %f", v0, v1, v2, v3, v4, v5, v6, v7);
$display("%f %f %f %f %f %f %f %f \n",v0, v1, v2, v3, v4, v5, v6, v7);
c = $fgetc(file);
end
end
end
r = $fclose(file);
end
When I compile it, I get this error:
ncvlog -SV tb_conv.v
ncvlog: 12.20-s008: (c) Copyright 1995-2013 Cadence Design Systems, Inc.
if (c == '#')
|
ncvlog: *E,BADBSE (tb_conv.v,47|12): illegal base specification: (#) [2.5][2.5.1(IEEE)].
The compiler thinks 'c' is an integer and should have a base indicator like 0x or 0o, I guess. But, I want to test if it is a special character '#'. How can I do this?

Declare the variable c as string. Since you are using $fgetc(file); which gets a character. A common syntax for string comparison is using double quotes like c== "#".

Related

How to implement Emission with MSL

I have been studying the Metal API (newbie here) and working with the examples and documentation to create a 3D animation in my iOS app. I know I can use SceneKit but for the moment I want to understand and learn how Metal API works.
One of the examples from Apple implements a decent number of textures mapping for the object, but the emission texture is missing. After a bit of study I came with a solution like this:
float3 computeDiffuse(LightingParameters parameters)
{
float3 diffuseRawValue = float3(((1.0/PI) * parameters.baseColor) * (1.0 - parameters.metalness));
return diffuseRawValue * parameters.nDotl * parameters.ambientOcclusion;
}
float3 computeSpecular(LightingParameters parameters)
{
float specularRoughness = parameters.roughness * (1.0 - parameters.metalness) + parameters.metalness;
float Ds = Distribution(parameters.nDoth, specularRoughness);
float3 Cspec0 = float3(1.0f);
float3 Fs = float3(mix(float3(Cspec0), float3(1), Fresnel(parameters.hDotl)));
float alphaG = sqr(specularRoughness * 0.5 + 0.5);
float Gs = Geometry(parameters.nDotl, alphaG) * Geometry(parameters.nDotv, alphaG);
float3 specularOutput = (Ds * Gs * Fs * parameters.irradiatedColor)
* (1.0 + parameters.metalness * float3(parameters.baseColor))
+ float3(parameters.metalness)
* parameters.irradiatedColor
* float3(parameters.baseColor);
return specularOutput * parameters.ambientOcclusion;
}
float3 computeEmissive(LightingParameters parameters)
{
float3 diffuseRawValue = float3(((1.0/PI) * parameters.emissiveColor));
return diffuseRawValue;
}
//...
fragment float4
fragmentLighting(ColorInOut in [[stage_in]],
constant AAPLUniforms & uniforms [[ buffer(AAPLBufferIndexUniforms) ]],
constant AAPLMaterialUniforms & materialUniforms [[ buffer(AAPLBufferIndexMaterialUniforms) ]],
texture2d<float> baseColorMap [[ texture(AAPLTextureIndexBaseColor), function_constant(has_base_color_map) ]],
texture2d<float> normalMap [[ texture(AAPLTextureIndexNormal), function_constant(has_normal_map) ]],
texture2d<float> metallicMap [[ texture(AAPLTextureIndexMetallic), function_constant(has_metallic_map) ]],
texture2d<float> roughnessMap [[ texture(AAPLTextureIndexRoughness), function_constant(has_roughness_map) ]],
texture2d<float> ambientOcclusionMap [[ texture(AAPLTextureIndexAmbientOcclusion), function_constant(has_ambient_occlusion_map) ]],
texture2d<float> emissiveMap [[ texture(AAPLTextureIndexEmissive), function_constant(has_emissive_map) ]],
texturecube<float> irradianceMap [[ texture(AAPLTextureIndexIrradianceMap), function_constant(has_irradiance_map)]])
{
float4 final_color = float4(0);
LightingParameters parameters = calculateParameters(in,
uniforms,
materialUniforms,
baseColorMap,
normalMap,
metallicMap,
roughnessMap,
ambientOcclusionMap,
emissiveMap,
irradianceMap);
final_color = float4(computeSpecular(parameters) + computeDiffuse(parameters) + computeEmissive(parameters), 1.0f);
return final_color;
}
This code above makes the object get slightly white, which is not the desired effect.
Note the line computeSpecular(parameters) + computeDiffuse(parameters) + computeEmissive(parameters), in the original source code computeEmissive(parameters) does not exist, that's something that I have implemented trying to make the color more intense, probably this is totally wrong and what I have should be doing is to change the contrast, brightness and luminance, but I am not sure how to do this.
I am probably doing everything wrong, I am open to any suggestion. :)
Thank you.

How to use sed or awk selectively regarding the line length (file-by-file)

I've got around 100 formatted files in the following format
[[ 1.102 -0.26499999 0. ]
[ 2.25999999 -0.88700002 0. ]
[-0.152 2.78900003 0. ]
[-2.23300004 -1.19700003 0. ]
[-2.30699992 1.43700004 0. ]]
where some files are in the form
[[ -1.22399998e+00 -4.05999988e-01 -0.00000000e+00]
[ -2.00000009e-03 1.70599997e+00 0.00000000e+00]
[ 1.29299998e+00 -3.49999994e-01 -0.00000000e+00]
[ 1.20299995e+00 1.10699999e+00 0.00000000e+00]
[ 2.12299991e+00 1.67100000e+00 0.00000000e+00]]
which is, however, unpredictable for me when I get this output.
I'd like to have these numbers being rounded to three decimals in the upper form. I've tried things like sed 's/^\(.\{8\}\).\{4\}/\1/' file, but this isn't specific regarding the length of a line (and it also doesn't round the numbers, obviously).
I'm sure that NumPy could do this, but I think sed or awk would do the job more efficient.
Additional information: If this is of interest, the output represents coordinates and comes from pymol, which uses NumPy for this.
Edit:
It doesn't matter wether the number of characters between two decimal points in a line differs from the example; having all files formatted in the same way is of interest, which means in detail that
the decimal points are placed in the same three columns (character positions).
every file has the same notation of numbers (e. g. decimal, scientific).
the brackets are either in every output/file at the very same position(s) or in none of any output/file.
the number of decimals differs neither in a file nor between them.
In short: the only difference between the files is the numeric characters representing the numbers and not how, how exact or where they're written.
Desired output of the above examples:
[[ 1.102 -0.264 0.000 ]
[ 2.256 -0.887 0.000 ]
[-0.152 2.789 0.000 ]
[-2.233 -1.197 0.000 ]
[-2.307 1.437 0.000 ]]
[[-1.224 -4.056 -0.000 ]
[-2.000 1.706 0.000 ]
[ 1.293 -3.500 -0.000 ]
[ 1.203 1.107 0.000 ]
[ 2.123 1.671 0.000 ]]
Massage the output spacing in the printf to suit whatever criteria works for you if this doesn't as-is:
$ cat tst.awk
{
gsub(/[][]+/," & ")
for (i=2; i<NF; i++) {
$i = sprintf("%.3f",$i)
}
printf "%2s%6s%12s%12s %-2s\n", $1, $2, $3, $4, $5
}
.
$ awk -f tst.awk file
[[ 1.102 -0.265 0.000 ]
[ 2.260 -0.887 0.000 ]
[-0.152 2.789 0.000 ]
[-2.233 -1.197 0.000 ]
[-2.307 1.437 0.000 ]]
[[-1.224 -0.406 -0.000 ]
[-0.002 1.706 0.000 ]
[ 1.293 -0.350 -0.000 ]
[ 1.203 1.107 0.000 ]
[ 2.123 1.671 0.000 ]]
The above was run against this input file:
$ cat file
[[ 1.102 -0.26499999 0. ]
[ 2.25999999 -0.88700002 0. ]
[-0.152 2.78900003 0. ]
[-2.23300004 -1.19700003 0. ]
[-2.30699992 1.43700004 0. ]]
[[ -1.22399998e+00 -4.05999988e-01 -0.00000000e+00]
[ -2.00000009e-03 1.70599997e+00 0.00000000e+00]
[ 1.29299998e+00 -3.49999994e-01 -0.00000000e+00]
[ 1.20299995e+00 1.10699999e+00 0.00000000e+00]
[ 2.12299991e+00 1.67100000e+00 0.00000000e+00]]
Perl to the rescue!
perl -lpe 's/([-0-9.e+]+) */sprintf "%.3f ", $1/ge' -- file
-l removes newlines from input and adds them to output
-p processes the input line by line and prints each line after processing
s/// is substitution, similar to the same command in sed
/e interprets the replacement as code and runs it, which in this case means every number is formatted using sprintf.

How to convert an xarray to std::vector?

The docs make it quite clear on how to adapt a std::vector to a tensor object.
https://xtensor.readthedocs.io/en/latest/adaptor.html
std::vector<double> v = {1., 2., 3., 4., 5., 6. };
std::vector<std::size_t> shape = { 2, 3 };
auto a1 = xt::adapt(v, shape);
But how can you do it for the other way round?
xt::xarray<double> a2 = { { 1., 2., 3.} };
std::vector<double> a2vector = ?;
You can construct a std::vector from iterators. For your example:
std::vector<double> w(a1.begin(), a1.end());
The complete example then becomes:
#include <vector>
#include <xtensor/xadapt.hpp>
#include <xtensor/xio.hpp>
int main()
{
std::vector<double> v = {1., 2., 3., 4., 5., 6.};
std::vector<std::size_t> shape = {2, 3};
auto a1 = xt::adapt(v, shape);
std::vector<double> w(a1.begin(), a1.end());
return 0;
}
References:
std::vector.
Constructors of std::vector (number (5) is the one relevant here).
xtensor documentation section 1.7.1 Adapting std::vector
Unfortunately Tom de Geus' answer does not maintain dimensionality and hence transforms the xarray of shape {2, 3} into a vector of size 6.
I stepped over this question, when attempting to construct a nested vector in order to plot a xarray with matplotlibcpp. For me it turned out, that Eigen::Matrix.. is a way more suitable class for this purpose. For the 2 dimensional case, one can comfortable convert the Eigen::Matrix to a nested std::vector. For higher dimensions, its worth to have a look here.
Code
transforms xt::xarray to Eigen::MatrixXf to nested std::vector
#include "xtensor/xarray.hpp"
#include "xtensor/xio.hpp"
#include <Eigen/Dense>
//https://stackoverflow.com/questions/8443102/convert-eigen-matrix-to-c-array
Eigen::MatrixXf xarray_to_matrixXf(xt::xarray<float> arr)
{
auto shape = arr.shape();
int nrows = shape[0];
int ncols = shape[1];
Eigen::MatrixXf mat = Eigen::Map<Eigen::MatrixXf>(arr.data(), nrows, ncols);
return mat;
}
// https://stackoverflow.com/a/29243033/7128154
std::vector<std::vector<float>> matrixXf2d_to_vector(Eigen::MatrixXf mat)
{
std::vector<std::vector<float>> vec;
for (int i=0; i<mat.rows(); ++i)
{
const float* begin = &mat.row(i).data()[0];
vec.push_back(std::vector<float>(begin, begin+mat.cols()));
}
return vec;
}
// print a vector
// https://stackoverflow.com/a/31130991/7128154
template<typename T1>
std::ostream& operator <<( std::ostream& out, const std::vector<T1>& object )
{
out << "[";
if ( !object.empty() )
{
for(typename std::vector<T1>::const_iterator
iter = object.begin();
iter != --object.end();
++iter) {
out << *iter << ", ";
}
out << *--object.end();
}
out << "]";
return out;
}
int main()
{
xt::xarray<float> xArr {{nan(""), 9}, {5, -6}, {1, 77}};
std::cout << "xt::xarray<float> xArr = \n" << xArr << std::endl;
Eigen::MatrixXf eigMat = xarray_to_matrixXf(xArr);
std::cout << "Eigen::MatrixXf eigMat = \n" << eigMat << std::endl;
std::vector<std::vector<float>> vec = matrixXf2d_to_vector(eigMat);
std::cout << "std::vector<std::vector<float>> vec = " << vec << std::endl;
return 0;
}
Output
xt::xarray<float> xArr =
{{nan., 9.},
{ 5., -6.},
{ 1., 77.}}
Eigen::MatrixXf eigMat =
nan -6
9 1
5 77
std::vector<std::vector<float>> vec = [[nan, 9], [9, 5], [5, -6]]

Clang, link time optimization fails for AVX horizontal add

I have a small piece of testing code which calculates the dot products of two vectors with a third vector using AVX instructions (A dot C and B dot C below). It also adds the two products, but that is just to make the function return something for this example.
#include <iostream>
#include <immintrin.h>
double compute(const double *x)
{
__m256d A = _mm256_loadu_pd(x);
__m256d B = _mm256_loadu_pd(x + 4);
__m256d C = _mm256_loadu_pd(x + 8);
__m256d c1 = _mm256_mul_pd(A, C);
__m256d c2 = _mm256_mul_pd(B, C);
__m256d tmp = _mm256_hadd_pd(c1, c2);
__m128d lo = _mm256_extractf128_pd(tmp, 0);
__m128d hi = _mm256_extractf128_pd(tmp, 1);
__m128d dotp = _mm_add_pd(lo, hi);
double y[2];
_mm_store_pd(y, dotp);
return y[0] + y[1];
}
int main(int argc, char *argv[])
{
const double v[12] = {0.3, 2.9, 1.3, 4.0, -1.0, -2.1, -3.0, -4.0, 0.0, 2.0, 1.3, 1.2};
double x = 0;
std::cout << "AVX" << std::endl;
x = compute(v);
std::cout << "x = " << x << std::endl;
return 0;
}
When I compile as
clang++ -O3 -mavx main.cc -o main
everything works fine. If I enable link time optimization:
clang++ -flto -O3 -mavx main.cc -o main
I get the following error "LLVM ERROR: Do not know how to split the result of this operator!". I have narrowed the culprit to the _mm256_hadd_pd statement. If this is exchanged with e.g. _m256_add_pd link time optimization works again. I realize that this is a silly example to use link-time optimization for, but the error ocurred in a different context where it link-time optimization is extremely helpful.
Can anyone explain what is going on here?

Make/makefile progress indication!

Look at this makefile, it has some sort of primitive progress indication (could have been a progress bar).
Please give me suggestions/comments on it!
# BUILD is initially undefined
ifndef BUILD
# max equals 256 x's
sixteen := x x x x x x x x x x x x x x x x
MAX := $(foreach x,$(sixteen),$(sixteen))
# T estimates how many targets we are building by replacing BUILD with a special string
T := $(shell $(MAKE) -nrRf $(firstword $(MAKEFILE_LIST)) $(MAKECMDGOALS) \
BUILD="COUNTTHIS" | grep -c "COUNTTHIS")
# N is the number of pending targets in base 1, well in fact, base x :-)
N := $(wordlist 1,$T,$(MAX))
# auto-decrementing counter that returns the number of pending targets in base 10
counter = $(words $N)$(eval N := $(wordlist 2,$(words $N),$N))
# BUILD is now defined to show the progress, this also avoids redefining T in loop
BUILD = #echo $(counter) of $(T)
endif
# dummy phony targets
.PHONY: all clean
all: target
#echo done
clean:
#rm -f target *.c
# dummy build rules
target: a.c b.c c.c d.c e.c f.c g.c
#touch $#
$(BUILD)
%.c:
#touch $#
$(BUILD)
All suggestions welcome!
This one is less intrusive and more awesome.
ifneq ($(words $(MAKECMDGOALS)),1)
.DEFAULT_GOAL = all
%:
#$(MAKE) $# --no-print-directory -rRf $(firstword $(MAKEFILE_LIST))
else
ifndef ECHO
T := $(shell $(MAKE) $(MAKECMDGOALS) --no-print-directory \
-nrRf $(firstword $(MAKEFILE_LIST)) \
ECHO="COUNTTHIS" | grep -c "COUNTTHIS")
N := x
C = $(words $N)$(eval N := x $N)
ECHO = echo "`expr " [\`expr $C '*' 100 / $T\`" : '.*\(....\)$$'`%]"
endif
.PHONY: all clean
all: target
#$(ECHO) All done
clean:
#rm -f target *.c
# #$(ECHO) Clean done
target: a.c b.c c.c d.c e.c
#$(ECHO) Linking $#
#sleep 0.1
#touch $#
%.c:
#$(ECHO) Compiling $#
#sleep 0.1
#touch $#
endif
There wasn't really a question so this is less of a standalone answer and more of an extension to Giovanni Funchai's solution. This question is the first google result for "GNU Make Progress" so I ended up here looking for how to do this.
As pointed out by Rob Wells, the solution doesn't work for <10%, but the technique can be extended with the print formatting done by a helper script in whatever language you feel is portable enough for your build. For example, using a python helper script:
echo_progress.py:
"""
Print makefile progress
"""
import argparse
import math
import sys
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--stepno", type=int, required=True)
parser.add_argument("--nsteps", type=int, required=True)
parser.add_argument("remainder", nargs=argparse.REMAINDER)
args = parser.parse_args()
nchars = int(math.log(args.nsteps, 10)) + 1
fmt_str = "[{:Xd}/{:Xd}]({:6.2f}%)".replace("X", str(nchars))
progress = 100 * args.stepno / args.nsteps
sys.stdout.write(fmt_str.format(args.stepno, args.nsteps, progress))
for item in args.remainder:
sys.stdout.write(" ")
sys.stdout.write(item)
sys.stdout.write("\n")
if __name__ == "__main__":
main()
And the modified Makefile:
_mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
I := $(patsubst %/,%,$(dir $(_mkfile_path)))
ifneq ($(words $(MAKECMDGOALS)),1)
.DEFAULT_GOAL = all
%:
#$(MAKE) $# --no-print-directory -rRf $(firstword $(MAKEFILE_LIST))
else
ifndef ECHO
T := $(shell $(MAKE) $(MAKECMDGOALS) --no-print-directory \
-nrRf $(firstword $(MAKEFILE_LIST)) \
ECHO="COUNTTHIS" | grep -c "COUNTTHIS")
N := x
C = $(words $N)$(eval N := x $N)
ECHO = python $(I)/echo_progress.py --stepno=$C --nsteps=$T
endif
.PHONY: all clean
all: target
#$(ECHO) All done
clean:
#rm -f target *.c
# #$(ECHO) Clean done
target: a.c b.c c.c d.c e.c f.c g.c h.c i.c j.c k.c l.c m.c n.c o.c p.c q.c \
r.c s.c t.c u.c v.c w.c x.c y.c z.c
#$(ECHO) Linking $#
#sleep 0.01
#touch $#
%.c:
#$(ECHO) Compiling $#
#sleep 0.01
#touch $#
endif
yields:
$ make
[ 1/28]( 3.57%) Compiling a.c
[ 2/28]( 7.14%) Compiling b.c
[ 3/28]( 10.71%) Compiling c.c
[ 4/28]( 14.29%) Compiling d.c
[ 5/28]( 17.86%) Compiling e.c
[ 6/28]( 21.43%) Compiling f.c
[ 7/28]( 25.00%) Compiling g.c
[ 8/28]( 28.57%) Compiling h.c
[ 9/28]( 32.14%) Compiling i.c
[10/28]( 35.71%) Compiling j.c
[11/28]( 39.29%) Compiling k.c
[12/28]( 42.86%) Compiling l.c
[13/28]( 46.43%) Compiling m.c
[14/28]( 50.00%) Compiling n.c
[15/28]( 53.57%) Compiling o.c
[16/28]( 57.14%) Compiling p.c
[17/28]( 60.71%) Compiling q.c
[18/28]( 64.29%) Compiling r.c
[19/28]( 67.86%) Compiling s.c
[20/28]( 71.43%) Compiling t.c
[21/28]( 75.00%) Compiling u.c
[22/28]( 78.57%) Compiling v.c
[23/28]( 82.14%) Compiling w.c
[24/28]( 85.71%) Compiling x.c
[25/28]( 89.29%) Compiling y.c
[26/28]( 92.86%) Compiling z.c
[27/28]( 96.43%) Linking target
[28/28](100.00%) All done
One could even print a fancy progress bar with unicode characters.
Modified echo_progress.py:
"""
Print makefile progress
"""
import argparse
import math
import sys
def get_progress_bar(numchars, fraction=None, percent=None):
"""
Return a high resolution unicode progress bar
"""
if percent is not None:
fraction = percent / 100.0
if fraction >= 1.0:
return "█" * numchars
blocks = [" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█"]
length_in_chars = fraction * numchars
n_full = int(length_in_chars)
i_partial = int(8 * (length_in_chars - n_full))
n_empty = max(numchars - n_full - 1, 0)
return ("█" * n_full) + blocks[i_partial] + (" " * n_empty)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--stepno", type=int, required=True)
parser.add_argument("--nsteps", type=int, required=True)
parser.add_argument("remainder", nargs=argparse.REMAINDER)
args = parser.parse_args()
nchars = int(math.log(args.nsteps, 10)) + 1
fmt_str = "\r[{:Xd}/{:Xd}]({:6.2f}%) ".replace("X", str(nchars))
progress = 100 * args.stepno / args.nsteps
sys.stdout.write(fmt_str.format(args.stepno, args.nsteps, progress))
sys.stdout.write(get_progress_bar(20, percent=progress))
remainder_str = " ".join(args.remainder)
sys.stdout.write(" {:20s}".format(remainder_str[:20]))
if args.stepno == args.nsteps:
sys.stdout.write("\n")
if __name__ == "__main__":
main()
Which would result in something like this:
$ make clean && make
[12/28]( 42.86%) ███████▊ Compiling k.c
during progress and:
$ make clean && make
[28/28](100.00%) ████████████████████ All done
upon completion.
This is a slight modification to #GiovanniFunchal's excellent answer.
So I wanted to understand this better and make it work for < 10% so I dug into the documentation and learned more about expr.
# PLACE AT THE TOP OF YOUR MAKEFILE
#---------------------------------
# Progress bar defs
#--------------------------------
# words = count the number of words
ifneq ($(words $(MAKECMDGOALS)),1) # if no argument was given to make...
.DEFAULT_GOAL = all # set the default goal to all
# http://www.gnu.org/software/make/manual/make.html
# $# = target name
# %: = last resort recipe
# --no-print-directory = don't print enter/leave messages for each output grouping
# MAKEFILE_LIST = has a list of all the parsed Makefiles that can be found *.mk, Makefile, etc
# -n = dry run, just print the recipes
# -r = no builtin rules, disables implicit rules
# -R = no builtin variables, disables implicit variables
# -f = specify the name of the Makefile
%: # define a last resort default rule
#$(MAKE) $# --no-print-directory -rRf $(firstword $(MAKEFILE_LIST)) # recursive make call,
else
ifndef ECHO
# execute a dry run of make, defining echo beforehand, and count all the instances of "COUNTTHIS"
T := $(shell $(MAKE) $(MAKECMDGOALS) --no-print-directory \
-nrRf $(firstword $(MAKEFILE_LIST)) \
ECHO="COUNTTHIS" | grep -c "COUNTTHIS")
# eval = evaluate the text and read the results as makefile commands
N := x
# Recursively expand C for each instance of ECHO to count more x's
C = $(words $N)$(eval N := x $N)
# Multipy the count of x's by 100, and divide by the count of "COUNTTHIS"
# Followed by a percent sign
# And wrap it all in square brackets
ECHO = echo -ne "\r [`expr $C '*' 100 / $T`%]"
endif
#------------------
# end progress bar
#------------------
# REST OF YOUR MAKEFILE HERE
#----- Progressbar endif at end Makefile
endif
I got rid of the : '.*\(....\)$$' part. It would return the last 4 characters of the inner expr command, but would fail if it was less than 4. And now it works for sub 10%!
And here is the comment free version:
ifneq ($(words $(MAKECMDGOALS)),1) # if no argument was given to make...
.DEFAULT_GOAL = all # set the default goal to all
%: # define a last resort default rule
#$(MAKE) $# --no-print-directory -rRf $(firstword $(MAKEFILE_LIST)) # recursive make call,
else
ifndef ECHO
T := $(shell $(MAKE) $(MAKECMDGOALS) --no-print-directory \
-nrRf $(firstword $(MAKEFILE_LIST)) \
ECHO="COUNTTHIS" | grep -c "COUNTTHIS")
N := x
C = $(words $N)$(eval N := x $N)
ECHO = echo -ne "\r [`expr $C '*' 100 / $T`%]"
endif
# ...
endif
Hope that helps.
Many thanks to #Giovanni Funchal and #phyatt for the question and answers!
I just simplified it even more for my own better understanding.
ifndef ECHO
HIT_TOTAL != ${MAKE} ${MAKECMDGOALS} --dry-run ECHO="HIT_MARK" | grep -c "HIT_MARK"
HIT_COUNT = $(eval HIT_N != expr ${HIT_N} + 1)${HIT_N}
ECHO = echo "[`expr ${HIT_COUNT} '*' 100 / ${HIT_TOTAL}`%]"
endif
!= assigns from shell command
= evaluates variable each time it's used
eval executes its argument without any output
expr allows to make arithmetic calculations
( Not sure though which approach is faster: to call shell with expr or to count 'x'-es with make. )
Usage is the same:
target:
#$(ECHO) $#
Nice trick! (-:
But not really scalable for growing projects that are distributed across many directories with lots of makefiles.
I'd be more inclined to have logging sprinkled through the [Mm]akefiles* in your project and use that to keep track of progress.
Just a thought. BTW Thanks for sharing this.
Edit: Just had a thought. This could be useful in a modified form to display a throbber to show progress while a long task proceeds, e.g unpacking a large distribution tarball instead of just specifying the -v option to the tar command. Still a bit of sugar coating but a bit of fun aswell. (-:
cheers,
Rob