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