Skip to content

Commit

Permalink
Upgrade gadgets when they change
Browse files Browse the repository at this point in the history
This adds a --upgrade option to install_gadget.py and makes
VimspectorUpdate only update things which have changed.

To do this, we record the gadget spec in a manfiest file and compare it
with the current spec when in upgrade mode.

'Changed' in this case means that the gadget spec has changed from the
last time the installer was run. It does _not_ actually check the
presence of the gadget.
  • Loading branch information
puremourning committed Jul 24, 2020
1 parent 56418e3 commit f9d20b9
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 70 deletions.
21 changes: 20 additions & 1 deletion install_gadget.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@
action = 'store_true',
help = 'Enable all unsupported completers' )

parser.add_argument( '--upgrade',
action = 'store_true',
help = 'Only update adapters changed from the manifest' )

parser.add_argument( '--quiet',
action = 'store_true',
help = 'Suppress installation output' )
Expand Down Expand Up @@ -175,6 +179,7 @@
succeeded = []
all_adapters = installer.ReadAdapters(
read_existing = args.update_gadget_config )
manifest = installer.Manifest()

for name, gadget in gadgets.GADGETS.items():
if not gadget.get( 'enabled', True ):
Expand All @@ -187,15 +192,27 @@
if getattr( args, 'disable_' + gadget[ 'language' ] ):
continue

if not args.upgrade:
manifest.Clear( name )

installer.InstallGagdet( name,
gadget,
manifest,
succeeded,
failed,
all_adapters )


for name, gadget in CUSTOM_GADGETS.items():
installer.InstallGagdet( name, gadget, succeeded, failed, all_adapters )
if not args.upgrade:
manifest.Clear( name )

installer.InstallGagdet( name,
gadget,
manifest,
succeeded,
failed,
all_adapters )

if args.no_gadget_config:
print( "" )
Expand All @@ -204,6 +221,8 @@
else:
installer.WriteAdapters( all_adapters )

manifest.Write()

