Make/makefile progress indication! - scripting

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

Related

Definition of operator(+) with multiple addends in Fortran with derived type. An issue with allocatable array

I am trying to define the (+) operator between Fortran derived types that describe matrices (linear operators).
My goal is to implicitly define a matrix M = M1 + M2 + M3 such that, given a vector x, Mx = M1x + M2x + M3x.
First, I defined an abstract type (abs_linop) with the abstract interface for a matrix vector multiplication (y = M *x).
Then, I built an derived type (add_linop), extending the abstract type (abs_linop).
The operator (+) is defined for the type (add_linop). I then create an example of concrete type (eye) extending the abstract type (abs_linop) that describes the identity matrix. This type is used in the main program. This is the source code
module LinearOperator
implicit none
private
public :: abs_linop,multiplication
type, abstract :: abs_linop
integer :: nrow=0
integer :: ncol=0
character(len=20) :: name='empty'
contains
!> Procedure for computation of (matrix) times (vector)
procedure(multiplication), deferred :: Mxv
end type abs_linop
abstract interface
!>-------------------------------------------------------------
!> Abstract procedure defining the interface for a general
!<-------------------------------------------------------------
subroutine multiplication(this,vec_in,vec_out,info,lun_err)
import abs_linop
implicit none
class(abs_linop), intent(inout) :: this
real(kind=8), intent(in ) :: vec_in(this%ncol)
real(kind=8), intent(inout) :: vec_out(this%nrow)
integer, optional, intent(inout) :: info
integer, optional, intent(in ) :: lun_err
end subroutine multiplication
end interface
!>---------------------------------------------------------
!> Structure variable for Identity matrix
!> (rectangular case included)
!>---------------------------------------------------------
type, extends(abs_linop), public :: eye
contains
!> Static constructor
procedure, public, pass :: init => init_eye
!> Compute matrix times vector operatoration
procedure, public, pass :: Mxv => apply_eye
end type eye
!>----------------------------------------------------------------
!> Structure variable to build implicit matrix defined
!> as composition and sum of linear operator
!>----------------------------------------------------------------
public :: add_linop, operator(+)
type, extends(abs_linop) :: add_linop
class(abs_linop) , pointer :: matrix_1
class(abs_linop) , pointer :: matrix_2
real(kind=8), allocatable :: scr(:)
contains
procedure, public , pass:: Mxv => add_Mxv
end type add_linop
INTERFACE OPERATOR (+)
module PROCEDURE mmsum
END INTERFACE OPERATOR (+)
contains
!>------------------------------------------------------
!> Function that give two linear operator A1 and A2
!> defines, implicitely, the linear operator
!> A=A1+A2
!> (public procedure for class add_linop)
!>
!> usage:
!> 'var' = A1 + A2
!<-------------------------------------------------------------
function mmsum(matrix_1,matrix_2) result(this)
implicit none
class(abs_linop), target, intent(in) :: matrix_1
class(abs_linop), target, intent(in) :: matrix_2
type(add_linop) :: this
! local
integer :: res
character(len=20) :: n1,n2
if (matrix_1%nrow .ne. matrix_2%nrow) &
write(*,*) 'Error mmproc dimension must agree '
if (matrix_1%ncol .ne. matrix_2%ncol) &
write(*,*) 'Error mmproc dimension must agree '
this%matrix_1 => matrix_1
this%matrix_2 => matrix_2
this%nrow = matrix_1%nrow
this%ncol = matrix_2%ncol
this%name=etb(matrix_1%name)//'+'//etb(matrix_2%name)
write(*,*) 'Sum Matrix initialization '
write(*,*) 'M1 : ',this%matrix_1%name
write(*,*) 'M2 : ',this%matrix_2%name
write(*,*) 'sum : ',this%name
allocate(this%scr(this%nrow),stat=res)
contains
function etb(strIn) result(strOut)
implicit none
! vars
character(len=*), intent(in) :: strIn
character(len=len_trim(adjustl(strIn))) :: strOut
strOut=trim(adjustl(strIn))
end function etb
end function mmsum
recursive subroutine add_Mxv(this,vec_in,vec_out,info,lun_err)
implicit none
class(add_linop), intent(inout) :: this
real(kind=8), intent(in ) :: vec_in(this%ncol)
real(kind=8), intent(inout) :: vec_out(this%nrow)
integer, optional, intent(inout) :: info
integer, optional, intent(in ) :: lun_err
write(*,*) 'Matrix vector multipliction',&
'matrix:',this%name,&
'M1: ',this%matrix_1%name,&
'M2: ',this%matrix_2%name
select type (mat=>this%matrix_1)
type is (add_linop)
write(*,*) 'is allocated(mat%scr) ?', allocated(mat%scr)
end select
call this%matrix_1%Mxv(vec_in,this%scr,info=info,lun_err=lun_err)
call this%matrix_2%Mxv(vec_in,vec_out,info=info,lun_err=lun_err)
vec_out = this%scr + vec_out
end subroutine add_Mxv
subroutine init_eye(this,nrow)
implicit none
class(eye), intent(inout) :: this
integer, intent(in ) :: nrow
this%nrow = nrow
this%ncol = nrow
end subroutine init_eye
subroutine apply_eye(this,vec_in,vec_out,info,lun_err)
class(eye), intent(inout) :: this
real(kind=8), intent(in ) :: vec_in(this%ncol)
real(kind=8), intent(inout) :: vec_out(this%nrow)
integer, optional, intent(inout) :: info
integer, optional, intent(in ) :: lun_err
! local
integer :: mindim
vec_out = vec_in
if (present(info)) info=0
end subroutine apply_eye
end module LinearOperator
program main
use LinearOperator
implicit none
real(kind=8) :: x(2),y(2),z(2),t(2)
type(eye) :: id1,id2,id3
type(add_linop) :: sum12,sum23,sum123_ok,sum123_ko
integer :: i
call id1%init(2)
id1%name='I1'
call id2%init(2)
id2%name='I2'
call id3%init(2)
id3%name='I3'
x=1.0d0
y=1.0d0
z=1.0d0
write(*,*) ' Vector x =', x
call id1%Mxv(x,t)
write(*,*) ' Vector t = I1 *x', t
write(*,*) ' '
sum12 = id1 + id2
call sum12%Mxv(x,t)
write(*,*) ' Vector t = (I1 +I2) *x', t
write(*,*) ' '
sum23 = id2 + id3
sum123_ok = id1 + sum23
call sum123_ok%Mxv(x,t)
write(*,*) ' Vector t = ( I1 + (I2 + I3) )*x', t
write(*,*) ' '
sum123_ko = id1 + id2 + id3
call sum123_ko%Mxv(x,t)
write(*,*) ' Vector t = ( I1 +I2 + I3) *x', t
end program main
I compile this code with gfortran version 7.5.0 and flags
"-g -C -Wall -fcheck=all -O -ffree-line-length-none -mcmodel=medium "
and this is what I get
Vector x = 1.0000000000000000 1.0000000000000000
Vector t = I1 *x 1.0000000000000000 1.0000000000000000
Sum Matrix initialization
M1 : I1
M2 : I2
sum : I1+I2
Matrix vector multiplictionmatrix:I1+I2 M1: I1 M2: I2
Vector t = (I1 +I2) *x 2.0000000000000000 2.0000000000000000
Sum Matrix initialization
M1 : I2
M2 : I3
sum : I2+I3
Sum Matrix initialization
M1 : I1
M2 : I2+I3
sum : I1+I2+I3
Matrix vector multiplictionmatrix:I1+I2+I3 M1: I1 M2: I2+I3
Matrix vector multiplictionmatrix:I2+I3 M1: I2 M2: I3
Vector t = ( I1 + (I2 + I3) )*x 3.0000000000000000 3.0000000000000000
Sum Matrix initialization
M1 : I1
M2 : I2
sum : I1+I2
Sum Matrix initialization
M1 : I1+I2
M2 : I3
sum : I1+I2+I3
Matrix vector multiplictionmatrix:I1+I2+I3 M1: I1+I2 M2: I3
is allocated(mat%scr) ? F
Matrix vector multiplictionmatrix:I1+I2 M1: I1 M2: I2
At line 126 of file LinearOperator.f90
Fortran runtime error: Allocatable actual argument &apos;this&apos; is not allocated
Everthing works fine when I use the (+) operator with 2 terms. But when 3 terms are used there is an issue with the allocatable array scr, member of type (add_linop), that is not allocated.
Does anybody knows the reason of this issue and how to solve it?
I include the Makefile used for compiling the code.
#Gfortran compiler
FC = gfortran
OPENMP = -fopenmp
MODEL = -mcmodel=medium
OFLAGS = -O5 -ffree-line-length-none
DFLAGS = -g -C -Wall -fcheck=all -O -ffree-line-length-none
#DFLAGS = -g -C -Wall -ffree-line-length-none -fcheck=all
PFLAGS = -pg
CPPFLAGS = -D_GFORTRAN_COMP
ARFLAGS =
ODIR = objs
MDIR = mods
LDIR = libs
INCLUDE = -J$(MODDIR)
OBJDIR = $(CURDIR)/$(ODIR)
MODDIR = $(CURDIR)/$(MDIR)
LIBDIR = $(CURDIR)/$(LDIR)
INCLUDE += -I$(MODDIR)
FFLAGS = $(OFLAGS) $(MODEL) $(INCLUDE)
LIBSRCS =
DEST = .
EXTHDRS =
HDRS =
LIBS = -llapack -lblas
LIBMODS =
LDFLAGS = $(MODEL) $(INCLUDE) -L. -L/usr/lib -L/usr/local/lib -L$(LIBDIR)
LINKER = $(FC)
MAKEFILE = Makefile
PRINT = pr
CAT = cat
PROGRAM = main.out
SRCS = LinearOperator.f90
OBJS = LinearOperator.f90
PRJS= $(SRCS:jo=.prj)
OBJECTS = $(SRCS:%.f90=$(OBJDIR)/%.o)
MODULES = $(addprefix $(MODDIR)/,$(MODS))
.SUFFIXES: .prj .f90
print-% :
#echo $* = $($*)
.f.prj:
ftnchek -project -declare -noverbose $<
.f90.o:
$(FC) $(FFLAGS) $(INCLUDE) -c $<
all::
#make dirs
#make $(PROGRAM)
$(PROGRAM): $(LIBS) $(MODULES) $(OBJECTS)
$(LINKER) -o $(PROGRAM) $(LDFLAGS) $(OBJECTS) $(LIBS)
$(LIBS):
#set -e; for i in $(LIBSRCS); do cd $$i; $(MAKE) --no-print-directory -e CURDIR=$(CURDIR); cd $(CURDIR); done
$(OBJECTS): $(OBJDIR)/%.o: %.f90
$(FC) $(CPPFLAGS) $(FFLAGS) -o $# -c $<
dirs:
#-mkdir -p $(OBJDIR) $(MODDIR) $(LIBDIR)
clean-emacs:
#-rm -f $(CURDIR)/*.*~
#-rm -f $(CURDIR)/*\#*
check: $(PRJS)
ftnchek -noverbose -declare $(PRJS) -project -noextern -library > $(PROGRAM).ftn
profile:; #make "FFLAGS=$(PFLAGS) $(MODEL) " "CFLAGS=$(PFLAGS) $(MODEL)" "LDFLAGS=$(PFLAGS) $(LDFLAGS)" $(PROGRAM)
debug:; #make "FFLAGS=$(DFLAGS) $(MODEL) $(INCLUDE)" "LDFLAGS=$(DFLAGS) $(LDFLAGS)" $(PROGRAM)
openmp:; #make "FFLAGS=$(OFLAGS) $(OPENMP) $(MODEL) $(INCLUDE)" "LDFLAGS=$(LDFLAGS) $(OPENMP)" $(PROGRAM)
clean:; #rm -f $(OBJECTS) $(MODULES) $(PROGRAM).cat $(PROGRAM).ftn
#set -e; for i in $(LIBSRCS); do cd $$i; $(MAKE) --no-print-directory clean; cd $(CURDIR); done
clobber:; #rm -f $(OBJECTS) $(MODULES) $(PROGRAM).cat $(PROGRAM).ftn $(PROGRAM)
#-rm -rf $(OBJDIR) $(MODDIR) $(LIBDIR)
#-rm -f $(CURDIR)/*.*~
#-rm -f $(CURDIR)/*\#*
.PHONY: mods
index:; ctags -wx $(HDRS) $(SRCS)
install: $(PROGRAM)
install -s $(PROGRAM) $(DEST)
print:; $(PRINT) $(HDRS) $(SRCS)
cat:; $(CAT) $(HDRS) $(SRCS) > $(PROGRAM).cat
program: $(PROGRAM)
profile: $(PROFILE)
tags: $(HDRS) $(SRCS); ctags $(HDRS) $(SRCS)
update: $(DEST)/$(PROGRAM)
main.o: linearoperator.mod
# DO NOT EDIT --- auto-generated file
linearoperator.mod : LinearOperator.f90
$(FC) $(FCFLAGS) -c $<
Your program is not valid Fortran.
The function result of mmsum has a pointer component which, during the execution of the function, is pointer associated with a dummy argument. This dummy argument (correctly for this use) has the target attribute. However, the actual argument does not have the target attribute: when the function execution completes the pointer component becomes of undefined pointer association status.
In the subroutine add_Mxv there is an attempt to dereference this pointer. This is not allowed.
It will be necessary to revisit how the operands are handled in your data type. Note in particular that an expression cannot have the target attribute: in the case of id1+id2+id3 the id1+id2 expression won't usefully remain as something to reference later on.

