Auto_execok problem on cygwin - cygwin

I have a problem:
the auto_execok command doesn't work on Cygwin platform as expected.
It cannot find anything from your PATH enviroment variable, as
info body auto_execok
"...
foreach dir [split $path {;}] {
"
It thinks by default that ; is right separator, but Cygwin uses :!
How to elegantly overcome this problem?
I don't want to change PATH variable as other programs/scripts could correctly use : as it should be for Cygwin.

Have you got a proper Cygwin-aware build of Tcl? As you've found, a straight Windows build runs into problems precisely because the Cygwin environment is a sort of mix between Unix and Windows. (This an example of why we don't fully support doing things in Cygwin; it gets some love from time to time, but it's not a primary platform because it is fully of fiddly complexities.) That said, this is the sort of question which it is almost certainly better to ask on comp.lang.tcl as that's got a community likely to be able to help with this sort of thing.
Also, what patch-level of Tcl is this? This matters because the level of support has most certainly varied over time…

We can use a mix of set ar [info args auto_execok], set bd [info body auto_execok],
some regsub on body with set cygdir [exec cygpath -a $wdir] and eval proc auto_exeok {$ar} {$bd} to obtain needed result.
However, for the moment, I'm not yet ready with the complete solution.

You can wrap the native tcl version of auto_execok with one that will resolve the correct path. We can use the fact that the original auto_execok will find the cygpath.exe and in one fell swoop tell use that the current script is running windows and it is setup for cygwin. Once that it is known we can wrap the original auto_execok proc with one that will use cygpath.exe to resolve the true windows path. I have used the try command so this is for 8.5 and above but this can be written using catch for lower versions of tcl. Also because subst command is used the path to cygpath is hardcoded into the new auto_execok proc so lookup only happens once. Also only allow this code to run once. So as example
before code below runs
puts "[ auto_execok tar ]"
gives
"/usr/bin/tar"
after code is run auto_execok is wrapped:
puts "[ auto_execok tar ]"
gives (on my machine):
"C:/cygwin/bin/tar.EXE"
if { [string length [ auto_execok cygpath ] ] } {
set paths [ split $env(PATH) ";" ]
set cygexecpath ""
foreach p $paths {
set c [file join $p cygpath.exe ]
puts "checking for $c "
if {[file exists $c ] } {
set cygexecpath [file join $p cygpath.exe ]
break
}
}
if { $cygexecpath eq "" } {
puts "unable to find true path to [auto_execok cygpath.exe ]"
}
# rename original proc so we can use it in our wrapper proc
rename ::auto_execok ::auto_execok_orig
uplevel #0 [subst -nocommands {proc auto_execok { path } {
try {
set path [auto_execok_orig \$path ]
if { \$path ne \"\" } {
set path [string trim [exec $cygexecpath -w \$path ] ]
}
} on error { a b } {
set path \"\"
}
return \$path
} } ]
puts "[info body auto_execok ] "
}

Related

Unexpected switch command behavior in TCL

I am trying to run the below switch block using TCL. I am expecting the output to be 10 based on how the switch statement works. But the output comes out to be Default. I am not what's the explanation behind that and how I can fix it.
set a 10
set data 10
switch $data {
$a {
puts "10"
}
default {
puts "Default"
}
}
The output is:
Default
In tcl, the string enclosed in {} is literal. The variables in the string is not substituted. The same rule applies to the statements of the {} blocks passed to if/for/switch commands.
So in your case, $a is a literal string, not 10, for the switch command.
You may re-write your switch block as following thus $a is substituted to 10 before passed to switch command.
switch $data [list \
$a {
puts "10"
} \
default {
puts "Default"
} \
]

How to switch between gcc6 and gcc8 based on a variable in powershell

