diff --git a/install_gadget.py b/install_gadget.py index 4b42984..52621c3 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -69,6 +69,10 @@ parser.add_argument( '--force-all', 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' ) @@ -175,6 +179,7 @@ failed = [] 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 ): @@ -187,15 +192,27 @@ for name, gadget in gadgets.GADGETS.items(): 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( "" ) @@ -204,6 +221,8 @@ if args.no_gadget_config: else: installer.WriteAdapters( all_adapters ) +manifest.Write() + if args.basedir: print( "" ) print( "***NOTE***: You set --basedir to " + args.basedir + diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index a96c9f5..7e1d619 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -234,8 +234,7 @@ GADGETS = { }, 'macos': { 'file_name': 'netcoredbg-osx-master.tar.gz', - 'checksum': - 'c1dc6ed58c3f5b0473cfb4985a96552999360ceb9795e42d9c9be64af054f821', + 'checksum': '', }, 'linux': { 'file_name': 'netcoredbg-linux-master.tar.gz', @@ -385,7 +384,7 @@ GADGETS = { '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, diff --git a/python3/vimspector/install.py b/python3/vimspector/install.py index 7dc0897..d6ceb78 100644 --- a/python3/vimspector/install.py +++ b/python3/vimspector/install.py @@ -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' ) diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index 3e4440d..485aed5 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -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 ) @@ -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', [] ): @@ -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' ) @@ -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 ), @@ -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: @@ -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() diff --git a/syntax/vimspector-installer.vim b/syntax/vimspector-installer.vim index 98ea48b..4eb6170 100644 --- a/syntax/vimspector-installer.vim +++ b/syntax/vimspector-installer.vim @@ -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