Align trailing backslashes vertically

I have a file that looks like this:
abc \
d \
efgh \
i
jklmnop \
q \rst \
uv
wx
y \
z
For each group of consecutive lines that have a backslash at the end, I want to arrange those backslashes in a straight vertical line. So the expected output for above sample is:
abc \
d \
efgh \
i
jklmnop \
q \rst \
uv
wx
y \
z
I managed to align all backslashes with this program:
$ awk '/\\$/ { sub(/\\$/,""); printf "%-20s\\\n",$0; next} 1' file
abc \
d \
efgh \
i
jklmnop \
q \rst \
uv
wx
y \
z
But I have no idea how to proceed from here, so I'm asking for guidance. I tried searching on SO, but top results were all about removing trailing backslashes.
Details about the actual input:
Lines may contain any character including backslash and consist of any number of characters, there is no limit.
There might be multiple blanks and tabs before and after the last backslash.
There is always at least one blank or tab preceding the last backslash.
There is no line that consists of only a backslash and zero or more blanks/tabs around it.
P.S. I'm not looking for a Perl solution.
If you don't have any tabs before the spaces-then-backslash-then-spaces at the end of each line:
$ cat tst.awk
FNR == 1 { prevHasEsc = blockNr = 0 }
hasEsc = match($0,/[[:space:]]*\\[[:space:]]*$/) {
$0 = substr($0,1,RSTART-1)
if ( ! prevHasEsc ) {
++blockNr
}
}
{ prevHasEsc = hasEsc }
NR == FNR {
if ( hasEsc ) {
lgth[blockNr] = (lgth[blockNr] > length($0) ? lgth[blockNr] : length($0))
}
next
}
hasEsc {
$0 = sprintf("%-*s \\", lgth[blockNr], $0)
}
{ print }
.
$ awk -f tst.awk file file
abc \
d \
efgh \
i
jklmnop \
q \
r
If you do then I'd suggest running the text through pr -e -t first to convert tabs to the corresponding number of blanks.
Here is one attempt using gnu-awk with a custom RS that breaks input on each substring that ends without a blackslash:
awk -v RS='[^\n]*[^\\\\[:space:]][[:blank:]]*(\n|$)' '
{
sub(/\n$/, "", RT)
}
n = split($0, lines, /[[:blank:]]+\\[[:blank:]]*\n/) {
lines[n] = RT
mlen = 0
# determine max length of a block
for (i=1; i<=n; i++)
if (mlen < length(lines[i]))
mlen = length(lines[i])
# print each segment with backslash at the end of max length
for (i=1; i<n; i++)
printf "%-" (mlen+1) "s\\\n", lines[i]
}
RT {
print RT
}' file
abc \
d \
efgh \
i
jklmnop \
q \rst \
uv
wx
y \
z
Code Demo
Details:
-v RS='[^\n]*[^\\\\[:space:]][[:blank:]]*(\n|$): Sets record separator using this regex which basically matches a line that doesn't end with a \. As a result we'll get all contiguous lines that end with \ in each record.
split($0, lines, /[[:blank:]]+\\[[:blank:]]*\n/: Splits each record by ending \ and following line break.
In the first for loop, we loop through each array element and determine longest length of the line i.e. mlen
In the second for loop, we just print each line segment using mlen+1 as length to place trailing \
Finally we print RT which is the substring we capture as a result of RS=...

Can't add Dictionary to Repl

How can I add this to the repl with line returns?
import Dict
fruit = Dict.fromList \
[ \
((0,0), 'Apple') \
,((0,1), ' ') \
]
Error:
> fruit = Dict.fromList \
| [ \
| ((0,0), 'Apple') \
| ,((0,1), ' ') \
-- SYNTAX PROBLEM -------------------------------------------- repl-temp-000.elm
The = operator is reserved for defining variables. Maybe you want == instead? Or
maybe you are defining a variable, but there is whitespace before it?
5| fruit = Dict.fromList
Is it just not possible to do this in the repl with lists where you want to add line returns?
Not a language I know but thought I'd take a look. This appears to work:
import Dict
fruit = Dict.fromList \
[ \
((0,0), "Apple") \
,((0,1), " ") \
]
You seem to have some trailing whitespace after ,((0,1), ' ') \
Also I needed double quotes which appears to be supported by https://elmprogramming.com/string.html
By way of minimal test - this behaves like your example if the trailing space is included:
import Dict
fruit = Dict.fromList [ \

Awk - Substring comparison

Working native bash code :
while read line
do
a=${line:112:7}
b=${line:123:7}
if [[ $a != "0000000" || $b != "0000000" ]]
then
echo "$line" >> FILE_OT_YHAV
else
echo "$line" >> FILE_OT_NHAV
fi
done <$FILE_IN
I have the following file (its a dummy), the substrings being checked are both on the 4th field, so nm the exact numbers.
AAAAAAAAAAAAAA XXXXXX BB CCCCCCC 12312312443430000000
BBBBBBB AXXXXXX CC DDDDDDD 10101010000000000000
CCCCCCCCCC C C QWEQWEE DDD AAAAAAA A12312312312312310000
I m trying to write an awk script that compares two specific substrings, if either one is not 000000 it outputs the line into File A, if both of them are 000000 it outputs the line into File B, this is the code i have so far :
# Before first line.
BEGIN {
print "Awk Started"
FILE_OT_YHAV="FILE_OT_YHAV.test"
FILE_OT_NHAV="FILE_OT_NHAV.test"
FS=""
}
# For each line of input.
{
fline=$0
# print "length = #" length($0) "#"
print "length = #" length(fline) "#"
print "##" substr($0,112,7) "##" substr($0,123,7) "##"
if ( (substr($0,112,7) != "0000000") || (substr($0,123,7) != "0000000") )
print $0 > FILE_OT_YHAV;
else
print $0 > FILE_OT_NHAV;
}
# After last line.
END {
print "Awk Ended"
}
The problem is that when i run it, it :
a) Treats every line as having a different length
b) Therefore the substrings are applied to different parts of it (that is why i added the print length stuff before the if, to check on it.
This is a sample output of the line length awk reads and the different substrings :
Awk Started
length = #130#
## ## ##
length = #136#
##0000000##22016 ##
length = #133#
##0000001##16 ##
length = #129#
##0010220## ##
length = #138#
##0000000##1022016##
length = #136#
##0000000##22016 ##
length = #134#
##0000000##016 ##
length = #137#
##0000000##022016 ##
Is there a reason why awk treats lines of the same length as having a different length? Does it have something to do with the spacing of the input file?
Thanks in advance for any help.
After the comments about cleaning the file up with sed, i got this output (and yes now the lines have a different size) :
1 0M-DM-EM-G M-A.M-E. #DEH M-SM-TM-OM-IM-WM-EM-IM-A M-DM-V/M-DM-T/M-TM-AM-P 01022016 $
2 110000080103M-CM-EM-QM-OM-MM-TM-A M-A. 6M-AM-HM-GM-MM-A 1055801001102 0000120000012001001142 19500000120 0100M-D000000000000000000000001022016 $
3 110000106302M-TM-AM-QM-EM-KM-KM-A 5M-AM-AM-HM-GM-MM-A 1043801001101 0000100000010001001361 19500000100M-IM-SM-O0100M-D000000000000000000000001022016 $
4 110000178902M-JM-AM-QM-AM-CM-IM-AM-MM-MM-G M-KM-EM-KM-AM-S 71M-AM-HM-GM-MM-A 1136101001101 0000130000013001006061 19500000130 0100M-D000000000000000000000001022016 $

Repeat printf arguments with command line operators

I want to repeat the same argument $i for the instances 03-12. I'm really trying to use some nco operators - but the printf statement is hanging me up.
I'm trying to use an netcdf operator on it - where these outputs of the printf are the input files to the command. While this works now with the printf statements, it's not piping into the netcdf command. Which goes as: ncea -v T,U inputfiles outputfile
#!/bin/csh
set i = 1
while ($i < 2)
ncea -v T,U
foreach j ( {3,4,6,7,8,9,10,11,12} )
`printf O3_BDBP_1979ghg.cam.h0.00%02d-%02d.nc $j $i `
end
O3_BDBP_1979.nc
# i = $i + 1
end
Other printf statements I've tried are
ncea -v T,U `printf O3_BDBP_1979ghg.cam.h0.00{03,04,05,06,07,08,09,10,11,12}-%02d.nc $i` O3_BDBP_1979.nc
ncea -v T,U `printf O3_BDBP_1979ghg.cam.h0.00{03,04,05,06,07,08,09,10,11,12}-%1$02d.nc $i` O3_BDBP_1979.nc