Perl-LanguageServer icon indicating copy to clipboard operation
Perl-LanguageServer copied to clipboard

Environment from calling shell not remembered

Open dseynhae opened this issue 4 years ago • 7 comments

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?

dseynhae avatar Aug 13 '20 02:08 dseynhae

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...

dseynhae avatar Aug 18 '20 22:08 dseynhae

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.

richterger avatar Aug 29 '20 20:08 richterger

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).

dseynhae avatar Aug 30 '20 22:08 dseynhae

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.

dseynhae avatar Sep 05 '20 15:09 dseynhae

I replaced code with mycode.

  1. mycode will take a .code-workspace file, and update it: environment definitions marked by anchors get replaced by the "current environment".
  2. 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...

dseynhae avatar Sep 06 '20 18:09 dseynhae