I am trying to switch between gcc-6 and gcc-8 based on a variable in a powershell script. I have both installed and my ~/.bashrc file is empty.
I have in script.ps1
$gccVersion = 'gcc8'
if ($gccVersion -eq 'gcc6'){
'source /opt/rh/devtoolset-6/enable'
} else {
'source /opt/rh/devtoolset-8/enable'
}
However this does not work. It just sticks with the default gcc version no matter what variable I give it. What am I missing?
What is the easiest way to dynamically switch between gcc6 and gcc8 based on a variable?
As stated in above comment, you've the invoke the given strings. I'd try to invoke them via the &-operator, like this:
$gccVersion = 'gcc8'
if ($gccVersion -eq 'gcc6'){
& 'source /opt/rh/devtoolset-6/enable'
} else {
& 'source /opt/rh/devtoolset-8/enable'
}
# Check the last exit code and execution status
if ((-not $?) -or ($LASTEXITCODE -ne 0)) {
Write-Error "Command failed"
}
You should also check the LASTEXITCODE and the execution status of last operation.
Hope that helps.
Your if does not properly test the variable. Write it either as
if [ "$gccVersion" = gcc6 ]
or
if test $gccVersion" = gcc6
or
if [[ $gccVersion == gcc6 ]]

Having a small Issue running a Perl scripts IF statement.

I created a small script in Perl and I am really new to this. I'm supposed to have a script that looks at an argument given and create a directory tree in the given argument. This part of the script works. The second part (which is the nested if statement) does not when you do not give an argument and it asks you to input a directory of your choice. I believe the nested if statement is messing up due to the $file input but I'm not entirely sure whats wrong. This is probably something really simple, but I have not been able to find the solution. Thank you in advance for the help and tips.
#! /usr/bin/perl
if ($#ARGV == -1)
{
print "Please enter default directory:";
my $file=<STDIN>;
if (-d $file)
{
chdir $file;
system("mkdir Data");
system("mkdir Data/Image");
system("mkdir Data/Cache");
print "Structure Created";
}
else
{
print "Directory does not exsist";
}
}
else
{
chdir $ARGV[0];
system("mkdir Data");
system("mkdir Data/Image");
system("mkdir Data/Cache");
print ("Structure Created");
}
print ("\n");
The test -d $file is failing because what is entered via STDIN also has the newline, after the string that specifies the directory name. You need chomp($file);
However, there are a few more points I would like to bring up.
Most importantly, there is repeated code in both branches. You really do not want to do that. It can, and does, cause trouble later. Instead, decide on the directory name, and then make it.
Second, there is no reason to go out to the system in order to make a directory. It is far better to do it in Perl, and there are good modules for this.
use strict;
use warnings;
use File::Path qw(make_path);
my $dir;
if (not #ARGV) {
print "Please enter default directory: ";
$dir = <STDIN>;
chomp $dir;
}
else {
$dir = $ARGV[0];
}
die "No directory $dir" if not -d $dir;
my $orig_cwd = chdir $dir or die "Can't chdir to $dir: $!";
my #dirs = map { "Data/$_" } qw(Image Cache);
my #dirs_made = make_path( #dirs, { verbose => 1 } );
print "Created directories:\n";
print "$_\n" for #dirs_made;
I build the directory list using map so to avoid repeated strings with Data/..., and for later flexibility. You can of course just type the names in, but that tends to invite silly mistakes.
I used File::Path to make the directories. It builds the whole path, like mkdir -p, and has a few other useful options that you can pass in { }, including error handling. There are other modules as well, for example Path::Tiny with its mkpath (and a lot of other goodies).
Note that with chdir you probably want to record the current working directory, that it returns, and that you want to check for error. But you don't have to chdir, if there are no other reasons for that. Just include the $dir name in the map
# No chdir needed here
my #dirs = map { "$dir/Data/$_" } qw(Image Cache);

TCL list element in quotes followed by } instead of space

