Improve gadget installation; add system wide adapter config
This commit is contained in:
parent
0b1a06f47e
commit
14f6814ff1
6 changed files with 169 additions and 22 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -7,3 +7,4 @@ tests/messages
|
||||||
tests/debuglog
|
tests/debuglog
|
||||||
test.log
|
test.log
|
||||||
gadgets/
|
gadgets/
|
||||||
|
*.pyc
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,20 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
# vimspector - A multi-language debugging system for Vim
|
||||||
|
# Copyright 2019 Ben Jackson
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import urllib.request as urllib2
|
import urllib.request as urllib2
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
@ -8,7 +23,6 @@ except ImportError:
|
||||||
import contextlib
|
import contextlib
|
||||||
import os
|
import os
|
||||||
import collections
|
import collections
|
||||||
import platform
|
|
||||||
import string
|
import string
|
||||||
import zipfile
|
import zipfile
|
||||||
import shutil
|
import shutil
|
||||||
|
|
@ -16,6 +30,14 @@ import subprocess
|
||||||
import traceback
|
import traceback
|
||||||
import tarfile
|
import tarfile
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
|
||||||
|
# Include vimspector source, for utils
|
||||||
|
sys.path.insert( 1, os.path.join( os.path.dirname( __file__ ),
|
||||||
|
'python3' ) )
|
||||||
|
|
||||||
|
from vimspector import install
|
||||||
|
|
||||||
GADGETS = {
|
GADGETS = {
|
||||||
'vscode-cpptools': {
|
'vscode-cpptools': {
|
||||||
|
|
@ -23,6 +45,7 @@ GADGETS = {
|
||||||
'url': ( 'https://github.com/Microsoft/vscode-cpptools/releases/download/'
|
'url': ( 'https://github.com/Microsoft/vscode-cpptools/releases/download/'
|
||||||
'${version}/${file_name}' ),
|
'${version}/${file_name}' ),
|
||||||
},
|
},
|
||||||
|
'do': lambda name, root: InstallCppTools( name, root ),
|
||||||
'all': {
|
'all': {
|
||||||
'version': '0.21.0',
|
'version': '0.21.0',
|
||||||
},
|
},
|
||||||
|
|
@ -38,6 +61,18 @@ GADGETS = {
|
||||||
'file_name': 'cpptools-win32.vsix',
|
'file_name': 'cpptools-win32.vsix',
|
||||||
'checksum': None,
|
'checksum': None,
|
||||||
},
|
},
|
||||||
|
"adapters": {
|
||||||
|
"vscode-cpptools": {
|
||||||
|
"name": "cppdbg",
|
||||||
|
"command": [
|
||||||
|
"${gadgetDir}/vscode-cpptools/debugAdapters/OpenDebugAD7"
|
||||||
|
],
|
||||||
|
"attach": {
|
||||||
|
"pidProperty": "processId",
|
||||||
|
"pidSelect": "ask"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
'vscode-python': {
|
'vscode-python': {
|
||||||
'download': {
|
'download': {
|
||||||
|
|
@ -55,7 +90,7 @@ GADGETS = {
|
||||||
'url': 'https://github.com/puremourning/TclProDebug',
|
'url': 'https://github.com/puremourning/TclProDebug',
|
||||||
'ref': 'master',
|
'ref': 'master',
|
||||||
},
|
},
|
||||||
'do': lambda root: InstallTclProDebug( root )
|
'do': lambda name, root: InstallTclProDebug( name, root )
|
||||||
},
|
},
|
||||||
'vscode-mono-debug': {
|
'vscode-mono-debug': {
|
||||||
'enabled': False,
|
'enabled': False,
|
||||||
|
|
@ -94,7 +129,33 @@ def CurrentWorkingDir( d ):
|
||||||
finally:
|
finally:
|
||||||
os.chdir( cur_d )
|
os.chdir( cur_d )
|
||||||
|
|
||||||
def InstallTclProDebug( root ):
|
|
||||||
|
def MakeExecutable( file_path ):
|
||||||
|
# TODO: import stat and use them by _just_ adding the X bit.
|
||||||
|
print( 'Making executable: {}'.format( file_path ) )
|
||||||
|
os.chmod( file_path, 0o755 )
|
||||||
|
|
||||||
|
|
||||||
|
def InstallCppTools( name, root ):
|
||||||
|
extension = os.path.join( root, 'extension' )
|
||||||
|
|
||||||
|
# It's hilarious, but the execute bits aren't set in the vsix. So they
|
||||||
|
# actually have javascript code which does this. It's just a horrible horrible
|
||||||
|
# hoke that really is not funny.
|
||||||
|
MakeExecutable( os.path.join( extension, 'debugAdapters', 'OpenDebugAD7' ) )
|
||||||
|
with open( os.path.join( extension, 'package.json' ) ) as f:
|
||||||
|
package = json.load( f )
|
||||||
|
runtime_dependencies = package[ 'runtimeDependencies' ]
|
||||||
|
for dependency in runtime_dependencies:
|
||||||
|
for binary in dependency.get( 'binaries' ):
|
||||||
|
file_path = os.path.abspath( os.path.join( extension, binary ) )
|
||||||
|
if os.path.exists( file_path ):
|
||||||
|
MakeExecutable( os.path.join( extension, binary ) )
|
||||||
|
|
||||||
|
MakeExtensionSymlink( name, root )
|
||||||
|
|
||||||
|
|
||||||
|
def InstallTclProDebug( name, root ):
|
||||||
configure = [ './configure' ]
|
configure = [ './configure' ]
|
||||||
|
|
||||||
if OS == 'macos':
|
if OS == 'macos':
|
||||||
|
|
@ -127,7 +188,7 @@ def InstallTclProDebug( root ):
|
||||||
subprocess.check_call( configure )
|
subprocess.check_call( configure )
|
||||||
subprocess.check_call( [ 'make' ] )
|
subprocess.check_call( [ 'make' ] )
|
||||||
|
|
||||||
MakeSymlink( gadget_dir, 'tclpro', root )
|
MakeSymlink( gadget_dir, name, root )
|
||||||
|
|
||||||
|
|
||||||
def DownloadFileTo( url, destination, file_name = None, checksum = None ):
|
def DownloadFileTo( url, destination, file_name = None, checksum = None ):
|
||||||
|
|
@ -186,11 +247,28 @@ def ValidateCheckSumSHA256( file_path, checksum ):
|
||||||
|
|
||||||
def RemoveIfExists( destination ):
|
def RemoveIfExists( destination ):
|
||||||
if os.path.exists( destination ) or os.path.islink( destination ):
|
if os.path.exists( destination ) or os.path.islink( destination ):
|
||||||
print( "Removing existing {}".format( destination ) )
|
if os.path.islink( destination ):
|
||||||
if os.path.isdir( destination ):
|
print( "Removing file {}".format( destination ) )
|
||||||
shutil.rmtree( destination )
|
|
||||||
else:
|
|
||||||
os.remove( destination )
|
os.remove( destination )
|
||||||
|
else:
|
||||||
|
print( "Removing dir {}".format( destination ) )
|
||||||
|
shutil.rmtree( destination )
|
||||||
|
|
||||||
|
|
||||||
|
# Python's ZipFile module strips execute bits from files, for no good reason
|
||||||
|
# other than crappy code. Let's do it's job for it.
|
||||||
|
class ModePreservingZipFile( zipfile.ZipFile ):
|
||||||
|
def extract( self, member, path = None, pwd = None ):
|
||||||
|
if not isinstance(member, zipfile.ZipInfo):
|
||||||
|
member = self.getinfo(member)
|
||||||
|
|
||||||
|
if path is None:
|
||||||
|
path = os.getcwd()
|
||||||
|
|
||||||
|
ret_val = self._extract_member(member, path, pwd)
|
||||||
|
attr = member.external_attr >> 16
|
||||||
|
os.chmod(ret_val, attr)
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
|
||||||
def ExtractZipTo( file_path, destination, format ):
|
def ExtractZipTo( file_path, destination, format ):
|
||||||
|
|
@ -198,7 +276,7 @@ def ExtractZipTo( file_path, destination, format ):
|
||||||
RemoveIfExists( destination )
|
RemoveIfExists( destination )
|
||||||
|
|
||||||
if format == 'zip':
|
if format == 'zip':
|
||||||
with zipfile.ZipFile( file_path ) as f:
|
with ModePreservingZipFile( file_path ) as f:
|
||||||
f.extractall( path = destination )
|
f.extractall( path = destination )
|
||||||
return
|
return
|
||||||
elif format == 'tar':
|
elif format == 'tar':
|
||||||
|
|
@ -213,8 +291,16 @@ def ExtractZipTo( file_path, destination, format ):
|
||||||
subprocess.check_call( [ 'tar', 'zxvf', file_path ] )
|
subprocess.check_call( [ 'tar', 'zxvf', file_path ] )
|
||||||
|
|
||||||
|
|
||||||
|
def MakeExtensionSymlink( name, root ):
|
||||||
|
MakeSymlink( gadget_dir, name, os.path.join( root, 'extension' ) ),
|
||||||
|
|
||||||
|
|
||||||
def MakeSymlink( in_folder, link, pointing_to ):
|
def MakeSymlink( in_folder, link, pointing_to ):
|
||||||
RemoveIfExists( os.path.join( in_folder, link ) )
|
RemoveIfExists( os.path.join( in_folder, link ) )
|
||||||
|
|
||||||
|
in_folder = os.path.abspath( in_folder )
|
||||||
|
pointing_to = os.path.relpath( os.path.abspath( pointing_to ),
|
||||||
|
in_folder )
|
||||||
os.symlink( pointing_to, os.path.join( in_folder, link ) )
|
os.symlink( pointing_to, os.path.join( in_folder, link ) )
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -223,16 +309,14 @@ def CloneRepoTo( url, ref, destination ):
|
||||||
subprocess.check_call( [ 'git', 'clone', url, destination ] )
|
subprocess.check_call( [ 'git', 'clone', url, destination ] )
|
||||||
subprocess.check_call( [ 'git', '-C', destination, 'checkout', ref ] )
|
subprocess.check_call( [ 'git', '-C', destination, 'checkout', ref ] )
|
||||||
|
|
||||||
if platform.system() == 'Darwin':
|
OS = install.GetOS()
|
||||||
OS = 'macos'
|
gadget_dir = install.GetGadgetDir( os.path.dirname( __file__ ), OS )
|
||||||
elif platform.system() == 'Winwdows':
|
|
||||||
OS = 'windows'
|
|
||||||
else:
|
|
||||||
OS = 'linux'
|
|
||||||
|
|
||||||
gadget_dir = os.path.join( os.path.dirname( __file__ ), 'gadgets', OS )
|
print( 'OS = ' + OS )
|
||||||
|
print( 'gadget_dir = ' + gadget_dir )
|
||||||
|
|
||||||
failed = []
|
failed = []
|
||||||
|
all_adapters = {}
|
||||||
for name, gadget in GADGETS.items():
|
for name, gadget in GADGETS.items():
|
||||||
if not gadget.get( 'enabled', True ):
|
if not gadget.get( 'enabled', True ):
|
||||||
continue
|
continue
|
||||||
|
|
@ -269,9 +353,12 @@ for name, gadget in GADGETS.items():
|
||||||
root = destination
|
root = destination
|
||||||
|
|
||||||
if 'do' in gadget:
|
if 'do' in gadget:
|
||||||
gadget[ 'do' ]( root )
|
gadget[ 'do' ]( name, root )
|
||||||
else:
|
else:
|
||||||
MakeSymlink( gadget_dir, name, os.path.join( root, 'extenstion') ),
|
MakeExtensionSymlink( name, root )
|
||||||
|
|
||||||
|
all_adapters.update( gadget.get( 'adapters', {} ) )
|
||||||
|
|
||||||
|
|
||||||
print( "Done installing {}".format( name ) )
|
print( "Done installing {}".format( name ) )
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -280,6 +367,12 @@ for name, gadget in GADGETS.items():
|
||||||
print( "FAILED installing {}: {}".format( name, e ) )
|
print( "FAILED installing {}: {}".format( name, e ) )
|
||||||
|
|
||||||
|
|
||||||
|
with open( install.GetGadgetConfigFile( os.path.dirname( __file__ ) ),
|
||||||
|
'w' ) as f:
|
||||||
|
json.dump( { 'adapters': all_adapters }, f, indent=2, sort_keys=True )
|
||||||
|
|
||||||
if failed:
|
if failed:
|
||||||
raise RuntimeError( 'Failed to install gadgets: {}'.format(
|
raise RuntimeError( 'Failed to install gadgets: {}'.format(
|
||||||
','.join( failed ) ) )
|
','.join( failed ) ) )
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,16 +25,27 @@ from collections import defaultdict
|
||||||
from vimspector import ( breakpoints,
|
from vimspector import ( breakpoints,
|
||||||
code,
|
code,
|
||||||
debug_adapter_connection,
|
debug_adapter_connection,
|
||||||
|
install,
|
||||||
output,
|
output,
|
||||||
stack_trace,
|
stack_trace,
|
||||||
utils,
|
utils,
|
||||||
variables )
|
variables )
|
||||||
|
|
||||||
|
VIMSPECTOR_HOME = os.path.abspath( os.path.join( os.path.dirname( __file__ ),
|
||||||
|
'..',
|
||||||
|
'..' ) )
|
||||||
|
|
||||||
|
|
||||||
class DebugSession( object ):
|
class DebugSession( object ):
|
||||||
def __init__( self ):
|
def __init__( self ):
|
||||||
self._logger = logging.getLogger( __name__ )
|
self._logger = logging.getLogger( __name__ )
|
||||||
utils.SetUpLogging( self._logger )
|
utils.SetUpLogging( self._logger )
|
||||||
|
|
||||||
|
self._logger.info( 'VIMSPECTOR_HOME = %s', VIMSPECTOR_HOME )
|
||||||
|
self._logger.info( 'gadgetDir = %s',
|
||||||
|
install.GetGadgetDir( VIMSPECTOR_HOME,
|
||||||
|
install.GetOS() ) )
|
||||||
|
|
||||||
self._uiTab = None
|
self._uiTab = None
|
||||||
self._stackTraceView = None
|
self._stackTraceView = None
|
||||||
self._variablesView = None
|
self._variablesView = None
|
||||||
|
|
@ -66,7 +77,15 @@ class DebugSession( object ):
|
||||||
database = json.load( f )
|
database = json.load( f )
|
||||||
|
|
||||||
configurations = database.get( 'configurations' )
|
configurations = database.get( 'configurations' )
|
||||||
adapters = database.get( 'adapters' )
|
adapters = {}
|
||||||
|
|
||||||
|
for gadget_config_file in [ install.GetGadgetConfigFile( VIMSPECTOR_HOME ),
|
||||||
|
utils.PathToConfigFile( '.gadgets.json' ) ]:
|
||||||
|
if gadget_config_file and os.path.exists( gadget_config_file ):
|
||||||
|
with open( gadget_config_file, 'r' ) as f:
|
||||||
|
adapters.update( json.load( f ).get( 'adapters' ) or {} )
|
||||||
|
|
||||||
|
adapters.update( database.get( 'adapters' ) or {} )
|
||||||
|
|
||||||
if len( configurations ) == 1:
|
if len( configurations ) == 1:
|
||||||
configuration_name = next( iter( configurations.keys() ) )
|
configuration_name = next( iter( configurations.keys() ) )
|
||||||
|
|
@ -90,7 +109,8 @@ class DebugSession( object ):
|
||||||
# way to load .vimspector.local.json which just sets variables
|
# way to load .vimspector.local.json which just sets variables
|
||||||
self._variables = {
|
self._variables = {
|
||||||
'dollar': '$', # HACK
|
'dollar': '$', # HACK
|
||||||
'workspaceRoot': self._workspace_root
|
'workspaceRoot': self._workspace_root,
|
||||||
|
'gadgetDir': install.GetGadgetDir( VIMSPECTOR_HOME, install.GetOS() )
|
||||||
}
|
}
|
||||||
self._variables.update( adapter.get( 'variables', {} ) )
|
self._variables.update( adapter.get( 'variables', {} ) )
|
||||||
self._variables.update( configuration.get( 'variables', {} ) )
|
self._variables.update( configuration.get( 'variables', {} ) )
|
||||||
|
|
|
||||||
32
python3/vimspector/install.py
Normal file
32
python3/vimspector/install.py
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
# vimspector - A multi-language debugging system for Vim
|
||||||
|
# Copyright 2019 Ben Jackson
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import platform
|
||||||
|
import os
|
||||||
|
|
||||||
|
def GetOS():
|
||||||
|
if platform.system() == 'Darwin':
|
||||||
|
return 'macos'
|
||||||
|
elif platform.system() == 'Winwdows':
|
||||||
|
return 'windows'
|
||||||
|
else:
|
||||||
|
return 'linux'
|
||||||
|
|
||||||
|
def GetGadgetDir( vimspector_base, OS ):
|
||||||
|
return os.path.join( os.path.abspath( vimspector_base ), 'gadgets', OS )
|
||||||
|
|
||||||
|
def GetGadgetConfigFile( vimspector_base ):
|
||||||
|
return os.path.join( GetGadgetDir( vimspector_base, GetOS() ),
|
||||||
|
'.gadgets.json' )
|
||||||
|
|
@ -279,7 +279,7 @@ def AppendToBuffer( buf, line_or_lines, modified=False ):
|
||||||
line = 1
|
line = 1
|
||||||
buf[:] = line_or_lines
|
buf[:] = line_or_lines
|
||||||
except:
|
except:
|
||||||
# There seem to be a lot of Vim bugs that lead to E351, whose help says that
|
# There seem to be a lot of Vim bugs that lead to E315, whose help says that
|
||||||
# this is an internal error. Ignore the error, but write a trace to the log.
|
# this is an internal error. Ignore the error, but write a trace to the log.
|
||||||
logging.getLogger( __name__ ).exception(
|
logging.getLogger( __name__ ).exception(
|
||||||
'Internal error while updating buffer %s (%s)', buf.name, buf.number )
|
'Internal error while updating buffer %s (%s)', buf.name, buf.number )
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ function! SetUp_Test_Mappings_Are_Added_HUMAN()
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! Test_Mappings_Are_Added_HUMAN()
|
function! Test_Mappings_Are_Added_HUMAN()
|
||||||
|
|
||||||
call assert_true( hasmapto( 'vimspector#Continue()' ) )
|
call assert_true( hasmapto( 'vimspector#Continue()' ) )
|
||||||
call assert_false( hasmapto( 'vimspector#Launch()' ) )
|
call assert_false( hasmapto( 'vimspector#Launch()' ) )
|
||||||
call assert_true( hasmapto( 'vimspector#Stop()' ) )
|
call assert_true( hasmapto( 'vimspector#Stop()' ) )
|
||||||
|
|
@ -89,4 +88,6 @@ function! Test_Signs_Placed_Using_API_Are_Shown()
|
||||||
|
|
||||||
call assert_true( len( signs ) == 1 )
|
call assert_true( len( signs ) == 1 )
|
||||||
call assert_true( len( signs[ 0 ].signs ) == 0 )
|
call assert_true( len( signs[ 0 ].signs ) == 0 )
|
||||||
|
|
||||||
|
" TODO: Use the screen dump test ?
|
||||||
endfunction
|
endfunction
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue