makefile variable automatically assignment with index - makefile

I have a question about variable assignments in makefile.
given 2 lists of the same size as below :
(there are no any characters or string match between 2 lists)
index := 1 2 3 4 5
objects := one_obj two_obj three_obj four_obj five_obj
and I need some index to be active, for example, 1 and 3
act_idx := 1 3
all :
#echo act_obj = $(act_obj)
how should I assign $(act_obj) to get the output as "act_obj = one_obj three_obj" after I type "make all" in shell ?

Something like this should do the trick:
act_obj := $(foreach I,$(act_idx),$(filter $I_%,$(objects)))
Based on the adjusted question you can do something like this:
act_obj := $(foreach I,$(act_idx),$(word $I,$(objects)))
You can probably work this out yourself from the set of functions described in the GNU make manual.

The GNUmake library at https://github.com/markpiffer/gmtt was originally written with this exact use case in mind (it grew into a everything-but-the-kitchen-sink, but oh well).
The formulation for the selection process which you are basically needing here is done with the relational table idiom. You produce a table as GNUmake string list and execute a select statement which mimicks a simple SQL select.
include gmtt/gmtt.mk
# define a table with 2 columns (no empty cells allowed!)
define object_tbl :=
2
key1 one_obj
key2 two_obj
key3 three_obj
key4 four_obj
key5 five_obj
endef
act_keys := key1 key3
# SELECT column 2 FROM object_tbl WHERE column 1 is found in act_keys
objects := $(call select,2,$(object_tbl),$$(filter $$1,$(act_keys)))
$(info $(objects))
Output:
one_obj three_obj
make: *** No targets. Stop.
More complex clauses (using arithmetic e.g.) are also possible.

Related

When do we use := and when do we use = in GNU makefile?

