2026-05-11 00:38:09 +00:00
|
|
|
" scripts/download_bin.vim — install `nuwiki-ls` from a release tarball,
|
2026-06-24 12:57:15 +00:00
|
|
|
" falling back to `cargo build --release` from the nuwiki-rs repo.
|
2026-05-11 00:38:09 +00:00
|
|
|
"
|
|
|
|
|
" Invoked by Dein / vim-plug build hooks:
|
|
|
|
|
" vim -e -s -c "source scripts/download_bin.vim" -c "q"
|
2026-06-05 02:05:56 +00:00
|
|
|
"
|
|
|
|
|
" That invocation runs in 'compatible' mode, where leading-backslash line
|
|
|
|
|
" continuations aren't recognised (they'd raise E10, and any error in silent
|
|
|
|
|
" Ex mode makes Vim exit non-zero — a spurious build failure). Reset
|
|
|
|
|
" 'cpoptions' to the Vim default for the duration so the script parses, and
|
|
|
|
|
" restore it at the end.
|
|
|
|
|
let s:cpo_save = &cpo
|
|
|
|
|
set cpo&vim
|
2026-05-11 00:38:09 +00:00
|
|
|
|
|
|
|
|
let s:plugin_dir = fnamemodify(resolve(expand('<sfile>:p')), ':h:h')
|
|
|
|
|
let s:bin_dir = s:plugin_dir . '/bin'
|
|
|
|
|
let s:suffix = has('win32') ? '.exe' : ''
|
|
|
|
|
let s:bin = s:bin_dir . '/nuwiki-ls' . s:suffix
|
2026-06-24 12:57:15 +00:00
|
|
|
let s:rs_repo = fnamemodify(s:plugin_dir, ':h:h') . '/nuwiki-rs'
|
2026-05-11 00:38:09 +00:00
|
|
|
|
|
|
|
|
call mkdir(s:bin_dir, 'p')
|
|
|
|
|
|
2026-08-07 14:58:48 -03:00
|
|
|
function! s:is_musl(arch) abort
|
|
|
|
|
if filereadable('/lib/ld-musl-' . a:arch . '.so.1')
|
|
|
|
|
return 1
|
|
|
|
|
endif
|
|
|
|
|
if !executable('ldd')
|
|
|
|
|
return 0
|
|
|
|
|
endif
|
|
|
|
|
" musl's ldd writes its banner to stderr.
|
|
|
|
|
return stridx(system('ldd --version 2>&1'), 'musl') >= 0
|
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
|
|
" NixOS and Guix System are not FHS distributions: glibc lives in the store,
|
|
|
|
|
" so the /lib64/ld-linux-*.so.2 a generic -gnu binary is linked against is
|
|
|
|
|
" either absent or — on NixOS — a stub that only prints an error and exits
|
|
|
|
|
" 127. `ldd --version` still reports GNU libc there, so libc detection alone
|
|
|
|
|
" picks the wrong asset. The musl builds are statically linked and need no
|
|
|
|
|
" loader at all, so they run on these systems unchanged.
|
|
|
|
|
function! s:is_non_fhs_linux() abort
|
|
|
|
|
if filereadable('/etc/NIXOS')
|
|
|
|
|
return 1
|
|
|
|
|
endif
|
|
|
|
|
if !filereadable('/etc/os-release')
|
|
|
|
|
return 0
|
|
|
|
|
endif
|
|
|
|
|
for l:line in readfile('/etc/os-release')
|
|
|
|
|
let l:id = matchstr(l:line, '^ID=\s*"\?\zs[[:alnum:]._-]\+')
|
|
|
|
|
if l:id ==# 'nixos' || l:id ==# 'guix'
|
|
|
|
|
return 1
|
|
|
|
|
endif
|
|
|
|
|
endfor
|
|
|
|
|
return 0
|
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
|
|
" Release triples to try, best first. More than one entry means the later ones
|
|
|
|
|
" are fallbacks for a host that turns out not to be able to run the first.
|
|
|
|
|
function! s:targets() abort
|
|
|
|
|
if get(g:, 'nuwiki_ls_target', '') !=# ''
|
|
|
|
|
return [g:nuwiki_ls_target]
|
|
|
|
|
endif
|
2026-05-11 00:38:09 +00:00
|
|
|
if has('win32')
|
2026-08-07 14:58:48 -03:00
|
|
|
return ['x86_64-pc-windows-msvc']
|
2026-05-11 00:38:09 +00:00
|
|
|
elseif has('mac')
|
2026-08-07 14:58:48 -03:00
|
|
|
return [has('arm64') ? 'aarch64-apple-darwin' : 'x86_64-apple-darwin']
|
2026-05-11 00:38:09 +00:00
|
|
|
elseif has('linux')
|
|
|
|
|
let l:uname_m = trim(system('uname -m'))
|
2026-08-07 14:58:48 -03:00
|
|
|
let l:arch = (l:uname_m ==# 'aarch64' || l:uname_m ==# 'arm64')
|
|
|
|
|
\ ? 'aarch64' : 'x86_64'
|
|
|
|
|
let l:musl = l:arch . '-unknown-linux-musl'
|
|
|
|
|
if s:is_musl(l:arch) || s:is_non_fhs_linux()
|
|
|
|
|
return [l:musl]
|
2026-05-11 00:38:09 +00:00
|
|
|
endif
|
2026-08-07 14:58:48 -03:00
|
|
|
" Static musl runs anywhere, so keep it as a last resort on glibc hosts
|
|
|
|
|
" too (old glibc, exotic loader layouts).
|
|
|
|
|
return [l:arch . '-unknown-linux-gnu', l:musl]
|
2026-05-11 00:38:09 +00:00
|
|
|
endif
|
2026-08-07 14:58:48 -03:00
|
|
|
return []
|
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
|
|
" Can this host actually start the binary? Exit codes 126 and 127 mean the
|
|
|
|
|
" kernel or the dynamic loader refused to exec it (missing/stub loader, wrong
|
|
|
|
|
" architecture); any other code means it ran and exited on its own terms.
|
|
|
|
|
" system() gives the child an empty stdin, so the LSP loop sees EOF and stops.
|
|
|
|
|
function! s:can_exec(path) abort
|
|
|
|
|
call system(shellescape(a:path) . ' --version')
|
|
|
|
|
return v:shell_error != 126 && v:shell_error != 127
|
2026-05-11 00:38:09 +00:00
|
|
|
endfunction
|
|
|
|
|
|
|
|
|
|
function! s:build_from_source() abort
|
|
|
|
|
if !executable('cargo')
|
|
|
|
|
echohl ErrorMsg | echom 'nuwiki: cargo not found, cannot build from source' | echohl None
|
|
|
|
|
return 0
|
|
|
|
|
endif
|
2026-06-24 12:57:15 +00:00
|
|
|
echom 'nuwiki: cloning nuwiki-rs …'
|
|
|
|
|
if !filereadable(s:rs_repo . '/Cargo.toml')
|
|
|
|
|
call system('git clone --depth 1 https://code.gfran.co/gffranco/nuwiki-rs.git ' . shellescape(s:rs_repo))
|
|
|
|
|
if v:shell_error != 0
|
|
|
|
|
echohl ErrorMsg | echom 'nuwiki: failed to clone nuwiki-rs' | echohl None
|
|
|
|
|
return 0
|
|
|
|
|
endif
|
|
|
|
|
endif
|
2026-05-11 00:38:09 +00:00
|
|
|
echom 'nuwiki: cargo build --release …'
|
2026-06-24 12:57:15 +00:00
|
|
|
call system('cargo build --release -p nuwiki-ls --manifest-path ' . shellescape(s:rs_repo . '/Cargo.toml'))
|
2026-05-11 00:38:09 +00:00
|
|
|
if v:shell_error != 0
|
|
|
|
|
echohl ErrorMsg | echom 'nuwiki: cargo build failed' | echohl None
|
|
|
|
|
return 0
|
|
|
|
|
endif
|
2026-06-24 12:57:15 +00:00
|
|
|
let l:built = s:rs_repo . '/target/release/nuwiki-ls' . s:suffix
|
2026-05-11 00:38:09 +00:00
|
|
|
call system('cp ' . shellescape(l:built) . ' ' . shellescape(s:bin))
|
|
|
|
|
call system('chmod +x ' . shellescape(s:bin))
|
|
|
|
|
return executable(s:bin)
|
|
|
|
|
endfunction
|
|
|
|
|
|
2026-08-07 14:58:48 -03:00
|
|
|
function! s:download_release(target) abort
|
2026-05-11 00:38:09 +00:00
|
|
|
if !executable('curl') || !executable('tar')
|
|
|
|
|
return 0
|
|
|
|
|
endif
|
2026-06-24 12:57:15 +00:00
|
|
|
let l:url = 'https://code.gfran.co/gffranco/nuwiki-rs/releases/download/latest/nuwiki-ls-'
|
2026-08-07 14:58:48 -03:00
|
|
|
\ . a:target . '.tar.gz'
|
2026-05-11 00:38:09 +00:00
|
|
|
let l:archive = tempname() . '.tar.gz'
|
|
|
|
|
call system('curl -fsSL -o ' . shellescape(l:archive) . ' ' . shellescape(l:url))
|
|
|
|
|
if v:shell_error != 0
|
|
|
|
|
return 0
|
|
|
|
|
endif
|
|
|
|
|
let l:extract = tempname()
|
|
|
|
|
call mkdir(l:extract, 'p')
|
|
|
|
|
call system('tar -xzf ' . shellescape(l:archive) . ' -C ' . shellescape(l:extract))
|
|
|
|
|
if v:shell_error != 0
|
|
|
|
|
return 0
|
|
|
|
|
endif
|
2026-08-07 14:58:48 -03:00
|
|
|
" Vet the staged copy before it becomes the installed one, so a binary this
|
|
|
|
|
" host cannot exec never lands at s:bin for the LSP client to pick up.
|
|
|
|
|
let l:staged = l:extract . '/nuwiki-ls' . s:suffix
|
|
|
|
|
call system('chmod +x ' . shellescape(l:staged))
|
|
|
|
|
if !executable(l:staged) || !s:can_exec(l:staged)
|
|
|
|
|
return 0
|
|
|
|
|
endif
|
|
|
|
|
call system('cp ' . shellescape(l:staged) . ' ' . shellescape(s:bin))
|
2026-05-11 00:38:09 +00:00
|
|
|
call system('chmod +x ' . shellescape(s:bin))
|
|
|
|
|
return executable(s:bin)
|
|
|
|
|
endfunction
|
|
|
|
|
|
2026-08-07 14:58:48 -03:00
|
|
|
let s:installed = 0
|
|
|
|
|
for s:t in s:targets()
|
|
|
|
|
if s:download_release(s:t)
|
|
|
|
|
echom 'nuwiki: installed ' . s:bin . ' (' . s:t . ')'
|
|
|
|
|
let s:installed = 1
|
|
|
|
|
break
|
|
|
|
|
endif
|
|
|
|
|
endfor
|
|
|
|
|
|
|
|
|
|
if !s:installed
|
2026-05-11 00:38:09 +00:00
|
|
|
echohl WarningMsg | echom 'nuwiki: download failed, falling back to source build' | echohl None
|
|
|
|
|
if !s:build_from_source()
|
|
|
|
|
echohl ErrorMsg | echom 'nuwiki: install failed' | echohl None
|
|
|
|
|
endif
|
|
|
|
|
endif
|
2026-06-05 02:05:56 +00:00
|
|
|
|
|
|
|
|
let &cpo = s:cpo_save
|