Autoconf : How to get program output in a string and check if another string is present in that - sh

I am developing a Qt application in Python. It uses a resource file, which needs to be compiled. I am using autotools to manage compilation and installation of my projects.
Now, in order for the resource file to be usable by the application, it needs to be compiled with a certain version of the compilation program (pyrcc). I can get the version by putting the output of pyrcc -version in a variable in configure.ac. But then, I don't know how to check whether the string pyrcc5 is present in the output. If it is not present, I want to tell the user that his PyRCC programm has the wrong version, and abort configure.
Additionally, I would like to avoid the need of an extra variable for the program output, but instead do it like this (Pseudo code):
if "pyrcc5" not in output of "pyrcc -version":
say "pyrcc has wrong version"
exit 1
How can I do this ?

When writing a configure.ac for Autoconf, always remember that you are basically writing a shell script. Autoconf provides a host of macros that afford you a lot of leverage, but you can usually at least get an idea about basic "How can I do X in Autoconf?" questions by asking instead "How would I do X in a portable shell script?"
In particular, for ...
I would like to avoid the need of an extra variable for the program
output, but instead do it like this (Pseudo code):
if "pyrcc5" not in output of "pyrcc -version":
say "pyrcc has wrong version"
exit 1
... the usual tool for a portable shell script to use for such a task is grep, and, happily, the easiest way to apply it to the task does not require an intermediate variable. For example, this implements exactly your pseudocode (without emitting any extraneous messaging to the console):
if ! pyrcc -version | grep pyrcc5 >/dev/null 2>/dev/null; then
echo "pyrcc has wrong version"
exit 1
fi
That pipes the output of pyrcc -version into grep, and relies on the fact that grep exits with a success status if and only if it finds any matches.
You could, in fact, put exactly that in your configure.ac, but it would be more idiomatic to
Use the usual Autoconf mechanisms to locate pyrcc and grep, and to use the versions discovered that way;
Use the Autoconf AS_IF macro to write the if construct, instead of writing it literally;
Use standard Autoconf mechanisms for emitting a "checking..." message and reporting on its result; and
Use the standard Autoconf mechanism for outputting a failure message and terminating.
Of course, all of that makes the above considerably more complex, but also more flexible and portable. It might look like this:
AC_ARG_VAR([PYRCC], [The name or full path of pyrcc. Version 5 is required.])
# ...
AC_PROG_GREP
AC_CHECK_PROGS([PYRCC], [pyrcc5 pyrcc], [])
AS_IF([test "x${PYRCC}" = x],
[AC_MSG_ERROR([Required program pyrcc was not found])])
# ...
AC_MSG_CHECKING([whether ${PYRCC} has an appropriate version])
AS_IF([! pyrcc -version | grep pyrcc5 >/dev/null 2>/dev/null], [
AC_MSG_RESULT([no])
AC_MSG_ERROR([pyrcc version 5 is required, but ${PYRCC} is a different version])
], [
AC_MSG_RESULT([yes])
])
In addition to portability and conventional Autoconf progress messaging, that also gets the builder a way to specify a particular pyrcc executable to configure (by setting variable PYRCC in its environment), documents that in configure's help text, and exports PYRCC as a make variable.
Oh, and I snuck in a check for pyrcc under the name pyrcc5, too, though I don't know whether that's useful in practice.
The final result no longer looks much like the shell script fragment I offered first, I grant. But again, the pure shell script fragment could be used as is, and also, the fully Autoconfiscated version is derived directly from the pure script.

Related

Is Perl slower when compiled with DDEBUGGING?

