From 83ca8b0336134a3d0c2ff08c91f46577891c0347 Mon Sep 17 00:00:00 2001 From: Alexandre Carlton Date: Fri, 28 Nov 2014 23:22:28 +1100 Subject: [PATCH 1/7] Add check for whether executables are present If a plugin has a 'needs' parameter (a list of executables) then vim-plug will only execute the 'do' command if all the executables are installed on the system --- plug.vim | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/plug.vim b/plug.vim index 3732574..1136a50 100644 --- a/plug.vim +++ b/plug.vim @@ -578,6 +578,14 @@ function! s:assign_name() silent! execute 'f' fnameescape(name) endfunction +function! s:all_executable(executables) + if len(a:executables) == 0 + return 1 + endif + let are_executable = map(a:executables, 'executable(v:val)') + return index(are_executable, 0) == -1 +endfunction + function! s:do(pull, force, todo) for [name, spec] in items(a:todo) if !isdirectory(spec.dir) @@ -586,7 +594,15 @@ function! s:do(pull, force, todo) let installed = has_key(s:update.new, name) let updated = installed ? 0 : \ (a:pull && !empty(s:system_chomp('git log --pretty=format:"%h" "HEAD...HEAD@{1}"', spec.dir))) - if a:force || installed || updated + " TODO(alexandre): use the function executable on all the parameters in + " the 'needs' attribute - if not then don't update. + if has_key(spec, 'needs') + echom 'Has needs.' + let canbuild = s:all_executable(s:to_a(spec.needs)) + else + let canbuild = 1 + endif + if (a:force || installed || updated) && canbuild execute 'cd' s:esc(spec.dir) call append(3, '- Post-update hook for '. name .' ... ') let type = type(spec.do) @@ -656,6 +672,8 @@ function! s:names(...) return sort(filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)')) endfunction +" TODO (alexandre) - Maybe if you don't have the build commands, +" you don't pull it at all, (citing the reason). function! s:update_impl(pull, force, args) abort let args = copy(a:args) let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ? From 58eadd8f76b5370bb3dd74da414f72c84d60d710 Mon Sep 17 00:00:00 2001 From: Alexandre Carlton Date: Sun, 30 Nov 2014 15:26:58 +1100 Subject: [PATCH 2/7] Add check for executable in Ruby threads Now whenever an update is called a check is made (not just on installation) --- plug.vim | 127 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 69 insertions(+), 58 deletions(-) diff --git a/plug.vim b/plug.vim index 1136a50..4a6bad4 100644 --- a/plug.vim +++ b/plug.vim @@ -75,7 +75,7 @@ let s:mac_gui = has('gui_macvim') && has('gui_running') let s:is_win = has('win32') || has('win64') let s:nvim = exists('##JobActivity') && !s:is_win let s:me = resolve(expand(':p')) -let s:base_spec = { 'branch': 'master', 'frozen': 0 } +let s:base_spec = { 'branch': 'master', 'frozen': 0, 'needs': []} let s:TYPE = { \ 'string': type(''), \ 'list': type([]), @@ -578,13 +578,6 @@ function! s:assign_name() silent! execute 'f' fnameescape(name) endfunction -function! s:all_executable(executables) - if len(a:executables) == 0 - return 1 - endif - let are_executable = map(a:executables, 'executable(v:val)') - return index(are_executable, 0) == -1 -endfunction function! s:do(pull, force, todo) for [name, spec] in items(a:todo) @@ -594,15 +587,7 @@ function! s:do(pull, force, todo) let installed = has_key(s:update.new, name) let updated = installed ? 0 : \ (a:pull && !empty(s:system_chomp('git log --pretty=format:"%h" "HEAD...HEAD@{1}"', spec.dir))) - " TODO(alexandre): use the function executable on all the parameters in - " the 'needs' attribute - if not then don't update. - if has_key(spec, 'needs') - echom 'Has needs.' - let canbuild = s:all_executable(s:to_a(spec.needs)) - else - let canbuild = 1 - endif - if (a:force || installed || updated) && canbuild + if a:force || installed || updated execute 'cd' s:esc(spec.dir) call append(3, '- Post-update hook for '. name .' ... ') let type = type(spec.do) @@ -672,8 +657,6 @@ function! s:names(...) return sort(filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)')) endfunction -" TODO (alexandre) - Maybe if you don't have the build commands, -" you don't pull it at all, (citing the reason). function! s:update_impl(pull, force, args) abort let args = copy(a:args) let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ? @@ -894,6 +877,10 @@ function! s:update_vim() call s:tick() endfunction +function! s:missing_executables(executables) + return filter(copy(s:to_a(a:executables)), '!executable(v:val)') +endfunction + function! s:tick() while 1 " Without TCO, Vim stack is bound to explode if empty(s:update.todo) @@ -910,27 +897,34 @@ while 1 " Without TCO, Vim stack is bound to explode let new = !isdirectory(spec.dir) call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...') + echom 'blah' redraw - if !new - let [valid, msg] = s:git_valid(spec, 0) - if valid - if pull - call s:spawn(name, - \ printf('git checkout -q %s 2>&1 && git pull --progress --no-rebase origin %s 2>&1 && git submodule update --init --recursive 2>&1', - \ s:shellesc(spec.branch), s:shellesc(spec.branch)), { 'dir': spec.dir }) + let executables = s:missing_executables(spec.needs) + if !empty(executables) + let msg = 'Please install '. join(executables, ', ') . '.' + let s:jobs[name] = { 'running': 0, 'result': msg, 'error': 1 } + else + if !new + let [valid, msg] = s:git_valid(spec, 0) + if valid + if pull + call s:spawn(name, + \ printf('git checkout -q %s 2>&1 && git pull --progress --no-rebase origin %s 2>&1 && git submodule update --init --recursive 2>&1', + \ s:shellesc(spec.branch), s:shellesc(spec.branch)), { 'dir': spec.dir }) + else + let s:jobs[name] = { 'running': 0, 'result': 'Already installed', 'error': 0 } + endif else - let s:jobs[name] = { 'running': 0, 'result': 'Already installed', 'error': 0 } + let s:jobs[name] = { 'running': 0, 'result': msg, 'error': 1 } endif else - let s:jobs[name] = { 'running': 0, 'result': msg, 'error': 1 } + call s:spawn(name, + \ printf('git clone --progress --recursive %s -b %s %s 2>&1', + \ s:shellesc(spec.uri), + \ s:shellesc(spec.branch), + \ s:shellesc(s:trim(spec.dir))), { 'new': 1 }) endif - else - call s:spawn(name, - \ printf('git clone --progress --recursive %s -b %s %s 2>&1', - \ s:shellesc(spec.uri), - \ s:shellesc(spec.branch), - \ s:shellesc(s:trim(spec.dir))), { 'new': 1 }) endif if !s:jobs[name].running @@ -979,6 +973,17 @@ function! s:update_ruby() pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil } end + def which cmd + exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : [''] + ENV['PATH'].split(File::PATH_SEPARATOR).each do |path| + exts.each { |ext| + exe = File.join(path, "#{cmd}#{ext}") + return exe if File.executable?(exe) && !File.directory?(exe) + } + end + return nil + end + require 'thread' require 'fileutils' require 'timeout' @@ -1106,39 +1111,45 @@ function! s:update_ruby() threads << Thread.new { while pair = take1.call name = pair.first - dir, uri, branch = pair.last.values_at *%w[dir uri branch] + dir, uri, branch, needs = pair.last.values_at *%w[dir uri branch needs] branch = esc branch subm = "git submodule update --init --recursive 2>&1" exists = File.directory? dir + needs = needs.kind_of?(Array) ? needs : [needs] + executables = needs.select{ |exe| !which exe } ok, result = - if exists - dir = esc dir - ret, data = bt.call "#{cd} #{dir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url", nil, nil, nil - current_uri = data.lines.to_a.last - if !ret - if data =~ /^Interrupted|^Timeout/ - [false, data] + if executables.empty? + if exists + dir = esc dir + ret, data = bt.call "#{cd} #{dir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url", nil, nil, nil + current_uri = data.lines.to_a.last + if !ret + if data =~ /^Interrupted|^Timeout/ + [false, data] + else + [false, [data.chomp, "PlugClean required."].join($/)] + end + elsif current_uri.sub(/git::?@/, '') != uri.sub(/git::?@/, '') + [false, ["Invalid URI: #{current_uri}", + "Expected: #{uri}", + "PlugClean required."].join($/)] else - [false, [data.chomp, "PlugClean required."].join($/)] + if pull + log.call name, 'Updating ...', :update + bt.call "#{cd} #{dir} && git checkout -q #{branch} 2>&1 && (git pull --no-rebase origin #{branch} #{progress} 2>&1 && #{subm})", name, :update, nil + else + [true, skip] + end end - elsif current_uri.sub(/git::?@/, '') != uri.sub(/git::?@/, '') - [false, ["Invalid URI: #{current_uri}", - "Expected: #{uri}", - "PlugClean required."].join($/)] else - if pull - log.call name, 'Updating ...', :update - bt.call "#{cd} #{dir} && git checkout -q #{branch} 2>&1 && (git pull --no-rebase origin #{branch} #{progress} 2>&1 && #{subm})", name, :update, nil - else - [true, skip] - end + d = esc dir.sub(%r{[\\/]+$}, '') + log.call name, 'Installing ...', :install + bt.call "git clone #{progress} --recursive #{uri} -b #{branch} #{d} 2>&1", name, :install, proc { + FileUtils.rm_rf dir + } end else - d = esc dir.sub(%r{[\\/]+$}, '') - log.call name, 'Installing ...', :install - bt.call "git clone #{progress} --recursive #{uri} -b #{branch} #{d} 2>&1", name, :install, proc { - FileUtils.rm_rf dir - } + [false, "Please install " + executables.join(", ") + " first."] end mtx.synchronize { VIM::command("let s:update.new['#{name}'] = 1") } if !exists && ok log.call name, result, ok From 74e0b2c2bb14078ecfa6e7839185ebaf1ae41665 Mon Sep 17 00:00:00 2001 From: Alexandre Carlton Date: Sun, 30 Nov 2014 15:34:08 +1100 Subject: [PATCH 3/7] Add 'needs' option in README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c5827ba..ff1462c 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ Reload .vimrc and `:PlugInstall` to install plugins. | `dir` | Custom directory for the plugin | | `do` | Post-update hook (string or funcref) | | `on` | On-demand loading: Commands or ``-mappings | +| `needs` | Check for executables before installing/updating | | `for` | On-demand loading: File types | | `frozen` | Do not update unless explicitly specified | From 73551bf34f3b03413f8717aa482ecb5635222952 Mon Sep 17 00:00:00 2001 From: Alexandre Carlton Date: Sun, 30 Nov 2014 21:01:49 +1100 Subject: [PATCH 4/7] Add tests --- test/workflow.vader | 63 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/test/workflow.vader b/test/workflow.vader index e5ced79..ac9fa93 100644 --- a/test/workflow.vader +++ b/test/workflow.vader @@ -613,6 +613,69 @@ Execute (Retry failed tasks): AssertExpect! '[xxx]', 1 q +********************************************************************** +~ Build requirements check (`needs` option) +********************************************************************** + +Execute (Cleanup): + call plug#begin() + call plug#end() + PlugClean! + q + +Execute (Single uninstalled executable): + call plug#begin() + Plug 'junegunn/vim-easy-align', { 'needs': 'does-not-exist' } + call plug#end() + PlugInstall! + q + Assert !isdirectory(g:plugs['vim-easy-align'].dir), + \ 'vim-easy-align should not exist' + +Execute (Multiple uninstalled executables): + call plug#begin() + Plug 'junegunn/vim-easy-align', { 'needs': ['does-not-exist', 'also-non-existent'] } + call plug#end() + PlugInstall! + q + Assert !isdirectory(g:plugs['vim-easy-align'].dir), + \ 'vim-easy-align should not exist' + +Execute (Multiple uninstalled/installed executables): + call plug#begin() + Plug 'junegunn/vim-easy-align', { 'needs': ['does-not-exist', 'sh'] } + call plug#end() + PlugInstall! + q + Assert !isdirectory(g:plugs['vim-easy-align'].dir), + \ 'vim-easy-align should not exist' + + +Execute (Single installed executable): + call plug#begin() + Plug 'junegunn/vim-easy-align', { 'needs': 'sh' } + call plug#end() + PlugInstall! + q + Assert isdirectory(g:plugs['vim-easy-align'].dir), + \ 'vim-easy-align should exist' + +Execute (Cleanup): + call plug#begin() + call plug#end() + PlugClean! + q + +Execute (Multiple installed executables): + call plug#begin() + Plug 'junegunn/vim-easy-align', { 'needs': ['sh', 'bash'] } + call plug#end() + PlugInstall! + q + Assert isdirectory(g:plugs['vim-easy-align'].dir), + \ 'vim-easy-align should exist' + + ********************************************************************** ~ Post-update hook (`do` option) ********************************************************************** From 4e118cf10cd1e6152e068d69d5eaac1a4022106c Mon Sep 17 00:00:00 2001 From: Alexandre Carlton Date: Sun, 30 Nov 2014 21:15:46 +1100 Subject: [PATCH 5/7] Refine test cases by checking message output --- plug.vim | 2 +- test/workflow.vader | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/plug.vim b/plug.vim index 4a6bad4..4905fb7 100644 --- a/plug.vim +++ b/plug.vim @@ -587,7 +587,7 @@ function! s:do(pull, force, todo) let installed = has_key(s:update.new, name) let updated = installed ? 0 : \ (a:pull && !empty(s:system_chomp('git log --pretty=format:"%h" "HEAD...HEAD@{1}"', spec.dir))) - if a:force || installed || updated + if a:force || installed || updated execute 'cd' s:esc(spec.dir) call append(3, '- Post-update hook for '. name .' ... ') let type = type(spec.do) diff --git a/test/workflow.vader b/test/workflow.vader index ac9fa93..b200ef7 100644 --- a/test/workflow.vader +++ b/test/workflow.vader @@ -627,7 +627,8 @@ Execute (Single uninstalled executable): call plug#begin() Plug 'junegunn/vim-easy-align', { 'needs': 'does-not-exist' } call plug#end() - PlugInstall! + PlugInstall + AssertExpect 'Please install does-not-exist first.', 0 q Assert !isdirectory(g:plugs['vim-easy-align'].dir), \ 'vim-easy-align should not exist' @@ -636,7 +637,8 @@ Execute (Multiple uninstalled executables): call plug#begin() Plug 'junegunn/vim-easy-align', { 'needs': ['does-not-exist', 'also-non-existent'] } call plug#end() - PlugInstall! + PlugInstall + AssertExpect 'Please install does-not-exist, also-non-existent first.', 0 q Assert !isdirectory(g:plugs['vim-easy-align'].dir), \ 'vim-easy-align should not exist' @@ -645,7 +647,8 @@ Execute (Multiple uninstalled/installed executables): call plug#begin() Plug 'junegunn/vim-easy-align', { 'needs': ['does-not-exist', 'sh'] } call plug#end() - PlugInstall! + PlugInstall + AssertExpect 'Please install does-not-exist first.', 0 q Assert !isdirectory(g:plugs['vim-easy-align'].dir), \ 'vim-easy-align should not exist' From ff51e68edd595bf1f3092a603b71f8a099ef1b39 Mon Sep 17 00:00:00 2001 From: Alexandre Carlton Date: Sun, 30 Nov 2014 21:20:43 +1100 Subject: [PATCH 6/7] Minor cleanup --- plug.vim | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plug.vim b/plug.vim index 4905fb7..b30cfc9 100644 --- a/plug.vim +++ b/plug.vim @@ -75,7 +75,7 @@ let s:mac_gui = has('gui_macvim') && has('gui_running') let s:is_win = has('win32') || has('win64') let s:nvim = exists('##JobActivity') && !s:is_win let s:me = resolve(expand(':p')) -let s:base_spec = { 'branch': 'master', 'frozen': 0, 'needs': []} +let s:base_spec = { 'branch': 'master', 'frozen': 0, 'needs': [] } let s:TYPE = { \ 'string': type(''), \ 'list': type([]), @@ -897,7 +897,6 @@ while 1 " Without TCO, Vim stack is bound to explode let new = !isdirectory(spec.dir) call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...') - echom 'blah' redraw let executables = s:missing_executables(spec.needs) From 9a9139e802821991c54b36b45db2e96a63d68261 Mon Sep 17 00:00:00 2001 From: Alexandre Carlton Date: Sun, 30 Nov 2014 22:19:50 +1100 Subject: [PATCH 7/7] Another cleanup. --- plug.vim | 1 - 1 file changed, 1 deletion(-) diff --git a/plug.vim b/plug.vim index b30cfc9..0d5974d 100644 --- a/plug.vim +++ b/plug.vim @@ -578,7 +578,6 @@ function! s:assign_name() silent! execute 'f' fnameescape(name) endfunction - function! s:do(pull, force, todo) for [name, spec] in items(a:todo) if !isdirectory(spec.dir)