Perl-LanguageServer
Perl-LanguageServer copied to clipboard
Environment from calling shell not remembered
I have a rather complicated infrastructure that sets a rich environment. A Perl application then runs in this environment, and reacts to it. That needs debugging.
I start VS Code, and I am glad to see that a Bash Terminal inside VS Code inherits the complex environment.
However, when starting the Perl Debugger, none of my environment variables are preserved ($ENV{variable}
);
I can't set every single variable in the launch configuration, that is a daunting task (and it is different from one debug run to another).
I consider it a bug that the environment from the parent shell (where VS code is invoked) is not preserved... or am I doing something wrong?
Even the relevant environment variables are dropped (e.g. PERL5LIB)...
I tried to fix this by including this in my user settings.json
:
"perl.perlInc": [
"${env:PERL5LIB}",
],
However, this notation to resolve environment variables is not resolved?
Worked around that by providing the hard-coded full path to my library directories...
The environment is not passed to the LanguageServer by vscode, so LanguageServer can't do much here. Maybe it's possible to pass the parent environment inside of clients/vscode/perl/src/extension.ts where rhe LanguageServer is called, but I am not aware of how to do it.
Doesn't the Perl::LanguageServer just inherit the environment that it is invoked in?
As far as I know, VS Code does know all the environment variables (you can even access them in your task .json file)...
If I look at an alternative Perl Debugger extension, it seems to get those environment variables just fine... But I want to standardize on a VS Code extension who uses a LanguageServer instead (more features available).
I tested what could become a workaround: In my settings file, I just set the environment variables:
"env": [
"PATH",
"${env:PATH}",
],
The challenge is that our Perl applications use a bunch of environment variables, and you don't quite know which ones you need (we can grep for them in the source code, but we get a list of hundreds of possibilities, as simple as PATH, HOME, LD_LIBRARY_PATH, USER, to more application-specific details.
So I think I can script out the extraction of the complete environment, and paste it in my .code-workspace
file, before starting VS Code...
But still: I don't have to do this for any of my other languages (C++, Bash, Python, and even Perl - with the Perl Debug
extension).
I must be missing something very fundamental that makes my workaround redundant?
I admire the superior solution offered by Perl::LanguageServer
, but I must admit I don't have a clue how the VS Code extensions work. I suspect that the debugger is NOT just executing perl -d
, but actually running a VS Code intrinsic interpreter, managed by the Perl::LanguageServer?
Which is different from the other solutions I mentioned:
- My Bash extension runs
bashdb
for debugging - My Perl Debug extension runs
perl -d
for debugging - My C++ extension runs
gdb
for debugging
Any of those debuggers just inherit all environment variables from VS Code, which in turn inherited them from the calling shell...
So your statement about "VS Code doesn't pass the environment variables to Perl::LanguageServer
seems to shine a light on my ignorance: if Perl::LanguageServer
is now used by the VS Code debugger to interpret the Perl Statements, then we indeed don't know the environment variables... (unless manually provided in the settings file).
Thanks for your patience, I'm hoping that my understanding well enables me to help solve this all under the hood.
I replaced code
with mycode
.
-
mycode
will take a.code-workspace
file, and update it: environment definitions marked by anchors get replaced by the "current environment". - Then the actual
code
is called with this updated file...
This can be adjusted to work with folders as well, but here is a starting point:
#!/usr/bin/perl
package ImportPath 1.0;
use 5.010;
use strict;
use warnings;
use Carp;
use English '-no_match_vars';
sub get_env {
# Collect the output in an array of JSON statements
my $output = [];
my $json = q{};
# Process the full list of environment variables
foreach my $e ( keys %ENV ) {
# Filter out unwanted variables
next
if (
$e =~ m{(
^_| # Starts with underbar
_[$]| # Ends with underbar
[(][)] # Contains function declaration
)}sxm
);
# Construct the JSON statement
$json = "\"$e\": \"\$\{env:$e\}\",\n" or croak "$OS_ERROR";
# Add this JSON statement to the output
push @{$output}, $json;
}
return $output;
}
sub parse_file {
my ($fh) = @_;
# Collect the output in an array of lines
my $output = [];
# Boolean to control the printing of lines
my $print_line = 1;
# Process the file
while ( my $line = <$fh> ) {
# Search for start/stop anchors
if ($print_line) {
push @{$output}, $line;
}
if ( $line =~ m/^\s*\/\/\s*DMGS.*custom_env\s*/sxm ) {
if ( $line =~ m/start_/sxm ) {
# /!\ Disable printing the
# old environment variable definitions
$print_line = 0;
# Get the new environment variable definitions
my $env_list = get_env();
# Append the current environment variable definitions
push @{$output}, @{$env_list};
}
elsif ( $line =~ m/stop_/sxm ) {
# /!\ Reenable printing the lines
$print_line = 1;
# /!\ This line was skipped!
# Print anyway
push @{$output}, $line;
}
else {
croak "Go get a cup of coffee;\n Then check for typos\n";
}
}
}
return $output;
}
sub write_fi1e {
my ( $fh, $content ) = @_;
# Simply put all the array items
# as new lines in the file
foreach my $line ( @{$content} ) {
print {$fh} $line or croak "$OS_ERROR";
}
return;
}
my $file = $ARGV[0];
my $fh;
# Open the file that needs the JSON statements
# defining the environment
open $fh, '<', "$file" or croak "$OS_ERROR";
# [i] Parse this file
# <lines>
# <start_anchor> // DMGS start_custom_env
# <old_env_statements>
# <stop_anchor> // DMGS stope_custom_env
# <lines>
# Copy <lines> and <anchors>
# Replace <old_env_statements by
# <current_env_statements>
my $output = parse_file($fh);
close $fh or croak "$OS_ERROR";
# Open the original file to update
open $fh, '>', "$file" or croak "$OS_ERROR";
# Write the updated content in the original file
write_fi1e( $fh, $output );
close $fh or croak "$OS_ERROR";
# Start `code` with the current file
system "code $file";
1;
I don't know enough about VS Code extensions, to see if I can do the equivalent (basically rewrite this in JavaScript?)... However, I think it is essential that similar functionality gets built in to add tremendous value to this already great debugger...