According to this slide from Supercharging Perl - Daina Pettit,
It reads,
Avoid versions of perl compiled with threading or DDEBUGGING unless you know you need them.
I know most distros compile Perl with threading, but my Perl on Debian (as observed with perl -V_ is compiled with -DDEBUGGING=-g does this slow it down?
Perl with debugging enabled is slower.,
Note that a perl built with -DDEBUGGING will be much bigger and will run much, much more slowly than a standard perl.
However, -DDEBUGGING=-g does not enable debugging:
As a convenience, debugging code (-DDEBUGGING) and debugging symbols (-g) can be enabled jointly or separately using a Configure switch, also (somewhat confusingly) named -DDEBUGGING. For a more eye appealing call, -DEBUGGING is defined to be an alias for -DDEBUGGING. For both, the -U calls are also supported, in order to be able to overrule the hints or Policy.sh settings.
and also documented:
Configure -DEBUGGING=-g
Adds -g to optimize, but does not set -DDEBUGGING. (Note: Your system may actually require something like cc -g2. Check your man pages for cc(1) and also any hint file for your system.)
You can test status with: perl -D, if you see the following you do not have -DDEBUGGING,
Recompile perl with -DDEBUGGING to use -D switch (did you mean -d ?)

What are those links '"[" & "[[" in /usr/bin

Busybox creates those links linking to /bin/busybox.
Why are they there and what are they used for?
They're commands used to figure things out in shell scripts. They're usually used to decide between multiple possible actions in shell scripts. For example:
#!/bin/sh
if [ "$1" -eq "5" ]; then
echo You gave me a five.
else
echo You didn't give me a five.
fi
The part that starts with [ checks whether the first argument to the script is 5. To do that, it uses the [ command. [[ is similar but has different capabilities.
The link to /bin/busybox is a trick used by several commands on BusyBox. The /bin/busybox program handles the tasks performed by all of those commands, figuring out which command to perform by looking at which command you originally used to start up /bin/busybox.
This is to save some space on the disk by not having to duplicate things like executable file headers and common functionality and so on for each executable file.
Instead, there's only one executable file for a bunch of commands, so there's only one file header and the common functionality only needs to be included in one file, allowing tricks like static linking without a bunch of duplication, which is nice for embedded systems.

Perl and SNMP - input options

script uses Net::SNMP module for Perl.
I'm trying to run snmpget command with some options added e.g. ( -Ir ) (here is list of options), but I haven't found any way to do that. In documentation for this module I didn't found anything about adding input options to snmp command.
If there is any other module that supports this, it would bi nice but it wouldn't be first pick as that would require a lot of changes in script (not mine script, just doing minor changes).
I could run system (or backticks) command from Perl, e.g.:
snmpget -v2c -c COMMUNITY -Ir HOST OID
and parse output but I would like to avoid that also.
Any advice or solution would be welcome since I'm still new to Perl.
Thx.
You linked to the documentation of Net::SNMP so I'm sure you've read it all before asking... Right?
There is no "command", there is only your script's calls to the API.
[Edit after the below comments]
Net::SNMP has no option to check indexes before sending the request. So, you could say the equivalent of -Ir is enabled by default. In fact, Net::SNMP does not load your MIB, so it has no way of checking the validity of your requested variables before sending the request.

Using Perl modules vs. using system() calls

Quite recently, I wrote a few scripts in Perl for a cPanel plugin in which, though most of the code was in Perl, there was quite a lot of system() commands as well which I used to execute shell commands directly.
I am pretty sure that there are Perl modules that I could have used instead. Keeping in mind the time crunch, I thought using the system command was easier (to complete the project in time). In retrospective, I think that was a bad programming practice.
My question is, is there any tradeoff, memory-wise or otherwise when using Perl's modules and using system() commands. For example, what would be the difference in using:
my $directory = "temp";
mkdir $directory;
and
system ("mkdir temp");
Also, if I am to use Perl modules, wouldn't that involve installing a whole lot of modules in the beginning?
The most obvious economy is that, in the first case, your Perl process is creating the directory, while in the second, Perl is starting a new process that runs a command shell which parses the command line and runs the shell mkdir command to create the directory, and then the child process is deleted. You would be creating and deleting a process and running the shell for every call to system: there is no caching of processes or similar economy.
The second thing that comes to mind is that, if your original mkdir fails, it is simple to handle the error in Perl, whereas shelling out to run a mkdir command puts your program at a distance from the error, and it is far more awkward to handle the many different problems that may arise.
There is also the question of maintainability and portability, which will affect you even if you aren't expecting to run your program on more than one machine. Once you abandon control to a system command you have no control over what happens. I could have written a mkdir that will delete your home directory or, less disastrously, your program may find itself on a system where mkdir doesn't exist, or does something slightly different.
In the particular case of mkdir, this is a built-in Perl operator and is part of every Perl installation. There are also many core libraries that require you to put use Module in your program, but are already installed and need no further action.
I am sure others will come up with more reasons to prefer a Perl operator or module over a shell command. In general you should prefer to keep everything you can within the language. There are only a few cases where you have to run a third-party program, and they usually involve custom software that allows you act on proprietary data formats.

How can I find out what script, program, or shell executed my Perl script?

How would I determine what script, program, or shell executed my Perl script?
Example: I might want to have human readable output if executed from shell (customized for each type of shell), a different type of output if called as a script from another perl script, and a machine readable format if executed from a program such as a continuous integration server.
Motivation: I have a tool that changes its output based on which shell executes it. I'd normally implement this behavior as an option to the script, but this tool's design doesn't allow for options. Other shells have environment variables that indicate what shell is running. I'm working on a patch to support Powershell, which has no such special variable.
Edit: Many of these answers happen to be linux specific. Unfortuantely, Powershell is for Windows. getppid, the $ENV{SHELL} variable, and shelling out to ps won't help in this case. This script needs to run cross-platform.
You use getppid(). Take this snippet in child.pl:
my $ppid = getppid();
system("ps --no-headers $ppid");
If you run it from the command line, system will show bash or similar (among other things). Execute it with system("perl child.pl"); in another script, e.g. parent.pl, and you will see that perl parent.pl executed it.
To capture just the name of the process with arguments (thanks to ikegami for the correct ps syntax):
my $ppid = getppid();
my $ps = `ps --no-headers -o cmd $ppid`;
chomp $ps;
EDIT: An alternative to this approach, might be to create soft links to your script, make the different contexts use different links to access your script and inspect $0 to build logic around that.
I would suggest a different approach to accomplish your goal. Instead of guessing at the context, make it more explicit. Each use case is wholly separate, so have three different interfaces.
A function which can be called inside a Perl program. This would likely return a Perl data structure. This is far easier, faster and more reliable than parsing script output. It would also serve as the basis for the scripts.
A script which outputs for the current shell. It can look at $ENV{SHELL} to discover what shell is running. For bonus points, provide a switch to explicitly override.
A script which can be called inside a non-Perl program, such as your continuous integration server, and issue machine readable output. XML and/or JSON or whatever.
2 and 3 would be just thin wrappers to format the data coming out of 1.
Each is tailored to fit its specific need. Each will work without heuristics. Each will be far simpler than trying to guess the context and what the user wants.
If you can't separate 2 and 3, have the continuous integration server set an environment variable and look for it.
Depending on your environment, you may be able to pick it up from the environment variables. Consider the following code:
/usr/bin/perl -MData::Dumper -e 'print Dumper(\%ENV);' | grep sh
On my Ubuntu system, it gets me:
'SHELL' => '/bin/bash',
So I guess that says I'm running perl from a bash shell. If you use something else, the SHELL variable may give you a hint.
But let's say you know you're in bash, but perl is run from a subshell. Then try:
/bin/sh -c "/usr/bin/perl -MData::Dumper -e 'print Dumper(\%ENV);'" | grep sh
You will find:
'_' => '/bin/sh',
'SHELL' => '/bin/bash',
So the shell is still bash, but bash has a variable $_ which also show the absolute filename of the shell or script being executed, which may also give a valuable hint. Similarily, for other environments there will most probably be clues left in the perl %ENV hash that should give you valuable hints.
If you're running PowerShell 2.0 or above (most likely), you can infer the shell as a parent process by examining the environment variable %psmodulepath%. By default, it points to the system modules under %windir%\system32\windowspowershell\v1.0\modules; this is what you would see if you examine the variable from cmd.exe.
However, when PowerShell starts up, it prepends the user's default module search path to this environment variable which looks like: %userprofile%\documents\windowspowershell\modules. This is inherited by child processes. So, your logic would be to test if %psmodulepath% starts with %userprofile% to detect powershell 2.0 or higher. This won't work in PowerShell 1.0 because it does not support modules.
This is on Windows XP with PowerShell v2.0, so take it with a grain of salt.
In a cmd.exe shell, I get:
PSModulePath=C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\
whereas in the PowerShell console window, I get:
PSModulePath=E:\Home\user\WindowsPowerShell\Modules;C:\WINDOWS\system32\WindowsP
owerShell\v1.0\Modules\
where E:\Home\user is where my "My Documents" folder is. So, one heuristic may be to check if PSModulePath contains a user dependent path.
In addition, in a console window, I get:
!::=::\
in the environment. From the PowerShell ISE, I get:
!::=::\
!C:=C:\Documents and Settings\user