How to reuse code in CMake - linux

I've got the following piece of code which gets the current system IP and stores it in the SERVER_IP variable:
EXECUTE_PROCESS(
COMMAND ip route get 8.8.8.8
COMMAND awk "NR==1 {print $NF}"
OUTPUT_VARIABLE SERVER_IP
OUTPUT_STRIP_TRAILING_WHITESPACE
)
I need to use this IP several places in my CMakeLists.txt file hierarchy. What's the best approach to reuse this code? My first thought is to make it a function like function(GetIP), but I am not sure where to put this function to make it visible to all CMakeLists.txt file.

If you make the CMake function available in the top-level CMakeLists.txt file, it will be also available in the CMakeLists.txt files of the subdirectories you added with ADD_SUBDIRECTORY.
Either define the function directly in the top-level CMakeLists.txt file or use something like INCLUDE(GetIP.cmake) there.

If it's just about the IP itself, just put it in a variable.
Variables set in a directory are inherited by all subdirectories, but not by parent directories. You can extend the scope of a local variable by one level with the PARENT_SCOPE parameter of the set command.
Alternatively, put the variable in the cache to make it accessible globally. Unless marked as internal, this will also make the variable configurable via the CMake GUI.
set(MY_SERVER_IP 8.8.8.8 CACHE STRING "IP address of the server responsible for X")
[...]
EXECUTE_PROCESS(
COMMAND ip route get ${MY_SERVER_IP}
COMMAND awk "NR==1 {print $NF}"
OUTPUT_VARIABLE SERVER_IP
OUTPUT_STRIP_TRAILING_WHITESPACE
)

Related

How to address some of many files that include one of a few sequences in their name, in Linux file-name system?

I have some files in my directory named as:
...
asdfab-18-121.csv
asdfab-19-221.csv
gafaac-19-289.csv
asdfax-19-311.csv
aasdfb-20-122.csv
aasdfb-20-220.csv
aberrc-20-281.csv
aasdfb-21-127.csv
aasdfb-21-224.csv
acadff-21-286.csv
...
I need to list the files that have "-19-" OR "-20-" in the middle part of their name (e.g. lines 2-7 above), at the same time. I know if only one character was variable I could use [seq] syntax. I tried
ls *#["-19-"|"-20-"]*
but it doesn't seem to work. Any ideas?
If using bash with the extglob option turned on, or ksh93, or zsh with the KSH_GLOB option turned on:
ls *-#(19|20)-*.csv

How does one create a wrapper around a program?

