make option --what-if consider also other outdated files - gnu-make

I'm trying to get the list of targets that depends on a single file.
The "--what-if" option of make seems to do the job, but I'd like to ignore any other outdated files, except the one I'm giving as argument.
This is my test case (working on debian with gnu make 4.0):
$ cat makefile
all: exe1 exe2
exe1: obj1
echo "a" > exe1
exe2: obj2
echo "b" > exe2
obj1: src1
echo "c" > obj1
obj2: src2
echo "d" > obj2
$ make
make: Nothing to be done for 'all'.
$ make -n -W src1
echo "c" > obj1
echo "a" > exe1
Perfect. This is the answer I want: modifying src1 causes exe1 to be rebuilt.
But if I change the other file "src2"...
$ touch src2
$ make -n -W src1
echo "c" > obj1
echo "a" > exe1
echo "d" > obj2
echo "b" > exe2
So make is "making" also "exe2".
Is there a way to tell make to ignore any other outdated file?
In this case, I'm not interested to know that "exe2" would be rebuilt since it does not depends on the file "src1".
In other words, I'm looking the answer to this question: "What targets depends upon src1?"
Thanks for any advice!

Regardless of what options you give to -W, make will always try to build the standard set of targets (the "goal targets"). -W has no impact on the selection of goal targets.
If you want make to build one a specific target, then you give that target on the command line. So, if you want to only have make build exe1 but not exe2, you would run make exe1.
So to do what you want you combine these features:
make -n -W src1 exe1
If you want to assume that src2 is not new you can use the -o option:
make -n -o src2 -W src1

Related

Initializing variables in bash