if args.basedir:
print( "" )
print( "***NOTE***: You set --basedir to " + args.basedir +
Expand Down
5 changes: 2 additions & 3 deletions python3/vimspector/gadgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,7 @@
},
'macos': {
'file_name': 'netcoredbg-osx-master.tar.gz',
'checksum':
'c1dc6ed58c3f5b0473cfb4985a96552999360ceb9795e42d9c9be64af054f821',
'checksum': '',
},
'linux': {
'file_name': 'netcoredbg-linux-master.tar.gz',
Expand Down Expand Up @@ -385,7 +384,7 @@
'enabled': False,
'repo': {
'url': 'https://github.com/microsoft/vscode-node-debug2',
'ref': 'v1.42.0',
'ref': 'v1.42.5'
},
'do': lambda name, root, gadget: installer.InstallNodeDebug( name,
root,
Expand Down
5 changes: 5 additions & 0 deletions python3/vimspector/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ def GetGadgetDir( vimspector_base ):
return os.path.join( os.path.abspath( vimspector_base ), 'gadgets', GetOS() )


def GetManifestFile( vimspector_base ):
return os.path.join( GetGadgetDir( vimspector_base ),
'.gadgets.manifest.json' )


def GetGadgetConfigFile( vimspector_base ):
return os.path.join( GetGadgetDir( vimspector_base ), '.gadgets.json' )

Expand Down
193 changes: 127 additions & 66 deletions python3/vimspector/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ def RunUpdate( api_prefix, leave_open, *args ):
insatller_args.extend( FindGadgetForAdapter( adapter_name ) )

if insatller_args:
insatller_args.append( '--upgrade' )
RunInstaller( api_prefix, leave_open, *insatller_args )


Expand Down Expand Up @@ -217,6 +218,91 @@ def FindGadgetForAdapter( adapter_name ):
return candidates


class Manifest:
manifest: dict

def __init__( self ):
self.manifest = {}
self.Read()

def Read( self ):
try:
with open( install.GetManifestFile( options.vimspector_base ), 'r' ) as f:
self.manifest = json.load( f )
except OSError:
pass

def Write( self ):
with open( install.GetManifestFile( options.vimspector_base ), 'w' ) as f:
json.dump( self.manifest, f )


def Clear( self, name: str ):
try:
del self.manifest[ name ]
except KeyError:
pass


def Update( self, name: str, gadget_spec: dict ):
self.manifest[ name ] = gadget_spec


def RequiresUpdate( self, name: str, gadget_spec: dict ):
try:
current_spec = self.manifest[ name ]
except KeyError:
# It's new.
return True

# If anything changed in the spec, update
if not current_spec == gadget_spec:
return True

# Always update if the version string is 'master'. Probably a git repo
# that pulls master (which tbh we shouldn't have)
if current_spec.get( 'version' ) in ( 'master', '' ):
return True
if current_spec.get( 'repo', {} ).get( 'ref' ) == 'master':
return True

return False


def ReadAdapters( read_existing = True ):
all_adapters = {}
if read_existing:
try:
with open( install.GetGadgetConfigFile( options.vimspector_base ),
'r' ) as f:
all_adapters = json.load( f ).get( 'adapters', {} )
except OSError:
pass

# Include "built-in" adapter for multi-session mode
all_adapters.update( {
'multi-session': {
'port': '${port}',
'host': '${host}'
},
} )

return all_adapters


def WriteAdapters( all_adapters, to_file=None ):
adapter_config = json.dumps ( { 'adapters': all_adapters },
indent=2,
sort_keys=True )

if to_file:
to_file.write( adapter_config )
else:
with open( install.GetGadgetConfigFile( options.vimspector_base ),
'w' ) as f:
f.write( adapter_config )


def InstallGeneric( name, root, gadget ):
extension = os.path.join( root, 'extension' )
for f in gadget.get( 'make_executable', [] ):
Expand Down Expand Up @@ -300,52 +386,57 @@ def InstallTclProDebug( name, root, gadget ):


def InstallNodeDebug( name, root, gadget ):
node_version = subprocess.check_output( [ 'node', '--version' ],
universal_newlines=True ).strip()
Print( "Node.js version: {}".format( node_version ) )
if list( map( int, node_version[ 1: ].split( '.' ) ) ) >= [ 12, 0, 0 ]:
Print( "Can't install vscode-debug-node2:" )
Print( "Sorry, you appear to be running node 12 or later. That's not "
"compatible with the build system for this extension, and as far as "
"we know, there isn't a pre-built independent package." )
Print( "My advice is to install nvm, then do:" )
Print( " $ nvm install --lts 10" )
Print( " $ nvm use --lts 10" )
Print( " $ ./install_gadget.py --enable-node ..." )
raise RuntimeError( 'Node 10 is required to install node debugger (sadly)' )

with CurrentWorkingDir( root ):
CheckCall( [ 'npm', 'install' ] )
CheckCall( [ 'npm', 'run', 'build' ] )
MakeSymlink( name, root )


def InstallGagdet( name, gadget, succeeded, failed, all_adapters ):
def InstallGagdet( name: str,
gadget: dict,
manifest: Manifest,
succeeded: list,
failed: list,
all_adapters: dict ):

try:
print( f"Installing {name}..." )
v = {}
v.update( gadget.get( 'all', {} ) )
v.update( gadget.get( install.GetOS(), {} ) )
# Spec is an os-specific definition of the gadget
spec = {}
spec.update( gadget.get( 'all', {} ) )
spec.update( gadget.get( install.GetOS(), {} ) )

def save_adapters():
# allow per-os adapter overrides. v already did that for us...
all_adapters.update( spec.get( 'adapters', {} ) )
# add any other "all" adapters
all_adapters.update( gadget.get( 'adapters', {} ) )

if 'download' in gadget:
if 'file_name' not in v:
if 'file_name' not in spec:
raise RuntimeError( "Unsupported OS {} for gadget {}".format(
install.GetOS(),
name ) )

print( f"Installing {name}@{spec[ 'version' ]}..." )
spec[ 'download' ] = gadget[ 'download' ]
if not manifest.RequiresUpdate( name, spec ):
save_adapters()
print( " - Skip - up to date" )
return

destination = os.path.join(
install.GetGadgetDir( options.vimspector_base ),
'download',
name,
v[ 'version' ] )
spec[ 'version' ] )

url = string.Template( gadget[ 'download' ][ 'url' ] ).substitute( v )
url = string.Template( gadget[ 'download' ][ 'url' ] ).substitute( spec )

file_path = DownloadFileTo(
url,
destination,
file_name = gadget[ 'download' ].get( 'target' ),
checksum = v.get( 'checksum' ),
checksum = spec.get( 'checksum' ),
check_certificate = not options.no_check_certificate )

root = os.path.join( destination, 'root' )
Expand All @@ -354,8 +445,15 @@ def InstallGagdet( name, gadget, succeeded, failed, all_adapters ):
root,
format = gadget[ 'download' ].get( 'format', 'zip' ) )
elif 'repo' in gadget:
url = string.Template( gadget[ 'repo' ][ 'url' ] ).substitute( v )
ref = string.Template( gadget[ 'repo' ][ 'ref' ] ).substitute( v )
url = string.Template( gadget[ 'repo' ][ 'url' ] ).substitute( spec )
ref = string.Template( gadget[ 'repo' ][ 'ref' ] ).substitute( spec )