I want to learn to create a wrapper around a program in linux. How does one do this? A tutorial reference web-page/link or example will do. To clarify what I want to learn, I will explain with an example.
I use vim for editing text files. And use rcs as my simple revision control system. rcs allows you to check-in and checkout-files. I would like to create a warpper program named vir which when I type in the shell as:
$ vir temp.txt
will load the file temp.txt into rcs with ci -u temp.txt and then allows me to edit the file using vim.
When I get out and go back in, It will need to check out the file first, using ci -u temp.txt and allow me to edit the file as one normally does with vim, and then when I save and exit, it should check-in the file using co -u temp.txt and as part of that I should be able to add a version control comment.
Basically, all I want to be doing on the command line is:
$ vir temp.txt
as one would with vim. And the wrapper should take care of the version control for me.
Take a look at rcsvers.vim, a vim plugin for automatically saving versions in RCS; you could modify that. There are also other RCS plugins for vim at vim.org
I have a wrapper to enhance the ping command (using zsh) it could, maybe help you:
# ping command wrapper - Last Change: out 27 2019 18:47
# source: https://www.cyberciti.biz/tips/unix-linux-bash-shell-script-wrapper-examples.html
ping(){
# Name: ping() wrapper
# Arg: (url|domain|ip)
# Purpose: Send ping request to domain by removing urls, protocol, username:pass using system /usr/bin/ping
local array=( $# ) # get all args in an array
local host=${array[-1]} # get the last arg
local args=${array[1,-2]} # get all args before last arg in $#
#local _ping="/usr/bin/ping"
local _ping="/bin/ping"
local c=$(_getdomainnameonly "$host")
[ "$host" != "$c" ] && echo "Sending ICMP ECHO_REQUEST to \"$c\"..."
# pass args and host
# $_ping $args $c
# default args for ping
$_ping -n -c 2 -i 1 -W1 $c
}
_getdomainnameonly(){
# Name: _getdomainnameonly
# Arg: Url/domain/ip
# Returns: Only domain name
# Purpose: Get domain name and remove protocol part, username:password and other parts from url
# get url
local h="$1"
# upper to lowercase
local f="${h:l}"
# remove protocol part of hostname
f="${f#http://}"
f="${f#https://}"
f="${f#ftp://}"
f="${f#scp://}"
f="${f#scp://}"
f="${f#sftp://}"
# Remove username and/or username:password part of hostname
f="${f#*:*#}"
f="${f#*#}"
# remove all /foo/xyz.html*
f=${f%%/*}
# show domain name only
echo "$f"
}
What it hides the local ping using a function called "ping", so if your script has precedence on your path it will find at first the function ping. Then inside the script I define an internal variable called ping that points out to the real ping command:
local _ping="/bin/ping"
You can also notice that the args are stored in one array.

Redirect to file mysteriously does nothing in Bash

Background
I've a script. It's purpose is to generate config files for various system services from templates whenever my gateway acquires a new IP from my ISP. This process includes making successive edits with sed to replace $[template] strings in my custom templates with the correct information.
And to do that I've created a small function designed to take input from stdin, redirect it to a temporary file passed as an argument, and then move that file to replace the destination (and also, often, source) config file. The "edit-in-place dance", if you will.
I created a simple test script with the problematic function:
#!/bin/bash
inplace_dance() {
read -r -d '' data
printf '%s' "${data}" > "${1}~"
mv "${1}~" "${1}"
}
# ATTN: ls is only being used to generate input for testing. It is not being parsed.
ls -l ~/ | inplace_dance ~/test.out
Unfortunately, this works. So it's not the function itself. I also tried it with my custom logging utility (see "complications" below):
#!/bin/bash
. /usr/local/lib/logging.bash
log_identifier='test'
log_console='on'
inplace_dance() {
read -r -d '' data
printf '%s' "${data}" > "${1}~"
mv "${1}~" "${1}"
}
# ATTN: ls is only being used to generate input for testing. It is not being parsed.
bashlog 'notice' $(ls -l ~/ | inplace_dance '/home/wolferz/test.out')
This also works.
The Problem
In its original context, however, it does not work. I've confirmed that ${data} gets set just fine. And that ${1} contains the correct filename. What fails is the second line of the function. I've confirmed printf is being run (see, "Additional Info - Without The Redirect" below)... but the file its output is being redirected to is never created.
And I've been over the code a dozen-dozen times (not an exaggeration) and have yet to identify the cause. So, in desperation, I'm going to post it here and hope some kind soul will wade through my code and maybe spot the problem that I'm missing. I'd also happily take advice on improvements/replacements to my logging utility (in the hopes of tracking down the problem) or further troubleshooting steps.
Here is the original context. The important lines are 106-110, 136-140, 144-147, and 151-155
Additional Info
☛ PATH/Environment
The PATH is PATH=/usr/local/sbin:/usr/local/bin:/usr/bin. I believe this is being inherited from systemd (systemd=>dhcpcd.service=>dhcpcd=>dhcpcd-run-hooks=>dhcpcd.exit-hook).
dhcpcd-run-hooks (see "Complications" below) does clear the environment (keeping the above PATH) when it runs. Thus, I've added an example of the environment the script runs in to the "original context" gist. In this case, the environment when $reason == 'BOUND'. This is output by printenv | sort at the end of execution (and thus should show the final state of the environment).
NOTE: Be aware this is Arch Linux and the absence of /bin, /sbin, and /usr/sbin in the PATH is normal (they are just symlinks to /usr/bin anyway).
☛ Return Code
Inserting echo $? after the second line of the function gives me a return code of "0". This is true both with the redirect in line 2 and without (just the printf).
☛ Without The Redirect
Without the redirect, in the original context, the second line of the function prints the contents of ${data} to stdout (which is then captured by bashlog()) exactly as expected.
⚠️ Execute Instead of Source.
Turns out that $0 was /usr/lib/dhcpcd/dhcpcd-run-hooks rather than my script. Apparently dhcpcd-run-hooks doesn't execute the script... it sources it. I made some changes to line 196 to fix this.
♔ Aaaaaand that seems to have fixed all problems. ♔
I'm trying to confirm that was the silver bullet now... I didn't notice it was working till I had made several other changes as well. If I can confirm it I'll submit an answer.
Complications
What complicates matters quite a bit is that it's original context is a /etc/dhcpcd.exit-hook script. dhcpcd-run-hooks appears to eat all stderr and stdout which makes troubleshooting... unpleasant. I've implemented my own logging utility to capture the output of commands in the script and pass it to journald but it's not helping in this case. Either no error is being generated or, somehow, the error is not getting captured by my logging utility. The script is running as root and there is no mandatory access control installed so it shouldn't be a permissions issue.

How to create an alias with relative pwd + string

I want to set an alias to switch from two WordPress instances on the CLI. Each of them have the same paths except for the names of their respective sites e.g:
srv/deployment/work/sitename1/wp-content/uploads/2018/
srv/deployment/work/sitename2/wp-content/uploads/2018/
How do I create an alias that takes the "pwd" of the current location and cd
s to exactly the same location on the other site?
How about a bash function instead of an alias, gives you a little more freedom.
Save this bash function to a file like switchsite.sh. Modify the variables to your needs. Then load it into your bash with:
source switchsite.sh
If you are in /srv/deployment/work/sitename1/wp-content/uploads/2018, do
switchsite sitename2
and you will be in /srv/deployment/work/sitename2/wp-content/uploads/2018.
switchsite() {
# modify this to reflect where your sites are located, no trailing slash
where_my_sites_are=/srv/deployment/work
# modify this so it includes all characters that can be in a site name
pattern_matching_sitenames=[a-z0-9_\-]
# this is the first argument when the function is called
newsite=$1
# this replaces the site name in the current working directory
newdir=$(pwd | sed -n -e "s#\($where_my_sites_are\)/\($pattern_matching_sitenames\+\)/\(.*\)#\1/$newsite/\3#p")
cd $newdir
}
How it works: The sed expression splits the output of pwd into three parts: what is before the current site name, the current site name, and what comes after. Then sed puts it back together with the new site name. Just make sure the pattern can match all characters that could be in your site name. Research character classes for details.
Add the below lines into ~/.bash_aliases
export sitename1=srv/deployment/work/sitename1/wp-content/uploads/2018/
export sitename2=srv/deployment/work/sitename2/wp-content/uploads/2018/
After that
source ~/.bash_aliases
Then you can simply type sitename1 and sitename2 from anywhere to switch to respective directories

Read environment variable in make file

I have a environment variable set with name $MY_ENV_VARIABLE.
How do I use this variable inside my makefile to (for example) include some source files?
LOCAL_SRC_FILES = $(MY_ENV_VARIABLE)/libDEMO.so
Something like above doesn't seem to work.
Note: in my case this is needed for building with the Android NDK but I guess this applies to make in general.
Just to add some information...
The syntax to access the environment variable in make is like other variables in make...
#export the variable. e.g. in the terminal,
export MY_ENV_VARIABLE="hello world"
...
#in the makefile (replace before call)
echo $(MY_ENV_VARIABLE)
This performs the substitution before executing the commmand. If you instead, want the substitution to happen during the command execution, you need to escape the $ (For example, echo $MY_ENV_VARIABLE is incorrect and will attempt to substitute the variable M in make, and append it to Y_ENV_VARIABLE)...
#in the makefile (replace during call)
echo $$MY_ENV_VARIABLE
Make sure you exported the variable from your shell. Running:
echo $MY_ENV_VARIABLE
shows you whether it's set in your shell. But to know whether you've exported it so that subshells and other sub-commands (like make) can see it try running:
env | grep MY_ENV_VARIABLE
If it's not there, be sure to run export MY_ENV_VARIABLE before running make.
That's all you need to do: make automatically imports all environment variables as make variables when it starts up.
I just had a similar issue (under Cygwin):
Running echo $OSTYPE on the shell prints the value, but
running env | grep OSTYPE doesn't give any output.
As I can't guarantee that this variable is exported on all machines I want to run that makefile on, I used the following to get the variable from within the makefile:
OSTYPE = $(shell echo $$OSTYPE)
Which of course can also be used within a condition like the following:
ifeq ($(shell echo $$OSTYPE),cygwin)
# ...do something...
else
# ...do something else...
endif
EDIT:
Some things I found after experimenting with the info from jozxyqk's answer, all from within the makefile:
If I run #echo $$OSTYPE or #echo "$$OSTYPE" in a recipe, the variable is successfully expanded into cygwin.
However, using that in a condition like ifeq ($$OSTYPE,cygwin) or ifeq ("$$OSTYPE","cygwin") doesn't expand it.
Thus it is logical that first setting a variable like TEST = "$$OSTYPE" will lead to echo $(TEST) printing cygwin (the expansion is done by the echo call) but that doesn't work in a condition - ifeq ($(TEST),cygwin) is false.

Resources