What are the scenarios , where = is preferred over := ?
What are the scenarios, where := is preferred over = ?
I read from gnu site, that usage of = makes make run slower. I just wonder, when do we use = in makefile?
To answer your question, = is used when you want to delay expansion of the right side until the variable is used.
This allows you to define variables in any order. It also allows you to create variables with values that refer to automatic variables (remember automatic variables have no value until the rule is being run). So for example:
my_FLAGS = -a
your_FLAGS = -b
FLAGS = $($#_FLAGS)
my your : ; #echo $(FLAGS)
this cannot work if you use := because when the FLAGS variable is defined, $# has no value.
They are also useful when defining user-defined functions that can later be invoked with $(call ...); you don't want those to be expanded until they are called.
With simply defined variables you can do things like recursively use the variable:
ITEMS := one two three
ITEMS := $(addsuffix $(ITEMS))
This is because the simple assignment (:=) happens in the order you read them
Non simple assignment (=) is recursively expanded so if you assign it to other variables they are in turn expanded until you end up with the final result that contains all the expanded parts. Note that the makefile first parses the file so that the order you do the assignment is not so important, examples to follow:
i.e. this is not allowed:
ITEMS = one two three
ITEMS = $(addsuffix $(ITEMS))
So this affects when you want to use each type. With non simple you can do:
ITEMS1 = a b c
ITEMS_all = $(ITEMS1) $(ITEMS2)
ITEMS2 = d e f
And now ITEMS_ALL will contain a b c d e f - even though they are not defined in order, this can be very useful. So if you just want to assign a simply value - stick with := if you want to keep adding things to a variable you might want to use =...

Extracting the value labels of a categorical variable

I have a categorical variable comprised of 12 levels with numerical values from 1 to 12.
Each one of these numerical values is assigned a label. For example, 1 = heart, 2 = brain, 3 = liver and so on. What i would like is to do is extract the label (heart, brain, liver) and place it into a local macro. Is this possible?
I have tried lots of different commands such as describe and codebook.
I have also tried the following:
levelsof var, local(diseases)
The above code gets the levels of the categorical variable var and stores them in the local macro diseases. However this only outputs the numerical values, that is 1,2,3,4, not the labels.
Below is a flexible solution relying on macro extended functions:
sysuse auto, clear
levelsof foreign, local(levels)
local lab : value label foreign
foreach l of local levels {
local all `all' `: label `lab' `l''
}
display "`all'"
Domestic Foreign
If you also want to keep the numerical values change the loop as follows:
foreach l of local levels {
local all `all' `l' `: label `lab' `l''
}
display "`all'"
0 Domestic 1 Foreign
The decode command is also helpful for this issue:
decode var, generate(labvar)
levelsof labvar, local(diseases) clean

Why do tabulate or summarize not take into account missing values when implemented inside a program?

As an illustrative example, suppose this is your dataset:
cat sex age
1 1 13
1 0 14
1 1 .
2 1 23
2 1 45
2 1 15
If you want to create a table of frequencies between cat and sex, you tabulate these two variables and you get the following result:
tab cat sex
| sex
cat | 0 1 | Total
-----------+----------------------+----------
1 | 1 2 | 3
2 | 0 3 | 3
-----------+----------------------+----------
Total | 1 5 | 6
I am writing a Stata program where the three variables are involved, i.e. cat, sex and age. Getting the matrix of frequencies for the first two variables is just an intermediate step that I need for further computation.
cap program drop myexample
program def myexample, rclass byable(recall) sortpreserve
version 14
syntax varlist [aweight iweight fweight] [if] [in] [ , AGgregate ]
args var1 var2 var3
tempname F
marksample touse
set more off
if "`aggregate'" == "" {
local var1: word 1 of `varlist'
local var2: word 2 of `varlist'
local var3: word 3 of `varlist'
qui: tab `var1' `var2' [`weight' `exp'] if `touse', matcell(`F') label matcol(`var2')
mat list `F'
}
end
However, when I run:
myexample cat sex age
I get this result which is not what I expected:
__000001[2,2]
c1 c2
r1 1 1
r2 0 3
That is, given that age contains a missing value, even if it is not directly involved in the tabulation, the program ignores the missing value and does not take into account that observation. I need to get the result of the first tabulation. I have tried using summarize instead, but the same problem arises. When implemented inside the program, missing values are not counted.
You are complaining about behaviour which you built into your own program. The responsibility and the explanation are in your hands.
The effect of
marksample touse
followed by calling up a command with the qualifier
if `touse'
is to ignore missing values. marksample by default marks as "to use" those observations in which all variables specified have non-missing values; the other observations are marked as to be ignored. It also takes account of any if or in qualifiers and any zero weights.
It's also true, as #Noobie explains, that omitting missing values from a tabulation is default for tabulate in any case.
So, to get the result you want you'd need to modify your marksample call to
marksample touse, novarlist
and to call up tabulate with the missing option (if it's compulsory) or to allow users to specify a missing option which you then pass to tabulate.
You also ask about summarize. By design that command ignores missing values. I don't know what you would expect summarize to do about them. It could report a count of missing values. If you want that, several other commands will oblige, such as codebook or missings (Stata Journal). You can always include a report on missings in your program, such as using count to count the missings and display the result.
I understand your program to be very much work in progress, so won't comment on details you don't ask about.
This is caused by marksample. Rule 5 in help mark states
The marker variable is set to 0 in observations for which any of the
numeric variables in varlist contain a numeric missing value.
You should use the novarlist option. According to the help file,
novarlist is for use with marksample. It specifies that missing values
among variables in varlist not cause the marker variable to be set to 0.
if I understand well you want tab to include missing values? If so, you just have to ask for it
tab myvar1 myvar2, mi
from the documentation
missing : treat missing values like other values

pathsubst with wildcard appearing 2 times

I want to replace a string like this
OntVeip -> ManagedObjects/OntVeip/OntVeipConfigDef.xml
so logically to me this is like :
% -> ManagedObjects/%/%ConfigDef.xml
If I try to do this with pathsubst, the % is only replaced once
ie
ManagedObjects := OntVeip OntMoca
XMLSOURCES := $(patsubst %,ManagedObjects/%/%ConfigDef.xml,$(ManagedObjects))
does not work as I was hoping for.
How can I achieve the result I am after ?
Liberally stolen adapted from here
XMLSOURCES := $(foreach obj,$(ManagedObjects),ManagedObjects/$(obj)/$(obj)ConfigDef.xml)

Deferred assignment in makefile with one round of expansion

This may be a very esoteric question, but it's a larger problem that boils down to this. There is a global variable keeping track of state (in this case it's NUM), that is changing. The problem is I need to do a deferred assignment (to LIST) but I need the current value of NUM to be expanded.
NUM := ONE
LIST = three four $(VAR_$(NUM))
NUM := TWO
VAR_ONE := SUCCESS
VAR_TWO := FAILURE
$(info LIST => $(LIST))
$(info LIST VALUE => $(value LIST) )
This results in:
LIST => three four FAILURE
LIST VALUE => three four $(VAR_$(NUM))
What I would like to see:
LIST => three four SUCCESS
LIST VALUE => three four $(VAR_ONE)
I have a very cumbersome solution, which I'll post below, but if there is a simpler solution I would be glad to hear it.
Thank you.
My (pretty ugly) solution is to use a macro to construct the assignment, to force one round of expansion:
define append_deferred
$(1) += $(2)
endef
NUM := ONE
LIST = three four
$(eval $(call append_deferred,LIST,$$(VAR_$(NUM))))
NUM := TWO
VAR_ONE := SUCCESS
VAR_TWO := FAILURE
$(info LIST == $(LIST))
$(info LIST VALUE == $(value LIST) )
This prints the desired result:
LIST => three four SUCCESS
LIST VALUE => three four $(VAR_ONE)
But I'm wondering if there is a better way.

Resources