I'm trying to write an automated validation test on a small program using TCL. It should evaluate the input h1=7 and pass if the output is 7.000000. Likewise, the input h1=9 should pass if the output is 9.0000. However, I get the following error:
ERROR: list element in quotes followed by "}" instead of space
while executing
"foreach pattern $testdata {
set inputs [ lindex $pattern 0 ]
set expected [ lindex $pattern 1 ]
eval "spawn $CLIC $inputs"
expect {..."
(file "./test/clic/test-clic.exp" line 22)
Here is the code:
#!/usr/bin/expect
set tool "clic"
set CLIC "./clic "
set testdata {
{"h1=7" "7.000000"}
{"h1=9" "9.000000"}
}
# global CLIC
foreach pattern $testdata {
set inputs [ lindex $pattern 0 ]
set expected [ lindex $pattern 1 ]
eval "spawn $CLIC $inputs"
expect {
$expected { pass $inputs }
default { fail $inputs }
}
}
How do I resolve this? Thank you.
Given the error message given and the discrepancy between the line number in the error message and the number of lines in the script you told us about, I'm guessing that you've trimmed down the script a little bit before asking the question. Which would be perfectly OK (and a good thing) except that in the trimming process you removed the thing that was causing the problem! The code that you posted doesn't have the issue.
The issue is almost certainly in one of the lines of testdata that you removed. It's either that you've got malformatted list as testdata, or that it produces a malformatted script when you do the concatenations for the eval "spawn …"; unfortunately, I can't be sure which with the info you've given us. (It's also possibly an issue in the expect with it not liking taking a value from a variable when that argument is in braces; the documentation for expect isn't very clear about this case, yet it gives hints that it might do what you want.)
A good start would be to update the script to actually use the features of Tcl 8.6 (or Tcl 8.5) since you're already using that version. The key changes happen to these lines:
foreach pattern $testdata {
set inputs [ lindex $pattern 0 ]
set expected [ lindex $pattern 1 ]
eval "spawn $CLIC $inputs"
Which are much better written as:
foreach pattern $testdata {
lassign $pattern inputs expected
spawn {*}$CLIC {*}$inputs
That has far fewer ways of being misinterpreted than what you were using before, as well as being shorter. We can also wrap that all up in code to give better error handling:
foreach pattern $testdata {
if {[catch {
lassign $pattern inputs expected
spawn {*}$CLIC {*}$inputs
} msg]} {
puts stderr "Problem handling pattern '$pattern': $msg"
continue
}
If you still get the same failure at that point, the problem is almost certainly that your overall testdata is a malformed list (and it would be malformed like this: "something"{something else} with no whitespace between closing quotes and opening braces); since that's under your complete control, you'll just have to fix it…

How can I check if an environmental variable is set?

I have the following code in perl
my %Opt =
(
boards_txt => "$ENV{'ARDUINO_DIR'}/hardware/arduino/boards.txt",
);
In this you can see that the env variable ARDUINO_DIR is append. Some users might not have this variable set. If that is the case, then I want to hardcode a path.
Question: How can I check if the env variable is set or not?
The correct answers have been given, but I wanted to add that you might make use of the rather handy defined-or assignment operator //=:
my $dir = $ENV{'ARDUINO_DIR'};
$dir //= "/other/path";
Or, as RobEarl points out in the comment:
my $dir = $ENV{'ARDUINO_DIR'} // "/other/path";
This is the logical equivalent of
my $dir;
if (defined $ENV{'ARDUINO_DIR'}) {
$dir = $ENV{'ARDUINO_DIR'};
} else {
$dir = "/other/path";
}
As mob points out, the defined-or operator requires perl v5.10. For those who still have not upgraded to that version it is also possible to use the || operator:
my $dir = $ENV{'ARDUINO_DIR'} || "/other/path";
The caveat being that this will overwrite values that are interpreted as false, which may in some context be considered proper values, such as the empty string or zero. In this case, however, it is unlikely that 0 or the empty string are valid paths.
You are already using the %ENV hash. It contains all environment variables, so you could do something like:
if (defined $ENV{'ARDUINO_DIR'}) { $prefix = $ENV{'ARDUINO_DIR'} }
else { $prefix = '/path/to/arduino/dir/' }
my $path_to_txt = $prefix . 'boards.txt';
I suggest you use File::Spec for working with paths.
You can check for existence of a hash key with exists:
perl -le 'print "fnord!" if exists $ENV{"ARDUINO_DIR"}'

Resources