print( f"Installing {name}@{gadget[ 'repo' ][ 'ref' ]}..." )
spec[ 'repo' ] = gadget[ 'repo' ]
if not manifest.RequiresUpdate( name, spec ):
save_adapters()
print( " - Skip - up to date" )
return

destination = os.path.join(
install.GetGadgetDir( options.vimspector_base ),
Expand All @@ -365,15 +463,12 @@ def InstallGagdet( name, gadget, succeeded, failed, all_adapters ):
root = destination

if 'do' in gadget:
gadget[ 'do' ]( name, root, v )
gadget[ 'do' ]( name, root, spec )
else:
InstallGeneric( name, root, v )

# Allow per-OS adapter overrides. v already did that for us...
all_adapters.update( v.get( 'adapters', {} ) )
# Add any other "all" adapters
all_adapters.update( gadget.get( 'adapters', {} ) )
InstallGeneric( name, root, spec )

save_adapters()
manifest.Update( name, spec )
succeeded.append( name )
print( f" - Done installing {name}" )
except Exception as e:
Expand All @@ -383,40 +478,6 @@ def InstallGagdet( name, gadget, succeeded, failed, all_adapters ):
print( f" - FAILED installing {name}: {e}".format( name, e ) )


def ReadAdapters( read_existing = True ):
all_adapters = {}
if read_existing:
try:
with open( install.GetGadgetConfigFile( options.vimspector_base ),
'r' ) as f:
all_adapters = json.load( f ).get( 'adapters', {} )
except OSError:
pass

# Include "built-in" adapter for multi-session mode
all_adapters.update( {
'multi-session': {
'port': '${port}',
'host': '${host}'
},
} )

return all_adapters


def WriteAdapters( all_adapters, to_file=None ):
adapter_config = json.dumps ( { 'adapters': all_adapters },
indent=2,
sort_keys=True )

if to_file:
to_file.write( adapter_config )
else:
with open( install.GetGadgetConfigFile( options.vimspector_base ),
'w' ) as f:
f.write( adapter_config )


@contextlib.contextmanager
def CurrentWorkingDir( d ):
cur_d = os.getcwd()
Expand Down
8 changes: 8 additions & 0 deletions syntax/vimspector-installer.vim
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,18 @@ endif

let b:current_syntax = 'vimspector-installer'

syn match VimspectorGadget /[^ ]*\ze@/
syn match VimspectorGadgetVersion /@\@<=[^ ]*\ze\.\.\./


syn keyword VimspectorInstalling Installing
syn keyword VimspectorDone Done
syn keyword VimspectorSkip Skip
syn keyword VimspectorError Failed FAILED

hi default link VimspectorInstalling Constant
hi default link VimspectorDone DiffAdd
hi default link VimspectorSkip DiffAdd
hi default link VimspectorError WarningMsg
hi default link VimspectorGadget String
hi default link VimspectorGadgetVersion Identifier

0 comments on commit f9d20b9

Please sign in to comment.