I'm migrating windows CMD script to bin/bash on unix.
The goal of initial script was to setting up some variables, so after anything run from this cmd window was using that variables.
How can I do same in UNIX? Looks like simple
MyVar="value"
doesn't work. It visible only in script itself, not from terminal where it was run.
You can initialize shell variables with simple assignments
$ foo="fooval"
$ echo $foo
fooval
These variables won't spread to unrelated child processes:
$ foo=fooval
$ sh -c 'printf "\"%s\"" $foo'
""
To make them spread, you need to export them into the process's (shell's)
environment (make them into "environment variables" (these are commonly capitalized, i.e.,
FOO instead of foo)
$ export foo
$ sh -c 'echo $foo'
fooval
You can assign and export in one step:
$ export foo=fooval
Environment variables will never spread anywhere but down the process hierarchy.
(Only to children, never to parents or completely unrelated processes)
Therefore, if you have a script with variable assignments, you need to source it, not execute it:
$ ./envvars #won't affect the parent shell
$ . ./envvars #this will
There are no per-terminal variables (though there are per-terminal configurations with fixed keys accessible manipulatable with the stty tool).
Create a file test.sh
add the following line:
export b="key"
Now goto the terminal and do the following :
source ./test.sh
echo $b
Output:
key

Executing two commands in one line

I have a particular problem that requires me to run cd command with rm following afterwards. But I'm restrained by the fact that I have to execute both commands in a single line.
Let's say I have two folders in my current directory, "A" and "B". And within "A" folder, I have two more folders, "Fol1" and "Fol2", with a text file called "File1". And lastly, within "Fol1" and "Fol2", they both have a single text file (doesn't matter what they're called).
To give an illustration:
(Current dir)
A B
| |
---------------------------
| | |
Fol1 Fol2 File1
| |
FileA FileB
I want to go into "A", then remove everything remove everything except what's in "Fol1" and "Fol2".
I've found that to remove everything except certain directories you can run:
rm -r !(Fol1|Fol2)
And I saw on another post that you can use & to combine two commands together. So from the current directory, I decided to run:
cd A & rm -r !(Fol1|Fol2)
But when I ran those commands, I got:
[1] 17854
[1]+ Done cd A
And it ended up deleting "A" and "B" and everything else in it.
Is there something that I'm missing within the commands? Anything would be appreciated!
The ampersand just ran the cd command in the background, which started a subshell, changed directories, the exited. Then the rm -r ran in the current directory, which had no Fol1 or Fol2 to ignore. You want a semi-colon to separate commands like this:
cd A; rm -r !(Fol1|Fol2)

How to append a dynamic string to the end of a copy's filename

Conside File is :
/home/dev/a1234.txt
I want to copy this file in /home/sys/ directory but I want to add some number on the file name while copying.
Date1=date +%Y%m%d_%H:%M:%S
Output should be :
/home/sys/a1234.txt.$Date1
Number on the filename "1234" will be different everytime. so File name is not fixed.
Please suggest.
Something like this should get you on the way:
for i in $( ls /home/dev | grep 'a[0-9]*.txt$'); do
cp /home/dev/$i /home/sys/$i.`date +%Y%m%d_%H:%M:%S`;
done
You can improve it by seeing if the file has already been copied, and prevent it from being copied a second time.
NAME=a1234.txt
cp dev/$NAME sys/$NAME.$Date1
Use the date command to get the current date.
$ DATE=`date +%Y%m%d_%H:%M:%S`; NAME=a; EXT=txt; for i in {0..4}; do echo $NAME$i$.$EXT.$DATE; done
a0.txt.20130625
a1.txt.20130625
a2.txt.20130625
a3.txt.20130625
a4.txt.20130625
Change the echo line to be a cp:
$ DATE=`date +%Y%m%d_%H:%M:%S`; FROMDIR=/home/dev; TODIR=/home/sys; NAME=a; EXT=txt; for i in {0..4}; do cp $FROMDIR/$NAME$i.$EXT $TODIR/$NAME$i$.$EXT.$DATE; done
This is probably better in a bash script where you can modify it easier than a single liner:
#!/bin/bash
# get current date
DATE=`date +%Y%m%d_%H:%M:%S`;
# specify from directory and to directory for cp
FROMDIR=/home/dev;
TODIR=/home/sys;
# set base filename and extension
NAME=a;
EXT=txt;
# count from 0 to 4
for i in {0..4}; do
# copy file and add date to end of new filename
cp $FROMDIR/$NAME$i.$EXT $TODIR/$NAME$i$.$EXT.$DATE;
done
i increments from 0 to 4 in this example, while a filename of the form $NAME$i.$EXT is copied from $FROMDIR to TODIR -- with the current date in $DATE appended to the end of the copy's filename.
In two steps:
Copy the file to /home/sys.
cp /home/dev/a1234.txt /home/sys
Move from a1234.txt to a1234.txt.130625. This way you can make it more general.
mv /home/sys/a1234.txt{,$date} #is expanded to mv /home/sys/a1234.txt /home/sys/a1234.$date
As you indicate your file name will change, you can generalise my answer with $file_name instead of a1234.txt.
Test
$ touch a
$ date=23
$ mv aa{,$date}
$ ls
aa23

How to write a GNUMAKE based testharness

I am not very good at write make files. But I have a need to write a GNUMAKE based test harness. I did some reserch, but I was not able to find anything useful. So I am not even sure where to begin.
TEST_SUITE_DIR:=testSuite
#Get all test names with path & extention
TEST_SCRIPTS_WITH_PATH:=$(wildcard $(TEST_SUITE_DIR)/*.txt)
#Test name with out path and extention
TEST_SCRIPT_NAME:=$(notdir $(patsubst %.txt,%,$(TEST_SCRIPTS_WITH_PATH)))
vpath %.txt $(TEST_SUITE_DIR)
TEST_LOG_FILE:=$(TEST_SCRIPT_NAME)OutPutFile.txt
#This is the program ./bin/programName being tested
PUT:=man
#Test requrements:
#1) Each test in /testSuite dir should have its own logFile
#2) Each testout will be checked against a goldenout file in /goldenOutput dir to see
# if the expected resuls match with the test output
# #3) If the test & golden output file hasnt been modified, we do not want to run that test so
# we can save time
# #4) STDERR should be redirected to a stderr.log
#5) During the regression, if a test failed, test name should be written into the regressionReport.log
.PHONY: clean test
test:
for i in $(TEST_SCRIPTS_WITH_PATH); do \
echo $$i; \
$(PUT) `head -n 1 $$i` > $$iOutPutFile.txt; \
done
#$(foreach i, $(TEST_SCRIPTS_WITH_PATH), $(PUT) `head -n 1 $($i)` > $($i)OutPutFile.txt )
#$(PUT) `head -n 1 $(TEST_SCRIPTS) ` > $(TEST_SCRIPTS)logFile.log
clean:
rm -f *.d $(OBJ_DIR)/*.o $(PROG)
-include *.d
Here is my dataFile.txt(at the moment, I am only trying to get 1 command working),
date
A makefile is a way of automating certain tasks, so you can't do anything with Make until you know how how to do it without Make.
There is more than one way to do what you want (a common situation with Make), and you should think about how you want the makefile to scale. The simplest way to construct that command is probably:
man `head -n 1 dataFile.txt` > logFile.log
So this makefile would suffice:
.PHONY: all
all:
man `head -n 1 dataFile.txt` > logFile.log
Many advances on this are possible, but not until we know what you intend to do beyond this.
I'm not sure I understand what you are trying to do, why do you need dependencies for the test rule (specially that you made it phony).
Also if you want to test content of file you need to use diff not test

With Unix find(1), how do I find files in one tree newer than counterparts in another tree?

Let's say I have two directory structures:
/var/www/site1.prod
/var/www/site1.test
I want to use find(1) to see the files that are newer in /var/www/site1.test than their counterparts in /var/www/site1.prod.
Can I do this with find(1), and if so, how?
You also could use rsync -n
rsync -av -n /var/www/site1.test /var/www/site1.prod
should do it.
Using find,
cd /var/www/site1.test
find . -type f -print | while read file ; do
[ "$file" -nt /var/www/site1.prod/"$file" ] && echo "File '$file' changed"
done
This will work with filenames containing blanks, as well as work for a large volume of files.
To distinguish between modified and missing files, as per Eddie's comments,
cd /var/www/site1.test
find . -type f -print | while read file ; do
[ "$file" -nt /var/www/site1.prod/"$file" ] && reason=changed
[ \! -e /var/www/site1.prod/"$file" ] && reason=created
[ -n "$reason" ] && echo "$file $reason"
done
I think you can't do it with find alone, but you can do something like
$ cd /var/www/site1.test
$ files=`find .`
$ for $f in $files; do
if [ $f -nt /var/www/site1.prod/$f ]; then echo "$f changed!"; fi;
done
If you look at the -fprintf option you should be able to create two finds that output into two files that list the files name and modification time. You should then be able to just diff the two files to see what has changed.
I understand that you specifically asked for newer, but I think it's always good to know all your options.
I would use diff -ur dir1 dir2.
Maybe the timestamp changed, maybe it didn't, but that doesn't necessarily tell you if the contents are the same. Diff will tell you if the contents changed. If you really don't want to see the contents use diff -rq to just see the files that changed.

Resources