1" usage example: 2" 3" call nix#ExportPluginsForNix({'path_to_nixpkgs': '/etc/nixos/nixpkgs', 'names': ["vim-addon-manager", "vim-addon-nix"], 'cache_file': 'cache'}) 4let s:plugin_root = expand('<sfile>:h:h') 5 6fun! nix#ToNixAttrName(s) abort 7 return nix#ToNixName(a:s) 8endf 9 10fun! nix#ToNixName(s) abort 11 return substitute(substitute(a:s, '[:/.]', '-', 'g'), 'github-', '', 'g') 12endf 13 14fun! s:System(...) 15 let args = a:000 16 let r = call('vam#utils#System', args) 17 if r is 0 18 throw "command ".join(args, '').' failed' 19 else 20 return r 21 endif 22endf 23 24fun! nix#DependenciesFromCheckout(opts, name, repository, dir) 25 " check for dependencies 26 " vam#PluginDirFromName(a:name) 27 let info = vam#ReadAddonInfo(vam#AddonInfoFile(a:dir, a:name)) 28 return keys(get(info, 'dependencies', {})) 29endf 30 31 32" without deps 33fun! nix#NixDerivation(opts, name, repository) abort 34 let n_a_name = nix#ToNixAttrName(a:name) 35 let n_n_name = nix#ToNixName(a:name) 36 let type = get(a:repository, 'type', '') 37 let created_notice = " # created by nix#NixDerivation" 38 39 let ancf = s:plugin_root.'/additional-nix-code/'.a:name 40 let additional_nix_code = file_readable(ancf) ? join(readfile(ancf), "\n") : "" 41 42 if type == 'git' 43 " should be using shell abstraction .. 44 echo 'fetching '. a:repository.url 45 let s = s:System('$ --fetch-submodules $ 2>&1',a:opts.nix_prefetch_git, a:repository.url) 46 let rev = matchstr(s, 'git revision is \zs[^\n\r]\+\ze') 47 let sha256 = matchstr(s, 'hash is \zs[^\n\r]\+\ze') 48 let dir = matchstr(s, 'path is \zs[^\n\r]\+\ze') 49 let date = matchstr(s, 'Commit date is \zs[0-9-]\+\ze') 50 51 let dependencies = nix#DependenciesFromCheckout(a:opts, a:name, a:repository, dir) 52 return {'n_a_name': n_a_name, 'n_n_name': n_n_name, 'dependencies': dependencies, 'derivation': join([ 53 \ ' '.n_a_name.' = buildVimPluginFrom2Nix {'.created_notice, 54 \ ' name = "'.n_n_name.'-'.date.'";', 55 \ ' src = fetchgit {', 56 \ ' url = "'. a:repository.url .'";', 57 \ ' rev = "'.rev.'";', 58 \ ' sha256 = "'.sha256.'";', 59 \ ' };', 60 \ ' dependencies = ['.join(map(copy(dependencies), "'\"'.nix#ToNixAttrName(v:val).'\"'")).'];', 61 \ additional_nix_code, 62 \ ' };', 63 \ '', 64 \ '', 65 \ ], "\n")} 66 67 elseif type == 'hg' 68 " should be using shell abstraction .. 69 echo 'fetching '. a:repository.url 70 let s = s:System('$ $ 2>&1',a:opts.nix_prefetch_hg, a:repository.url) 71 let rev = matchstr(s, 'hg revision is \zs[^\n\r]\+\ze') 72 let sha256 = matchstr(s, 'hash is \zs[^\n\r]\+\ze') 73 let dir = matchstr(s, 'path is \zs[^\n\r]\+\ze') 74 75 let dependencies = nix#DependenciesFromCheckout(a:opts, a:name, a:repository, dir) 76 return {'n_a_name': n_a_name, 'n_n_name': n_n_name, 'dependencies': dependencies, 'derivation': join([ 77 \ ' '.n_a_name.' = buildVimPluginFrom2Nix {'.created_notice, 78 \ ' name = "'.n_n_name.'";', 79 \ ' src = fetchhg {', 80 \ ' url = "'. a:repository.url .'";', 81 \ ' rev = "'.rev.'";', 82 \ ' sha256 = "'.sha256.'";', 83 \ ' };', 84 \ ' dependencies = ['.join(map(copy(dependencies), "'\"'.nix#ToNixAttrName(v:val).'\"'")).'];', 85 \ additional_nix_code, 86 \ ' };', 87 \ '', 88 \ '', 89 \ ], "\n")} 90 91 elseif type == 'archive' 92 let sha256 = split(s:System('nix-prefetch-url $ 2>/dev/null', a:repository.url), "\n")[0] 93 " we should unpack the sources, look for the addon-info.json file .. 94 " however most packages who have the addon-info.json file also are on 95 " github thus will be of type "git" instead. The dependency information 96 " from vim-pi is encoded in the reposiotry. Thus this is likely to do the 97 " right thing most of the time. 98 let addon_info = get(a:repository, 'addon-info', {}) 99 let dependencies = keys(get(addon_info, 'dependencies', {})) 100 101 return {'n_a_name': n_a_name, 'n_n_name': n_n_name, 'dependencies': dependencies, 'derivation': join([ 102 \ ' '.n_a_name.' = buildVimPluginFrom2Nix {'.created_notice, 103 \ ' name = "'.n_n_name.'";', 104 \ ' src = fetchurl {', 105 \ ' url = "'. a:repository.url .'";', 106 \ ' name = "'. a:repository.archive_name .'";', 107 \ ' sha256 = "'.sha256.'";', 108 \ ' };', 109 \ ' buildInputs = [ unzip ];', 110 \ ' dependencies = ['.join(map(copy(dependencies), "'\"'.nix#ToNixAttrName(v:val).'\"'")).'];', 111 \ ' meta = {', 112 \ ' homepage = "http://www.vim.org/scripts/script.php?script_id='.a:repository.vim_script_nr.'";', 113 \ ' };', 114 \ addon_info == {} ? '' : (' addon_info = '.nix#ToNix(string(addon_info), [], "").';'), 115 \ additional_nix_code, 116 \ ' };', 117 \ '', 118 \ '', 119 \ ], "\n")} 120 else 121 throw a:name.' TODO: implement source '.string(a:repository) 122 endif 123endf 124 125" also tries to handle dependencies 126fun! nix#AddNixDerivation(opts, cache, name, ...) abort 127 if has_key(a:cache, a:name) | return | endif 128 let repository = a:0 > 0 ? a:1 : {} 129 let name = a:name 130 131 if repository == {} 132 call vam#install#LoadPool() 133 let list = matchlist(a:name, 'github:\([^/]*\)\%(\/\(.*\)\)\?$') 134 if len(list) > 0 135 if '' != list[2] 136 let name = list[2] 137 let repository = { 'type': 'git', 'owner': list[1], 'repo': list[2], 'url': 'https://github.com/'.list[1].'/'.list[2] } 138 else 139 let name = list[1] 140 let repository = { 'type': 'git', 'owner': list[1], 'repo': 'vim-addon-'.list[1], 'url': 'https://github.com/'.list[1].'/vim-addon-'.list[1] } 141 endif 142 else 143 let repository = get(g:vim_addon_manager.plugin_sources, a:name, {}) 144 if repository == {} 145 throw "repository ".a:name." unkown!" 146 else 147 if repository.url =~ 'github' 148 let owner = matchstr(repository.url, 'github.com/\zs.\+\ze/') 149 let repo = matchstr(repository.url, '\/\zs[^\/]\+\ze$') 150 let url = repository.url 151 let repository = { 'type': 'git', 'owner': owner, 'repo': repo, 'url': url } 152 endif 153 endif 154 endif 155 endif 156 157 let a:cache[a:name] = nix#NixDerivation(a:opts, name, repository) 158 159 " take known dependencies into account: 160 let deps = get(a:cache[a:name], 'dependencies', []) 161 call extend(a:opts.names_to_process, deps) 162 call extend(a:opts.names_to_export, deps) 163endfun 164 165fun! nix#TopNixOptsByParent(parents) 166 if (a:parents == []) 167 return {'ind': ' ', 'next_ind': ' ', 'sep': "\n"} 168 else 169 return {'ind': '', 'next_ind': '', 'sep': ' '} 170 endif 171endf 172 173fun! nix#ToNix(x, parents, opts_fun) abort 174 let opts = a:opts_fun == "" ? "" : call(a:opts_fun, [a:parents]) 175 let next_parents = [a:x] + a:parents 176 let seps = a:0 > 1 ? a:2 : [] 177 178 let ind = get(opts, 'ind', '') 179 let next_ind = get(opts, 'next_ind', ind.' ') 180 let sep = get(opts, 'sep', ind.' ') 181 182 if type(a:x) == type("") 183 return "''". substitute(a:x, '[$]', '$$', 'g')."''" 184 elseif type(a:x) == type({}) 185 let s = ind."{".sep 186 for [k,v] in items(a:x) 187 let s .= '"'.k.'" = '.nix#ToNix(v, next_parents, a:opts_fun).";".sep 188 unlet k v 189 endfor 190 return s.ind."}" 191 192 " let s = ind."{\n" 193 " for [k,v] in items(a:x) 194 " let s .= next_ind . nix#ToNix(k).' = '.nix#ToNix(v, next_ind)."\n" 195 " unlet k v 196 " endfor 197 " return s.ind."}\n" 198 elseif type(a:x) == type([]) 199 let s = ind."[".sep 200 for v in a:x 201 let s .= next_ind . nix#ToNix(v, next_parents, a:opts_fun)."".sep 202 unlet v 203 endfor 204 return s.ind."]" 205 endif 206endf 207 208 209" with dependencies 210" opts.cache_file (caches the checkout and dependency information 211" opts.path_to_nixpkgs or opts.nix_prefetch_{git,hg} 212" opts.plugin_dictionaries: list of any 213" - string 214" - dictionary having key name or names 215" This is so that plugin script files can be loaded/ merged 216fun! nix#ExportPluginsForNix(opts) abort 217 let cache_file = get(a:opts, 'cache_file', '') 218 219 let opts = a:opts 220 221 " set nix_prefetch_* scripts 222 for scm in ['git', 'hg'] 223 if !has_key(opts, 'nix_prefetch_'.scm) 224 let opts['nix_prefetch_'.scm] = a:opts.path_to_nixpkgs.'/pkgs/build-support/fetch'.scm.'/nix-prefetch-'.scm 225 endif 226 endfor 227 228 " create list of names from dictionaries 229 let a:opts.names_to_process = [] 230 for x in a:opts.plugin_dictionaries 231 if type(x) == type('') 232 call add(opts.names_to_process, x) 233 elseif type(x) == type({}) && has_key(x, 'name') 234 call add(opts.names_to_process, x.name) 235 elseif type(x) == type({}) && has_key(x, 'names') 236 call extend(opts.names_to_process, x.names) 237 else 238 throw "unexpected" 239 endif 240 unlet x 241 endfor 242 let a:opts.names_to_export = a:opts.names_to_process 243 244 let cache = (cache_file == '' || !filereadable(cache_file)) ? {} : eval(readfile(cache_file)[0]) 245 let failed = {} 246 while len(opts.names_to_process) > 0 247 let name = opts.names_to_process[0] 248 if get(opts, 'try_catch', 1) 249 try 250 call nix#AddNixDerivation(opts, cache, name) 251 catch /.*/ 252 echom 'failed : '.name.' '.v:exception 253 let failed[name] = v:exception 254 endtry 255 else 256 call nix#AddNixDerivation(opts, cache, name) 257 endif 258 let opts.names_to_process = opts.names_to_process[1:] 259 endwhile 260 echom join(keys(failed), ", ") 261 echom string(failed) 262 263 if cache_file != '' 264 call writefile([string(cache)], cache_file) 265 endif 266 267 enew 268 269 let uniq = {} 270 for x in a:opts.names_to_export 271 let uniq[x] = 1 272 endfor 273 274 for k in sort(keys(uniq)) 275 call append('$', split(cache[k].derivation,"\n")) 276 endfor 277 278 " for VAM users output vam.pluginDictionaries which can be fed to 279 " vim_customizable.customize.vimrc.vam.pluginDictionaries 280 call append('$', ["", "", "", '# vam.pluginDictionaries']) 281 282 let ns = [] 283 for x in a:opts.plugin_dictionaries 284 if type(x) == type("") 285 call add(ns, nix#ToNixAttrName(x)) 286 elseif type(x) == type({}) 287 if has_key(x, 'name') 288 call add(ns, extend({'name': nix#ToNixAttrName(x.name)}, x, "keep")) 289 elseif has_key(x, 'names') 290 call add(ns, extend({'names': map(copy(x.names), 'nix#ToNixAttrName(v:val)')}, x, "keep")) 291 else 292 throw "unexpected" 293 endif 294 else 295 throw "unexpected" 296 endif 297 unlet x 298 endfor 299 300 call append('$', split(nix#ToNix(ns, [], 'nix#TopNixOptsByParent'), "\n")) 301 302 " failures: 303 for [k,v] in items(failed) 304 call append('$', ['# '.k.', failure: '.v]) 305 unlet k v 306 endfor 307endf